From 6aab15a94830458b9e6797f2633d3199b5046d42 Mon Sep 17 00:00:00 2001 From: Sejun Park Date: Fri, 24 Nov 2017 17:12:58 +0900 Subject: [PATCH 02/12] Initial version of libomxil-vc4 for RPI3 build: changed build specs. Changed packaging names as below: libomxil-vc4-0.0.1-0.1.armv7l.rpm libomxil-vc4-debuginfo-0.0.1-0.1.armv7l.rpm libomxil-vc4-debugsource-0.0.1-0.1.armv7l.rpm libomxil-vc4-devel-0.0.1-0.1.armv7l.rpm libomxil-vc4-utils-0.0.1-0.1.armv7l.rpm libomxil-vc4-utils-debuginfo-0.0.1-0.1.armv7l.rpm Change-Id: I53d1515c6af3ba427740fc57479608c4e5abd98e Signed-off-by: Hackseung Lee --- .gitignore | 32 + CMakeLists.txt | 132 + COPYING | 21 + LICENCE | 26 + README.md | 6 + buildme | 44 + containers/CMakeLists.txt | 124 + containers/asf/CMakeLists.txt | 19 + containers/asf/asf_reader.c | 2247 ++++++++ containers/asf/asf_writer.c | 577 ++ containers/avi/CMakeLists.txt | 19 + containers/avi/avi_reader.c | 1521 ++++++ containers/avi/avi_writer.c | 1171 ++++ containers/binary/CMakeLists.txt | 19 + containers/binary/binary_reader.c | 267 + containers/binary/binary_writer.c | 160 + containers/containers.h | 746 +++ containers/containers_codecs.h | 214 + containers/containers_types.h | 100 + containers/core/containers.c | 637 +++ containers/core/containers_bits.c | 490 ++ containers/core/containers_bits.h | 344 ++ containers/core/containers_bytestream.h | 389 ++ containers/core/containers_codecs.c | 209 + containers/core/containers_common.h | 73 + containers/core/containers_filters.c | 222 + containers/core/containers_filters.h | 110 + containers/core/containers_index.c | 192 + containers/core/containers_index.h | 82 + containers/core/containers_io.c | 1088 ++++ containers/core/containers_io.h | 229 + containers/core/containers_io_helpers.c | 244 + containers/core/containers_io_helpers.h | 716 +++ containers/core/containers_list.c | 221 + containers/core/containers_list.h | 102 + containers/core/containers_loader.c | 436 ++ containers/core/containers_loader.h | 41 + containers/core/containers_logging.c | 111 + containers/core/containers_logging.h | 77 + containers/core/containers_private.h | 183 + containers/core/containers_time.h | 103 + containers/core/containers_uri.c | 1120 ++++ containers/core/containers_uri.h | 241 + containers/core/containers_utils.c | 366 ++ containers/core/containers_utils.h | 86 + containers/core/containers_waveformat.h | 180 + containers/core/containers_writer_utils.c | 127 + containers/core/containers_writer_utils.h | 96 + containers/core/packetizers.c | 233 + containers/core/packetizers_private.h | 111 + containers/dummy/CMakeLists.txt | 12 + containers/dummy/dummy_writer.c | 137 + containers/flash/CMakeLists.txt | 13 + containers/flash/flv_reader.c | 1174 ++++ containers/h264/avc1_packetizer.c | 343 ++ containers/io/io_file.c | 154 + containers/io/io_http.c | 898 +++ containers/io/io_net.c | 379 ++ containers/io/io_null.c | 91 + containers/io/io_pktfile.c | 261 + containers/metadata/id3/CMakeLists.txt | 13 + containers/metadata/id3/id3_metadata_reader.c | 450 ++ containers/metadata/id3/id3_metadata_strings.h | 179 + containers/mkv/CMakeLists.txt | 13 + containers/mkv/matroska_reader.c | 2323 ++++++++ containers/mp4/CMakeLists.txt | 19 + containers/mp4/mp4_common.h | 128 + containers/mp4/mp4_reader.c | 1879 +++++++ containers/mp4/mp4_writer.c | 1441 +++++ containers/mpeg/CMakeLists.txt | 13 + containers/mpeg/ps_reader.c | 1268 +++++ containers/mpga/CMakeLists.txt | 13 + containers/mpga/mpga_common.h | 147 + containers/mpga/mpga_packetizer.c | 288 + containers/mpga/mpga_reader.c | 618 +++ containers/mpgv/mpgv_packetizer.c | 431 ++ containers/net/net_sockets.h | 263 + containers/net/net_sockets_bsd.c | 117 + containers/net/net_sockets_bsd.h | 43 + containers/net/net_sockets_common.c | 619 +++ containers/net/net_sockets_null.c | 175 + containers/net/net_sockets_priv.h | 85 + containers/net/net_sockets_win32.c | 140 + containers/net/net_sockets_win32.h | 38 + containers/packetizers.h | 159 + containers/pcm/pcm_packetizer.c | 269 + containers/qsynth/CMakeLists.txt | 13 + containers/qsynth/qsynth_reader.c | 482 ++ containers/raw/CMakeLists.txt | 18 + containers/raw/raw_video_common.h | 66 + containers/raw/raw_video_reader.c | 465 ++ containers/raw/raw_video_writer.c | 264 + containers/rcv/CMakeLists.txt | 13 + containers/rcv/rcv_reader.c | 358 ++ containers/rtp/CMakeLists.txt | 17 + containers/rtp/rtp_base64.c | 165 + containers/rtp/rtp_base64.h | 49 + containers/rtp/rtp_h264.c | 803 +++ containers/rtp/rtp_h264.h | 42 + containers/rtp/rtp_mpeg4.c | 788 +++ containers/rtp/rtp_mpeg4.h | 42 + containers/rtp/rtp_priv.h | 111 + containers/rtp/rtp_reader.c | 1158 ++++ containers/rtsp/CMakeLists.txt | 14 + containers/rtsp/rtsp_reader.c | 1983 +++++++ containers/rv9/CMakeLists.txt | 13 + containers/rv9/rv9_reader.c | 335 ++ containers/simple/CMakeLists.txt | 18 + containers/simple/simple_common.h | 42 + containers/simple/simple_reader.c | 599 ++ containers/simple/simple_writer.c | 348 ++ containers/test/CMakeLists.txt | 66 + containers/test/autotest.cpp | 1958 +++++++ containers/test/check_frame_int.c | 348 ++ containers/test/crc_32.c | 227 + containers/test/datagram_receiver.c | 80 + containers/test/datagram_sender.c | 77 + containers/test/dump_pktfile.c | 147 + containers/test/nb_io.h | 47 + containers/test/nb_io_unix.c | 80 + containers/test/nb_io_win32.c | 53 + containers/test/rtp_decoder.c | 449 ++ containers/test/stream_client.c | 145 + containers/test/stream_server.c | 148 + containers/test/test.c | 623 +++ containers/test/test_bits.c | 600 ++ containers/test/test_uri.c | 824 +++ containers/test/uri_pipe.c | 116 + containers/wav/CMakeLists.txt | 13 + containers/wav/wav_reader.c | 366 ++ helpers/dtoverlay/CMakeLists.txt | 25 + helpers/dtoverlay/dtoverlay.c | 2006 +++++++ helpers/dtoverlay/dtoverlay.h | 197 + helpers/v3d/v3d_common.h | 39 + helpers/v3d/v3d_ver.h | 66 + helpers/vc_image/metadata_fourcc.h | 107 + helpers/vc_image/vc_image.h | 804 +++ helpers/vc_image/vc_image_helper.h | 252 + helpers/vc_image/vc_image_metadata.h | 99 + .../android/apps/vidtex/CMakeLists.txt | 12 + host_applications/android/apps/vidtex/applog.h | 48 + host_applications/android/apps/vidtex/launcher.h | 87 + .../android/apps/vidtex/launcher_rpi.c | 87 + .../android/apps/vidtex/launcher_rpi.h | 61 + host_applications/android/apps/vidtex/main.cpp | 115 + host_applications/android/apps/vidtex/svp.c | 637 +++ host_applications/android/apps/vidtex/svp.h | 146 + host_applications/android/apps/vidtex/vidtex.c | 555 ++ host_applications/android/apps/vidtex/vidtex.h | 89 + host_applications/framework/common/host_ilcore.h | 79 + host_applications/framework/common/ilcore.c | 353 ++ host_applications/linux/CMakeLists.txt | 25 + .../linux/apps/dtmerge/CMakeLists.txt | 20 + host_applications/linux/apps/dtmerge/dtmerge.c | 172 + .../linux/apps/dtoverlay/CMakeLists.txt | 33 + .../linux/apps/dtoverlay/dtoverlay-post | 10 + .../linux/apps/dtoverlay/dtoverlay-pre | 10 + .../linux/apps/dtoverlay/dtoverlay_main.c | 1073 ++++ host_applications/linux/apps/dtoverlay/utils.c | 463 ++ host_applications/linux/apps/dtoverlay/utils.h | 74 + host_applications/linux/apps/gencmd/CMakeLists.txt | 20 + host_applications/linux/apps/gencmd/gencmd.c | 147 + .../linux/apps/hello_pi/CMakeLists.txt | 31 + .../linux/apps/hello_pi/Makefile.include | 28 + host_applications/linux/apps/hello_pi/README | 21 + .../linux/apps/hello_pi/hello_audio/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_audio/Makefile | 6 + .../linux/apps/hello_pi/hello_audio/audio.c | 426 ++ .../linux/apps/hello_pi/hello_audio/audioplay.h | 159 + .../linux/apps/hello_pi/hello_audio/sinewave.c | 160 + .../apps/hello_pi/hello_dispmanx/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_dispmanx/Makefile | 5 + .../linux/apps/hello_pi/hello_dispmanx/dispmanx.c | 163 + .../apps/hello_pi/hello_encode/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_encode/Makefile | 6 + .../linux/apps/hello_pi/hello_encode/encode.c | 320 ++ .../linux/apps/hello_pi/hello_fft/gpu_fft.c | 135 + .../linux/apps/hello_pi/hello_fft/gpu_fft.h | 101 + .../linux/apps/hello_pi/hello_fft/gpu_fft.txt | 159 + .../linux/apps/hello_pi/hello_fft/gpu_fft_base.c | 190 + .../apps/hello_pi/hello_fft/gpu_fft_shaders.c | 102 + .../linux/apps/hello_pi/hello_fft/gpu_fft_trans.c | 95 + .../linux/apps/hello_pi/hello_fft/gpu_fft_trans.h | 45 + .../apps/hello_pi/hello_fft/gpu_fft_twiddles.c | 315 ++ .../linux/apps/hello_pi/hello_fft/hello_fft.c | 109 + .../linux/apps/hello_pi/hello_fft/hello_fft_2d.c | 135 + .../apps/hello_pi/hello_fft/hello_fft_2d_bitmap.h | 53 + .../apps/hello_pi/hello_fft/hex/shader_1024k.hex | 948 ++++ .../apps/hello_pi/hello_fft/hex/shader_128k.hex | 735 +++ .../apps/hello_pi/hello_fft/hex/shader_16k.hex | 688 +++ .../apps/hello_pi/hello_fft/hex/shader_1k.hex | 523 ++ .../apps/hello_pi/hello_fft/hex/shader_2048k.hex | 1353 +++++ .../apps/hello_pi/hello_fft/hex/shader_256.hex | 359 ++ .../apps/hello_pi/hello_fft/hex/shader_256k.hex | 861 +++ .../apps/hello_pi/hello_fft/hex/shader_2k.hex | 765 +++ .../apps/hello_pi/hello_fft/hex/shader_32k.hex | 697 +++ .../apps/hello_pi/hello_fft/hex/shader_4096k.hex | 1523 ++++++ .../apps/hello_pi/hello_fft/hex/shader_4k.hex | 514 ++ .../apps/hello_pi/hello_fft/hex/shader_512.hex | 494 ++ .../apps/hello_pi/hello_fft/hex/shader_512k.hex | 983 ++++ .../apps/hello_pi/hello_fft/hex/shader_64k.hex | 940 ++++ .../apps/hello_pi/hello_fft/hex/shader_8k.hex | 603 ++ .../apps/hello_pi/hello_fft/hex/shader_trans.hex | 126 + .../linux/apps/hello_pi/hello_fft/mailbox.c | 262 + .../linux/apps/hello_pi/hello_fft/mailbox.h | 47 + .../linux/apps/hello_pi/hello_fft/makefile | 36 + .../apps/hello_pi/hello_fft/qasm/gpu_fft.qinc | 509 ++ .../hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm | 319 ++ .../apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm | 319 ++ .../apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm | 282 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm | 231 + .../hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm | 336 ++ .../hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc | 91 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm | 233 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm | 326 ++ .../apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm | 265 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm | 271 + .../hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm | 356 ++ .../apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm | 276 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm | 240 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm | 329 ++ .../apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm | 306 ++ .../apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm | 276 + .../apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc | 112 + .../hello_pi/hello_fft/qasm/gpu_fft_trans.qasm | 133 + .../linux/apps/hello_pi/hello_font/CMakeLists.txt | 9 + .../linux/apps/hello_pi/hello_font/Makefile | 7 + .../linux/apps/hello_pi/hello_font/Vera.ttf | Bin 0 -> 65932 bytes .../linux/apps/hello_pi/hello_font/main.c | 138 + .../linux/apps/hello_pi/hello_jpeg/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_jpeg/Makefile | 6 + .../linux/apps/hello_pi/hello_jpeg/jpeg.c | 696 +++ .../linux/apps/hello_pi/hello_jpeg/jpeg.h | 68 + .../linux/apps/hello_pi/hello_mmal_encode/Makefile | 6 + .../apps/hello_pi/hello_mmal_encode/mmal_encode.c | 261 + .../apps/hello_pi/hello_teapot/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_teapot/Makefile | 7 + .../linux/apps/hello_pi/hello_teapot/README.md | 4 + .../hello_teapot/cube_texture_and_coords.h | 100 + .../linux/apps/hello_pi/hello_teapot/models.c | 515 ++ .../linux/apps/hello_pi/hello_teapot/models.h | 36 + .../apps/hello_pi/hello_teapot/teapot.obj.dat | Bin 0 -> 635016 bytes .../linux/apps/hello_pi/hello_teapot/triangle.c | 468 ++ .../linux/apps/hello_pi/hello_teapot/triangle.h | 30 + .../linux/apps/hello_pi/hello_teapot/video.c | 266 + .../linux/apps/hello_pi/hello_tiger/CMakeLists.txt | 9 + .../linux/apps/hello_pi/hello_tiger/Makefile | 8 + .../linux/apps/hello_pi/hello_tiger/license.txt | 53 + .../linux/apps/hello_pi/hello_tiger/main.c | 533 ++ .../linux/apps/hello_pi/hello_tiger/readme.txt | 263 + .../linux/apps/hello_pi/hello_tiger/tiger.c | 1952 +++++++ .../linux/apps/hello_pi/hello_tiger/tiger.h | 45 + .../apps/hello_pi/hello_triangle/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_triangle/Makefile | 5 + .../hello_triangle/cube_texture_and_coords.h | 100 + .../linux/apps/hello_pi/hello_triangle/triangle.c | 551 ++ .../apps/hello_pi/hello_triangle2/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_triangle2/Makefile | 5 + .../apps/hello_pi/hello_triangle2/triangle2.c | 509 ++ .../linux/apps/hello_pi/hello_video/CMakeLists.txt | 10 + .../linux/apps/hello_pi/hello_video/Makefile | 6 + .../linux/apps/hello_pi/hello_video/README | 1 + .../hello_pi/hello_video/omx_comp_debug_levels.h | 77 + .../linux/apps/hello_pi/hello_video/queue.c | 134 + .../linux/apps/hello_pi/hello_video/queue.h | 92 + .../linux/apps/hello_pi/hello_video/tags | 10 + .../linux/apps/hello_pi/hello_video/tsemaphore.c | 111 + .../linux/apps/hello_pi/hello_video/tsemaphore.h | 87 + .../apps/hello_pi/hello_video/user_debug_levels.h | 73 + .../linux/apps/hello_pi/hello_video/video.c | 1055 ++++ .../linux/apps/hello_pi/hello_video/video.h | 40 + .../apps/hello_pi/hello_videocube/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_videocube/Makefile | 7 + .../linux/apps/hello_pi/hello_videocube/README.md | 4 + .../hello_videocube/cube_texture_and_coords.h | 100 + .../linux/apps/hello_pi/hello_videocube/triangle.c | 481 ++ .../linux/apps/hello_pi/hello_videocube/triangle.h | 30 + .../linux/apps/hello_pi/hello_videocube/video.c | 266 + .../linux/apps/hello_pi/hello_world/CMakeLists.txt | 8 + .../linux/apps/hello_pi/hello_world/Makefile | 5 + .../linux/apps/hello_pi/hello_world/world.c | 36 + .../linux/apps/hello_pi/libs/ilclient/Makefile | 5 + .../linux/apps/hello_pi/libs/ilclient/ilclient.c | 1836 +++++++ .../linux/apps/hello_pi/libs/ilclient/ilclient.h | 1039 ++++ .../linux/apps/hello_pi/libs/ilclient/ilcore.c | 308 ++ .../linux/apps/hello_pi/libs/vgfont/Makefile | 7 + .../linux/apps/hello_pi/libs/vgfont/font.c | 355 ++ .../linux/apps/hello_pi/libs/vgfont/graphics.c | 1609 ++++++ .../apps/hello_pi/libs/vgfont/graphics_x_private.h | 366 ++ .../linux/apps/hello_pi/libs/vgfont/vgfont.h | 136 + .../linux/apps/hello_pi/libs/vgfont/vgft.c | 424 ++ .../linux/apps/hello_pi/libs/vgfont/vgft.h | 70 + host_applications/linux/apps/hello_pi/rebuild.sh | 34 + .../linux/apps/raspicam/CMakeLists.txt | 36 + host_applications/linux/apps/raspicam/Doxyfile | 1869 +++++++ host_applications/linux/apps/raspicam/Makefile | 6 + host_applications/linux/apps/raspicam/README.md | 477 ++ host_applications/linux/apps/raspicam/RaspiCLI.c | 155 + host_applications/linux/apps/raspicam/RaspiCLI.h | 56 + .../linux/apps/raspicam/RaspiCamControl.c | 1773 ++++++ .../linux/apps/raspicam/RaspiCamControl.h | 236 + .../linux/apps/raspicam/RaspiPreview.c | 281 + .../linux/apps/raspicam/RaspiPreview.h | 67 + host_applications/linux/apps/raspicam/RaspiStill.c | 2169 ++++++++ .../linux/apps/raspicam/RaspiStillYUV.c | 1472 +++++ host_applications/linux/apps/raspicam/RaspiTex.c | 754 +++ host_applications/linux/apps/raspicam/RaspiTex.h | 192 + .../linux/apps/raspicam/RaspiTexUtil.c | 597 ++ .../linux/apps/raspicam/RaspiTexUtil.h | 116 + host_applications/linux/apps/raspicam/RaspiVid.c | 3018 +++++++++++ .../linux/apps/raspicam/RaspiVidYUV.c | 1651 ++++++ .../raspicam/gl_scenes/cube_texture_and_coords.h | 100 + .../linux/apps/raspicam/gl_scenes/mirror.c | 123 + .../linux/apps/raspicam/gl_scenes/mirror.h | 37 + .../linux/apps/raspicam/gl_scenes/models.c | 521 ++ .../linux/apps/raspicam/gl_scenes/models.h | 36 + .../linux/apps/raspicam/gl_scenes/sobel.c | 192 + .../linux/apps/raspicam/gl_scenes/sobel.h | 37 + .../linux/apps/raspicam/gl_scenes/square.c | 121 + .../linux/apps/raspicam/gl_scenes/square.h | 37 + .../linux/apps/raspicam/gl_scenes/teapot.c | 332 ++ .../linux/apps/raspicam/gl_scenes/teapot.h | 35 + .../linux/apps/raspicam/gl_scenes/vcsm_square.c | 332 ++ .../linux/apps/raspicam/gl_scenes/vcsm_square.h | 37 + .../linux/apps/raspicam/gl_scenes/yuv.c | 145 + .../linux/apps/raspicam/gl_scenes/yuv.h | 37 + .../linux/apps/raspicam/imv_examples/README.md | 39 + .../linux/apps/raspicam/imv_examples/imv2pgm.c | 88 + .../linux/apps/raspicam/imv_examples/imv2txt.c | 86 + .../linux/apps/raspicam/imv_examples/plot.par | 353 ++ host_applications/linux/apps/raspicam/tga.c | 113 + host_applications/linux/apps/raspicam/tga.h | 73 + host_applications/linux/apps/smem/CMakeLists.txt | 20 + host_applications/linux/apps/smem/smem.c | 449 ++ .../linux/apps/tvservice/CMakeLists.txt | 5 + host_applications/linux/apps/tvservice/tvservice.c | 1156 ++++ .../linux/apps/vcmailbox/CMakeLists.txt | 5 + host_applications/linux/apps/vcmailbox/vcmailbox.c | 107 + .../linux/kernel_headers/vmcs_sm_ioctl.h | 292 + .../linux/libs/bcm_host/CMakeLists.txt | 25 + host_applications/linux/libs/bcm_host/bcm_host.c | 168 + .../linux/libs/bcm_host/include/bcm_host.h | 61 + .../linux/libs/debug_sym/CMakeLists.txt | 15 + host_applications/linux/libs/debug_sym/debug_sym.c | 834 +++ host_applications/linux/libs/debug_sym/debug_sym.h | 215 + host_applications/linux/libs/sm/CMakeLists.txt | 18 + host_applications/linux/libs/sm/user-vcsm.c | 1700 ++++++ host_applications/linux/libs/sm/user-vcsm.h | 463 ++ host_applications/vmcs/test_apps/mmalcam/mmalcam.c | 299 + host_applications/vmcs/test_apps/mmalcam/mmalcam.h | 98 + .../vmcs/test_apps/mmalcam/viewfinder.c | 1479 +++++ .../vmcs/test_apps/mmalplay/mmalplay.c | 512 ++ .../vmcs/test_apps/mmalplay/mmalplay.h | 116 + .../vmcs/test_apps/mmalplay/playback.c | 1249 +++++ host_applications/vmcs/vmcs_config.h.in | 29 + host_support/include/vc_debug_sym.h | 186 + host_support/include/vc_mem.h | 52 + interface/khronos/CMakeLists.txt | 95 + .../khrn_client_platform_filler_abstract.h | 113 + .../direct/khrn_client_platform_filler_direct.h | 113 + interface/khronos/common/khrn_client.c | 612 +++ interface/khronos/common/khrn_client.h | 485 ++ interface/khronos/common/khrn_client_cache.c | 382 ++ interface/khronos/common/khrn_client_cache.h | 92 + interface/khronos/common/khrn_client_check_types.h | 87 + interface/khronos/common/khrn_client_cr.c | 140 + .../khronos/common/khrn_client_global_image_map.c | 55 + .../khronos/common/khrn_client_global_image_map.h | 47 + interface/khronos/common/khrn_client_mangle.h | 467 ++ interface/khronos/common/khrn_client_platform.h | 331 ++ interface/khronos/common/khrn_client_pointermap.c | 37 + interface/khronos/common/khrn_client_pointermap.h | 47 + interface/khronos/common/khrn_client_rpc.h | 838 +++ interface/khronos/common/khrn_client_unmangle.h | 467 ++ interface/khronos/common/khrn_client_vector.c | 82 + interface/khronos/common/khrn_client_vector.h | 43 + interface/khronos/common/khrn_int_color.h | 89 + interface/khronos/common/khrn_int_common.h | 177 + interface/khronos/common/khrn_int_generic_map.c | 341 ++ interface/khronos/common/khrn_int_generic_map.h | 205 + interface/khronos/common/khrn_int_hash.c | 310 ++ interface/khronos/common/khrn_int_hash.h | 161 + interface/khronos/common/khrn_int_hash_asm.s | 229 + interface/khronos/common/khrn_int_ids.h | 440 ++ interface/khronos/common/khrn_int_image.c | 299 + interface/khronos/common/khrn_int_image.h | 383 ++ interface/khronos/common/khrn_int_math.h | 182 + interface/khronos/common/khrn_int_misc_impl.h | 40 + interface/khronos/common/khrn_int_util.c | 163 + interface/khronos/common/khrn_int_util.h | 511 ++ interface/khronos/common/khrn_int_util_cr.h | 141 + interface/khronos/common/khrn_options.c | 98 + interface/khronos/common/khrn_options.h | 56 + .../common/linux/khrn_client_platform_linux.c | 829 +++ .../khronos/common/linux/khrn_client_rpc_linux.c | 528 ++ .../common/openwfc/khrn_client_platform_openwfc.c | 328 ++ .../common/vcos/khrn_client_platform_filler_vcos.h | 122 + .../khrn_client_platform_filler_vcos_vchiq.h | 116 + interface/khronos/egl/egl_client.c | 2511 +++++++++ interface/khronos/egl/egl_client_config.c | 410 ++ interface/khronos/egl/egl_client_config.h | 146 + interface/khronos/egl/egl_client_config_cr.c | 776 +++ interface/khronos/egl/egl_client_context.c | 341 ++ interface/khronos/egl/egl_client_context.h | 81 + interface/khronos/egl/egl_client_cr.c | 579 ++ interface/khronos/egl/egl_client_get_proc.c | 262 + interface/khronos/egl/egl_client_surface.c | 918 ++++ interface/khronos/egl/egl_client_surface.h | 346 ++ interface/khronos/egl/egl_int.h | 57 + interface/khronos/egl/egl_int_config.h | 34 + interface/khronos/egl/egl_int_impl.h | 181 + .../khronos/ext/egl_brcm_driver_monitor_client.c | 148 + .../khronos/ext/egl_brcm_driver_monitor_client.h | 29 + interface/khronos/ext/egl_brcm_flush_client.c | 50 + .../khronos/ext/egl_brcm_global_image_client.c | 221 + .../khronos/ext/egl_brcm_perf_monitor_client.c | 149 + .../khronos/ext/egl_brcm_perf_monitor_client.h | 29 + interface/khronos/ext/egl_khr_image_client.c | 530 ++ .../khronos/ext/egl_khr_lock_surface_client.c | 190 + interface/khronos/ext/egl_khr_sync_client.c | 405 ++ interface/khronos/ext/egl_khr_sync_client.h | 35 + interface/khronos/ext/egl_openmaxil_client.c | 45 + interface/khronos/ext/egl_openmaxil_client.h | 31 + interface/khronos/ext/ext_gl_debug_marker.c | 71 + interface/khronos/ext/gl_oes_draw_texture_client.c | 95 + interface/khronos/ext/gl_oes_egl_image_client.c | 152 + interface/khronos/ext/gl_oes_framebuffer_object.c | 134 + interface/khronos/ext/gl_oes_map_buffer.c | 195 + .../khronos/ext/gl_oes_matrix_palette_client.c | 132 + interface/khronos/ext/gl_oes_query_matrix_client.c | 60 + interface/khronos/glxx/gl11_int_config.h | 49 + interface/khronos/glxx/gl11_int_impl.h | 129 + interface/khronos/glxx/gl20_int_impl.h | 118 + interface/khronos/glxx/glxx_client.c | 5733 ++++++++++++++++++++ interface/khronos/glxx/glxx_client.h | 164 + interface/khronos/glxx/glxx_int_attrib.h | 143 + interface/khronos/glxx/glxx_int_config.h | 42 + interface/khronos/glxx/glxx_int_impl.h | 142 + interface/khronos/include/EGL/egl.h | 329 ++ interface/khronos/include/EGL/eglext.h | 205 + interface/khronos/include/EGL/eglext_android.h | 99 + interface/khronos/include/EGL/eglext_brcm.h | 205 + interface/khronos/include/EGL/eglext_nvidia.h | 54 + interface/khronos/include/EGL/eglplatform.h | 205 + interface/khronos/include/GLES/gl.h | 798 +++ interface/khronos/include/GLES/glext.h | 1147 ++++ interface/khronos/include/GLES/glplatform.h | 64 + interface/khronos/include/GLES2/gl2.h | 649 +++ interface/khronos/include/GLES2/gl2ext.h | 1218 +++++ interface/khronos/include/GLES2/gl2platform.h | 64 + interface/khronos/include/KHR/khrplatform.h | 295 + interface/khronos/include/VG/openvg.h | 745 +++ interface/khronos/include/VG/vgext.h | 233 + interface/khronos/include/VG/vgplatform.h | 86 + interface/khronos/include/VG/vgu.h | 131 + interface/khronos/include/WF/wfc.h | 275 + interface/khronos/include/WF/wfcplatform.h | 71 + interface/khronos/vg/vg_client.c | 5666 +++++++++++++++++++ interface/khronos/vg/vg_client.h | 229 + interface/khronos/vg/vg_int.h | 47 + interface/khronos/vg/vg_int_config.h | 57 + interface/khronos/vg/vg_int_impl.h | 115 + interface/khronos/vg/vg_int_mat3x3.c | 646 +++ interface/khronos/vg/vg_int_mat3x3.h | 156 + interface/khronos/vg/vg_int_util.h | 280 + interface/khronos/wf/wfc_client.c | 2597 +++++++++ interface/khronos/wf/wfc_client_ipc.c | 521 ++ interface/khronos/wf/wfc_client_ipc.h | 82 + interface/khronos/wf/wfc_client_server_api.c | 673 +++ interface/khronos/wf/wfc_client_stream.c | 1043 ++++ interface/khronos/wf/wfc_client_stream.h | 135 + interface/khronos/wf/wfc_int.h | 253 + interface/khronos/wf/wfc_ipc.h | 350 ++ interface/khronos/wf/wfc_server_api.h | 302 ++ interface/mmal/CMakeLists.txt | 46 + interface/mmal/client/CMakeLists.txt | 1 + interface/mmal/client/brcmjpeg/CMakeLists.txt | 6 + interface/mmal/client/brcmjpeg/brcmjpeg.c | 914 ++++ interface/mmal/client/brcmjpeg/brcmjpeg.h | 152 + interface/mmal/client/brcmjpeg/brcmjpeg_test.c | 236 + interface/mmal/components/CMakeLists.txt | 34 + interface/mmal/components/aaf_audio_render.cpp | 504 ++ interface/mmal/components/aggregator.c | 188 + interface/mmal/components/android_media_codec.cpp | 861 +++ interface/mmal/components/artificial_camera.c | 287 + interface/mmal/components/avcodec_audio_decoder.c | 607 +++ interface/mmal/components/avcodec_video_decoder.c | 586 ++ interface/mmal/components/clock.c | 900 +++ interface/mmal/components/container_reader.c | 1000 ++++ interface/mmal/components/copy.c | 327 ++ interface/mmal/components/null_sink.c | 125 + interface/mmal/components/passthrough.c | 284 + interface/mmal/components/scheduler.c | 485 ++ interface/mmal/components/sdl_audio_render.c | 278 + interface/mmal/components/sdl_video_render.c | 394 ++ interface/mmal/components/spdif.c | 496 ++ interface/mmal/components/splitter.c | 345 ++ interface/mmal/core/CMakeLists.txt | 25 + interface/mmal/core/mmal_buffer.c | 188 + interface/mmal/core/mmal_buffer_private.h | 86 + interface/mmal/core/mmal_clock.c | 892 +++ interface/mmal/core/mmal_clock_private.h | 204 + interface/mmal/core/mmal_component.c | 780 +++ interface/mmal/core/mmal_component_private.h | 169 + interface/mmal/core/mmal_core_private.h | 40 + interface/mmal/core/mmal_events.c | 138 + interface/mmal/core/mmal_events_private.h | 67 + interface/mmal/core/mmal_format.c | 183 + interface/mmal/core/mmal_logging.c | 43 + interface/mmal/core/mmal_pool.c | 303 ++ interface/mmal/core/mmal_port.c | 1508 +++++ interface/mmal/core/mmal_port_clock.c | 803 +++ interface/mmal/core/mmal_port_private.h | 213 + interface/mmal/core/mmal_queue.c | 198 + interface/mmal/mmal.h | 390 ++ interface/mmal/mmal_buffer.h | 251 + interface/mmal/mmal_clock.h | 202 + interface/mmal/mmal_common.h | 83 + interface/mmal/mmal_component.h | 148 + interface/mmal/mmal_encodings.h | 267 + interface/mmal/mmal_events.h | 109 + interface/mmal/mmal_format.h | 223 + interface/mmal/mmal_logging.h | 71 + interface/mmal/mmal_metadata.h | 84 + interface/mmal/mmal_parameters.h | 194 + interface/mmal/mmal_parameters_audio.h | 66 + interface/mmal/mmal_parameters_camera.h | 967 ++++ interface/mmal/mmal_parameters_clock.h | 88 + interface/mmal/mmal_parameters_common.h | 191 + interface/mmal/mmal_parameters_video.h | 512 ++ interface/mmal/mmal_pool.h | 167 + interface/mmal/mmal_port.h | 286 + interface/mmal/mmal_queue.h | 116 + interface/mmal/mmal_types.h | 100 + interface/mmal/openmaxil/CMakeLists.txt | 20 + interface/mmal/openmaxil/mmalomx.h | 130 + interface/mmal/openmaxil/mmalomx_buffer.c | 235 + interface/mmal/openmaxil/mmalomx_buffer.h | 39 + interface/mmal/openmaxil/mmalomx_commands.c | 462 ++ interface/mmal/openmaxil/mmalomx_commands.h | 85 + interface/mmal/openmaxil/mmalomx_core.c | 1577 ++++++ interface/mmal/openmaxil/mmalomx_logging.c | 176 + interface/mmal/openmaxil/mmalomx_logging.h | 46 + interface/mmal/openmaxil/mmalomx_marks.c | 101 + interface/mmal/openmaxil/mmalomx_marks.h | 36 + interface/mmal/openmaxil/mmalomx_parameters.c | 578 ++ interface/mmal/openmaxil/mmalomx_parameters.h | 37 + interface/mmal/openmaxil/mmalomx_registry.c | 159 + interface/mmal/openmaxil/mmalomx_registry.h | 41 + interface/mmal/openmaxil/mmalomx_roles.c | 210 + interface/mmal/openmaxil/mmalomx_roles.h | 61 + interface/mmal/openmaxil/mmalomx_util_params.c | 193 + interface/mmal/openmaxil/mmalomx_util_params.h | 114 + .../mmal/openmaxil/mmalomx_util_params_audio.c | 34 + .../mmal/openmaxil/mmalomx_util_params_camera.c | 556 ++ .../mmal/openmaxil/mmalomx_util_params_common.h | 133 + .../mmal/openmaxil/mmalomx_util_params_misc.c | 157 + .../mmal/openmaxil/mmalomx_util_params_video.c | 277 + interface/mmal/test/CMakeLists.txt | 29 + interface/mmal/test/examples/example_basic_1.c | 241 + interface/mmal/test/examples/example_basic_2.c | 375 ++ interface/mmal/test/examples/example_connections.c | 196 + interface/mmal/test/examples/example_graph.c | 98 + interface/mmal/util/CMakeLists.txt | 28 + interface/mmal/util/mmal_component_wrapper.c | 368 ++ interface/mmal/util/mmal_component_wrapper.h | 157 + interface/mmal/util/mmal_connection.c | 529 ++ interface/mmal/util/mmal_connection.h | 230 + interface/mmal/util/mmal_default_components.h | 92 + interface/mmal/util/mmal_graph.c | 1560 ++++++ interface/mmal/util/mmal_graph.h | 243 + interface/mmal/util/mmal_il.c | 1022 ++++ interface/mmal/util/mmal_il.h | 214 + interface/mmal/util/mmal_list.c | 221 + interface/mmal/util/mmal_list.h | 127 + interface/mmal/util/mmal_param_convert.c | 176 + interface/mmal/util/mmal_param_convert.h | 92 + interface/mmal/util/mmal_util.c | 473 ++ interface/mmal/util/mmal_util.h | 191 + interface/mmal/util/mmal_util_params.c | 223 + interface/mmal/util/mmal_util_params.h | 210 + interface/mmal/util/mmal_util_rational.c | 158 + interface/mmal/util/mmal_util_rational.h | 127 + interface/mmal/vc/CMakeLists.txt | 26 + interface/mmal/vc/mmal_vc_api.c | 1505 +++++ interface/mmal/vc/mmal_vc_api.h | 239 + interface/mmal/vc/mmal_vc_api_drm.c | 77 + interface/mmal/vc/mmal_vc_api_drm.h | 55 + interface/mmal/vc/mmal_vc_client.c | 827 +++ interface/mmal/vc/mmal_vc_client_priv.h | 80 + interface/mmal/vc/mmal_vc_dbglog.h | 160 + interface/mmal/vc/mmal_vc_diag.c | 880 +++ interface/mmal/vc/mmal_vc_msgnames.c | 78 + interface/mmal/vc/mmal_vc_msgnames.h | 37 + interface/mmal/vc/mmal_vc_msgs.h | 535 ++ interface/mmal/vc/mmal_vc_opaque_alloc.c | 87 + interface/mmal/vc/mmal_vc_opaque_alloc.h | 73 + interface/mmal/vc/mmal_vc_shm.c | 241 + interface/mmal/vc/mmal_vc_shm.h | 62 + interface/peer/vc_vchi_dispmanx_common.h | 85 + interface/vchi/common/endian.h | 44 + interface/vchi/connections/connection.h | 324 ++ interface/vchi/message_drivers/message.h | 197 + interface/vchi/vchi.h | 382 ++ interface/vchi/vchi_cfg.h | 222 + interface/vchi/vchi_cfg_internal.h | 65 + interface/vchi/vchi_common.h | 170 + interface/vchi/vchi_mh.h | 36 + interface/vchiq_arm/CMakeLists.txt | 20 + interface/vchiq_arm/vchiq.h | 36 + interface/vchiq_arm/vchiq_cfg.h | 63 + interface/vchiq_arm/vchiq_if.h | 200 + interface/vchiq_arm/vchiq_ioctl.h | 116 + interface/vchiq_arm/vchiq_lib.c | 1764 ++++++ interface/vchiq_arm/vchiq_test.c | 1766 ++++++ interface/vchiq_arm/vchiq_test.h | 135 + interface/vchiq_arm/vchiq_test_if.h | 35 + interface/vchiq_arm/vchiq_util.c | 111 + interface/vchiq_arm/vchiq_util.h | 65 + interface/vcos/CMakeLists.txt | 68 + interface/vcos/generic/CMakeLists.txt | 21 + interface/vcos/generic/vcos_abort.c | 85 + interface/vcos/generic/vcos_cmd.c | 722 +++ interface/vcos/generic/vcos_common.h | 96 + interface/vcos/generic/vcos_deprecated.h | 36 + interface/vcos/generic/vcos_generic_blockpool.c | 568 ++ interface/vcos/generic/vcos_generic_blockpool.h | 294 + interface/vcos/generic/vcos_generic_event_flags.c | 320 ++ interface/vcos/generic/vcos_generic_event_flags.h | 127 + interface/vcos/generic/vcos_generic_named_sem.c | 268 + interface/vcos/generic/vcos_generic_named_sem.h | 101 + .../vcos/generic/vcos_generic_quickslow_mutex.h | 95 + .../vcos/generic/vcos_generic_reentrant_mtx.c | 76 + .../vcos/generic/vcos_generic_reentrant_mtx.h | 95 + interface/vcos/generic/vcos_generic_safe_string.c | 92 + interface/vcos/generic/vcos_generic_tls.h | 164 + interface/vcos/generic/vcos_init.c | 84 + .../vcos/generic/vcos_joinable_thread_from_plain.h | 229 + interface/vcos/generic/vcos_latch_from_sem.h | 68 + interface/vcos/generic/vcos_logcat.c | 571 ++ interface/vcos/generic/vcos_mem_from_malloc.c | 98 + interface/vcos/generic/vcos_mem_from_malloc.h | 80 + interface/vcos/generic/vcos_msgqueue.c | 389 ++ .../vcos/generic/vcos_mutexes_are_reentrant.h | 88 + interface/vcos/generic/vcos_thread_reaper.h | 55 + interface/vcos/glibc/vcos_backtrace.c | 56 + interface/vcos/pthreads/CMakeLists.txt | 46 + interface/vcos/pthreads/vcos_dlfcn.c | 71 + interface/vcos/pthreads/vcos_futex_mutex.h | 102 + interface/vcos/pthreads/vcos_platform.h | 828 +++ interface/vcos/pthreads/vcos_platform_types.h | 71 + interface/vcos/pthreads/vcos_pthreads.c | 897 +++ interface/vcos/user_nodefs.h | 47 + interface/vcos/vcos.h | 226 + interface/vcos/vcos_assert.h | 324 ++ interface/vcos/vcos_atomic_flags.h | 92 + interface/vcos/vcos_attr.h | 153 + interface/vcos/vcos_blockpool.h | 171 + interface/vcos/vcos_build_info.h | 32 + interface/vcos/vcos_cfg.h | 126 + interface/vcos/vcos_cmd.h | 120 + interface/vcos/vcos_ctype.h | 49 + interface/vcos/vcos_dlfcn.h | 86 + interface/vcos/vcos_event.h | 117 + interface/vcos/vcos_event_flags.h | 118 + interface/vcos/vcos_init.h | 110 + interface/vcos/vcos_inttypes.h | 49 + interface/vcos/vcos_isr.h | 90 + interface/vcos/vcos_legacy_isr.h | 102 + interface/vcos/vcos_logging.h | 315 ++ interface/vcos/vcos_logging_control.h | 28 + interface/vcos/vcos_lowlevel_thread.h | 129 + interface/vcos/vcos_mem.h | 101 + interface/vcos/vcos_mempool.h | 109 + interface/vcos/vcos_msgqueue.h | 280 + interface/vcos/vcos_mutex.h | 112 + interface/vcos/vcos_named_semaphore.h | 113 + interface/vcos/vcos_once.h | 62 + interface/vcos/vcos_queue.h | 105 + interface/vcos/vcos_quickslow_mutex.h | 101 + interface/vcos/vcos_reentrant_mutex.h | 86 + interface/vcos/vcos_semaphore.h | 158 + interface/vcos/vcos_stdbool.h | 47 + interface/vcos/vcos_stdint.h | 107 + interface/vcos/vcos_string.h | 129 + interface/vcos/vcos_thread.h | 282 + interface/vcos/vcos_thread_attr.h | 96 + interface/vcos/vcos_timer.h | 117 + interface/vcos/vcos_tls.h | 84 + interface/vcos/vcos_types.h | 293 + interface/vctypes/vc_display_types.h | 114 + interface/vctypes/vc_image_structs.h | 245 + interface/vctypes/vc_image_types.h | 173 + interface/vmcs_host/CMakeLists.txt | 35 + interface/vmcs_host/khronos/IL/OMX_Audio.h | 1391 +++++ interface/vmcs_host/khronos/IL/OMX_Broadcom.h | 2685 +++++++++ interface/vmcs_host/khronos/IL/OMX_Component.h | 579 ++ interface/vmcs_host/khronos/IL/OMX_Core.h | 1467 +++++ interface/vmcs_host/khronos/IL/OMX_ILCS.h | 65 + interface/vmcs_host/khronos/IL/OMX_IVCommon.h | 1107 ++++ interface/vmcs_host/khronos/IL/OMX_Image.h | 347 ++ interface/vmcs_host/khronos/IL/OMX_Index.h | 549 ++ interface/vmcs_host/khronos/IL/OMX_Other.h | 347 ++ interface/vmcs_host/khronos/IL/OMX_Types.h | 372 ++ interface/vmcs_host/khronos/IL/OMX_Video.h | 1082 ++++ interface/vmcs_host/linux/vcfiled/CMakeLists.txt | 33 + .../vmcs_host/linux/vcfiled/etc/init.d/vcfiled | 132 + interface/vmcs_host/linux/vcfiled/vcfiled.c | 220 + interface/vmcs_host/linux/vcfiled/vcfiled_check.c | 169 + interface/vmcs_host/linux/vcfiled/vcfiled_check.h | 48 + .../vmcs_host/linux/vcfiled/vcfiled_lock_test.c | 82 + interface/vmcs_host/linux/vcfilesys.c | 1131 ++++ interface/vmcs_host/linux/vchost_config.h | 61 + interface/vmcs_host/linux/vcmisc.c | 33 + interface/vmcs_host/vc_cec.h | 513 ++ interface/vmcs_host/vc_cecservice.h | 515 ++ interface/vmcs_host/vc_cecservice_defs.h | 182 + interface/vmcs_host/vc_cma.h | 70 + interface/vmcs_host/vc_dispmanx.h | 142 + interface/vmcs_host/vc_dispmanx_types.h | 228 + interface/vmcs_host/vc_dispservice_defs.h | 250 + interface/vmcs_host/vc_dispservice_x_defs.h | 276 + interface/vmcs_host/vc_fileservice_defs.h | 119 + interface/vmcs_host/vc_gencmd_defs.h | 36 + interface/vmcs_host/vc_hdmi.h | 545 ++ interface/vmcs_host/vc_hdmi_property.h | 137 + interface/vmcs_host/vc_ilcs_defs.h | 288 + interface/vmcs_host/vc_imageconv_defs.h | 51 + interface/vmcs_host/vc_sdtv.h | 147 + interface/vmcs_host/vc_service_common.c | 57 + interface/vmcs_host/vc_service_common.h | 46 + interface/vmcs_host/vc_tvservice.h | 524 ++ interface/vmcs_host/vc_tvservice_defs.h | 357 ++ interface/vmcs_host/vc_vchi_audioserv_defs.h | 165 + interface/vmcs_host/vc_vchi_bufman.h | 116 + interface/vmcs_host/vc_vchi_bufman_defs.h | 136 + interface/vmcs_host/vc_vchi_cecservice.c | 1365 +++++ interface/vmcs_host/vc_vchi_dispmanx.c | 1321 +++++ interface/vmcs_host/vc_vchi_dispmanx.h | 69 + interface/vmcs_host/vc_vchi_fileservice_defs.h | 75 + interface/vmcs_host/vc_vchi_filesys.c | 2509 +++++++++ interface/vmcs_host/vc_vchi_filesys.h | 184 + interface/vmcs_host/vc_vchi_gencmd.c | 532 ++ interface/vmcs_host/vc_vchi_gencmd.h | 88 + interface/vmcs_host/vc_vchi_gpuserv.c | 261 + interface/vmcs_host/vc_vchi_gpuserv.h | 94 + interface/vmcs_host/vc_vchi_tvservice.c | 1639 ++++++ interface/vmcs_host/vcfilesys.h | 166 + interface/vmcs_host/vcfilesys_defs.h | 88 + interface/vmcs_host/vcgencmd.h | 95 + interface/vmcs_host/vchost.h | 273 + interface/vmcs_host/vchost_platform_config.h | 32 + interface/vmcs_host/vcilcs.c | 1071 ++++ interface/vmcs_host/vcilcs.h | 103 + interface/vmcs_host/vcilcs_common.c | 122 + interface/vmcs_host/vcilcs_common.h | 83 + interface/vmcs_host/vcilcs_in.c | 271 + interface/vmcs_host/vcilcs_out.c | 917 ++++ makefiles/cmake/arm-linux.cmake | 130 + makefiles/cmake/cmake_config.h.in | 15 + makefiles/cmake/global_settings.cmake | 83 + makefiles/cmake/srcs/test-mtrace.c | 35 + .../cmake/toolchains/arm-linux-gnueabihf.cmake | 22 + .../cmake/toolchains/bcm2708-glibc-linux.cmake | 21 + makefiles/cmake/vmcs.cmake | 80 + middleware/dlloader/dlfcn.h | 99 + middleware/imageconv/imageconv.h | 448 ++ .../khronos/common/2708/khrn_interlock_filler_4.h | 50 + middleware/khronos/common/2708/khrn_prod_4.h | 545 ++ middleware/khronos/common/khrn_hw.h | 236 + middleware/khronos/common/khrn_image.h | 339 ++ middleware/khronos/common/khrn_interlock.h | 74 + middleware/khronos/common/khrn_map.h | 49 + middleware/khronos/common/khrn_mem.h | 30 + middleware/khronos/common/khrn_misc.h | 90 + middleware/khronos/common/khrn_pid_map.h | 51 + middleware/khronos/common/khrn_pid_map_value.h | 64 + middleware/khronos/common/khrn_server_pointermap.h | 48 + middleware/khronos/dispatch/khrn_dispatch.h | 51 + middleware/khronos/egl/egl_disp.h | 123 + middleware/khronos/egl/egl_server.h | 255 + middleware/khronos/ext/egl_khr_image.h | 68 + middleware/khronos/vg/2708/vg_config_filler_4.h | 33 + middleware/khronos/vg/vg_image.h | 132 + middleware/khronos/wf/wfc_server_stream.h | 95 + middleware/openmaxil/CMakeLists.txt | 52 + opensrc/helpers/libfdt/CMakeLists.txt | 13 + opensrc/helpers/libfdt/fdt.c | 251 + opensrc/helpers/libfdt/fdt.h | 111 + opensrc/helpers/libfdt/fdt_empty_tree.c | 84 + opensrc/helpers/libfdt/fdt_ro.c | 679 +++ opensrc/helpers/libfdt/fdt_rw.c | 494 ++ opensrc/helpers/libfdt/fdt_strerror.c | 96 + opensrc/helpers/libfdt/fdt_sw.c | 288 + opensrc/helpers/libfdt/fdt_wip.c | 118 + opensrc/helpers/libfdt/libfdt.h | 1653 ++++++ opensrc/helpers/libfdt/libfdt_env.h | 118 + opensrc/helpers/libfdt/libfdt_internal.h | 95 + packaging/libomxil-vc4.manifest | 5 + packaging/libomxil-vc4.spec | 109 + pkgconfig/bcm_host.pc.in | 10 + pkgconfig/brcmegl.pc.in | 13 + pkgconfig/brcmglesv2.pc.in | 12 + pkgconfig/brcmvg.pc.in | 11 + pkgconfig/egl.pc.in | 13 + pkgconfig/glesv2.pc.in | 12 + pkgconfig/mmal.pc.in | 11 + pkgconfig/vcsm.pc.in | 10 + pkgconfig/vg.pc.in | 11 + vcfw/drivers/driver.h | 159 + vcfw/logging/logging.h | 350 ++ vcfw/rtos/common/rtos_common_mem.h | 1360 +++++ vcfw/rtos/rtos.h | 540 ++ vcfw/vclib/vclib.h | 239 + vcinclude/common.h | 140 + vcinclude/vc_image_types.h | 34 + vcinclude/vcore.h | 64 + 817 files changed, 256888 insertions(+) create mode 100755 .gitignore create mode 100755 CMakeLists.txt create mode 100755 COPYING create mode 100755 LICENCE create mode 100755 README.md create mode 100755 buildme create mode 100755 containers/CMakeLists.txt create mode 100755 containers/asf/CMakeLists.txt create mode 100755 containers/asf/asf_reader.c create mode 100755 containers/asf/asf_writer.c create mode 100755 containers/avi/CMakeLists.txt create mode 100755 containers/avi/avi_reader.c create mode 100755 containers/avi/avi_writer.c create mode 100755 containers/binary/CMakeLists.txt create mode 100755 containers/binary/binary_reader.c create mode 100755 containers/binary/binary_writer.c create mode 100755 containers/containers.h create mode 100755 containers/containers_codecs.h create mode 100755 containers/containers_types.h create mode 100755 containers/core/containers.c create mode 100755 containers/core/containers_bits.c create mode 100755 containers/core/containers_bits.h create mode 100755 containers/core/containers_bytestream.h create mode 100755 containers/core/containers_codecs.c create mode 100755 containers/core/containers_common.h create mode 100755 containers/core/containers_filters.c create mode 100755 containers/core/containers_filters.h create mode 100755 containers/core/containers_index.c create mode 100755 containers/core/containers_index.h create mode 100755 containers/core/containers_io.c create mode 100755 containers/core/containers_io.h create mode 100755 containers/core/containers_io_helpers.c create mode 100755 containers/core/containers_io_helpers.h create mode 100755 containers/core/containers_list.c create mode 100755 containers/core/containers_list.h create mode 100755 containers/core/containers_loader.c create mode 100755 containers/core/containers_loader.h create mode 100755 containers/core/containers_logging.c create mode 100755 containers/core/containers_logging.h create mode 100755 containers/core/containers_private.h create mode 100755 containers/core/containers_time.h create mode 100755 containers/core/containers_uri.c create mode 100755 containers/core/containers_uri.h create mode 100755 containers/core/containers_utils.c create mode 100755 containers/core/containers_utils.h create mode 100755 containers/core/containers_waveformat.h create mode 100755 containers/core/containers_writer_utils.c create mode 100755 containers/core/containers_writer_utils.h create mode 100755 containers/core/packetizers.c create mode 100755 containers/core/packetizers_private.h create mode 100755 containers/dummy/CMakeLists.txt create mode 100755 containers/dummy/dummy_writer.c create mode 100755 containers/flash/CMakeLists.txt create mode 100755 containers/flash/flv_reader.c create mode 100755 containers/h264/avc1_packetizer.c create mode 100755 containers/io/io_file.c create mode 100755 containers/io/io_http.c create mode 100755 containers/io/io_net.c create mode 100755 containers/io/io_null.c create mode 100755 containers/io/io_pktfile.c create mode 100755 containers/metadata/id3/CMakeLists.txt create mode 100755 containers/metadata/id3/id3_metadata_reader.c create mode 100755 containers/metadata/id3/id3_metadata_strings.h create mode 100755 containers/mkv/CMakeLists.txt create mode 100755 containers/mkv/matroska_reader.c create mode 100755 containers/mp4/CMakeLists.txt create mode 100755 containers/mp4/mp4_common.h create mode 100755 containers/mp4/mp4_reader.c create mode 100755 containers/mp4/mp4_writer.c create mode 100755 containers/mpeg/CMakeLists.txt create mode 100755 containers/mpeg/ps_reader.c create mode 100755 containers/mpga/CMakeLists.txt create mode 100755 containers/mpga/mpga_common.h create mode 100755 containers/mpga/mpga_packetizer.c create mode 100755 containers/mpga/mpga_reader.c create mode 100755 containers/mpgv/mpgv_packetizer.c create mode 100755 containers/net/net_sockets.h create mode 100755 containers/net/net_sockets_bsd.c create mode 100755 containers/net/net_sockets_bsd.h create mode 100755 containers/net/net_sockets_common.c create mode 100755 containers/net/net_sockets_null.c create mode 100755 containers/net/net_sockets_priv.h create mode 100755 containers/net/net_sockets_win32.c create mode 100755 containers/net/net_sockets_win32.h create mode 100755 containers/packetizers.h create mode 100755 containers/pcm/pcm_packetizer.c create mode 100755 containers/qsynth/CMakeLists.txt create mode 100755 containers/qsynth/qsynth_reader.c create mode 100755 containers/raw/CMakeLists.txt create mode 100755 containers/raw/raw_video_common.h create mode 100755 containers/raw/raw_video_reader.c create mode 100755 containers/raw/raw_video_writer.c create mode 100755 containers/rcv/CMakeLists.txt create mode 100755 containers/rcv/rcv_reader.c create mode 100755 containers/rtp/CMakeLists.txt create mode 100755 containers/rtp/rtp_base64.c create mode 100755 containers/rtp/rtp_base64.h create mode 100755 containers/rtp/rtp_h264.c create mode 100755 containers/rtp/rtp_h264.h create mode 100755 containers/rtp/rtp_mpeg4.c create mode 100755 containers/rtp/rtp_mpeg4.h create mode 100755 containers/rtp/rtp_priv.h create mode 100755 containers/rtp/rtp_reader.c create mode 100755 containers/rtsp/CMakeLists.txt create mode 100755 containers/rtsp/rtsp_reader.c create mode 100755 containers/rv9/CMakeLists.txt create mode 100755 containers/rv9/rv9_reader.c create mode 100755 containers/simple/CMakeLists.txt create mode 100755 containers/simple/simple_common.h create mode 100755 containers/simple/simple_reader.c create mode 100755 containers/simple/simple_writer.c create mode 100755 containers/test/CMakeLists.txt create mode 100755 containers/test/autotest.cpp create mode 100755 containers/test/check_frame_int.c create mode 100755 containers/test/crc_32.c create mode 100755 containers/test/datagram_receiver.c create mode 100755 containers/test/datagram_sender.c create mode 100755 containers/test/dump_pktfile.c create mode 100755 containers/test/nb_io.h create mode 100755 containers/test/nb_io_unix.c create mode 100755 containers/test/nb_io_win32.c create mode 100755 containers/test/rtp_decoder.c create mode 100755 containers/test/stream_client.c create mode 100755 containers/test/stream_server.c create mode 100755 containers/test/test.c create mode 100755 containers/test/test_bits.c create mode 100755 containers/test/test_uri.c create mode 100755 containers/test/uri_pipe.c create mode 100755 containers/wav/CMakeLists.txt create mode 100755 containers/wav/wav_reader.c create mode 100755 helpers/dtoverlay/CMakeLists.txt create mode 100755 helpers/dtoverlay/dtoverlay.c create mode 100755 helpers/dtoverlay/dtoverlay.h create mode 100755 helpers/v3d/v3d_common.h create mode 100755 helpers/v3d/v3d_ver.h create mode 100755 helpers/vc_image/metadata_fourcc.h create mode 100755 helpers/vc_image/vc_image.h create mode 100755 helpers/vc_image/vc_image_helper.h create mode 100755 helpers/vc_image/vc_image_metadata.h create mode 100755 host_applications/android/apps/vidtex/CMakeLists.txt create mode 100755 host_applications/android/apps/vidtex/applog.h create mode 100755 host_applications/android/apps/vidtex/launcher.h create mode 100755 host_applications/android/apps/vidtex/launcher_rpi.c create mode 100755 host_applications/android/apps/vidtex/launcher_rpi.h create mode 100755 host_applications/android/apps/vidtex/main.cpp create mode 100755 host_applications/android/apps/vidtex/svp.c create mode 100755 host_applications/android/apps/vidtex/svp.h create mode 100755 host_applications/android/apps/vidtex/vidtex.c create mode 100755 host_applications/android/apps/vidtex/vidtex.h create mode 100755 host_applications/framework/common/host_ilcore.h create mode 100755 host_applications/framework/common/ilcore.c create mode 100755 host_applications/linux/CMakeLists.txt create mode 100755 host_applications/linux/apps/dtmerge/CMakeLists.txt create mode 100755 host_applications/linux/apps/dtmerge/dtmerge.c create mode 100755 host_applications/linux/apps/dtoverlay/CMakeLists.txt create mode 100755 host_applications/linux/apps/dtoverlay/dtoverlay-post create mode 100755 host_applications/linux/apps/dtoverlay/dtoverlay-pre create mode 100755 host_applications/linux/apps/dtoverlay/dtoverlay_main.c create mode 100755 host_applications/linux/apps/dtoverlay/utils.c create mode 100755 host_applications/linux/apps/dtoverlay/utils.h create mode 100755 host_applications/linux/apps/gencmd/CMakeLists.txt create mode 100755 host_applications/linux/apps/gencmd/gencmd.c create mode 100755 host_applications/linux/apps/hello_pi/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/Makefile.include create mode 100755 host_applications/linux/apps/hello_pi/README create mode 100755 host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_audio/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_audio/audio.c create mode 100755 host_applications/linux/apps/hello_pi/hello_audio/audioplay.h create mode 100755 host_applications/linux/apps/hello_pi/hello_audio/sinewave.c create mode 100755 host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c create mode 100755 host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_encode/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_encode/encode.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hello_fft_2d.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hello_fft_2d_bitmap.h create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1024k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/mailbox.c create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/mailbox.h create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc create mode 100755 host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm create mode 100755 host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_font/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_font/Vera.ttf create mode 100755 host_applications/linux/apps/hello_pi/hello_font/main.c create mode 100755 host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_jpeg/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c create mode 100755 host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h create mode 100755 host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/README.md create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/models.c create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/models.h create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/triangle.c create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/triangle.h create mode 100755 host_applications/linux/apps/hello_pi/hello_teapot/video.c create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/license.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/main.c create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/readme.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/tiger.c create mode 100755 host_applications/linux/apps/hello_pi/hello_tiger/tiger.h create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle/triangle.c create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle2/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c create mode 100755 host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_video/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_video/README create mode 100755 host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h create mode 100755 host_applications/linux/apps/hello_pi/hello_video/queue.c create mode 100755 host_applications/linux/apps/hello_pi/hello_video/queue.h create mode 100755 host_applications/linux/apps/hello_pi/hello_video/tags create mode 100755 host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c create mode 100755 host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h create mode 100755 host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h create mode 100755 host_applications/linux/apps/hello_pi/hello_video/video.c create mode 100755 host_applications/linux/apps/hello_pi/hello_video/video.h create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/README.md create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/triangle.c create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/triangle.h create mode 100755 host_applications/linux/apps/hello_pi/hello_videocube/video.c create mode 100755 host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt create mode 100755 host_applications/linux/apps/hello_pi/hello_world/Makefile create mode 100755 host_applications/linux/apps/hello_pi/hello_world/world.c create mode 100755 host_applications/linux/apps/hello_pi/libs/ilclient/Makefile create mode 100755 host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c create mode 100755 host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.h create mode 100755 host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/Makefile create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/font.c create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c create mode 100755 host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h create mode 100755 host_applications/linux/apps/hello_pi/rebuild.sh create mode 100755 host_applications/linux/apps/raspicam/CMakeLists.txt create mode 100755 host_applications/linux/apps/raspicam/Doxyfile create mode 100755 host_applications/linux/apps/raspicam/Makefile create mode 100755 host_applications/linux/apps/raspicam/README.md create mode 100755 host_applications/linux/apps/raspicam/RaspiCLI.c create mode 100755 host_applications/linux/apps/raspicam/RaspiCLI.h create mode 100755 host_applications/linux/apps/raspicam/RaspiCamControl.c create mode 100755 host_applications/linux/apps/raspicam/RaspiCamControl.h create mode 100755 host_applications/linux/apps/raspicam/RaspiPreview.c create mode 100755 host_applications/linux/apps/raspicam/RaspiPreview.h create mode 100755 host_applications/linux/apps/raspicam/RaspiStill.c create mode 100755 host_applications/linux/apps/raspicam/RaspiStillYUV.c create mode 100755 host_applications/linux/apps/raspicam/RaspiTex.c create mode 100755 host_applications/linux/apps/raspicam/RaspiTex.h create mode 100755 host_applications/linux/apps/raspicam/RaspiTexUtil.c create mode 100755 host_applications/linux/apps/raspicam/RaspiTexUtil.h create mode 100755 host_applications/linux/apps/raspicam/RaspiVid.c create mode 100755 host_applications/linux/apps/raspicam/RaspiVidYUV.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/mirror.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/mirror.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/models.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/models.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/sobel.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/sobel.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/square.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/square.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/teapot.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/teapot.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/yuv.c create mode 100755 host_applications/linux/apps/raspicam/gl_scenes/yuv.h create mode 100755 host_applications/linux/apps/raspicam/imv_examples/README.md create mode 100755 host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c create mode 100755 host_applications/linux/apps/raspicam/imv_examples/imv2txt.c create mode 100755 host_applications/linux/apps/raspicam/imv_examples/plot.par create mode 100755 host_applications/linux/apps/raspicam/tga.c create mode 100755 host_applications/linux/apps/raspicam/tga.h create mode 100755 host_applications/linux/apps/smem/CMakeLists.txt create mode 100755 host_applications/linux/apps/smem/smem.c create mode 100755 host_applications/linux/apps/tvservice/CMakeLists.txt create mode 100755 host_applications/linux/apps/tvservice/tvservice.c create mode 100755 host_applications/linux/apps/vcmailbox/CMakeLists.txt create mode 100755 host_applications/linux/apps/vcmailbox/vcmailbox.c create mode 100755 host_applications/linux/kernel_headers/vmcs_sm_ioctl.h create mode 100755 host_applications/linux/libs/bcm_host/CMakeLists.txt create mode 100755 host_applications/linux/libs/bcm_host/bcm_host.c create mode 100755 host_applications/linux/libs/bcm_host/include/bcm_host.h create mode 100755 host_applications/linux/libs/debug_sym/CMakeLists.txt create mode 100755 host_applications/linux/libs/debug_sym/debug_sym.c create mode 100755 host_applications/linux/libs/debug_sym/debug_sym.h create mode 100755 host_applications/linux/libs/sm/CMakeLists.txt create mode 100755 host_applications/linux/libs/sm/user-vcsm.c create mode 100755 host_applications/linux/libs/sm/user-vcsm.h create mode 100755 host_applications/vmcs/test_apps/mmalcam/mmalcam.c create mode 100755 host_applications/vmcs/test_apps/mmalcam/mmalcam.h create mode 100755 host_applications/vmcs/test_apps/mmalcam/viewfinder.c create mode 100755 host_applications/vmcs/test_apps/mmalplay/mmalplay.c create mode 100755 host_applications/vmcs/test_apps/mmalplay/mmalplay.h create mode 100755 host_applications/vmcs/test_apps/mmalplay/playback.c create mode 100755 host_applications/vmcs/vmcs_config.h.in create mode 100755 host_support/include/vc_debug_sym.h create mode 100755 host_support/include/vc_mem.h create mode 100755 interface/khronos/CMakeLists.txt create mode 100755 interface/khronos/common/abstract/khrn_client_platform_filler_abstract.h create mode 100755 interface/khronos/common/direct/khrn_client_platform_filler_direct.h create mode 100755 interface/khronos/common/khrn_client.c create mode 100755 interface/khronos/common/khrn_client.h create mode 100755 interface/khronos/common/khrn_client_cache.c create mode 100755 interface/khronos/common/khrn_client_cache.h create mode 100755 interface/khronos/common/khrn_client_check_types.h create mode 100755 interface/khronos/common/khrn_client_cr.c create mode 100755 interface/khronos/common/khrn_client_global_image_map.c create mode 100755 interface/khronos/common/khrn_client_global_image_map.h create mode 100755 interface/khronos/common/khrn_client_mangle.h create mode 100755 interface/khronos/common/khrn_client_platform.h create mode 100755 interface/khronos/common/khrn_client_pointermap.c create mode 100755 interface/khronos/common/khrn_client_pointermap.h create mode 100755 interface/khronos/common/khrn_client_rpc.h create mode 100755 interface/khronos/common/khrn_client_unmangle.h create mode 100755 interface/khronos/common/khrn_client_vector.c create mode 100755 interface/khronos/common/khrn_client_vector.h create mode 100755 interface/khronos/common/khrn_int_color.h create mode 100755 interface/khronos/common/khrn_int_common.h create mode 100755 interface/khronos/common/khrn_int_generic_map.c create mode 100755 interface/khronos/common/khrn_int_generic_map.h create mode 100755 interface/khronos/common/khrn_int_hash.c create mode 100755 interface/khronos/common/khrn_int_hash.h create mode 100755 interface/khronos/common/khrn_int_hash_asm.s create mode 100755 interface/khronos/common/khrn_int_ids.h create mode 100755 interface/khronos/common/khrn_int_image.c create mode 100755 interface/khronos/common/khrn_int_image.h create mode 100755 interface/khronos/common/khrn_int_math.h create mode 100755 interface/khronos/common/khrn_int_misc_impl.h create mode 100755 interface/khronos/common/khrn_int_util.c create mode 100755 interface/khronos/common/khrn_int_util.h create mode 100755 interface/khronos/common/khrn_int_util_cr.h create mode 100755 interface/khronos/common/khrn_options.c create mode 100755 interface/khronos/common/khrn_options.h create mode 100755 interface/khronos/common/linux/khrn_client_platform_linux.c create mode 100755 interface/khronos/common/linux/khrn_client_rpc_linux.c create mode 100755 interface/khronos/common/openwfc/khrn_client_platform_openwfc.c create mode 100755 interface/khronos/common/vcos/khrn_client_platform_filler_vcos.h create mode 100755 interface/khronos/common/vcos_vchiq/khrn_client_platform_filler_vcos_vchiq.h create mode 100755 interface/khronos/egl/egl_client.c create mode 100755 interface/khronos/egl/egl_client_config.c create mode 100755 interface/khronos/egl/egl_client_config.h create mode 100755 interface/khronos/egl/egl_client_config_cr.c create mode 100755 interface/khronos/egl/egl_client_context.c create mode 100755 interface/khronos/egl/egl_client_context.h create mode 100755 interface/khronos/egl/egl_client_cr.c create mode 100755 interface/khronos/egl/egl_client_get_proc.c create mode 100755 interface/khronos/egl/egl_client_surface.c create mode 100755 interface/khronos/egl/egl_client_surface.h create mode 100755 interface/khronos/egl/egl_int.h create mode 100755 interface/khronos/egl/egl_int_config.h create mode 100755 interface/khronos/egl/egl_int_impl.h create mode 100755 interface/khronos/ext/egl_brcm_driver_monitor_client.c create mode 100755 interface/khronos/ext/egl_brcm_driver_monitor_client.h create mode 100755 interface/khronos/ext/egl_brcm_flush_client.c create mode 100755 interface/khronos/ext/egl_brcm_global_image_client.c create mode 100755 interface/khronos/ext/egl_brcm_perf_monitor_client.c create mode 100755 interface/khronos/ext/egl_brcm_perf_monitor_client.h create mode 100755 interface/khronos/ext/egl_khr_image_client.c create mode 100755 interface/khronos/ext/egl_khr_lock_surface_client.c create mode 100755 interface/khronos/ext/egl_khr_sync_client.c create mode 100755 interface/khronos/ext/egl_khr_sync_client.h create mode 100755 interface/khronos/ext/egl_openmaxil_client.c create mode 100755 interface/khronos/ext/egl_openmaxil_client.h create mode 100755 interface/khronos/ext/ext_gl_debug_marker.c create mode 100755 interface/khronos/ext/gl_oes_draw_texture_client.c create mode 100755 interface/khronos/ext/gl_oes_egl_image_client.c create mode 100755 interface/khronos/ext/gl_oes_framebuffer_object.c create mode 100755 interface/khronos/ext/gl_oes_map_buffer.c create mode 100755 interface/khronos/ext/gl_oes_matrix_palette_client.c create mode 100755 interface/khronos/ext/gl_oes_query_matrix_client.c create mode 100755 interface/khronos/glxx/gl11_int_config.h create mode 100755 interface/khronos/glxx/gl11_int_impl.h create mode 100755 interface/khronos/glxx/gl20_int_impl.h create mode 100755 interface/khronos/glxx/glxx_client.c create mode 100755 interface/khronos/glxx/glxx_client.h create mode 100755 interface/khronos/glxx/glxx_int_attrib.h create mode 100755 interface/khronos/glxx/glxx_int_config.h create mode 100755 interface/khronos/glxx/glxx_int_impl.h create mode 100755 interface/khronos/include/EGL/egl.h create mode 100755 interface/khronos/include/EGL/eglext.h create mode 100755 interface/khronos/include/EGL/eglext_android.h create mode 100755 interface/khronos/include/EGL/eglext_brcm.h create mode 100755 interface/khronos/include/EGL/eglext_nvidia.h create mode 100755 interface/khronos/include/EGL/eglplatform.h create mode 100755 interface/khronos/include/GLES/gl.h create mode 100755 interface/khronos/include/GLES/glext.h create mode 100755 interface/khronos/include/GLES/glplatform.h create mode 100755 interface/khronos/include/GLES2/gl2.h create mode 100755 interface/khronos/include/GLES2/gl2ext.h create mode 100755 interface/khronos/include/GLES2/gl2platform.h create mode 100755 interface/khronos/include/KHR/khrplatform.h create mode 100755 interface/khronos/include/VG/openvg.h create mode 100755 interface/khronos/include/VG/vgext.h create mode 100755 interface/khronos/include/VG/vgplatform.h create mode 100755 interface/khronos/include/VG/vgu.h create mode 100755 interface/khronos/include/WF/wfc.h create mode 100755 interface/khronos/include/WF/wfcplatform.h create mode 100755 interface/khronos/vg/vg_client.c create mode 100755 interface/khronos/vg/vg_client.h create mode 100755 interface/khronos/vg/vg_int.h create mode 100755 interface/khronos/vg/vg_int_config.h create mode 100755 interface/khronos/vg/vg_int_impl.h create mode 100755 interface/khronos/vg/vg_int_mat3x3.c create mode 100755 interface/khronos/vg/vg_int_mat3x3.h create mode 100755 interface/khronos/vg/vg_int_util.h create mode 100755 interface/khronos/wf/wfc_client.c create mode 100755 interface/khronos/wf/wfc_client_ipc.c create mode 100755 interface/khronos/wf/wfc_client_ipc.h create mode 100755 interface/khronos/wf/wfc_client_server_api.c create mode 100755 interface/khronos/wf/wfc_client_stream.c create mode 100755 interface/khronos/wf/wfc_client_stream.h create mode 100755 interface/khronos/wf/wfc_int.h create mode 100755 interface/khronos/wf/wfc_ipc.h create mode 100755 interface/khronos/wf/wfc_server_api.h create mode 100755 interface/mmal/CMakeLists.txt create mode 100755 interface/mmal/client/CMakeLists.txt create mode 100755 interface/mmal/client/brcmjpeg/CMakeLists.txt create mode 100755 interface/mmal/client/brcmjpeg/brcmjpeg.c create mode 100755 interface/mmal/client/brcmjpeg/brcmjpeg.h create mode 100755 interface/mmal/client/brcmjpeg/brcmjpeg_test.c create mode 100755 interface/mmal/components/CMakeLists.txt create mode 100755 interface/mmal/components/aaf_audio_render.cpp create mode 100755 interface/mmal/components/aggregator.c create mode 100755 interface/mmal/components/android_media_codec.cpp create mode 100755 interface/mmal/components/artificial_camera.c create mode 100755 interface/mmal/components/avcodec_audio_decoder.c create mode 100755 interface/mmal/components/avcodec_video_decoder.c create mode 100755 interface/mmal/components/clock.c create mode 100755 interface/mmal/components/container_reader.c create mode 100755 interface/mmal/components/copy.c create mode 100755 interface/mmal/components/null_sink.c create mode 100755 interface/mmal/components/passthrough.c create mode 100755 interface/mmal/components/scheduler.c create mode 100755 interface/mmal/components/sdl_audio_render.c create mode 100755 interface/mmal/components/sdl_video_render.c create mode 100755 interface/mmal/components/spdif.c create mode 100755 interface/mmal/components/splitter.c create mode 100755 interface/mmal/core/CMakeLists.txt create mode 100755 interface/mmal/core/mmal_buffer.c create mode 100755 interface/mmal/core/mmal_buffer_private.h create mode 100755 interface/mmal/core/mmal_clock.c create mode 100755 interface/mmal/core/mmal_clock_private.h create mode 100755 interface/mmal/core/mmal_component.c create mode 100755 interface/mmal/core/mmal_component_private.h create mode 100755 interface/mmal/core/mmal_core_private.h create mode 100755 interface/mmal/core/mmal_events.c create mode 100755 interface/mmal/core/mmal_events_private.h create mode 100755 interface/mmal/core/mmal_format.c create mode 100755 interface/mmal/core/mmal_logging.c create mode 100755 interface/mmal/core/mmal_pool.c create mode 100755 interface/mmal/core/mmal_port.c create mode 100755 interface/mmal/core/mmal_port_clock.c create mode 100755 interface/mmal/core/mmal_port_private.h create mode 100755 interface/mmal/core/mmal_queue.c create mode 100755 interface/mmal/mmal.h create mode 100755 interface/mmal/mmal_buffer.h create mode 100755 interface/mmal/mmal_clock.h create mode 100755 interface/mmal/mmal_common.h create mode 100755 interface/mmal/mmal_component.h create mode 100755 interface/mmal/mmal_encodings.h create mode 100755 interface/mmal/mmal_events.h create mode 100755 interface/mmal/mmal_format.h create mode 100755 interface/mmal/mmal_logging.h create mode 100755 interface/mmal/mmal_metadata.h create mode 100755 interface/mmal/mmal_parameters.h create mode 100755 interface/mmal/mmal_parameters_audio.h create mode 100755 interface/mmal/mmal_parameters_camera.h create mode 100755 interface/mmal/mmal_parameters_clock.h create mode 100755 interface/mmal/mmal_parameters_common.h create mode 100755 interface/mmal/mmal_parameters_video.h create mode 100755 interface/mmal/mmal_pool.h create mode 100755 interface/mmal/mmal_port.h create mode 100755 interface/mmal/mmal_queue.h create mode 100755 interface/mmal/mmal_types.h create mode 100755 interface/mmal/openmaxil/CMakeLists.txt create mode 100755 interface/mmal/openmaxil/mmalomx.h create mode 100755 interface/mmal/openmaxil/mmalomx_buffer.c create mode 100755 interface/mmal/openmaxil/mmalomx_buffer.h create mode 100755 interface/mmal/openmaxil/mmalomx_commands.c create mode 100755 interface/mmal/openmaxil/mmalomx_commands.h create mode 100755 interface/mmal/openmaxil/mmalomx_core.c create mode 100755 interface/mmal/openmaxil/mmalomx_logging.c create mode 100755 interface/mmal/openmaxil/mmalomx_logging.h create mode 100755 interface/mmal/openmaxil/mmalomx_marks.c create mode 100755 interface/mmal/openmaxil/mmalomx_marks.h create mode 100755 interface/mmal/openmaxil/mmalomx_parameters.c create mode 100755 interface/mmal/openmaxil/mmalomx_parameters.h create mode 100755 interface/mmal/openmaxil/mmalomx_registry.c create mode 100755 interface/mmal/openmaxil/mmalomx_registry.h create mode 100755 interface/mmal/openmaxil/mmalomx_roles.c create mode 100755 interface/mmal/openmaxil/mmalomx_roles.h create mode 100755 interface/mmal/openmaxil/mmalomx_util_params.c create mode 100755 interface/mmal/openmaxil/mmalomx_util_params.h create mode 100755 interface/mmal/openmaxil/mmalomx_util_params_audio.c create mode 100755 interface/mmal/openmaxil/mmalomx_util_params_camera.c create mode 100755 interface/mmal/openmaxil/mmalomx_util_params_common.h create mode 100755 interface/mmal/openmaxil/mmalomx_util_params_misc.c create mode 100755 interface/mmal/openmaxil/mmalomx_util_params_video.c create mode 100755 interface/mmal/test/CMakeLists.txt create mode 100755 interface/mmal/test/examples/example_basic_1.c create mode 100755 interface/mmal/test/examples/example_basic_2.c create mode 100755 interface/mmal/test/examples/example_connections.c create mode 100755 interface/mmal/test/examples/example_graph.c create mode 100755 interface/mmal/util/CMakeLists.txt create mode 100755 interface/mmal/util/mmal_component_wrapper.c create mode 100755 interface/mmal/util/mmal_component_wrapper.h create mode 100755 interface/mmal/util/mmal_connection.c create mode 100755 interface/mmal/util/mmal_connection.h create mode 100755 interface/mmal/util/mmal_default_components.h create mode 100755 interface/mmal/util/mmal_graph.c create mode 100755 interface/mmal/util/mmal_graph.h create mode 100755 interface/mmal/util/mmal_il.c create mode 100755 interface/mmal/util/mmal_il.h create mode 100755 interface/mmal/util/mmal_list.c create mode 100755 interface/mmal/util/mmal_list.h create mode 100755 interface/mmal/util/mmal_param_convert.c create mode 100755 interface/mmal/util/mmal_param_convert.h create mode 100755 interface/mmal/util/mmal_util.c create mode 100755 interface/mmal/util/mmal_util.h create mode 100755 interface/mmal/util/mmal_util_params.c create mode 100755 interface/mmal/util/mmal_util_params.h create mode 100755 interface/mmal/util/mmal_util_rational.c create mode 100755 interface/mmal/util/mmal_util_rational.h create mode 100755 interface/mmal/vc/CMakeLists.txt create mode 100755 interface/mmal/vc/mmal_vc_api.c create mode 100755 interface/mmal/vc/mmal_vc_api.h create mode 100755 interface/mmal/vc/mmal_vc_api_drm.c create mode 100755 interface/mmal/vc/mmal_vc_api_drm.h create mode 100755 interface/mmal/vc/mmal_vc_client.c create mode 100755 interface/mmal/vc/mmal_vc_client_priv.h create mode 100755 interface/mmal/vc/mmal_vc_dbglog.h create mode 100755 interface/mmal/vc/mmal_vc_diag.c create mode 100755 interface/mmal/vc/mmal_vc_msgnames.c create mode 100755 interface/mmal/vc/mmal_vc_msgnames.h create mode 100755 interface/mmal/vc/mmal_vc_msgs.h create mode 100755 interface/mmal/vc/mmal_vc_opaque_alloc.c create mode 100755 interface/mmal/vc/mmal_vc_opaque_alloc.h create mode 100755 interface/mmal/vc/mmal_vc_shm.c create mode 100755 interface/mmal/vc/mmal_vc_shm.h create mode 100755 interface/peer/vc_vchi_dispmanx_common.h create mode 100755 interface/vchi/common/endian.h create mode 100755 interface/vchi/connections/connection.h create mode 100755 interface/vchi/message_drivers/message.h create mode 100755 interface/vchi/vchi.h create mode 100755 interface/vchi/vchi_cfg.h create mode 100755 interface/vchi/vchi_cfg_internal.h create mode 100755 interface/vchi/vchi_common.h create mode 100755 interface/vchi/vchi_mh.h create mode 100755 interface/vchiq_arm/CMakeLists.txt create mode 100755 interface/vchiq_arm/vchiq.h create mode 100755 interface/vchiq_arm/vchiq_cfg.h create mode 100755 interface/vchiq_arm/vchiq_if.h create mode 100755 interface/vchiq_arm/vchiq_ioctl.h create mode 100755 interface/vchiq_arm/vchiq_lib.c create mode 100755 interface/vchiq_arm/vchiq_test.c create mode 100755 interface/vchiq_arm/vchiq_test.h create mode 100755 interface/vchiq_arm/vchiq_test_if.h create mode 100755 interface/vchiq_arm/vchiq_util.c create mode 100755 interface/vchiq_arm/vchiq_util.h create mode 100755 interface/vcos/CMakeLists.txt create mode 100755 interface/vcos/generic/CMakeLists.txt create mode 100755 interface/vcos/generic/vcos_abort.c create mode 100755 interface/vcos/generic/vcos_cmd.c create mode 100755 interface/vcos/generic/vcos_common.h create mode 100755 interface/vcos/generic/vcos_deprecated.h create mode 100755 interface/vcos/generic/vcos_generic_blockpool.c create mode 100755 interface/vcos/generic/vcos_generic_blockpool.h create mode 100755 interface/vcos/generic/vcos_generic_event_flags.c create mode 100755 interface/vcos/generic/vcos_generic_event_flags.h create mode 100755 interface/vcos/generic/vcos_generic_named_sem.c create mode 100755 interface/vcos/generic/vcos_generic_named_sem.h create mode 100755 interface/vcos/generic/vcos_generic_quickslow_mutex.h create mode 100755 interface/vcos/generic/vcos_generic_reentrant_mtx.c create mode 100755 interface/vcos/generic/vcos_generic_reentrant_mtx.h create mode 100755 interface/vcos/generic/vcos_generic_safe_string.c create mode 100755 interface/vcos/generic/vcos_generic_tls.h create mode 100755 interface/vcos/generic/vcos_init.c create mode 100755 interface/vcos/generic/vcos_joinable_thread_from_plain.h create mode 100755 interface/vcos/generic/vcos_latch_from_sem.h create mode 100755 interface/vcos/generic/vcos_logcat.c create mode 100755 interface/vcos/generic/vcos_mem_from_malloc.c create mode 100755 interface/vcos/generic/vcos_mem_from_malloc.h create mode 100755 interface/vcos/generic/vcos_msgqueue.c create mode 100755 interface/vcos/generic/vcos_mutexes_are_reentrant.h create mode 100755 interface/vcos/generic/vcos_thread_reaper.h create mode 100755 interface/vcos/glibc/vcos_backtrace.c create mode 100755 interface/vcos/pthreads/CMakeLists.txt create mode 100755 interface/vcos/pthreads/vcos_dlfcn.c create mode 100755 interface/vcos/pthreads/vcos_futex_mutex.h create mode 100755 interface/vcos/pthreads/vcos_platform.h create mode 100755 interface/vcos/pthreads/vcos_platform_types.h create mode 100755 interface/vcos/pthreads/vcos_pthreads.c create mode 100755 interface/vcos/user_nodefs.h create mode 100755 interface/vcos/vcos.h create mode 100755 interface/vcos/vcos_assert.h create mode 100755 interface/vcos/vcos_atomic_flags.h create mode 100755 interface/vcos/vcos_attr.h create mode 100755 interface/vcos/vcos_blockpool.h create mode 100755 interface/vcos/vcos_build_info.h create mode 100755 interface/vcos/vcos_cfg.h create mode 100755 interface/vcos/vcos_cmd.h create mode 100755 interface/vcos/vcos_ctype.h create mode 100755 interface/vcos/vcos_dlfcn.h create mode 100755 interface/vcos/vcos_event.h create mode 100755 interface/vcos/vcos_event_flags.h create mode 100755 interface/vcos/vcos_init.h create mode 100755 interface/vcos/vcos_inttypes.h create mode 100755 interface/vcos/vcos_isr.h create mode 100755 interface/vcos/vcos_legacy_isr.h create mode 100755 interface/vcos/vcos_logging.h create mode 100755 interface/vcos/vcos_logging_control.h create mode 100755 interface/vcos/vcos_lowlevel_thread.h create mode 100755 interface/vcos/vcos_mem.h create mode 100755 interface/vcos/vcos_mempool.h create mode 100755 interface/vcos/vcos_msgqueue.h create mode 100755 interface/vcos/vcos_mutex.h create mode 100755 interface/vcos/vcos_named_semaphore.h create mode 100755 interface/vcos/vcos_once.h create mode 100755 interface/vcos/vcos_queue.h create mode 100755 interface/vcos/vcos_quickslow_mutex.h create mode 100755 interface/vcos/vcos_reentrant_mutex.h create mode 100755 interface/vcos/vcos_semaphore.h create mode 100755 interface/vcos/vcos_stdbool.h create mode 100755 interface/vcos/vcos_stdint.h create mode 100755 interface/vcos/vcos_string.h create mode 100755 interface/vcos/vcos_thread.h create mode 100755 interface/vcos/vcos_thread_attr.h create mode 100755 interface/vcos/vcos_timer.h create mode 100755 interface/vcos/vcos_tls.h create mode 100755 interface/vcos/vcos_types.h create mode 100755 interface/vctypes/vc_display_types.h create mode 100755 interface/vctypes/vc_image_structs.h create mode 100755 interface/vctypes/vc_image_types.h create mode 100755 interface/vmcs_host/CMakeLists.txt create mode 100755 interface/vmcs_host/khronos/IL/OMX_Audio.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Broadcom.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Component.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Core.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_ILCS.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_IVCommon.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Image.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Index.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Other.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Types.h create mode 100755 interface/vmcs_host/khronos/IL/OMX_Video.h create mode 100755 interface/vmcs_host/linux/vcfiled/CMakeLists.txt create mode 100755 interface/vmcs_host/linux/vcfiled/etc/init.d/vcfiled create mode 100755 interface/vmcs_host/linux/vcfiled/vcfiled.c create mode 100755 interface/vmcs_host/linux/vcfiled/vcfiled_check.c create mode 100755 interface/vmcs_host/linux/vcfiled/vcfiled_check.h create mode 100755 interface/vmcs_host/linux/vcfiled/vcfiled_lock_test.c create mode 100755 interface/vmcs_host/linux/vcfilesys.c create mode 100755 interface/vmcs_host/linux/vchost_config.h create mode 100755 interface/vmcs_host/linux/vcmisc.c create mode 100755 interface/vmcs_host/vc_cec.h create mode 100755 interface/vmcs_host/vc_cecservice.h create mode 100755 interface/vmcs_host/vc_cecservice_defs.h create mode 100755 interface/vmcs_host/vc_cma.h create mode 100755 interface/vmcs_host/vc_dispmanx.h create mode 100755 interface/vmcs_host/vc_dispmanx_types.h create mode 100755 interface/vmcs_host/vc_dispservice_defs.h create mode 100755 interface/vmcs_host/vc_dispservice_x_defs.h create mode 100755 interface/vmcs_host/vc_fileservice_defs.h create mode 100755 interface/vmcs_host/vc_gencmd_defs.h create mode 100755 interface/vmcs_host/vc_hdmi.h create mode 100755 interface/vmcs_host/vc_hdmi_property.h create mode 100755 interface/vmcs_host/vc_ilcs_defs.h create mode 100755 interface/vmcs_host/vc_imageconv_defs.h create mode 100755 interface/vmcs_host/vc_sdtv.h create mode 100755 interface/vmcs_host/vc_service_common.c create mode 100755 interface/vmcs_host/vc_service_common.h create mode 100755 interface/vmcs_host/vc_tvservice.h create mode 100755 interface/vmcs_host/vc_tvservice_defs.h create mode 100755 interface/vmcs_host/vc_vchi_audioserv_defs.h create mode 100755 interface/vmcs_host/vc_vchi_bufman.h create mode 100755 interface/vmcs_host/vc_vchi_bufman_defs.h create mode 100755 interface/vmcs_host/vc_vchi_cecservice.c create mode 100755 interface/vmcs_host/vc_vchi_dispmanx.c create mode 100755 interface/vmcs_host/vc_vchi_dispmanx.h create mode 100755 interface/vmcs_host/vc_vchi_fileservice_defs.h create mode 100755 interface/vmcs_host/vc_vchi_filesys.c create mode 100755 interface/vmcs_host/vc_vchi_filesys.h create mode 100755 interface/vmcs_host/vc_vchi_gencmd.c create mode 100755 interface/vmcs_host/vc_vchi_gencmd.h create mode 100755 interface/vmcs_host/vc_vchi_gpuserv.c create mode 100755 interface/vmcs_host/vc_vchi_gpuserv.h create mode 100755 interface/vmcs_host/vc_vchi_tvservice.c create mode 100755 interface/vmcs_host/vcfilesys.h create mode 100755 interface/vmcs_host/vcfilesys_defs.h create mode 100755 interface/vmcs_host/vcgencmd.h create mode 100755 interface/vmcs_host/vchost.h create mode 100755 interface/vmcs_host/vchost_platform_config.h create mode 100755 interface/vmcs_host/vcilcs.c create mode 100755 interface/vmcs_host/vcilcs.h create mode 100755 interface/vmcs_host/vcilcs_common.c create mode 100755 interface/vmcs_host/vcilcs_common.h create mode 100755 interface/vmcs_host/vcilcs_in.c create mode 100755 interface/vmcs_host/vcilcs_out.c create mode 100755 makefiles/cmake/arm-linux.cmake create mode 100755 makefiles/cmake/cmake_config.h.in create mode 100755 makefiles/cmake/global_settings.cmake create mode 100755 makefiles/cmake/srcs/test-mtrace.c create mode 100755 makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake create mode 100755 makefiles/cmake/toolchains/bcm2708-glibc-linux.cmake create mode 100755 makefiles/cmake/vmcs.cmake create mode 100755 middleware/dlloader/dlfcn.h create mode 100755 middleware/imageconv/imageconv.h create mode 100755 middleware/khronos/common/2708/khrn_interlock_filler_4.h create mode 100755 middleware/khronos/common/2708/khrn_prod_4.h create mode 100755 middleware/khronos/common/khrn_hw.h create mode 100755 middleware/khronos/common/khrn_image.h create mode 100755 middleware/khronos/common/khrn_interlock.h create mode 100755 middleware/khronos/common/khrn_map.h create mode 100755 middleware/khronos/common/khrn_mem.h create mode 100755 middleware/khronos/common/khrn_misc.h create mode 100755 middleware/khronos/common/khrn_pid_map.h create mode 100755 middleware/khronos/common/khrn_pid_map_value.h create mode 100755 middleware/khronos/common/khrn_server_pointermap.h create mode 100755 middleware/khronos/dispatch/khrn_dispatch.h create mode 100755 middleware/khronos/egl/egl_disp.h create mode 100755 middleware/khronos/egl/egl_server.h create mode 100755 middleware/khronos/ext/egl_khr_image.h create mode 100755 middleware/khronos/vg/2708/vg_config_filler_4.h create mode 100755 middleware/khronos/vg/vg_image.h create mode 100755 middleware/khronos/wf/wfc_server_stream.h create mode 100755 middleware/openmaxil/CMakeLists.txt create mode 100755 opensrc/helpers/libfdt/CMakeLists.txt create mode 100755 opensrc/helpers/libfdt/fdt.c create mode 100755 opensrc/helpers/libfdt/fdt.h create mode 100755 opensrc/helpers/libfdt/fdt_empty_tree.c create mode 100755 opensrc/helpers/libfdt/fdt_ro.c create mode 100755 opensrc/helpers/libfdt/fdt_rw.c create mode 100755 opensrc/helpers/libfdt/fdt_strerror.c create mode 100755 opensrc/helpers/libfdt/fdt_sw.c create mode 100755 opensrc/helpers/libfdt/fdt_wip.c create mode 100755 opensrc/helpers/libfdt/libfdt.h create mode 100755 opensrc/helpers/libfdt/libfdt_env.h create mode 100755 opensrc/helpers/libfdt/libfdt_internal.h create mode 100755 packaging/libomxil-vc4.manifest create mode 100755 packaging/libomxil-vc4.spec create mode 100755 pkgconfig/bcm_host.pc.in create mode 100755 pkgconfig/brcmegl.pc.in create mode 100755 pkgconfig/brcmglesv2.pc.in create mode 100755 pkgconfig/brcmvg.pc.in create mode 100755 pkgconfig/egl.pc.in create mode 100755 pkgconfig/glesv2.pc.in create mode 100755 pkgconfig/mmal.pc.in create mode 100755 pkgconfig/vcsm.pc.in create mode 100755 pkgconfig/vg.pc.in create mode 100755 vcfw/drivers/driver.h create mode 100755 vcfw/logging/logging.h create mode 100755 vcfw/rtos/common/rtos_common_mem.h create mode 100755 vcfw/rtos/rtos.h create mode 100755 vcfw/vclib/vclib.h create mode 100755 vcinclude/common.h create mode 100755 vcinclude/vc_image_types.h create mode 100755 vcinclude/vcore.h diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..63570f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Build directory +build/ + +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so + +# Compiled Static libraries +*.lai +*.la +*.a + +*.raw +*.rgb +*.bgr +*.h264 +*.264 +*.yuyv +*.uyvy +*.yvyu +*.vyuy +*.i420 +*.yuv +*.jpg +*.avi +*.pts +*.ppm +*.mkv diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..cfc8ae5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,132 @@ +cmake_minimum_required(VERSION 2.8) + +project(vmcs_host_apps) + +SET(PROJECT_VER_MAJOR 1) +SET(PROJECT_VER_MINOR 0) +SET(PROJECT_VER_PATCH 0) +SET(PROJECT_VER "${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}.${PROJECT_VER_PATCH}") +SET(PROJECT_APIVER "${PROJECT_VER}") + +if(ARM64) + set(BUILD_MMAL FALSE) + set(BUILD_MMAL_APPS FALSE) +else() + set(BUILD_MMAL TRUE) + set(BUILD_MMAL_APPS TRUE) +endif() +set(vmcs_root ${PROJECT_SOURCE_DIR}) +get_filename_component(VIDEOCORE_ROOT . ABSOLUTE) + +set(VCOS_PTHREADS_BUILD_SHARED TRUE) + +include(makefiles/cmake/global_settings.cmake) +include(makefiles/cmake/arm-linux.cmake) +include(makefiles/cmake/vmcs.cmake) + +enable_language(ASM) + +# Global include paths +include_directories(host_applications/framework) +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(interface/vcos/pthreads) +include_directories(interface/vmcs_host/linux) +include_directories(interface/vmcs_host) +include_directories(interface/vmcs_host/khronos) +include_directories(interface/khronos/include) +include_directories(${PROJECT_BINARY_DIR}) +include_directories(interface/vchiq_arm) +#include_directories(tools/inet_transport) +include_directories(host_support/include) + +# Global compiler flags +if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-multichar -Wall -Wno-unused-but-set-variable -fPIC") +endif() + +add_definitions(-D_REENTRANT) +add_definitions(-DUSE_VCHIQ_ARM -DVCHI_BULK_ALIGN=1 -DVCHI_BULK_GRANULARITY=1) +add_definitions(-DOMX_SKIP64BIT) +add_definitions(-DEGL_SERVER_DISPMANX) +add_definitions(-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) +add_definitions(-D_GNU_SOURCE) + +# do we actually need this? +add_definitions(-D__VIDEOCORE4__) +add_definitions(-DTV_SUPPORTED_MODE_NO_DEPRECATED) + +# add_definitions(-DKHRONOS_CLIENT_LOGGING) + +# Check for OpenWF-C value set via command line +if(KHRONOS_EGL_PLATFORM MATCHES "openwfc") + add_definitions(-DKHRONOS_EGL_PLATFORM_OPENWFC) +endif() + +# List of subsidiary CMakeLists +add_subdirectory(interface/vcos) +add_subdirectory(interface/vmcs_host) +add_subdirectory(interface/vchiq_arm) +if(NOT ARM64) + add_subdirectory(interface/khronos) +endif() + +#add_subdirectory(opensrc/tools/lua) +if(BUILD_MMAL) + include_directories(interface/mmal) + add_subdirectory(interface/mmal) + add_subdirectory(containers) +endif() + +# VidTex supports Android and Linux +if(BUILD_MMAL_APPS) +add_subdirectory(host_applications/android/apps/vidtex) +endif(BUILD_MMAL_APPS) + +if(NOT ARM64) + add_subdirectory(middleware/openmaxil) +endif() + +# 3d demo code +#if(NOT ANDROID) +# add_subdirectory(thirdparty/applications/demos) +# add_subdirectory(opensrc/applications/demos) +#endif() + +#if(ENABLE_3D_TESTS) +# add_subdirectory(thirdparty/applications/test) +#endif() + +# FIXME: we should use a pre-packaged version of freetype +# rather than the one included in the repo. +#add_subdirectory(opensrc/helpers/freetype) +#add_subdirectory(${PROJECT_SOURCE_DIR}/opensrc/helpers/fonts/ttf-bitstream-vera) + +# VMCS Host Applications +#add_subdirectory(host_applications/framework) + +# add_subdirectory(interface/vchiq/test/win32) + +# Apps and libraries supporting Camera Tuning Tool +#add_subdirectory(tools/inet_transport/linux) +#add_subdirectory(host_support/vcstandalone) + +# add linux apps +add_subdirectory(host_applications/linux) +add_subdirectory(opensrc/helpers/libfdt) +add_subdirectory(helpers/dtoverlay) + +set(vmcs_host_apps_VERSION_MAJOR 1) +set(vmcs_host_apps_VERSION_MINOR 0) + +include_directories("${PROJECT_BINARY_DIR}") +include(FindPkgConfig QUIET) +if(PKG_CONFIG_FOUND) + # Produce a pkg-config file + foreach(PCFILE bcm_host.pc egl.pc glesv2.pc vg.pc brcmegl.pc brcmglesv2.pc brcmvg.pc vcsm.pc mmal.pc ) + configure_file("pkgconfig/${PCFILE}.in" "${PCFILE}" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PCFILE}" + DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") + endforeach() +endif() +# Remove cache entry, if one added by command line +unset(KHRONOS_EGL_PLATFORM CACHE) diff --git a/COPYING b/COPYING new file mode 100755 index 0000000..a60e55d --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +Copyright (C) 2017 Samsung Electronics co., Ltd. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENCE b/LICENCE new file mode 100755 index 0000000..dea4c26 --- /dev/null +++ b/LICENCE @@ -0,0 +1,26 @@ +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2015, Raspberry Pi (Trading) Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100755 index 0000000..358d2b4 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +This repository contains the source code for the ARM side libraries used on Raspberry Pi. +These typically are installed in /opt/vc/lib and includes source for the ARM side code to interface to: +EGL, mmal, GLESv2, vcos, openmaxil, vchiq_arm, bcm_host, WFC, OpenVG. + +Use buildme to build. It requires cmake to be installed and an arm cross compiler. It is set up to use this one: +https://github.com/raspberrypi/tools/tree/master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian diff --git a/buildme b/buildme new file mode 100755 index 0000000..b8fd440 --- /dev/null +++ b/buildme @@ -0,0 +1,44 @@ +#!/bin/bash +BUILDTYPE=Release + +if [ "$1" = "--debug" ]; then + BUILDTYPE=Debug + shift +fi + +BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`; + +if [ "armv6l" = `arch` ] || [ "armv7l" = `arch` ]; then + # Native compile on the Raspberry Pi + mkdir -p build/raspberry/$BUILDSUBDIR + pushd build/raspberry/$BUILDSUBDIR + cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + if [ "armv6l" = `arch` ]; then + make + else + make -j4 + fi + if [ "$1" != "" ]; then + sudo make install DESTDIR=$1 + else + sudo make install + fi +elif [ "$1" = "--native" ]; then + # Build natively on the host + mkdir -p build/native/$BUILDSUBDIR + pushd build/native/$BUILDSUBDIR + cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + shift + make -j `nproc` $* +else + # Cross compile on a more capable machine + mkdir -p build/arm-linux/$BUILDSUBDIR + pushd build/arm-linux/$BUILDSUBDIR + cmake -DCMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + make -j `nproc` + + if [ "$1" != "" ]; then + sudo make install DESTDIR=$1 + fi +fi +popd diff --git a/containers/CMakeLists.txt b/containers/CMakeLists.txt new file mode 100755 index 0000000..5570038 --- /dev/null +++ b/containers/CMakeLists.txt @@ -0,0 +1,124 @@ +SET( SOURCE_DIR . ) + +# We support building both static and shared libraries +if (NOT DEFINED LIBRARY_TYPE) +set(LIBRARY_TYPE SHARED) +endif (NOT DEFINED LIBRARY_TYPE) + +# Make sure the compiler can find the necessary include files +include_directories (${SOURCE_DIR}/.. ${SOURCE_DIR}/../interface/vcos) + +# Needed for the container loader +add_definitions(-DDL_PATH_PREFIX="${VMCS_PLUGIN_DIR}/") + +SET( GCC_COMPILER_FLAGS -Wall -g -O2 -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wcast-qual -Wwrite-strings -Wundef ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wextra )#-Wno-missing-field-initializers ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -std=c99 -D_POSIX_C_SOURCE=200112L ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-missing-field-initializers ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-unused-value ) + +add_definitions( ${GCC_COMPILER_FLAGS} ) + +# Containers core library +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io_helpers.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_codecs.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_utils.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_writer_utils.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_loader.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_filters.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_logging.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_uri.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_bits.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_list.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_index.c) + +# Containers io library +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_file.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_null.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_net.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_pktfile.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_http.c) +add_definitions( -DENABLE_CONTAINER_IO_HTTP ) + +# Containers net library +if (DEFINED MSVC) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_win32.c) +elseif (DEFINED LINUX OR DEFINED UNIX) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_bsd.c) +else (DEFINED MSVC) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_null.c) +endif (DEFINED MSVC) +set(extra_net_SRCS net_sockets_win32.c net_sockets_win32.h net_sockets_null.c) +add_custom_target(containers_net_extra ALL + COMMAND touch ${extra_net_SRCS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/net) + +# Packetizers library +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/core/packetizers.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpga/mpga_packetizer.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpgv/mpgv_packetizer.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/pcm/pcm_packetizer.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/h264/avc1_packetizer.c) + +add_library(containers ${LIBRARY_TYPE} ${core_SRCS} ${io_SRCS} ${net_SRCS} ${packetizers_SRCS}) +target_link_libraries(containers vcos) +install(TARGETS containers DESTINATION lib) + +set(container_readers) +set(container_writers) + +# Container modules +add_subdirectory(mp4) +set(container_readers ${container_readers} reader_mp4) +set(container_writers ${container_writers} writer_mp4) +add_subdirectory(mpeg) +set(container_readers ${container_readers} reader_ps) +add_subdirectory(mpga) +set(container_readers ${container_readers} reader_mpga) +add_subdirectory(binary) +set(container_readers ${container_readers} reader_binary) +set(container_writers ${container_writers} writer_binary) +add_subdirectory(mkv) +set(container_readers ${container_readers} reader_mkv) +add_subdirectory(wav) +set(container_readers ${container_readers} reader_wav) +add_subdirectory(asf) +set(container_readers ${container_readers} reader_asf) +set(container_writers ${container_writers} writer_asf) +add_subdirectory(flash) +set(container_readers ${container_readers} reader_flv) +add_subdirectory(avi) +set(container_readers ${container_readers} reader_avi) +set(container_writers ${container_writers} writer_avi) +add_subdirectory(rtp) +set(container_readers ${container_readers} reader_rtp) +add_subdirectory(rtsp) +set(container_readers ${container_readers} reader_rtsp) +add_subdirectory(rcv) +set(container_readers ${container_readers} reader_rcv) +add_subdirectory(rv9) +set(container_readers ${container_readers} reader_rv9) +add_subdirectory(qsynth) +set(container_readers ${container_readers} reader_qsynth) +add_subdirectory(simple) +set(container_readers ${container_readers} reader_simple) +set(container_writers ${container_writers} writer_simple) +add_subdirectory(raw) +set(container_readers ${container_readers} reader_raw_video) +set(container_writers ${container_writers} writer_raw_video) +add_subdirectory(dummy) +set(container_writers ${container_writers} writer_dummy) + +add_subdirectory(metadata/id3) +set(container_readers ${container_readers} reader_metadata_id3) + +if (${LIBRARY_TYPE} STREQUAL STATIC) +target_link_libraries(containers ${container_readers} ${container_writers}) +endif (${LIBRARY_TYPE} STREQUAL STATIC) + +# Test apps +add_subdirectory(test) diff --git a/containers/asf/CMakeLists.txt b/containers/asf/CMakeLists.txt new file mode 100755 index 0000000..2cd7ac2 --- /dev/null +++ b/containers/asf/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_asf ${LIBRARY_TYPE} asf_reader.c) + +target_link_libraries(reader_asf containers) + +install(TARGETS reader_asf DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_asf ${LIBRARY_TYPE} asf_writer.c) + +target_link_libraries(writer_asf containers) + +install(TARGETS writer_asf DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/asf/asf_reader.c b/containers/asf/asf_reader.c new file mode 100755 index 0000000..c36ed5e --- /dev/null +++ b/containers/asf/asf_reader.c @@ -0,0 +1,2247 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +/* prevent double-defines when it is defined on the command line - as in the test app */ +#ifndef ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT +#endif +#ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#endif + +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level + +/* For the sanity of the Visual Studio debugger make local names for structures */ +#define VC_CONTAINER_TRACK_MODULE_T ASF_VC_CONTAINER_TRACK_MODULE_T +#define VC_CONTAINER_MODULE_T ASF_VC_CONTAINER_MODULE_T +#define VC_CONTAINER_T ASF_VC_CONTAINER_T + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ASF_TRACKS_MAX 2 +#define ASF_EXTRADATA_MAX 256 + +#define ASF_MAX_OBJECT_LEVEL 4 +#define ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS 5 +#define ASF_MAX_OBJECT_SIZE (1<<29) /* Does not apply to the data object */ +#define ASF_OBJECT_HEADER_SIZE (16+8) +#define ASF_UNKNOWN_PTS ((uint32_t)(-1)) +#define ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS 100 +#define ASF_MAX_SEARCH_PACKETS 1000 + +#define ASF_SKIP_GUID(ctx, size, n) (size -= 16, SKIP_GUID(ctx,n)) +#define ASF_SKIP_U8(ctx, size, n) (size -= 1, SKIP_U8(ctx,n)) +#define ASF_SKIP_U16(ctx, size, n) (size -= 2, SKIP_U16(ctx,n)) +#define ASF_SKIP_U24(ctx, size, n) (size -= 3, SKIP_U24(ctx,n)) +#define ASF_SKIP_U32(ctx, size, n) (size -= 4, SKIP_U32(ctx,n)) +#define ASF_SKIP_U64(ctx, size, n) (size -= 8, SKIP_U64(ctx,n)) +#define ASF_READ_GUID(ctx, size, buffer, n) (size -= 16, READ_GUID(ctx,(uint8_t *)buffer,n)) +#define ASF_READ_U8(ctx, size, n) (size -= 1, READ_U8(ctx,n)) +#define ASF_READ_U16(ctx, size, n) (size -= 2, READ_U16(ctx,n)) +#define ASF_READ_U24(ctx, size, n) (size -= 3, READ_U24(ctx,n)) +#define ASF_READ_U32(ctx, size, n) (size -= 4, READ_U32(ctx,n)) +#define ASF_READ_U64(ctx, size, n) (size -= 8, READ_U64(ctx,n)) +#define ASF_READ_STRING(ctx, size, buffer, to_read, n) (size -= to_read, READ_STRING_UTF16(ctx,buffer,to_read,n)) +#define ASF_SKIP_STRING(ctx, size, to_read, n) (size -= to_read, SKIP_STRING_UTF16(ctx,to_read,n)) +#define ASF_READ_BYTES(ctx, size, buffer, to_read) (size -= to_read, READ_BYTES(ctx,buffer,to_read)) +#define ASF_SKIP_BYTES(ctx, size, to_read) (size -= to_read, SKIP_BYTES(ctx,to_read)) + +/* Read variable length field from p_context. */ +#define READ_VLC(p_context, length, value_if_missing, txt) \ + (length) == 1 ? READ_U8(p_context, txt) : \ + (length) == 2 ? READ_U16(p_context, txt) : \ + (length) == 3 ? READ_U32(p_context, txt) : value_if_missing + +#define CHECK_POINT(p_context, amount_to_read) do { \ + if(amount_to_read < 0) return VC_CONTAINER_ERROR_CORRUPTED; \ + if(STREAM_STATUS(p_context)) return STREAM_STATUS(p_context); } while(0) + +/****************************************************************************** +Type definitions. +******************************************************************************/ + +/** Context for our reader + */ +typedef struct +{ + uint64_t start; /* The byte offset start of the current packet in the file */ + uint32_t size; + uint32_t padding_size; + uint64_t send_time; /* read in mS, stored in uS */ + bool eos; + bool corrupted; + uint16_t bad_packets; + + /* All the different Length Types for the VLC codes */ + unsigned int replicated_data_lt; + unsigned int offset_into_media_object_lt; + unsigned int media_object_number_lt; + unsigned int payload_lt; + + unsigned int multiple_payloads; + unsigned int compressed_payloads; + + uint8_t num_payloads; + uint8_t current_payload; + uint32_t current_offset; /* The offset in the current packet for the next byte to be read */ + + /* Info already read */ + uint32_t stream_num; /* Stream number and key-frame flag */ + uint32_t media_object_num; + uint32_t media_object_off; + uint32_t payload_size; + uint32_t subpayload_size; + + /* Info read from the replicated data */ + uint32_t media_object_size; + uint64_t media_object_pts; /**< Presentation timestamp in microseconds */ + uint64_t media_object_pts_delta; /**< Presentation timestamp delta in microseconds */ + +} ASF_PACKET_STATE; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + /* The ID of the stream (the index in the containing array need not be the ID) */ + unsigned int stream_id; + bool b_valid; + + uint8_t extradata[ASF_EXTRADATA_MAX]; + + ASF_PACKET_STATE *p_packet_state; + ASF_PACKET_STATE local_packet_state; + + /* Simple index structure. Corresponds to the simple index in 6.1 of the spec + * This index has locations in packets, not in bytes, and relies on the + * file having fixed-length packets - as is required */ + struct + { + uint64_t offset; /**< Offset to the start of the simple index data */ + uint32_t num_entries; + int64_t time_interval; /* in uS */ + bool incomplete; /* The index does not go to the end of the file */ + } simple_index; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int object_level; + + uint32_t packet_size; /**< Size of a data packet */ + uint64_t packets_num; /**< Number of packets contained in the data object */ + + bool broadcast; /**< Specifies if we are dealing with a broadcast stream */ + int64_t duration; /**< Duration of the stream in microseconds */ + int64_t preroll; /**< Duration of the preroll in microseconds. */ + /* This is the PTS of the first packet; all are offset by this amount. */ + uint64_t time_offset; /**< Offset added to timestamps in microseconds */ + + uint64_t data_offset; /**< Offset to the start of the data packets */ + int64_t data_size; /**< Size of the data contained in the data object */ + + /* The track objects. There's a count of these in VC_CONTAINER_T::tracks_num */ + VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; + + /* Translation table from stream_number to index in the tracks array */ + unsigned char stream_number_to_index[128]; + + /* Data for a top-level index structure as defined in 6.2 of the spec */ + struct + { + uint64_t entry_time_interval; /* The time interval between specifiers, scaled to uS */ + uint32_t specifiers_count; /* The number of specifiers in the file, 0 if no index */ + uint64_t active_specifiers[ASF_TRACKS_MAX]; /* the specifier in use for each track, + * or >=specifiers_count if none */ + uint64_t specifiers_offset; /* The file address of the first specifier. */ + uint32_t block_count; /* The number of index blocks */ + uint64_t blocks_offset; /* The file address of the first block */ + } top_level_index; + + /* A pointer to the track (in the tracks array) which is to be used with a simple index. + * null if there is no such track */ + ASF_VC_CONTAINER_TRACK_MODULE_T *simple_index_track; + + /* Shared packet state. This is used when the tracks are in sync, + and for the track at the earliest position in the file when they are not in sync */ + ASF_PACKET_STATE packet_state; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, + uint64_t track_positions[ASF_TRACKS_MAX], int64_t *p_time, + VC_CONTAINER_SEEK_FLAGS_T flags, unsigned int start_track, + bool seek_on_start_track); + +/****************************************************************************** +GUID list for the different ASF objects +******************************************************************************/ +static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; +static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; +static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; +static const GUID_T asf_guid_index_parameters = {0xD6E229DF, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; +static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; +static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; +static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; +static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; +static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; +static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; +static const GUID_T asf_guid_content_encryption = {0x2211B3FB, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}}; +static const GUID_T asf_guid_ext_content_encryption = {0x298AE614, 0x2622, 0x4C17, {0xB9, 0x35, 0xDA, 0xE0, 0x7E, 0xE9, 0x28, 0x9C}}; +static const GUID_T asf_guid_adv_content_encryption = {0x43058533, 0x6981, 0x49E6, {0x9B, 0x74, 0xAD, 0x12, 0xCB, 0x86, 0xD5, 0x8C}}; +static const GUID_T asf_guid_compatibility = {0x26F18B5D, 0x4584, 0x47EC, {0x9F, 0x5F, 0x0E, 0x65, 0x1F, 0x04, 0x52, 0xC9}}; +static const GUID_T asf_guid_script_command = {0x1EFB1A30, 0x0B62, 0x11D0, {0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; +static const GUID_T asf_guid_mutual_exclusion = {0xD6E229DC, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; + +static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; +static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; + +/****************************************************************************** +List of GUIDs and their associated processing functions +******************************************************************************/ +static struct { + const GUID_T *guid; + const char *psz_name; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t ); + +} asf_object_list[] = +{ + {&asf_guid_header, "header", asf_read_object_header}, + {&asf_guid_file_props, "file properties", asf_read_object_file_properties}, + {&asf_guid_stream_props, "stream properties", asf_read_object_stream_properties}, + {&asf_guid_ext_stream_props, "extended stream properties", asf_read_object_ext_stream_properties}, + {&asf_guid_data, "data", asf_read_object_data}, + {&asf_guid_simple_index, "simple index", asf_read_object_simple_index}, + {&asf_guid_index, "index", asf_read_object_index}, + {&asf_guid_index_parameters, "index parameters", asf_read_object_index_parameters}, + {&asf_guid_header_ext, "header extension", asf_read_object_header_ext}, + {&asf_guid_codec_list, "codec list", asf_read_object_codec_list}, + {&asf_guid_content_description, "content description", asf_read_object_content_description}, + {&asf_guid_ext_content_description, "extended content description", asf_skip_unprocessed_object}, + {&asf_guid_stream_bitrate_props, "stream bitrate properties", asf_read_object_stream_bitrate_props}, + {&asf_guid_language_list, "language list", asf_skip_unprocessed_object}, + {&asf_guid_metadata, "metadata", asf_skip_unprocessed_object}, + {&asf_guid_padding, "padding", asf_skip_unprocessed_object}, + {&asf_guid_compatibility, "compatibility", asf_skip_unprocessed_object}, + {&asf_guid_script_command, "script command", asf_skip_unprocessed_object}, + {&asf_guid_mutual_exclusion, "mutual exclusion", asf_skip_unprocessed_object}, + {&asf_guid_content_encryption, "content encryption", &asf_read_object_content_encryption}, + {&asf_guid_ext_content_encryption, "extended content encryption", &asf_read_object_ext_content_encryption}, + {&asf_guid_adv_content_encryption, "advanced content encryption", &asf_read_object_adv_content_encryption}, + {0, "unknown", asf_skip_unprocessed_object} +}; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Find the track associated with an ASF stream id */ +static VC_CONTAINER_TRACK_T *asf_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int stream_id, + bool b_create) +{ + VC_CONTAINER_TRACK_T *p_track = 0; + VC_CONTAINER_MODULE_T * module = p_ctx->priv->module; + unsigned int i; + + /* discard the key-frame flag */ + stream_id &= 0x7f; + + /* look to see if we have already allocated the stream */ + i = module->stream_number_to_index[stream_id]; + + if(i < p_ctx->tracks_num) /* We found it */ + p_track = p_ctx->tracks[i]; + + if(!p_track && b_create && p_ctx->tracks_num < ASF_TRACKS_MAX) + { + /* Allocate and initialise a new track */ + p_ctx->tracks[p_ctx->tracks_num] = p_track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(p_track) + { + /* store the stream ID */ + p_track->priv->module->stream_id = stream_id; + + /* Store the translation table value */ + module->stream_number_to_index[stream_id] = p_ctx->tracks_num; + + /* count the track */ + p_ctx->tracks_num++; + } + } + + if(!p_track && b_create) + LOG_DEBUG(p_ctx, "could not create track for stream id: %i", stream_id); + + return p_track; +} + +/** Base function used to read an ASF object from the ASF header. + * This will read the object header do lots of sanity checking and pass on the rest + * of the reading to the object specific reading function */ +static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t object_size, offset = STREAM_POSITION(p_ctx); + unsigned int i, unknown_objects = 0, is_data_object; + GUID_T guid; + + /* Sanity check the size of the data */ + if(size && size < ASF_OBJECT_HEADER_SIZE) + { + LOG_DEBUG(p_ctx, "invalid object header (too small)"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid)) + return STREAM_STATUS(p_ctx); + + /* Find out which GUID we are dealing with */ + for( i = 0; asf_object_list[i].guid; i++ ) + { + if(guid.word0 != asf_object_list[i].guid->word0) continue; + if(!memcmp(&guid, asf_object_list[i].guid, sizeof(guid))) break; + } + + LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name); + + /* Bail out if we find too many consecutive unknown objects */ + if(!asf_object_list[i].guid) unknown_objects++; + else unknown_objects = 0; + if(unknown_objects >= ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS) + { + LOG_DEBUG(p_ctx, "too many unknown objects"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + is_data_object = asf_object_list[i].pf_func == asf_read_object_data; + + object_size = READ_U64(p_ctx, "Object Size"); + + /* Sanity check the object size */ + if(object_size < 0 /* Shouldn't ever get that big */ || + /* Minimum size check (data object can have a size == 0) */ + (object_size < ASF_OBJECT_HEADER_SIZE && !(is_data_object && !object_size)) || + /* Only the data object can really be massive */ + (!is_data_object && object_size > ASF_MAX_OBJECT_SIZE)) + { + LOG_DEBUG(p_ctx, "object %s has an invalid size (%"PRIi64")", + asf_object_list[i].psz_name, object_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + if(size && object_size > size) + { + LOG_DEBUG(p_ctx, "object %s is bigger than it should (%"PRIi64" > %"PRIi64")", + asf_object_list[i].psz_name, object_size, size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + size = object_size; + + if(module->object_level >= 2 * ASF_MAX_OBJECT_LEVEL) + { + LOG_DEBUG(p_ctx, "object %s is too deep. skipping", asf_object_list[i].psz_name); + status = asf_skip_unprocessed_object(p_ctx, size - ASF_OBJECT_HEADER_SIZE); + /* Just bail out, hoping we have enough data */ + } + else + { + module->object_level++; + + /* Call the object specific parsing function */ + status = asf_object_list[i].pf_func(p_ctx, size - ASF_OBJECT_HEADER_SIZE); + + module->object_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "object %s appears to be corrupted (%i)", asf_object_list[i].psz_name, status); + } + + /* The stream position should be exactly at the end of the object */ + { + int64_t bytes_processed = STREAM_POSITION(p_ctx) - offset; + + /* fail with overruns */ + if (bytes_processed > size) + { + /* Things have gone really bad here and we ended up reading past the end of the + * object. We could maybe try to be clever and recover by seeking back to the end + * of the object. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of object %s", + bytes_processed-size, asf_object_list[i].psz_name); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Handle underruns by throwing away the data (this should never happen, but we don't really care if it does) */ + if (bytes_processed < size) + { + size -= bytes_processed; + LOG_DEBUG(p_ctx, "%"PRIi64" bytes left unread in object %s", size, asf_object_list[i].psz_name); + + if(size < ASF_MAX_OBJECT_SIZE) + SKIP_BYTES(p_ctx, size); /* read a small amount */ + else + SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */ + } + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF header object */ +static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + + /* Sanity check the size of the data */ + if((size -= 6) < 0) return VC_CONTAINER_ERROR_CORRUPTED; + + SKIP_U32(p_ctx, "Number of Header Objects"); /* FIXME: could use that */ + SKIP_U8(p_ctx, "Reserved1"); + SKIP_U8(p_ctx, "Reserved2"); + + /* Read contained objects */ + module->object_level++; + while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE) + { + offset = STREAM_POSITION(p_ctx); + status = asf_read_object(p_ctx, size); + size -= (STREAM_POSITION(p_ctx) - offset); + } + module->object_level--; + + return status; +} + +/** Reads an ASF extended header object */ +static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t data_size, offset; + + ASF_SKIP_GUID(p_ctx, size, "Reserved Field 1"); + ASF_SKIP_U16(p_ctx, size, "Reserved Field 2"); + data_size = ASF_READ_U32(p_ctx, size, "Header Extension Data Size"); + + if(data_size != size) + LOG_DEBUG(p_ctx, "invalid header extension data size (%"PRIi64",%"PRIi64")", data_size, size); + + CHECK_POINT(p_ctx, size); + + /* Read contained objects */ + module->object_level++; + while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE) + { + offset = STREAM_POSITION(p_ctx); + status = asf_read_object(p_ctx, size); + size -= (STREAM_POSITION(p_ctx) - offset); + } + module->object_level--; + + return status; +} + +/** Reads an ASF file properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t max_packet_size; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + ASF_SKIP_GUID(p_ctx, size, "File ID"); + ASF_SKIP_U64(p_ctx, size, "File Size"); + ASF_SKIP_U64(p_ctx, size, "Creation Date"); + ASF_SKIP_U64(p_ctx, size, "Data Packets Count"); + module->duration = ASF_READ_U64(p_ctx, size, "Play Duration") / UINT64_C(10); /* read in 100nS units, stored in uS */ + ASF_SKIP_U64(p_ctx, size, "Send Duration"); + module->preroll = ASF_READ_U64(p_ctx, size, "Preroll") * UINT64_C(1000); /* read in mS, storedin uS */ + module->broadcast = ASF_READ_U32(p_ctx, size, "Flags") & 0x1; + module->packet_size = ASF_READ_U32(p_ctx, size, "Minimum Data Packet Size"); + max_packet_size = ASF_READ_U32(p_ctx, size, "Maximum Data Packet Size"); + ASF_SKIP_U32(p_ctx, size, "Maximum Bitrate"); + + if(module->preroll < module->duration) module->duration -= module->preroll; + else module->duration = 0; + + /* Sanity check the packet size */ + if(!module->packet_size) + { + LOG_DEBUG(p_ctx, "packet size cannot be 0"); + return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; + } + + if(max_packet_size != module->packet_size) + { + LOG_DEBUG(p_ctx, "asf stream not supported (min packet size: %i != max packet size: %i)", + module->packet_size, max_packet_size); + return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads the bitmapinfoheader structure contained in a stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_bitmapinfoheader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track, int64_t size ) +{ + uint32_t bmih_size, formatdata_size; + uint32_t fourcc; + + /* Sanity check the size of the data */ + if(size < 40 + 11) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Read the preamble to the BITMAPINFOHEADER */ + ASF_SKIP_U32(p_ctx, size, "Encoded Image Width"); + ASF_SKIP_U32(p_ctx, size, "Encoded Image Height"); + ASF_SKIP_U8(p_ctx, size, "Reserved Flags"); + formatdata_size = ASF_READ_U16(p_ctx, size, "Format Data Size"); + + /* Sanity check the size of the data */ + if(formatdata_size < 40 || size < formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED; + bmih_size = ASF_READ_U32(p_ctx, size, "Format Data Size"); + if(bmih_size < 40 || bmih_size > formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Read BITMAPINFOHEADER structure */ + p_track->format->type->video.width = ASF_READ_U32(p_ctx, size, "Image Width"); + p_track->format->type->video.height = ASF_READ_U32(p_ctx, size, "Image Height"); /* Signed */ + ASF_SKIP_U16(p_ctx, size, "Reserved"); + ASF_SKIP_U16(p_ctx, size, "Bits Per Pixel Count"); + ASF_READ_BYTES(p_ctx, size, (char *)&fourcc, 4); /* Compression ID */ + LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); + p_track->format->codec = vfw_fourcc_to_codec(fourcc); + if(p_track->format->codec == VC_CONTAINER_CODEC_UNKNOWN) + p_track->format->codec = fourcc; + ASF_SKIP_U32(p_ctx, size, "Image Size"); + ASF_SKIP_U32(p_ctx, size, "Horizontal Pixels Per Meter"); + ASF_SKIP_U32(p_ctx, size, "Vertical Pixels Per Meter"); + ASF_SKIP_U32(p_ctx, size, "Colors Used Count"); + ASF_SKIP_U32(p_ctx, size, "Important Colors Count"); + + if(!(bmih_size -= 40))return VC_CONTAINER_SUCCESS; + + if(bmih_size > ASF_EXTRADATA_MAX) + { + LOG_DEBUG(p_ctx, "extradata truncated"); + bmih_size = ASF_EXTRADATA_MAX; + } + p_track->format->extradata = p_track->priv->module->extradata; + p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, bmih_size); + + return STREAM_STATUS(p_ctx); +} + +/** Reads the waveformatex structure contained in a stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_waveformatex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track, int64_t size) +{ + uint16_t extradata_size; + + /* Read WAVEFORMATEX structure */ + p_track->format->codec = waveformat_to_codec(ASF_READ_U16(p_ctx, size, "Codec ID")); + p_track->format->type->audio.channels = ASF_READ_U16(p_ctx, size, "Number of Channels"); + p_track->format->type->audio.sample_rate = ASF_READ_U32(p_ctx, size, "Samples per Second"); + p_track->format->bitrate = ASF_READ_U32(p_ctx, size, "Average Number of Bytes Per Second") * 8; + p_track->format->type->audio.block_align = ASF_READ_U16(p_ctx, size, "Block Alignment"); + p_track->format->type->audio.bits_per_sample = ASF_READ_U16(p_ctx, size, "Bits Per Sample"); + extradata_size = ASF_READ_U16(p_ctx, size, "Codec Specific Data Size"); + + CHECK_POINT(p_ctx, size); + + if(!extradata_size) return VC_CONTAINER_SUCCESS; + + /* Sanity check the size of the data */ + if(extradata_size > size) return VC_CONTAINER_ERROR_CORRUPTED; + + if(extradata_size > ASF_EXTRADATA_MAX) + { + LOG_DEBUG(p_ctx, "extradata truncated"); + extradata_size = ASF_EXTRADATA_MAX; + } + p_track->format->extradata = p_track->priv->module->extradata; + p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, extradata_size); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *p_track; + unsigned int ts_length, flags; + VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; + GUID_T stream_type; + int64_t offset; + + ASF_READ_GUID(p_ctx, size, &stream_type, "Stream Type"); + ASF_SKIP_GUID(p_ctx, size, "Error Correction Type"); + + /* The time_offset field is in 100nS units. Scale back to uS */ + module->time_offset = ASF_READ_U64(p_ctx, size, "Time Offset") / UINT64_C(10); + ts_length = ASF_READ_U32(p_ctx, size, "Type-Specific Data Length"); + ASF_SKIP_U32(p_ctx, size, "Error Correction Data Length"); + flags = ASF_READ_U16(p_ctx, size, "Flags"); + ASF_SKIP_U32(p_ctx, size, "Reserved"); + + CHECK_POINT(p_ctx, size); + + /* Zero is not a valid stream id */ + if(!(flags & 0x7F)) goto skip; + + if(!memcmp(&stream_type, &asf_guid_stream_type_video, sizeof(GUID_T))) + type = VC_CONTAINER_ES_TYPE_VIDEO; + else if(!memcmp(&stream_type, &asf_guid_stream_type_audio, sizeof(GUID_T))) + type = VC_CONTAINER_ES_TYPE_AUDIO; + + /* Check we know what to do with this track */ + if(type == VC_CONTAINER_ES_TYPE_UNKNOWN) goto skip; + + /* Sanity check sizes */ + if(ts_length > size) return VC_CONTAINER_ERROR_CORRUPTED; + + p_track = asf_reader_find_track( p_ctx, flags, true); + if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + p_track->format->es_type = type; + + offset = STREAM_POSITION(p_ctx); + if(type == VC_CONTAINER_ES_TYPE_AUDIO) + status = asf_read_waveformatex(p_ctx, p_track, (int64_t)ts_length); + else if(type == VC_CONTAINER_ES_TYPE_VIDEO) + status = asf_read_bitmapinfoheader(p_ctx, p_track, (int64_t)ts_length); + size -= STREAM_POSITION(p_ctx) - offset; + + if(status) return status; + + p_track->priv->module->b_valid = true; + p_track->is_enabled = true; + p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + + /* Codec specific work-arounds */ + switch(p_track->format->codec) + { + case VC_CONTAINER_CODEC_MPGA: + /* Can't guarantee that the data is framed */ + p_track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + break; + default: break; + } + + skip: + if(size) SKIP_BYTES(p_ctx, size); + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF extended stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *p_track; + unsigned int i, name_count, pes_count, length, stream_id; + + ASF_SKIP_U64(p_ctx, size, "Start Time"); + ASF_SKIP_U64(p_ctx, size, "End Time"); + ASF_SKIP_U32(p_ctx, size, "Data Bitrate"); + ASF_SKIP_U32(p_ctx, size, "Buffer Size"); + ASF_SKIP_U32(p_ctx, size, "Initial Buffer Fullness"); + ASF_SKIP_U32(p_ctx, size, "Alternate Data Bitrate"); + ASF_SKIP_U32(p_ctx, size, "Alternate Buffer Size"); + ASF_SKIP_U32(p_ctx, size, "Alternate Initial Buffer Fullness"); + ASF_SKIP_U32(p_ctx, size, "Maximum Object Size"); + ASF_SKIP_U32(p_ctx, size, "Flags"); + stream_id = ASF_READ_U16(p_ctx, size, "Stream Number"); + ASF_SKIP_U16(p_ctx, size, "Stream Language ID Index"); + ASF_SKIP_U64(p_ctx, size, "Average Time Per Frame"); + name_count = ASF_READ_U16(p_ctx, size, "Stream Name Count"); + pes_count = ASF_READ_U16(p_ctx, size, "Payload Extension System Count"); + + CHECK_POINT(p_ctx, size); + + p_track = asf_reader_find_track( p_ctx, stream_id, true); + if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + /* Stream Names */ + for(i = 0; i < name_count; i++) + { + if(size < 4) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_U16(p_ctx, size, "Language ID Index"); + length = ASF_READ_U16(p_ctx, size, "Stream Name Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); /* Stream Name */ + } + + CHECK_POINT(p_ctx, size); + + /* Payload Extension Systems */ + for(i = 0; i < pes_count; i++) + { + if(size < 22) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_GUID(p_ctx, size, "Extension System ID"); + ASF_SKIP_U16(p_ctx, size, "Extension Data Size"); + length = ASF_READ_U32(p_ctx, size, "Extension System Info Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); /* Extension System Info */ + } + + CHECK_POINT(p_ctx, size); + + /* Optional Stream Properties Object */ + if(size >= ASF_OBJECT_HEADER_SIZE) + status = asf_read_object(p_ctx, size); + + return status; +} + +/** Reads an ASF simple index object */ +static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = 0; + uint64_t time_interval, index_duration; + uint32_t count; + unsigned int i; + + ASF_SKIP_GUID(p_ctx, size, "File ID"); + + /* time in 100nS units, converted to uS */ + time_interval = ASF_READ_U64(p_ctx, size, "Index Entry Time Interval") / UINT64_C(10); + ASF_SKIP_U32(p_ctx, size, "Maximum Packet Count"); + count = ASF_READ_U32(p_ctx, size, "Index Entries Count"); + + CHECK_POINT(p_ctx, size); + + if(count > size / 6) + { + LOG_DEBUG(p_ctx, "invalid number of entries in the index (%i, %"PRIi64")", count, size / 6); + count = (uint32_t)(size / 6); + } + + /* Find the track corresponding to this index */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue; + if(p_ctx->tracks[i]->priv->module->simple_index.offset) continue; + break; + } + + /* Skip the index if we can't find the associated track */ + if(i == p_ctx->tracks_num || !count || !time_interval) return VC_CONTAINER_SUCCESS; + track_module = p_ctx->tracks[i]->priv->module; + + track_module->simple_index.offset = STREAM_POSITION(p_ctx); + track_module->simple_index.time_interval = time_interval; + track_module->simple_index.num_entries = count; + + /* Check that the index covers the whole duration of the stream */ + index_duration = (count * time_interval); + if(module->preroll + module->time_offset < index_duration) + index_duration -= module->preroll + module->time_offset; + else + index_duration = 0; + + if((uint64_t)module->duration > index_duration + time_interval) + { + track_module->simple_index.incomplete = true; + } + + LOG_DEBUG(p_ctx, "index covers %fS on %fS", + (float)index_duration / 1E6, (float)module->duration / 1E6); + +#if defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) + for(i = 0; i < count; i++) + { + LOG_FORMAT(p_ctx, "Entry: %u", i); + ASF_SKIP_U32(p_ctx, size, "Packet Number"); + ASF_SKIP_U16(p_ctx, size, "Packet Count"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; + } + size = i * 6; +#else + size = CACHE_BYTES(p_ctx, count * 6); +#endif + + /* Check that the index is complete */ + if(size / 6 != count ) + { + LOG_DEBUG(p_ctx, "index is incomplete (%i entries on %i)", (int)size / 6, count); + track_module->simple_index.num_entries = (uint32_t)(size / 6); + track_module->simple_index.incomplete = true; + } + + /* If we haven't had an index before, or this track is enabled, we'll store this one. + * (Usually there will only be one video track, and it will be enabled, so both tests + * will pass. This check is an attempt to handle content not structured as it should be) */ + if ((!module->simple_index_track) || (p_ctx->tracks[i]->is_enabled)) + { + /* Save the track so we don't have to look for it in when seeking */ + module->simple_index_track = track_module; + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF index object */ +static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t i, specifiers_count, blocks_count; + uint32_t best_specifier_type[ASF_TRACKS_MAX] = {0}; + + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + /* Read the time interval and scale to microseconds */ + module->top_level_index.entry_time_interval + = (uint64_t)ASF_READ_U32(p_ctx, size, "Index Entry Time Interval") * INT64_C(1000); + + module->top_level_index.specifiers_count + = specifiers_count + = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count"); + + module->top_level_index.block_count + = blocks_count + = ASF_READ_U32(p_ctx, size, "Index Blocks Count"); + + CHECK_POINT(p_ctx, size); + + /* Index specifiers. Search for the one we like best */ + if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED; + for(i = 0; i < specifiers_count; i++) + { + uint32_t stream_id = (uint32_t)ASF_READ_U16(p_ctx, size, "Stream Number"); + uint32_t index_type = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Type"); + + /* Find the track index for this stream */ + unsigned track = module->stream_number_to_index[stream_id]; + + if ((track < ASF_TRACKS_MAX) && + (index_type > best_specifier_type[track])) + { + /* We like this better than any we have seen before. Note - if we don't like any + * the file must be subtly corrupt - best we say nothing, and attempt a seek with + * the data for the first specifier, it will be better than nothing. At worst it + * will play until a seek is attempted */ + module->top_level_index.active_specifiers[track] = i; + best_specifier_type[track] = index_type; + } + } + + for (i = 0; i < p_ctx->tracks_num; i++) + { + LOG_DEBUG(p_ctx, "indexing track %"PRIu32" with specifier %"PRIu32, + i, module->top_level_index.active_specifiers[i]); + } + + CHECK_POINT(p_ctx, size); + + /* The blocks start here */ + module->top_level_index.blocks_offset = STREAM_POSITION(p_ctx); + + /* Index blocks */ +#if !(defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE)) + blocks_count = 0; /* Don't log the index. Note we'll get a warning on unprocessed data */ +#endif + /* coverity[dead_error_condition] Code needs to stay there for debugging purposes */ + for(i = 0; i < blocks_count; i++) + { + uint32_t j, k, count = ASF_READ_U32(p_ctx, size, "Index Entry Count"); + + for(j = 0; j < specifiers_count; j++) + { + ASF_SKIP_U64(p_ctx, size, "Block Positions"); + } + for(j = 0; j < count; j++) + { + for(k = 0; k < specifiers_count; k++) + { + ASF_SKIP_U32(p_ctx, size, "Offsets"); + } + } + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF index parameters object */ +static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size ) +{ +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + /* This is added for debugging only. The spec (section 4.9) states that this is also enclosed in + * the index object (see above) and that they are identical. I think they aren't always... */ + uint32_t i, specifiers_count; + + /* Read the time interval in milliseconds */ + ASF_SKIP_U32(p_ctx, size, "Index Entry Time Interval"); + + specifiers_count = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count"); + + CHECK_POINT(p_ctx, size); + + /* Index specifiers. Search for the one we like best */ + if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED; + for(i = 0; i < specifiers_count; i++) + { + ASF_SKIP_U16(p_ctx, size, "Stream Number"); + ASF_SKIP_U16(p_ctx, size, "Index Type"); + } +#endif + CHECK_POINT(p_ctx, size); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF codec list object */ +static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t i, count, length; + + ASF_SKIP_GUID(p_ctx, size, "Reserved"); + count = ASF_READ_U32(p_ctx, size, "Codec Entries Count"); + + CHECK_POINT(p_ctx, size); + + /* Codec entries */ + for(i = 0; i < count; i++) + { + ASF_SKIP_U16(p_ctx, size, "Type"); + length = ASF_READ_U16(p_ctx, size, "Codec Name Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Name"); + length = ASF_READ_U16(p_ctx, size, "Codec Description Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Description"); + length = ASF_READ_U16(p_ctx, size, "Codec Information Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); + + CHECK_POINT(p_ctx, size); + } + + return status; +} + +/** Reads an ASF content description object */ +static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint16_t t_length, a_length, c_length, d_length, r_length; + + t_length = ASF_READ_U16(p_ctx, size, "Title Length"); + a_length = ASF_READ_U16(p_ctx, size, "Author Length"); + c_length = ASF_READ_U16(p_ctx, size, "Copyright Length"); + d_length = ASF_READ_U16(p_ctx, size, "Description Length"); + r_length = ASF_READ_U16(p_ctx, size, "Rating Length"); + + CHECK_POINT(p_ctx, size); + + if(size < t_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, t_length, "Title"); + if(size < a_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, a_length, "Author"); + if(size < c_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, c_length, "Copyright"); + if(size < d_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, d_length, "Description"); + if(size < r_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, r_length, "Rating"); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF stream bitrate properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint16_t i, count; + + count = ASF_READ_U16(p_ctx, size, "Bitrate Records Count"); + + /* Bitrate records */ + if(size < count * 6) return VC_CONTAINER_ERROR_CORRUPTED; + for(i = 0; i < count; i++) + { + ASF_SKIP_U16(p_ctx, size, "Flags"); + ASF_SKIP_U32(p_ctx, size, "Average Bitrate"); + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF content encryption object */ +static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t length; + + length = ASF_READ_U32(p_ctx, size, "Secret Data Length"); + ASF_SKIP_BYTES(p_ctx, size, length); + + length = ASF_READ_U32(p_ctx, size, "Protection Type Length"); + ASF_SKIP_BYTES(p_ctx, size, length); + + length = ASF_READ_U32(p_ctx, size, "Key ID Length"); + ASF_SKIP_BYTES(p_ctx, size, length); + + length = ASF_READ_U32(p_ctx, size, "License URL Length"); + ASF_SKIP_BYTES(p_ctx, size, length); /* null-terminated ASCII string */ + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF extended content encryption object */ +static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t length; + + length = ASF_READ_U32(p_ctx, size, "Data Size"); + ASF_SKIP_BYTES(p_ctx, size, length); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF advanced content encryption object */ +static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t i, count; + + count = ASF_READ_U16(p_ctx, size, "Content Encryption Records Count"); + + for(i = 0; i < count; i++) + { + uint32_t j, rec_count, data_size, length; + + ASF_SKIP_GUID(p_ctx, size, "System ID"); + ASF_SKIP_U32(p_ctx, size, "System Version"); + rec_count = ASF_READ_U16(p_ctx, size, "Encrypted Object Record Count"); + + CHECK_POINT(p_ctx, size); + + for(j = 0; j < rec_count; j++) + { + ASF_SKIP_U16(p_ctx, size, "Encrypted Object ID Type"); + length = ASF_READ_U16(p_ctx, size, "Encrypted Object ID Length"); + if(length > size) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); + CHECK_POINT(p_ctx, size); + } + + data_size = ASF_READ_U32(p_ctx, size, "Data Size"); + if(data_size > size) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, data_size); + CHECK_POINT(p_ctx, size); + } + + return STREAM_STATUS(p_ctx); +} + +/** Skips over an object that is if a type we don't handle, or is nested too deep */ +static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes ignored in unhandled object", size); + + if(size < ASF_MAX_OBJECT_SIZE) + SKIP_BYTES(p_ctx, size); /* read a small amount */ + else + SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */ + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_find_packet_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int search_size = 64*1024; /* should be max packet size according to spec */ +#ifdef ENABLE_CONTAINER_LOG_DEBUG + uint64_t offset = STREAM_POSITION(p_ctx); +#endif + uint8_t h[3]; + VC_CONTAINER_PARAM_UNUSED(p_state); + + /* Limit the search up to what should theoretically be the packet boundary */ + if(module->packet_size) + search_size = module->packet_size - + (STREAM_POSITION(p_ctx) - module->data_offset) % module->packet_size; + + for(; search_size > sizeof(h); search_size--) + { + if(PEEK_BYTES(p_ctx, h, sizeof(h)) != sizeof(h)) + return STREAM_STATUS(p_ctx); + + if(!h[0] && !h[1] && h[2] == 0x82) + { + search_size = 2; + break; /* Got it */ + } + + SKIP_BYTES(p_ctx, 1); + } + + /* If we failed, we just skip to the theoretical packet boundary */ + SKIP_BYTES(p_ctx, search_size); + + LOG_DEBUG(p_ctx, "found potential sync, discarded %"PRIu64" bytes)", + STREAM_POSITION(p_ctx) - offset); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_packet_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state, uint64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint64_t offset = STREAM_POSITION(p_ctx); + uint8_t flags, property_flags, length; + VC_CONTAINER_PARAM_UNUSED(size); + + p_state->start = offset; + + LOG_FORMAT(p_ctx, "Packet Offset: %"PRIu64, offset); + + if ((module->data_size > 0) && (offset >= (module->data_size + module->data_offset))) + { + return VC_CONTAINER_ERROR_EOS; + } + + /* Find out whether we are dealing with error correction data or payload parsing info */ + if( PEEK_U8(p_ctx) >> 7 ) + { + /* We have error correction data */ + flags = READ_U8(p_ctx, "Error Correction Flags"); + length = flags & 0xF; + SKIP_BYTES(p_ctx, length); /* Error correction data */ + } + + /* Payload parsing information */ + flags = READ_U8(p_ctx, "Length Type Flags"); + p_state->multiple_payloads = flags & 1; + property_flags = READ_U8(p_ctx, "Property Flags"); + p_state->replicated_data_lt = (property_flags >> 0) & 0x3; + p_state->offset_into_media_object_lt = (property_flags >> 2) & 0x3; + p_state->media_object_number_lt = (property_flags >> 4) & 0x3; + + /* Sanity check stream number length type */ + if(((property_flags >> 6) & 0x3) != 1) + goto error; + + /* If there's no packet size field we default to the size in the file header. */ + p_state->size = READ_VLC(p_ctx, (flags >> 5) & 0x3 /* Packet length type */, + module->packet_size, "Packet Length"); + + READ_VLC(p_ctx, (flags>>1)&0x3 /* Sequence type */, 0, "Sequence"); + p_state->padding_size = READ_VLC(p_ctx, (flags>>3)&0x3 /* Padding length type */, 0, "Padding Length"); + p_state->send_time = READ_U32(p_ctx, "Send Time") * UINT64_C(1000); /* Read in millisecond units, stored in uS */ + SKIP_U16(p_ctx, "Duration"); /* in milliseconds */ + + p_state->num_payloads = 1; + p_state->current_payload = 0; + if(p_state->multiple_payloads) + { + LOG_FORMAT(p_ctx, "Multiple Payloads"); + flags = READ_U8(p_ctx, "Payload Flags"); + p_state->num_payloads = flags & 0x3F; + LOG_FORMAT(p_ctx, "Number of Payloads: %i", p_state->num_payloads); + p_state->payload_lt = (flags >> 6) & 3; + + /* Sanity check */ + if(!p_state->num_payloads) goto error; + } + + /* Update the current offset in the packet. */ + p_state->current_offset = STREAM_POSITION(p_ctx) - offset; + + /* Sanity check offset */ + if(p_state->current_offset > p_state->size) goto error; + + /* Sanity check padding size */ + if(p_state->padding_size + p_state->current_offset > p_state->size) goto error; + + /* Sanity check packet size */ + if(!module->broadcast && + (p_state->size != module->packet_size)) goto error; + + return STREAM_STATUS(p_ctx); + + error: + LOG_FORMAT(p_ctx, "Invalid payload parsing information (offset %"PRIu64")", STREAM_POSITION(p_ctx)); + return STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS ? + VC_CONTAINER_ERROR_CORRUPTED : STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_payload_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state /* uint64_t size */ ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t rep_data_length; + + if(p_state->current_payload >= p_state->num_payloads) + return VC_CONTAINER_ERROR_CORRUPTED; + + p_state->stream_num = READ_U8(p_ctx, "Stream Number"); + if(!(p_state->stream_num & 0x7F)) return VC_CONTAINER_ERROR_CORRUPTED; + + p_state->media_object_num = READ_VLC(p_ctx, p_state->media_object_number_lt, 0, "Media Object Number"); + + /* For a compressed packet this field is a timestamp, and is moved to p_state->media_object_pts later */ + p_state->media_object_off = READ_VLC(p_ctx, p_state->offset_into_media_object_lt, 0, "Offset Into Media Object"); + rep_data_length = READ_VLC(p_ctx, p_state->replicated_data_lt, 0, "Replicated Data Length"); + + /* Sanity check the replicated data length */ + if(rep_data_length && rep_data_length != 1 && + (rep_data_length < 8 || + STREAM_POSITION(p_ctx) - p_state->start + p_state->padding_size + rep_data_length > p_state->size)) + { + LOG_FORMAT(p_ctx, "invalid replicated data length"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Read what we need from the replicated data */ + if(rep_data_length > 1) + { + p_state->media_object_size = READ_U32(p_ctx, "Media Object Size"); + p_state->media_object_pts = READ_U32(p_ctx, "Presentation Time") * UINT64_C(1000); + p_state->compressed_payloads = 0; + SKIP_BYTES(p_ctx, rep_data_length - 8); /* Rest of replicated data */ + } + else if(rep_data_length == 1) + { + LOG_FORMAT(p_ctx, "Compressed Payload Data"); + p_state->media_object_pts_delta = READ_U8(p_ctx, "Presentation Time Delta") * UINT64_C(1000); + p_state->compressed_payloads = 1; + + /* Move the pts from media_object_off where it was read, and adjust it */ + p_state->media_object_off *= UINT64_C(1000); + p_state->media_object_pts = p_state->media_object_off - p_state->media_object_pts_delta; + p_state->media_object_off = 0; + p_state->media_object_size = 0; + } + else + { + p_state->media_object_size = 0; + p_state->media_object_pts = p_state->send_time; + p_state->compressed_payloads = 0; + } + + if(p_state->media_object_pts > module->preroll + module->time_offset) + p_state->media_object_pts -= (module->preroll + module->time_offset); + else p_state->media_object_pts = 0; + + p_state->payload_size = p_state->size - p_state->padding_size - (STREAM_POSITION(p_ctx) - p_state->start); + if(p_state->multiple_payloads) + { + p_state->payload_size = READ_VLC(p_ctx, p_state->payload_lt, 0, "Payload Length"); + if(!p_state->payload_size) return VC_CONTAINER_ERROR_CORRUPTED; + } + else + LOG_FORMAT(p_ctx, "Payload Length: %i", p_state->payload_size); + + if(p_state->payload_size >= p_state->size) return VC_CONTAINER_ERROR_CORRUPTED; + + p_state->subpayload_size = p_state->payload_size; + + /* Update current_offset to reflect the variable number of bytes we just read */ + p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start; + + /* Sanity check offset */ + if(p_state->current_offset > p_state->size) return VC_CONTAINER_ERROR_CORRUPTED; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + VC_CONTAINER_PARAM_UNUSED(size); + + SKIP_GUID(p_ctx, "File ID"); + SKIP_U64(p_ctx, "Total Data Packets"); + SKIP_U16(p_ctx, "Reserved"); + module->data_offset = STREAM_POSITION(p_ctx); + + /* Initialise state for all tracks */ + module->packet_state.start = module->data_offset; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; + p_track->priv->module->p_packet_state = &module->packet_state; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +/* Read the next sub-payload or next payload */ +static VC_CONTAINER_STATUS_T asf_read_next_payload_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state, uint32_t *pi_track, uint32_t *pi_length) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + if(p_state->subpayload_size) + { + /* We still haven't read the current subpayload, return the info we already have */ + goto end; + } + + /* Check if we're done reading a packet */ + if(p_state->current_payload >= p_state->num_payloads) + { + /* Skip the padding at the end */ + if(p_state->size) + { + int32_t pad_length = p_state->size - (STREAM_POSITION(p_ctx) - p_state->start); + if(pad_length < 0) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_BYTES(p_ctx, pad_length); /* Padding */ + } + + /* Read the header for the next packet */ + module->object_level = 0; /* For debugging */ + status = asf_read_packet_header( p_ctx, p_state, (uint64_t)0/*size???*/ ); + module->object_level = 1; /* For debugging */ + if(status != VC_CONTAINER_SUCCESS) return status; + } + + /* Check if we're done reading a payload */ + if(!p_state->payload_size) + { + /* Read the payload header */ + status = asf_read_payload_header( p_ctx, p_state ); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + /* For compressed payloads, payload_size != subpayload_size */ + if(p_state->compressed_payloads && p_state->payload_size) + { + p_state->payload_size--; + p_state->subpayload_size = READ_U8(p_ctx, "Sub-Payload Data Length"); + if(p_state->subpayload_size > p_state->payload_size) + { + /* TODO: do something ? */ + LOG_DEBUG(p_ctx, "subpayload is too big"); + p_state->subpayload_size = p_state->payload_size; + } + p_state->media_object_off = 0; + p_state->media_object_size = p_state->subpayload_size; + p_state->media_object_pts += p_state->media_object_pts_delta; + } + + end: + /* We've read the payload header, return the requested info */ + if(pi_track) *pi_track = module->stream_number_to_index[p_state->stream_num & 0x7F]; + if(pi_length) *pi_length = p_state->subpayload_size; + + p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +/* read the next payload (not sub-payload) */ +static VC_CONTAINER_STATUS_T asf_read_next_payload( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state, uint8_t *p_data, uint32_t *pi_size ) +{ + uint32_t subpayload_size = p_state->subpayload_size; + + if(p_data && *pi_size < subpayload_size) subpayload_size = *pi_size; + + if(!p_state->subpayload_size) + return VC_CONTAINER_SUCCESS; + + p_state->payload_size -= subpayload_size; + if(!p_state->payload_size) p_state->current_payload++; + p_state->subpayload_size -= subpayload_size; + p_state->media_object_off += subpayload_size; + + if(p_data) *pi_size = READ_BYTES(p_ctx, p_data, subpayload_size); + else *pi_size = SKIP_BYTES(p_ctx, subpayload_size); + + p_state->current_offset += subpayload_size; + + if(*pi_size!= subpayload_size) + return STREAM_STATUS(p_ctx); + + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +/*****************************************************************************/ +/** Read data from the ASF file + +@param p_ctx Context for the file being read from + +@param p_packet Packet information. Includes data buffer and stream ID as aprropriate. + +@param flags Flags controlling the read. + May request reading only, skipping a packet or force access to a set track. + +******************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *global_module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + ASF_PACKET_STATE *p_state; + uint32_t buffer_size = 0, track, data_size; + uint64_t state_pos; + + LOG_DEBUG(p_ctx, "asf_reader_read track %"PRIu32" flags %u", p_packet->track, flags); + + /* If a specific track has been selected, we need to use the track packet state */ + if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + vc_container_assert(p_packet->track < p_ctx->tracks_num); + /* The state to use is the one referred to by the track we selected */ + p_state = p_ctx->tracks[p_packet->track]->priv->module->p_packet_state; + } + else + { + /* No specific track was selected. Read the next data from the global position. */ + p_state = &global_module->packet_state; + } + + /* Stop if the stream can't be read */ + if(p_state->eos) return VC_CONTAINER_ERROR_EOS; + if(p_state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; + + /* If we aren't in the right position in the file go there now. */ + state_pos = p_state->start + p_state->current_offset; + if ((uint64_t)STREAM_POSITION(p_ctx) != state_pos) + { + LOG_DEBUG(p_ctx, "seeking from %"PRIu64" to %"PRIu64, STREAM_POSITION(p_ctx), state_pos); + SEEK(p_ctx, state_pos); + } + + /* Look at the next payload header */ + status = asf_read_next_payload_header( p_ctx, p_state, &track, &data_size ); + if((status == VC_CONTAINER_ERROR_CORRUPTED) + && (p_state->bad_packets < ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS)) + { + /* If the current packet is corrupted we will try to search for the next packet */ + uint32_t corrupted = p_state->bad_packets; + LOG_DEBUG(p_ctx, "packet offset %"PRIi64" is corrupted", p_state->start); + memset(p_state, 0, sizeof(*p_state)); + p_state->bad_packets = corrupted + 1; + + /* TODO: flag discontinuity */ + + if(asf_find_packet_header(p_ctx, p_state) == VC_CONTAINER_SUCCESS) + { + p_state->start = STREAM_POSITION(p_ctx); + return VC_CONTAINER_ERROR_CONTINUE; + } + } + if(status == VC_CONTAINER_ERROR_EOS) p_state->eos = true; + if(status == VC_CONTAINER_ERROR_CORRUPTED) p_state->corrupted = true; + if(status != VC_CONTAINER_SUCCESS) + { + return status; + } + + p_state->bad_packets = 0; + + /* bad track number or track is disabled */ + if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) + { + LOG_DEBUG(p_ctx, "skipping packet because track %u is invalid or disabled", track); + + /* Skip payload by reading with a null buffer */ + status = asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + if(status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + + track_module = p_ctx->tracks[track]->priv->module; + + /* If we are reading from the global state, and the track we found is not on the global state, + * either skip the data or reconnect it to the global state */ + if ((p_state == &global_module->packet_state) && + (track_module->p_packet_state != &global_module->packet_state)) + { + uint64_t track_pos = + track_module->p_packet_state->start + + track_module->p_packet_state->current_offset; + + /* Check if the end of the current packet is beyond the track's position */ + if (track_pos > state_pos + track_module->p_packet_state->size) + { + LOG_DEBUG(p_ctx, "skipping packet from track %u as it has already been read", track); + status = asf_read_next_payload(p_ctx, p_state, 0, &data_size); + + if(status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + else + { + LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" back to global state", track, track_pos); + track_module->p_packet_state = &global_module->packet_state; + + /* Update the global state to the precise position */ + global_module->packet_state = track_module->local_packet_state; + return VC_CONTAINER_ERROR_CONTINUE; + } + } + + /* If we are forcing, and the data is from a different track, skip it. + * We may need to move the track we want onto a local state. */ + if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + && (track != p_packet->track)) + { + track_module = p_ctx->tracks[p_packet->track]->priv->module; + + /* If the track we found is on the same state as the track we want they must both be on the global state */ + if (p_ctx->tracks[track]->priv->module->p_packet_state == p_state) + { + LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" away from global state", p_packet->track, state_pos); + + /* Change the track we want onto a local state */ + track_module->p_packet_state = &track_module->local_packet_state; + + /* Copy the global state into the local state for the track we are forcing */ + track_module->local_packet_state = global_module->packet_state; + } + + LOG_DEBUG(p_ctx, "skipping packet from track %u while forcing %u", track, p_packet->track); + status = asf_read_next_payload(p_ctx, track_module->p_packet_state, 0, &data_size ); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we arrive here either the data is from the track we are forcing, or we are not forcing + * and we haven't already read the data while forcing that track */ + + /* If skip, and no info required, skip over it and return now. */ + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) + return asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + + /* Fill-in the packet information */ + if(p_state->media_object_pts == ASF_UNKNOWN_PTS || p_state->media_object_off) + p_packet->dts = p_packet->pts = VC_CONTAINER_TIME_UNKNOWN; + else + p_packet->dts = p_packet->pts = p_state->media_object_pts; + + p_packet->flags = 0; + + if(p_state->stream_num >> 7) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + + if(!p_state->media_object_off) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if(p_state->media_object_size && + p_state->media_object_off + data_size >= p_state->media_object_size) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if(!p_state->media_object_size) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + p_packet->track = track; + + p_packet->frame_size = p_state->media_object_size; + + p_packet->size = data_size; + + /* If the skip flag is set (Info must have been too) skip the data and return */ + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + /* Skip payload by reading with a null buffer */ + return asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + } + else if(flags & VC_CONTAINER_READ_FLAG_INFO) + { + return VC_CONTAINER_SUCCESS; + } + + /* Read the payload data */ + buffer_size = p_packet->buffer_size; + status = asf_read_next_payload(p_ctx, p_state, p_packet->data, &buffer_size ); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + p_packet->size = buffer_size; + if(buffer_size != data_size) + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + LOG_DEBUG(p_ctx, "asf_reader_read exit %u PTS %"PRIi64" track %"PRIu32, status, p_packet->pts, track); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_index_find_time( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T* track_module, int64_t time, uint32_t *packet_num, bool forward ) +{ + VC_CONTAINER_STATUS_T status; + uint32_t entry, previous_packet_num; + bool eos = false; + + /* Default to beginning of file in case of error */ + *packet_num = 0; + + /* Special case - time zero is beginning of file */ + if(time == 0) {return VC_CONTAINER_SUCCESS;} + + /* Sanity checking */ + if(!track_module->simple_index.num_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + if(!track_module->simple_index.time_interval) return VC_CONTAINER_ERROR_CORRUPTED; + + entry = time / track_module->simple_index.time_interval; + LOG_DEBUG(p_ctx, "entry: %i, offset: %"PRIi64", interv: %"PRIi64, entry, + track_module->simple_index.offset, track_module->simple_index.time_interval); + if(entry >= track_module->simple_index.num_entries) + { + entry = track_module->simple_index.num_entries - 1; + eos = true; + } + + /* Fetch the entry from the index */ + status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry); + if(status != VC_CONTAINER_SUCCESS) return status; + *packet_num = READ_U32(p_ctx, "Packet Number"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return STREAM_STATUS(p_ctx); + + /* When asking for the following keyframe we need to find the next entry with a greater + * packet number */ + previous_packet_num = *packet_num; + while(!eos && forward && previous_packet_num == *packet_num) + { + if(++entry == track_module->simple_index.num_entries) {eos = true; break;} + status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry); + if(status != VC_CONTAINER_SUCCESS) break; + *packet_num = READ_U32(p_ctx, "Packet Number"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; + } + + if(eos && track_module->simple_index.incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + else if(eos) return VC_CONTAINER_ERROR_EOS; + else return STREAM_STATUS(p_ctx); +} + +#if 0 +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_index_find_packet( VC_CONTAINER_T *p_ctx, + unsigned int track, uint32_t *packet_num, bool forward ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = 0; + uint32_t i, prev_packet_num = 0, next_packet_num; + bool eos = false; + + /* Sanity checking */ + if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED; + track_module = p_ctx->tracks[track]->priv->module; + if(!track_module->num_index_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + if(!track_module->index_time_interval) return VC_CONTAINER_ERROR_CORRUPTED; + + status = SEEK(p_ctx, track_module->index_offset); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Loop through all the entries in the index */ + for(i = 0; i < track_module->num_index_entries; i++) + { + next_packet_num = READ_U32(p_ctx, "Packet Number"); + SKIP_U16(p_ctx, "Packet Count"); + if(next_packet_num > *packet_num) break; + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; + prev_packet_num = next_packet_num; + } + if(i == track_module->num_index_entries ) eos = true; + + if(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS && !eos) + { + if(forward) *packet_num = next_packet_num; + else *packet_num = prev_packet_num; + } + + if(eos && track_module->index_incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + else if(eos) return VC_CONTAINER_ERROR_EOS; + else return STREAM_STATUS(p_ctx); +} +#endif + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_find_next_frame( VC_CONTAINER_T *p_ctx, + unsigned int track, ASF_PACKET_STATE *p_state, bool keyframe, bool timeout ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t data_track, data_size; + unsigned int packets = 0; + + if(p_ctx->tracks[track]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + keyframe = false; + + /* We still need to go to the right payload */ + while(status == VC_CONTAINER_SUCCESS && + (!timeout || packets++ < ASF_MAX_SEARCH_PACKETS)) + { + status = asf_read_next_payload_header( p_ctx, p_state, &data_track, &data_size ); + if(status != VC_CONTAINER_SUCCESS) break; + + if(data_track == track && ((p_state->stream_num >> 7) || !keyframe) && + !p_state->media_object_off) break; + + /* Skip payload */ + status = asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + } + + return status; +} + +/*****************************************************************************/ +/* Helper for asf_reader_seek - seek when there is a top-level index (spec section 6.2) */ +static VC_CONTAINER_STATUS_T seek_by_top_level_index( + VC_CONTAINER_T *p_ctx, + int64_t *p_time, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned index; + uint64_t time = 0; + uint64_t block_address = module->top_level_index.blocks_offset; + uint64_t track_positions[ASF_TRACKS_MAX]; + + VC_CONTAINER_PARAM_UNUSED(mode); + LOG_DEBUG(p_ctx, "seek_by_top_level_index"); + + for (index = 0; index < ASF_TRACKS_MAX; ++index) + { + /* Set all to a stupid value */ + track_positions[index] = UINT64_MAX; + } + + /* Loop through the index blocks to find the one(s) that deal with the time(s) in question. + * Note that most ASF files only have one index block. */ + for (index = 0; index < module->top_level_index.block_count; ++index) + { + uint64_t block_duration, block_position; + uint32_t index_entry_count, stream; + LOG_DEBUG(p_ctx, "looking for index blocks at offset %"PRIu64, block_address); + status = SEEK(p_ctx, block_address); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Read the number of entries for this index block. */ + index_entry_count = READ_U32(p_ctx, "Index Entry Count"); + + /* Turn into a duration */ + block_duration = (uint64_t)index_entry_count * module->top_level_index.entry_time_interval; + + /* Go through each stream */ + for (stream = 0; stream < p_ctx->tracks_num; ++stream) + { + /* Work out the track's target time */ + uint64_t track_time = *p_time + module->preroll + module->time_offset; + + /* Have we the correct index block for the seek time? */ + if ((time <= track_time) && (track_time < time + block_duration)) + { + /* We have the correct index block for the seek time. Work out where in it. */ + uint32_t block_index = (track_time - time) / module->top_level_index.entry_time_interval; + uint64_t active_specifier = module->top_level_index.active_specifiers[stream]; + uint64_t new_position; + + /* Read the Block Positions value for the correct specifier */ + status = SEEK(p_ctx, + block_address + INT64_C(4) + + active_specifier * INT64_C(8)); + if (status != VC_CONTAINER_SUCCESS) + { + return status; + } + block_position = READ_U32(p_ctx, "Block Position"); + + /* Read the target address for the stream */ + status = SEEK(p_ctx, block_address + 4 /* skip index entry count */ + + (UINT64_C(8) * module->top_level_index.specifiers_count) /* block positions */ + + (UINT64_C(4) * module->top_level_index.specifiers_count * block_index) /* prior index entries */ + + (UINT64_C(4) * active_specifier)); /* correct specifier */ + LOG_DEBUG(p_ctx, "reading at %"PRIu64, STREAM_POSITION(p_ctx)); + + new_position = module->data_offset + block_position + (uint64_t)READ_U32(p_ctx, "Offset"); + LOG_DEBUG(p_ctx, "actual address for stream %"PRIu32" = %"PRIu64, stream, new_position); + track_positions[stream] = new_position; + } + } + + /* Work out where the next block is */ + block_address += (UINT64_C(8) * module->top_level_index.specifiers_count) + + (UINT64_C(4) * module->top_level_index.specifiers_count * index_entry_count); + } + + return seek_to_positions(p_ctx, track_positions, p_time, flags, 0, 0); +} + +/* Helper for asf_reader_seek - + * Given a set of positions seek the tracks. The status is the result of physically seeking each one. + * It is expected that the positions will be before *p_time; if the flags require it search + * for the next keyframe that is at or above *p_time. */ +static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, uint64_t track_positions[ASF_TRACKS_MAX], + int64_t *p_time, VC_CONTAINER_SEEK_FLAGS_T flags, + unsigned int start_track, bool seek_on_start_track) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint64_t global_position = UINT64_MAX; + unsigned int lowest_track, index, tracks; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + int64_t track_best_pts[ASF_TRACKS_MAX]; + + if (*p_time == 0) + { + // Special case: Time 0 means beginning of file. Don't search for the matching packet(s). + memset(&module->packet_state, 0, sizeof(module->packet_state)); + module->packet_state.start = track_positions[0]; + status = SEEK(p_ctx, module->packet_state.start); + + // Set each track to using the global state + for(index = 0; index < p_ctx->tracks_num; index++) + { + p_ctx->tracks[index]->priv->module->p_packet_state = &module->packet_state; + } + + return status; + } + + for(tracks = 0, index = start_track; tracks < p_ctx->tracks_num; + tracks++, index = (index+1) % p_ctx->tracks_num) + { + uint32_t data_size; + + /* Use an on-stack packet state. We can't use the global state, as we must leave it at + * the lowest position. We can't use any track's private state, as we will move it past + * the desired location. */ + ASF_PACKET_STATE private_state; + memset(&private_state, 0, sizeof(private_state)); + + track_best_pts[index] = INT64_MAX; + + status = SEEK(p_ctx, track_positions[index]); + + /* loop until we find the packet we're looking for. + * stop when we've seen a big enough PTS, and are on a key frame */ + while(status == VC_CONTAINER_SUCCESS) + { + /* Get the next key-frame */ + status = asf_reader_find_next_frame(p_ctx, index, &private_state, true, true); + if(status == VC_CONTAINER_SUCCESS) + { + /* Get the PTS, if any */ + int64_t pts = (int64_t)private_state.media_object_pts; + + if(pts != ASF_UNKNOWN_PTS) + { + if ((track_best_pts[index] == INT64_MAX) /* we don't have a time yet */ + || (pts <= *p_time) /* it's before our target */ + || (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) /* we want after target */ + { + /* Store this time. It's the best yet. */ + track_best_pts[index] = pts; + + /* Update the desired position */ + track_positions[index] = private_state.start + private_state.current_offset; + + /* Copy the local state into this track's private state */ + p_ctx->tracks[index]->priv->module->local_packet_state = private_state; + + LOG_DEBUG(p_ctx, "seek forward track %u to pts %"PRIu64, + index, track_best_pts[index]); + } + + /* If we've got to our target time we can stop. */ + if (pts >= *p_time) + { + /* Then stop. */ + break; + } + } + + status = asf_read_next_payload(p_ctx, &private_state, 0, &data_size ); + } + } + + /* If we are seeking using a specific track, usually this is the video track + * and we want all the other tracks to start at the same time or later */ + if (seek_on_start_track && start_track == index) + { + flags |= VC_CONTAINER_SEEK_FLAG_FORWARD; + *p_time = track_best_pts[index]; + } + + { + ASF_PACKET_STATE *p_state = &p_ctx->tracks[index]->priv->module->local_packet_state; + + LOG_DEBUG(p_ctx, "seek track %u to pts %"PRIu64" (key:%i,moo:%i)", + index, track_best_pts[index], p_state->stream_num >> 7, p_state->media_object_off); + } + } + + /* Find the smallest track address in track_positions. This will be the global position */ + /* Also the lowest PTS in track_best_pts, this will be the new global PTS */ + for (index = 0, lowest_track = 0; index < p_ctx->tracks_num; ++index) + { + /* If it is smaller, remember it */ + if (track_positions[index] < global_position) + { + global_position = track_positions[index]; + lowest_track = index; + } + + /* Put the lowest PTS into entry 0 of the array */ + if ((track_best_pts[index] != INT64_MAX) && (track_best_pts[index] < track_best_pts[0])) + { + track_best_pts[0] = track_best_pts[index]; + } + } + + /* Update the caller with the lowest real PTS, if any. (we may have already done this above) */ + if (track_best_pts[0] != INT64_MAX) + { + *p_time = track_best_pts[0]; + } + else + { + LOG_DEBUG(p_ctx, "no PTS suitable to update the caller"); + } + + /* As we did an extra read on the index track past the desired location seek back to it */ + status = SEEK(p_ctx, global_position); + + /* Copy the packet state for the stream with the lowest address into the global state */ + module->packet_state = p_ctx->tracks[lowest_track]->priv->module->local_packet_state; + + for(index = 0; index < p_ctx->tracks_num; index++) + { + VC_CONTAINER_TRACK_MODULE_T* track_mod = p_ctx->tracks[index]->priv->module; + + /* If the track position is the global position, or it is invalid, use the global state */ + if ((track_positions[index] <= global_position) || (track_positions[index] == UINT64_MAX)) + { + track_mod->p_packet_state = &module->packet_state; + } + else + { + /* Track is not at the global position. Use the local state. */ + LOG_DEBUG(p_ctx, "track %u local position %"PRIu64, index, track_positions[index]); + track_mod->p_packet_state = &track_mod->local_packet_state; + } + } + + return status; +} + +/*****************************************************************************/ +/* Seek to a location in the file, using whatever indices are available + * If flags bit VC_CONTAINER_SEEK_FLAG_FORWARD is set the position is guaranteed to + * be a keyframe at or after the requested location. Conversely if it is not set + * the position is guaranteed to be at or before the request. */ +static VC_CONTAINER_STATUS_T asf_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_time, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_EOS; /* initialised to known fail state */ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int stream; + + VC_CONTAINER_PARAM_UNUSED(mode); + + LOG_DEBUG(p_ctx, "asf_reader_seek"); + + /* Prefer the top-level index to the simple index - it has byte offsets not packet offsets, + * and is likely to have separate tables for every track */ + if (module->top_level_index.block_count) + { + status = seek_by_top_level_index(p_ctx, p_time, mode, flags); + } + else + { + uint64_t track_positions[ASF_TRACKS_MAX]; + int seek_track = -1; + uint32_t packet_num; + uint64_t new_position; + + if (*p_time == 0) + { + // Special optimisation - for time zero just go to the beginning. + packet_num = 0; + } + /* If there is a simple index use the packet number from it */ + else if (module->simple_index_track) + { + /* Correct time desired */ + uint64_t track_time = *p_time + module->preroll + module->time_offset; + + LOG_DEBUG(p_ctx, "using simple index"); + + /* Search the index for the correct packet */ + status = asf_reader_index_find_time(p_ctx, module->simple_index_track, track_time, + &packet_num, flags & VC_CONTAINER_SEEK_FLAG_FORWARD); + } + else + { + /* No index at all. Use arithmetic to guess the packet number. */ + LOG_DEBUG(p_ctx, "index not usable %u", (unsigned)status); + + if (module->packets_num == 0) + { + /* This is a broadcast stream, and we can't do the arithmetic. + * Set it to a value that will guarantee a seek fail. */ + LOG_DEBUG(p_ctx, "no packets in file"); + packet_num = UINT32_MAX; + } + else + { + packet_num = *p_time * module->packets_num / module->duration; + } + } + + /* calculate the byte address of the packet, relative to the start of data */ + new_position = (uint64_t)packet_num * (uint64_t)module->packet_size; + + LOG_DEBUG(p_ctx, "packet number %"PRIu32" approx byte offset %"PRIu64 , packet_num, new_position + module->data_offset); + if (new_position > (uint64_t)module->data_size) + { + new_position = module->data_size; + LOG_DEBUG(p_ctx, "arithmetic error, seeking to end of file %" PRIu64 , new_position + module->data_offset); + } + + new_position += module->data_offset; + + for(stream = 0; stream < p_ctx->tracks_num; stream++) + { + /* Use the 1st enabled video track as the seek stream */ + if(p_ctx->tracks[stream]->format->es_type == + VC_CONTAINER_ES_TYPE_VIDEO && + p_ctx->tracks[stream]->is_enabled && seek_track < 0) + seek_track = stream; + + track_positions[stream] = new_position; + } /* repeat for all tracks */ + + /* Work out if we actually got anywhere. If so, save the positions for the subsequent reads */ + status = seek_to_positions(p_ctx, track_positions, p_time, flags, + seek_track < 0 ? 0 : seek_track, seek_track >= 0); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + +/* FIXME: metadata is currently shared across all readers so freeing + it is left to the common layer but this isn't necessarily + the best solution. + for(i = 0; i meta_num; i++) + free(p_ctx->meta[i]); + if(p_ctx->meta_num) free(p_ctx->meta); + p_ctx->meta_num = 0; +*/ + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks_num = 0; + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + unsigned int i; + GUID_T guid; + + /* Check for an ASF top-level header object */ + if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) < sizeof(guid) || + memcmp(&guid, &asf_guid_header, sizeof(guid))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We are dealing with an ASF file + */ + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + + /* Set the translation table to all error values */ + memset(&module->stream_number_to_index, 0xff, sizeof(module->stream_number_to_index)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + /* Read the top level header object */ + status = asf_read_object(p_ctx, INT64_C(0)); + if(status != VC_CONTAINER_SUCCESS) + goto error; + + /* Bail out if we didn't find a track */ + if(!p_ctx->tracks_num) {status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; goto error;} + + /* + * The top level data object must come next + */ + if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid) || + memcmp(&guid, &asf_guid_data, sizeof(guid))) + goto error; + + LOG_FORMAT(p_ctx, "Object Name: data"); + module->data_size = READ_U64(p_ctx, "Object Size"); + + /* If the data size was supplied remove the size of the common object header and the local header for this object */ + if(module->data_size) module->data_size -= ASF_OBJECT_HEADER_SIZE + 16 + 8 + 2; + + /* Sanity check the data object size */ + if(module->data_size < 0) + goto error; + + module->object_level++; + SKIP_GUID(p_ctx, "File ID"); + module->packets_num = READ_U64(p_ctx, "Total Data Packets"); + if(module->broadcast) module->packets_num = 0; + SKIP_U16(p_ctx, "Reserved"); + + if (module->packet_size) + { + LOG_DEBUG(p_ctx, "object size %"PRIu64" means %f packets", + module->data_size, (float)(module->data_size) / (float)(module->packet_size)); + } + + module->data_offset = STREAM_POSITION(p_ctx); + LOG_DEBUG(p_ctx, "expect end of data at address %"PRIu64, module->data_size + module->data_offset); + + module->object_level--; + + /* + * We now have all the information we really need to start playing the stream + */ + + /* Initialise state for all tracks */ + module->packet_state.start = module->data_offset; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; + p_track->priv->module->p_packet_state = &module->packet_state; + } + + p_ctx->priv->pf_close = asf_reader_close; + p_ctx->priv->pf_read = asf_reader_read; + p_ctx->priv->pf_seek = asf_reader_seek; + + if(STREAM_SEEKABLE(p_ctx)) + { + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK; + } + + p_ctx->duration = module->duration; + + /* Check if we're done */ + if(!module->data_size || !STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_SUCCESS; + + /* If the stream is seekable and not a broadcast stream, we want to use any index there + * might be at the end of the stream */ + + /* Seek back to the end of the data object */ + if( SEEK(p_ctx, module->data_offset + module->data_size) == VC_CONTAINER_SUCCESS) + { + /* This will catch the simple index object if it is there */ + do { + status = asf_read_object(p_ctx, INT64_C(0)); + } while(status == VC_CONTAINER_SUCCESS); + } + + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->priv->module->simple_index.offset) + LOG_DEBUG(p_ctx, "track %i has an index", i); + } + + /* Seek back to the start of the data */ + return SEEK(p_ctx, module->data_offset); + + error: + if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "asf: error opening stream (%i)", status); + if(module) asf_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open asf_reader_open +#endif diff --git a/containers/asf/asf_writer.c b/containers/asf/asf_writer.c new file mode 100755 index 0000000..60da7ce --- /dev/null +++ b/containers/asf/asf_writer.c @@ -0,0 +1,577 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +//#define ENABLE_CONTAINERS_LOG_FORMAT +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "containers/core/containers_logging.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level + +VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ASF_TRACKS_MAX 16 +#define ASF_OBJECT_HEADER_SIZE (16+8) + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef enum { + ASF_OBJECT_TYPE_UNKNOWN = 0, + ASF_OBJECT_TYPE_HEADER, + ASF_OBJECT_TYPE_FILE_PROPS, + ASF_OBJECT_TYPE_STREAM_PROPS, + ASF_OBJECT_TYPE_EXT_STREAM_PROPS, + ASF_OBJECT_TYPE_DATA, + ASF_OBJECT_TYPE_SIMPLE_INDEX, + ASF_OBJECT_TYPE_INDEX, + ASF_OBJECT_TYPE_HEADER_EXT, + ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, + ASF_OBJECT_TYPE_CODEC_LIST, + ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, + ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, + ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, + ASF_OBJECT_TYPE_LANGUAGE_LIST, + ASF_OBJECT_TYPE_METADATA, + ASF_OBJECT_TYPE_PADDING, +} ASF_OBJECT_TYPE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + unsigned int stream_id; + uint64_t time_offset; + bool b_valid; + + uint64_t index_offset; + uint32_t num_index_entries; + int64_t index_time_interval; +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int object_level; + uint32_t packet_size; + + VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; + + VC_CONTAINER_WRITER_EXTRAIO_T null; + bool b_header_done; + + unsigned int current_track; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T object_type ); +static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ); +#if 0 +static VC_CONTAINER_STATUS_T asf_write_object_codec_list( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_content_description( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx ); +#endif + +static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; +static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; +static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; +static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; +static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; +static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; +static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; +static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; +static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; + +static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; +static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; +static const GUID_T asf_guid_error_correction = {0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; + +static struct { + const ASF_OBJECT_TYPE_T type; + const GUID_T *guid; + const char *psz_name; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); + +} asf_object_list[] = +{ + {ASF_OBJECT_TYPE_HEADER, &asf_guid_header, "header", asf_write_object_header}, + {ASF_OBJECT_TYPE_FILE_PROPS, &asf_guid_file_props, "file properties", asf_write_object_file_properties}, + {ASF_OBJECT_TYPE_STREAM_PROPS, &asf_guid_stream_props, "stream properties", asf_write_object_stream_properties}, + {ASF_OBJECT_TYPE_EXT_STREAM_PROPS, &asf_guid_ext_stream_props, "extended stream properties", asf_write_object_ext_stream_properties}, + {ASF_OBJECT_TYPE_DATA, &asf_guid_data, "data", asf_write_object_data}, + {ASF_OBJECT_TYPE_SIMPLE_INDEX, &asf_guid_simple_index, "simple index", asf_write_object_simple_index}, + {ASF_OBJECT_TYPE_INDEX, &asf_guid_index, "index", asf_write_object_index}, + {ASF_OBJECT_TYPE_HEADER_EXT, &asf_guid_header_ext, "header extension", asf_write_object_header_ext}, + {ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, &asf_guid_header_ext, "header extension", asf_write_object_header_ext_internal}, +#if 0 + {ASF_OBJECT_TYPE_CODEC_LIST, &asf_guid_codec_list, "codec list", asf_write_object_codec_list}, + {ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, &asf_guid_content_description, "content description", asf_write_object_content_description}, + {ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, &asf_guid_ext_content_description, "extended content description", 0}, + {ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, &asf_guid_stream_bitrate_props, "stream bitrate properties", asf_write_object_stream_bitrate_props}, +#endif + {ASF_OBJECT_TYPE_UNKNOWN, 0, "unknown", 0} +}; + +static GUID_T guid_null; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T type ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t object_size = 0; + unsigned int i; + + /* Find out which object we want to write */ + for( i = 0; asf_object_list[i].type && asf_object_list[i].type != type; i++ ); + + /* Check we found the requested type */ + if(!asf_object_list[i].type) + { + vc_container_assert(0); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* We need to find out the size of the object we're going to write. + * Because we want to be streamable, we can't just come back later to update the size field. + * The easiest way to find out the size of the data we're going to write is to write a dummy + * version of it and get the size from that. It is a bit wasteful but is so much easier and + * shouldn't really impact performance as there's no actual i/o involved. */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) + { + asf_object_list[i].pf_func(p_ctx); + object_size = STREAM_POSITION(p_ctx); + } + vc_container_writer_extraio_disable(p_ctx, &module->null); + + /* Special case for header extension internal function */ + if(type == ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL) + { + WRITE_U32(p_ctx, object_size, "Header Extension Data Size"); + /* Call the object specific writing function */ + status = asf_object_list[i].pf_func(p_ctx); + return status; + } + + /* Write the object header */ + + if(WRITE_GUID(p_ctx, asf_object_list[i].guid, "Object ID") != sizeof(GUID_T)) + return VC_CONTAINER_ERROR_EOS; + + LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name); + + WRITE_U64(p_ctx, object_size + ASF_OBJECT_HEADER_SIZE, "Object Size"); + + module->object_level++; + + /* Call the object specific writing function */ + status = asf_object_list[i].pf_func(p_ctx); + + module->object_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "object %s appears to be corrupted", asf_object_list[i].psz_name); + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_U32(p_ctx, 0, "Number of Header Objects"); /* FIXME: could use that */ + WRITE_U8(p_ctx, 0, "Reserved1"); + WRITE_U8(p_ctx, 0, "Reserved2"); + + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_FILE_PROPS); + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT); + + for(module->current_track = 0; module->current_track < p_ctx->tracks_num; + module->current_track++) + { + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_STREAM_PROPS); + } + + /* Codec List */ + /* Content Description */ + /* Stream Bitrate Properties */ + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ) +{ + WRITE_GUID(p_ctx, &guid_null, "Reserved Field 1"); + WRITE_U16(p_ctx, 0, "Reserved Field 2"); + + return asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL); +} + +static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + for(module->current_track = 0; module->current_track < p_ctx->tracks_num; + module->current_track++) + { + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_EXT_STREAM_PROPS); + } + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_GUID(p_ctx, &guid_null, "File ID"); + WRITE_U64(p_ctx, 0, "File Size"); + WRITE_U64(p_ctx, 0, "Creation Date"); + WRITE_U64(p_ctx, 0, "Data Packets Count"); + WRITE_U64(p_ctx, 0, "Play Duration"); + WRITE_U64(p_ctx, 0, "Send Duration"); + WRITE_U64(p_ctx, 0, "Preroll"); + WRITE_U32(p_ctx, 0, "Flags"); + WRITE_U32(p_ctx, module->packet_size, "Minimum Data Packet Size"); + WRITE_U32(p_ctx, module->packet_size, "Maximum Data Packet Size"); + WRITE_U32(p_ctx, 0, "Maximum Bitrate"); + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track ) +{ + uint32_t fourcc; + + /* Write the preamble to the BITMAPINFOHEADER */ + WRITE_U32(p_ctx, p_track->format->type->video.width, "Encoded Image Width"); + WRITE_U32(p_ctx, p_track->format->type->video.height, "Encoded Image Height"); + WRITE_U8(p_ctx, 0, "Reserved Flags"); + WRITE_U16(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size"); + + /* Write BITMAPINFOHEADER structure */ + WRITE_U32(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size"); + WRITE_U32(p_ctx, p_track->format->type->video.width, "Image Width"); + WRITE_U32(p_ctx, p_track->format->type->video.height, "Image Height"); + WRITE_U16(p_ctx, 0, "Reserved"); + WRITE_U16(p_ctx, 0, "Bits Per Pixel Count"); + fourcc = codec_to_fourcc(p_track->format->codec); + WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ + LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); + WRITE_U32(p_ctx, 0, "Image Size"); + WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Colors Used Count"); + WRITE_U32(p_ctx, 0, "Important Colors Count"); + + WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_waveformatex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track) +{ + /* Write WAVEFORMATEX structure */ + WRITE_U16(p_ctx, codec_to_waveformat(p_track->format->codec), "Codec ID"); + WRITE_U16(p_ctx, p_track->format->type->audio.channels, "Number of Channels"); + WRITE_U32(p_ctx, p_track->format->type->audio.sample_rate, "Samples per Second"); + WRITE_U32(p_ctx, p_track->format->bitrate, "Average Number of Bytes Per Second"); + WRITE_U16(p_ctx, p_track->format->type->audio.block_align, "Block Alignment"); + WRITE_U16(p_ctx, p_track->format->type->audio.bits_per_sample, "Bits Per Sample"); + WRITE_U16(p_ctx, p_track->format->extradata_size, "Codec Specific Data Size"); + WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int track = module->current_track, ts_size = 0; + const GUID_T *p_guid = &guid_null; + + if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + p_guid = &asf_guid_stream_type_video; + ts_size = 11 + 40 + p_ctx->tracks[track]->format->extradata_size; + } + else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + p_guid = &asf_guid_stream_type_audio; + ts_size = 18 + p_ctx->tracks[track]->format->extradata_size; + } + + WRITE_GUID(p_ctx, p_guid, "Stream Type"); + WRITE_GUID(p_ctx, &asf_guid_error_correction, "Error Correction Type"); + WRITE_U64(p_ctx, 0, "Time Offset"); + WRITE_U32(p_ctx, ts_size, "Type-Specific Data Length"); + WRITE_U32(p_ctx, 0, "Error Correction Data Length"); + WRITE_U16(p_ctx, track + 1, "Flags"); + WRITE_U32(p_ctx, 0, "Reserved"); + + /* Type-Specific Data */ + if(ts_size) + { + if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = asf_write_bitmapinfoheader( p_ctx, p_ctx->tracks[track]); + else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = asf_write_waveformatex( p_ctx, p_ctx->tracks[track]); + } + + /* Error Correction Data */ + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_U64(p_ctx, 0, "Start Time"); + WRITE_U64(p_ctx, 0, "End Time"); + WRITE_U32(p_ctx, 0, "Data Bitrate"); + WRITE_U32(p_ctx, 0, "Buffer Size"); + WRITE_U32(p_ctx, 0, "Initial Buffer Fullness"); + WRITE_U32(p_ctx, 0, "Alternate Data Bitrate"); + WRITE_U32(p_ctx, 0, "Alternate Buffer Size"); + WRITE_U32(p_ctx, 0, "Alternate Initial Buffer Fullness"); + WRITE_U32(p_ctx, 0, "Maximum Object Size"); + WRITE_U32(p_ctx, 0, "Flags"); + WRITE_U16(p_ctx, module->current_track + 1, "Stream Number"); + WRITE_U16(p_ctx, 0, "Stream Language ID Index"); + WRITE_U64(p_ctx, 0, "Average Time Per Frame"); + WRITE_U16(p_ctx, 0, "Stream Name Count"); + WRITE_U16(p_ctx, 0, "Payload Extension System Count"); + /* Stream Names */ + /* Payload Extension Systems */ + /* Stream Properties Object */ + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_write_header( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status; + + /* TODO Sanity check the tracks */ + + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER); + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_DATA); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PARAM_UNUSED(p_packet); + + if(!module->b_header_done) + { + module->b_header_done = true; + status = asf_write_header(p_ctx); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + + vc_container_writer_extraio_delete(p_ctx, &module->null); + free(module); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_T *track; + + /* TODO check we support this format */ + + if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= ASF_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status) goto error; + } + + vc_container_format_copy(track->format, format, format->extradata_size); + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + vc_container_free_track(p_ctx, track); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + { + VC_CONTAINER_ES_FORMAT_T *p_format = + (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + return asf_writer_add_track(p_ctx, p_format); + } + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + if(!module->b_header_done) + { + status = asf_write_header(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + module->b_header_done = true; + } + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "asf") && strcasecmp(extension, "wmv") && + strcasecmp(extension, "wma")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + /* Create a null i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_null(p_ctx, &module->null); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* We'll only write the header once we've got all our tracks */ + + p_ctx->priv->pf_close = asf_writer_close; + p_ctx->priv->pf_write = asf_writer_write; + p_ctx->priv->pf_control = asf_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "asf: error opening stream"); + for(i = 0; i < ASF_TRACKS_MAX && p_ctx->tracks && p_ctx->tracks[i]; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + free(module); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open asf_writer_open +#endif diff --git a/containers/avi/CMakeLists.txt b/containers/avi/CMakeLists.txt new file mode 100755 index 0000000..f8589c1 --- /dev/null +++ b/containers/avi/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_avi ${LIBRARY_TYPE} avi_reader.c) + +target_link_libraries(reader_avi containers) + +install(TARGETS reader_avi DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_avi ${LIBRARY_TYPE} avi_writer.c) + +target_link_libraries(writer_avi containers) + +install(TARGETS writer_avi DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/avi/avi_reader.c b/containers/avi/avi_reader.c new file mode 100755 index 0000000..4c4c3f4 --- /dev/null +++ b/containers/avi/avi_reader.c @@ -0,0 +1,1521 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_LITTLE_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_waveformat.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */ +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_TRUSTCKTYPE 0x00000800 /*< (OpenDML) keyframe information reliable. */ + +#define AVIIF_LIST 0x00000001 +#define AVIIF_KEYFRAME 0x00000010 +#define AVIIF_NOTIME 0x00000100 + +#define AVI_INDEX_OF_INDEXES 0x00 +#define AVI_INDEX_OF_CHUNKS 0x01 +#define AVI_INDEX_2FIELD 0x01 +#define AVI_INDEX_DELTAFRAME 0x80000000 + +#define AVI_TRACKS_MAX 16 /*< We won't try to handle streams with more tracks than this */ + +#define AVI_TWOCC(a,b) ((a) | (b << 8)) + +#define AVI_SYNC_CHUNK(ctx) \ + while(STREAM_POSITION(ctx) & 1) \ + { \ + if (SKIP_BYTES(ctx, 1) != 1) break; \ + } + +#define AVI_SKIP_CHUNK(ctx, size) \ + do { \ + SKIP_BYTES(ctx, size); \ + AVI_SYNC_CHUNK(ctx); \ + } while(0) + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct AVI_TRACK_STREAM_STATE_T +{ + unsigned current_track_num; /**< Number of track currently being read */ + int64_t data_offset; /**< Offset within the stream to find the track data */ + uint32_t chunk_size; /**< Size of the current chunk being read */ + uint32_t chunk_data_left; /**< Data left from the current chunk being read */ + + unsigned extra_chunk_track_num; /**< Temporary storage for in-band data e.g. 'dd' + chunks */ + uint32_t extra_chunk_data[4]; + uint32_t extra_chunk_data_offs; + uint32_t extra_chunk_data_len; +} AVI_TRACK_STREAM_STATE_T; + +typedef struct AVI_TRACK_CHUNK_STATE_T +{ + uint64_t index; + uint64_t offs; /**< offset into bytestream consisting of all chunks for this track */ + int64_t time_pos; /**< pts of chunk (if known) */ + uint32_t flags; /**< flags associated with chunk */ + AVI_TRACK_STREAM_STATE_T local_state; + AVI_TRACK_STREAM_STATE_T *state; +} AVI_TRACK_CHUNK_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + int64_t time_start; /**< i.e. 'dwStart' in 'strh' (converted to microseconds) */ + int64_t duration; /**< i.e. 'dwLength' in 'strh' (converted to microseconds) */ + uint32_t time_num; /**< i.e. 'dwScale' in 'strh' */ + uint32_t time_den; /**< i.e. 'dwRate' in 'strh', time_num / time_den = + samples (or frames) / second for audio (or video) */ + uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */ + + uint64_t index_offset; /**< Offset to the start of an OpenDML index i.e. 'indx' + (if available) */ + uint32_t index_size; /**< Size of the OpenDML index chunk */ + AVI_TRACK_CHUNK_STATE_T chunk; +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX]; + uint64_t data_offset; /**< Offset to the start of data packets i.e. + the data in the 'movi' list */ + uint64_t data_size; /**< Size of the chunk containing data packets */ + uint64_t index_offset; /**< Offset to the start of index data e.g. + the data in a 'idx1' list */ + uint32_t index_size; /**< Size of the chunk containing index data */ + AVI_TRACK_STREAM_STATE_T state; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T avi_find_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T id, uint32_t *size) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + + do { + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(chunk_id == id) + { + *size = chunk_size; + return VC_CONTAINER_SUCCESS; + } + /* Not interested in this chunk, skip it. */ + AVI_SKIP_CHUNK(p_ctx, chunk_size); + } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); + + return status; /* chunk not found */ +} + +static VC_CONTAINER_STATUS_T avi_find_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T fourcc, uint32_t *size) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + uint32_t peek_buf[1]; + + do { + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(chunk_id == VC_FOURCC('L','I','S','T')) + { + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if (peek_buf[0] == fourcc) + { + *size = chunk_size; + return VC_CONTAINER_SUCCESS; + } + } + /* Not interested in this chunk, skip it. */ + AVI_SKIP_CHUNK(p_ctx, chunk_size); + } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); + + return status; /* list not found */ +} + +static int64_t avi_stream_ticks_to_us(VC_CONTAINER_TRACK_MODULE_T *track_module, uint64_t ticks) +{ + int64_t time; + vc_container_assert(track_module->time_den != 0); + time = INT64_C(1000000) * track_module->time_num * ticks / track_module->time_den; + return time; +} + +static int64_t avi_calculate_chunk_time(VC_CONTAINER_TRACK_MODULE_T *track_module) +{ + if (track_module->sample_size == 0) + return track_module->time_start + avi_stream_ticks_to_us(track_module, track_module->chunk.index); + else + return track_module->time_start + avi_stream_ticks_to_us(track_module, + ((track_module->chunk.offs + (track_module->sample_size >> 1)) / track_module->sample_size)); +} + +static VC_CONTAINER_STATUS_T avi_read_stream_header_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_TRACK_MODULE_T *track_module) +{ + VC_CONTAINER_STATUS_T status; + int64_t list_offset; + uint32_t list_size, chunk_id, chunk_size; + + int stream_header_chunk_read = 0, stream_format_chunk_read = 0; + + /* Look for a 'strl' LIST (sub)chunk */ + status = avi_find_list(p_ctx, VC_FOURCC('s','t','r','l'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'strl' LIST not found for stream"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + list_offset = STREAM_POSITION(p_ctx); + list_size = chunk_size; + SKIP_FOURCC(p_ctx, "strl"); + + while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && STREAM_POSITION(p_ctx) < list_offset + list_size) + { + int64_t offset = STREAM_POSITION(p_ctx); + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + LOG_FORMAT(p_ctx, "chunk %4.4s, offset: %"PRIi64", size: %i", (char *)&chunk_id, offset, chunk_size); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(chunk_id == VC_FOURCC('s','t','r','h')) + { + VC_CONTAINER_FOURCC_T fourcc_type, fourcc_handler; + uint32_t flags, scale, rate, div, start, length, sample_size; + + /* We won't accept more than one 'strh' per stream */ + if (stream_header_chunk_read) + { + LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strh'"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + fourcc_type = READ_FOURCC(p_ctx, "fccType"); + fourcc_handler = READ_FOURCC(p_ctx, "fccHandler"); + flags = READ_U32(p_ctx, "dwFlags"); + SKIP_U16(p_ctx, "wPriority"); + SKIP_U16(p_ctx, "wLanguage"); + SKIP_U32(p_ctx, "dwInitialFrames"); + scale = READ_U32(p_ctx, "dwScale"); + rate = READ_U32(p_ctx, "dwRate"); + start = READ_U32(p_ctx, "dwStart"); + length = READ_U32(p_ctx, "dwLength"); + SKIP_U32(p_ctx, "dwSuggestedBufferSize"); + SKIP_U32(p_ctx, "dwQuality"); + sample_size = READ_U32(p_ctx, "dwSampleSize"); + SKIP_U16(p_ctx, "rcFrame.left"); + SKIP_U16(p_ctx, "rcFrame.top"); + SKIP_U16(p_ctx, "rcFrame.right"); + SKIP_U16(p_ctx, "rcFrame.bottom"); + + /* In AVI, sec/frame = scale/rate and frames/sec = rate/scale */ + if (rate == 0) + { + LOG_DEBUG(p_ctx, "invalid dwRate: 0, using 1 as a guess"); + LOG_DEBUG(p_ctx, "timestamps will almost certainly be wrong"); + rate = 1; + } + + div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate); + scale = scale / div; + rate = rate / div; + + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + if(fourcc_type == VC_FOURCC('v','i','d','s')) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + track->format->type->video.frame_rate_num = rate; + track->format->type->video.frame_rate_den = scale; + + if (sample_size != 0) + { + LOG_DEBUG(p_ctx, "ignoring dwSampleSize (%d) for video stream", sample_size); + sample_size = 0; + } + } + else if(fourcc_type == VC_FOURCC('a','u','d','s')) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + /* VBR audio is going to be non-framed */ + track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + } + else if(fourcc_type == VC_FOURCC('t','x','t','s')) + track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + + /* Don't overwrite any existing value (i.e. in the unlikely case where we + see 'strf' before 'strh') */ + if(!track->format->codec) track->format->codec = vfw_fourcc_to_codec(fourcc_handler); + + /* FIXME: enable this once read_media does the right thing */ + if (!(flags & AVISF_DISABLED) || 1) + track->is_enabled = 1; + + track_module->time_num = scale; + track_module->time_den = rate; + track_module->time_start = avi_stream_ticks_to_us(track_module, (uint64_t)start); + track_module->duration = avi_stream_ticks_to_us(track_module, (uint64_t)length); + track_module->sample_size = sample_size; + + p_ctx->duration = MAX(p_ctx->duration, track_module->duration); + + stream_header_chunk_read = 1; + } + else if(chunk_id == VC_FOURCC('s','t','r','f')) + { + uint8_t *buffer; + unsigned extra_offset = 0, extra_size = 0; + + /* We won't accept more than one 'strf' per stream */ + if (stream_format_chunk_read) + { + LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strf'"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* Use the extradata buffer for reading in the entire 'strf' (should not be a large chunk) */ + if ((status = vc_container_track_allocate_extradata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'strf' (%d bytes)", chunk_size); + return status; + } + + buffer = track->priv->extradata; + if(READ_BYTES(p_ctx, buffer, chunk_size) != chunk_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + AVI_SYNC_CHUNK(p_ctx); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + status = vc_container_bitmapinfoheader_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format); + } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + status = vc_container_waveformatex_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format); + if (track_module->sample_size != 0 && track_module->sample_size != track->format->type->audio.block_align) + { + LOG_DEBUG(p_ctx, "invalid dwSampleSize (%d), should match nBlockAlign (%d) for audio streams.", + track_module->sample_size, track->format->type->audio.block_align); + + /* Note that if nBlockAlign really is 0, strf is seriously broken... */ + if (track->format->type->audio.block_align != 0) + track_module->sample_size = track->format->type->audio.block_align; + } + else + { + /* Flawed muxers might only set nBlockAlign (i.e. not set dwSampleSize correctly). */ + if (track->format->type->audio.block_align == 1) + track_module->sample_size = 1; + } + } + + if (status != VC_CONTAINER_SUCCESS) return status; + + if (extra_size) + { + track->format->extradata = buffer + extra_offset; + track->format->extradata_size = extra_size; + } + + /* Codec specific fix-up */ + if (track->format->codec == VC_CONTAINER_CODEC_MP4A && + track->format->extradata_size) + { + /* This is going to be raw AAC so it will be framed */ + track_module->sample_size = 0; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + } + + /* WMA specific fix-up */ + if ((track->format->codec == VC_CONTAINER_CODEC_WMA1 || + track->format->codec == VC_CONTAINER_CODEC_WMA2 || + track->format->codec == VC_CONTAINER_CODEC_WMAP || + track->format->codec == VC_CONTAINER_CODEC_WMAL || + track->format->codec == VC_CONTAINER_CODEC_WMAV) && + track->format->extradata_size) + { + track_module->sample_size = 0; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + } + + stream_format_chunk_read = 1; + } + else if(chunk_id == VC_FOURCC('s','t','r','d')) + { + /* The data in a 'strd' chunk is either codec configuration data or DRM information, + we can safely assume it might be either as long as we don't overwrite any config + data read previously from a 'strf' chunk */ + if ((status = vc_container_track_allocate_drmdata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size); + return status; + } + + if(READ_BYTES(p_ctx, track->priv->drmdata, chunk_size) != chunk_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + AVI_SYNC_CHUNK(p_ctx); + + if (!track->format->extradata) + { + if (vc_container_track_allocate_extradata(p_ctx, track, chunk_size) == VC_CONTAINER_SUCCESS) + { + memcpy(track->format->extradata, track->priv->drmdata, chunk_size); + + track->format->extradata = track->priv->extradata; + track->format->extradata_size = chunk_size; + } + else + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size); + LOG_DEBUG(p_ctx, "no codec configuration data set"); + } + } + } + else if(chunk_id == VC_FOURCC('i','n','d','x')) + { + track_module->index_offset = STREAM_POSITION(p_ctx); + track_module->index_size = chunk_size; + } + else + { + /* Not interested in this chunk, skip it. */ + } + + /* Skip any left-over data */ + AVI_SKIP_CHUNK(p_ctx, offset + chunk_size + 8 - STREAM_POSITION(p_ctx) ); + } + + if (!stream_header_chunk_read || !stream_format_chunk_read) + { + LOG_DEBUG(p_ctx, "invalid 'strl', 'strh' and 'strf' are both required"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return status; +} + +static VC_CONTAINER_STATUS_T avi_find_next_data_chunk(VC_CONTAINER_T *p_ctx, uint32_t *id, uint32_t *size) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size = 0; + uint32_t peek_buf[1]; + + do + { + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + break; + + /* Check if this is a 'rec ' or a 'movi' LIST instead of a plain data chunk */ + if(chunk_id == VC_FOURCC('L','I','S','T')) + { + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_EOS; + if (peek_buf[0] == VC_FOURCC('r','e','c',' ')) + SKIP_FOURCC(p_ctx, "rec "); + else if (peek_buf[0] == VC_FOURCC('m','o','v','i')) + SKIP_FOURCC(p_ctx, "movi"); + else + AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this LIST chunk, skip it. */ + continue; + } + + /* Check if this is a 'AVIX' RIFF header instead of a data chunk */ + if(chunk_id == VC_FOURCC('R','I','F','F')) + { + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_EOS; + if (peek_buf[0] == VC_FOURCC('A','V','I','X')) + SKIP_FOURCC(p_ctx, "AVIX"); + else + AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this RIFF header, skip it. */ + continue; + } + + /* We treat only db/dc/dd or wb chunks as data */ + if((uint32_t)chunk_id >> 16 == AVI_TWOCC('d','c') || + (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','b') || + (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','d') || + (uint32_t)chunk_id >> 16 == AVI_TWOCC('w','b')) + { + *id = chunk_id; + *size = chunk_size; + break; + } + + /* Need to exit if a zero sized chunk encountered so we don't loop forever. */ + if(chunk_size == 0 && chunk_id == 0) return VC_CONTAINER_ERROR_EOS; + + /* Not interested in this chunk, skip it */ + AVI_SKIP_CHUNK(p_ctx, chunk_size); + } while ((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); + + return status; +} + +static void avi_track_from_chunk_id(VC_CONTAINER_FOURCC_T chunk_id, uint16_t *data_type, uint16_t *track_num) +{ + *track_num = (((uint8_t*)&chunk_id)[0] - 48) * 10 + ((uint8_t*)&chunk_id)[1] - 48; + *data_type = (uint32_t)chunk_id >> 16; +} + +static VC_CONTAINER_STATUS_T avi_check_track(VC_CONTAINER_T *p_ctx, uint16_t data_type, uint16_t track_num) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (track_num < p_ctx->tracks_num) + { + if (data_type == AVI_TWOCC('w','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('wb'), track %d is not an audio track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + if (data_type == AVI_TWOCC('d','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('db'), track %d is not a video track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + if (data_type == AVI_TWOCC('d','c') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('dc'), track %d is not a video track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + if (data_type == AVI_TWOCC('d','d') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('dd'), track %d is not a video track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + } + else + { + LOG_DEBUG(p_ctx, "invalid track number %d (no more than %d tracks expected)", + track_num, p_ctx->tracks_num); + status = VC_CONTAINER_ERROR_FAILED; + } + + return status; +} + +static int avi_compare_seek_time(int64_t chunk_time, int64_t seek_time, + int chunk_is_keyframe, VC_CONTAINER_SEEK_FLAGS_T seek_flags) +{ + if (chunk_time == seek_time && chunk_is_keyframe && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + return 0; + + if (chunk_time > seek_time && chunk_is_keyframe && (seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + return 0; + + if (chunk_time > seek_time && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + return 1; /* Chunk time is past seek time, caller should use the previous keyframe */ + + return -1; +} + +static VC_CONTAINER_STATUS_T avi_scan_legacy_index_chunk(VC_CONTAINER_T *p_ctx, int seek_track_num, + int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_MODULE_T *track_module; + AVI_TRACK_CHUNK_STATE_T selected_chunk; + int64_t base_offset = module->data_offset; + int64_t selected_chunk_offset = base_offset + 4; + int32_t extra_offset = 0; + int first_chunk_offset = 1; + uint64_t position; + + SEEK(p_ctx, module->index_offset); + memset(&selected_chunk, 0, sizeof(selected_chunk)); + + while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && + (uint64_t)STREAM_POSITION(p_ctx) < module->index_offset + module->index_size) + { + VC_CONTAINER_FOURCC_T chunk_id; + uint16_t data_type, track_num; + uint32_t chunk_flags, offset, size; + + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_flags = READ_U32(p_ctx, "dwFlags"); + offset = READ_U32(p_ctx, "dwOffset"); + size = READ_U32(p_ctx, "dwSize"); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break; + + /* Although it's rare, the offsets might be given from the start of the file + instead of the data chunk, we have to handle both cases. */ + if (first_chunk_offset) + { + if (offset > module->data_offset) base_offset = INT64_C(0); + selected_chunk_offset = base_offset + 4; + first_chunk_offset = 0; + } + + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + LOG_DEBUG(p_ctx, "reading track %"PRIu16, track_num); + + if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "skipping index entry for track %d/%d", track_num, p_ctx->tracks_num); + continue; + } + + track_module = p_ctx->tracks[track_num]->priv->module; + + if (data_type == AVI_TWOCC('d','d')) + { + if (track_num == seek_track_num) + track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED; + extra_offset = -(size + 8); + } + + /* If this entry does not affect timing, skip it */ + if ((chunk_flags & (AVIIF_LIST | AVIIF_NOTIME)) || data_type == AVI_TWOCC('d','d')) + continue; + + position = base_offset + offset + extra_offset; + extra_offset = INT64_C(0); + + /* Check validity of position */ + if (position <= module->data_offset /* || (*pos > module->data_offset + module->data_size*/) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (track_num == seek_track_num) + { + bool is_keyframe = true; + int res; + + if (p_ctx->tracks[track_num]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + is_keyframe = chunk_flags & AVIIF_KEYFRAME; + + if (is_keyframe) + track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + else + track_module->chunk.flags &= ~(VC_CONTAINER_PACKET_FLAG_KEYFRAME); + + res = avi_compare_seek_time(track_module->chunk.time_pos, *time, is_keyframe, flags); + if (res > 0) + break; /* We've found the keyframe we wanted */ + + if (is_keyframe) + { + selected_chunk_offset = position; + selected_chunk = track_module->chunk; + } + + if (res == 0) + break; /* We've found the keyframe we wanted */ + + track_module->chunk.index++; + track_module->chunk.offs += size; + track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); + + LOG_DEBUG(p_ctx, "index %"PRIu64", offs %"PRIu64", time %"PRIi64"us", track_module->chunk.index, + track_module->chunk.offs, track_module->chunk.time_pos); + } + } + + if (status == VC_CONTAINER_SUCCESS || + /* When seeking backwards, always return the last good position */ + !(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + { + *pos = selected_chunk_offset; + track_module = p_ctx->tracks[seek_track_num]->priv->module; + track_module->chunk.index = selected_chunk.index; + track_module->chunk.offs = selected_chunk.offs; + track_module->chunk.flags = selected_chunk.flags; + track_module->chunk.time_pos = selected_chunk.time_pos; + *time = track_module->chunk.time_pos; + return VC_CONTAINER_SUCCESS; + } + + return VC_CONTAINER_ERROR_NOT_FOUND; +} + +static VC_CONTAINER_STATUS_T avi_scan_standard_index_chunk(VC_CONTAINER_T *p_ctx, uint64_t index_offset, + unsigned seek_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + uint16_t data_type, track_num; + uint8_t index_type, index_sub_type; + uint32_t entry, entry_count = 0; + uint16_t entry_size; + uint64_t base_offset = UINT64_C(0); + uint64_t position = UINT64_C(0); + uint64_t prev_keyframe_offs = INT64_C(0); + AVI_TRACK_CHUNK_STATE_T prev_keyframe_chunk = { 0 }; + + SEEK(p_ctx, index_offset); + + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk Size"); + + entry_size = READ_U16(p_ctx, "wLongsPerEntry"); + index_sub_type = READ_U8(p_ctx, "bIndexSubType"); + index_type = READ_U8(p_ctx, "bIndexType"); + entry_count = READ_U32(p_ctx, "nEntriesInUse"); + chunk_id = READ_FOURCC(p_ctx, "dwChunkId"); + base_offset = READ_U64(p_ctx, "qwBaseOffset"); + SKIP_U32(p_ctx, "dwReserved"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + status = avi_check_track(p_ctx, data_type, track_num); + if (status || chunk_size < 24 || track_num != seek_track_num) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (entry_size != 2 || index_sub_type != 0 || index_type != AVI_INDEX_OF_CHUNKS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + entry_count = MIN(entry_count, (chunk_size - 24) / (entry_size * 4)); + + track_module = p_ctx->tracks[seek_track_num]->priv->module; + + for (entry = 0; entry < entry_count; ++entry) + { + uint32_t chunk_offset; + int key_frame = 0; + + chunk_offset = READ_U32(p_ctx, "dwOffset"); + chunk_size = READ_U32(p_ctx, "dwSize"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + break; + + status = VC_CONTAINER_ERROR_NOT_FOUND; + + if (!(chunk_size & AVI_INDEX_DELTAFRAME)) + key_frame = 1; + chunk_size &= ~AVI_INDEX_DELTAFRAME; + + position = base_offset + chunk_offset - 8; + + if (key_frame) + track_module->chunk.flags = VC_CONTAINER_PACKET_FLAG_KEYFRAME; + else + track_module->chunk.flags = 0; + + if (time != NULL) + { + int res; + status = VC_CONTAINER_ERROR_NOT_FOUND; + res = avi_compare_seek_time(track_module->chunk.time_pos, *time, key_frame, flags); + + if (res == 0) + { + *pos = position; + *time = track_module->chunk.time_pos; + status = VC_CONTAINER_SUCCESS; + break; + } + else if (res > 0) + { + if (prev_keyframe_offs) + { + *pos = prev_keyframe_offs; + track_module->chunk = prev_keyframe_chunk; + *time = track_module->chunk.time_pos; + status = VC_CONTAINER_SUCCESS; + } + break; + } + + if (key_frame) + { + prev_keyframe_offs = position; + prev_keyframe_chunk = track_module->chunk; + } + } + else + { + /* Not seeking to a time position, but scanning + track chunk state up to a certain file position + instead */ + if (position >= *pos) + { + status = VC_CONTAINER_SUCCESS; + break; + } + } + + track_module->chunk.index++; + track_module->chunk.offs += chunk_size; + track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); + } + + return status; +} + +static VC_CONTAINER_STATUS_T avi_scan_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned index_track_num, + int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + VC_CONTAINER_FOURCC_T chunk_id; + uint64_t index_offset; + uint32_t index_size; + uint16_t data_type, track_num; + uint32_t entry, entry_count; + uint16_t entry_size; + uint8_t index_sub_type, index_type; + + index_offset = p_ctx->tracks[index_track_num]->priv->module->index_offset; + index_size = p_ctx->tracks[index_track_num]->priv->module->index_size; + + SEEK(p_ctx, index_offset); + + entry_size = READ_U16(p_ctx, "wLongsPerEntry"); + index_sub_type = READ_U8(p_ctx, "bIndexSubType"); + index_type = READ_U8(p_ctx, "bIndexType"); + entry_count = READ_U32(p_ctx, "nEntriesInUse"); + chunk_id = READ_FOURCC(p_ctx, "dwChunkId"); + SKIP_U32(p_ctx, "dwReserved0"); + SKIP_U32(p_ctx, "dwReserved1"); + SKIP_U32(p_ctx, "dwReserved2"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + if (index_type == AVI_INDEX_OF_INDEXES) + { + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + status = avi_check_track(p_ctx, data_type, track_num); + if (status || index_size < 24 || track_num != index_track_num) return VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* FIXME: We should probably support AVI_INDEX_2FIELD as well */ + if (entry_size != 4 || index_sub_type != 0) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + entry_count = MIN(entry_count, (index_size - 24) / entry_size); + + for (entry = 0; entry < entry_count; ++entry) + { + uint64_t entry_offset, standard_index_offset; + standard_index_offset = READ_U64(p_ctx, "qwOffset"); + SKIP_U32(p_ctx, "dwSize"); + SKIP_U32(p_ctx, "dwDuration"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + break; + + if (standard_index_offset == UINT64_C(0)) + { + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Not plausible */ + break; + } + + entry_offset = STREAM_POSITION(p_ctx); + status = avi_scan_standard_index_chunk(p_ctx, standard_index_offset, index_track_num, time, flags, pos); + if (status != VC_CONTAINER_ERROR_NOT_FOUND) break; + SEEK(p_ctx, entry_offset); /* Move to next entry ('ix' chunk); */ + } + } + else if (index_type == AVI_INDEX_OF_CHUNKS) + { + /* It seems we are dealing with a standard index instead... */ + status = avi_scan_standard_index_chunk(p_ctx, index_offset, index_track_num, time, flags, pos); + } + else + { + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + return status; +} + +static VC_CONTAINER_STATUS_T avi_read_dd_chunk( VC_CONTAINER_T *p_ctx, + AVI_TRACK_STREAM_STATE_T *p_state, uint16_t data_type, uint32_t chunk_size, + uint16_t track_num ) +{ + if (data_type == AVI_TWOCC('d','d')) + { + if (p_state->extra_chunk_data_len || + chunk_size > sizeof(p_state->extra_chunk_data)) + { + LOG_DEBUG(p_ctx, "cannot handle multiple consecutive 'dd' chunks"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + if(READ_BYTES(p_ctx, p_state->extra_chunk_data, chunk_size) != chunk_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + AVI_SYNC_CHUNK(p_ctx); + p_state->extra_chunk_track_num = track_num; + p_state->extra_chunk_data_len = chunk_size; + p_state->extra_chunk_data_offs = 0; + + return VC_CONTAINER_ERROR_CONTINUE; + } + else if (p_state->extra_chunk_data_len && + p_state->extra_chunk_track_num != track_num) + { + LOG_DEBUG(p_ctx, "dropping data from '%02ddd' chunk, not for this track (%d)", + p_state->extra_chunk_track_num, track_num); + p_state->extra_chunk_data_len = 0; + } + + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T avi_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + AVI_TRACK_STREAM_STATE_T *p_state = &module->state; + + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + p_state = p_ctx->tracks[p_packet->track]->priv->module->chunk.state; + } + + LOG_DEBUG(p_ctx, "seeking to %"PRIi64, p_state->data_offset); + SEEK(p_ctx, p_state->data_offset); + + if (p_state->chunk_data_left == 0) + { + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + uint16_t data_type, track_num; + + if ((status = avi_find_next_data_chunk(p_ctx, &chunk_id, &chunk_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "unable to find the next data chunk %d", status); + p_state->data_offset = STREAM_POSITION(p_ctx); + return status; + } + + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + + if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS) + { + AVI_SKIP_CHUNK(p_ctx, chunk_size); + LOG_DEBUG(p_ctx, "skipping data for track %d/%d", track_num, p_ctx->tracks_num); + + p_state->data_offset = STREAM_POSITION(p_ctx); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we are reading from the global state (i.e. normal read or forced + read from the track on the global state), and the track we found is + not on the global state, connect the two */ + if (p_state == &module->state && + p_ctx->tracks[track_num]->priv->module->chunk.state != &module->state) + { + int64_t next_chunk; + + /* The track's offset is past the current position, skip it as we are + not interested in track data from before the track's offset. If we + were to read it we would return the same data multiple times. */ + next_chunk = (STREAM_POSITION(p_ctx) + chunk_size + 1) & ~1; + if (p_ctx->tracks[track_num]->priv->module->chunk.state->data_offset > next_chunk) + { + AVI_SKIP_CHUNK(p_ctx, chunk_size); + LOG_DEBUG(p_ctx, "skipping track %d/%d as we have already read it", track_num, p_ctx->tracks_num); + p_state->data_offset = STREAM_POSITION(p_ctx); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* The track state must be pointing to the current chunk. We need to + reconnect the track to the global state. */ + LOG_DEBUG(p_ctx, "reconnect track %u to the global state", track_num); + + p_ctx->tracks[track_num]->priv->module->chunk.state = &module->state; + + module->state = p_ctx->tracks[track_num]->priv->module->chunk.local_state; + + vc_container_assert(chunk_size >= p_state->chunk_data_left); + vc_container_assert(!p_state->chunk_data_left || + ((p_state->data_offset + p_state->chunk_data_left + 1) & ~1) == next_chunk); + vc_container_assert(p_state->current_track_num == track_num); + + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we are not forcing, or if we are and found the track we are + interested in, check for dd data and set the track module for the later code */ + if (!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) || + (track_num == p_packet->track)) + { + if ((status = avi_read_dd_chunk(p_ctx, p_state, data_type, chunk_size, track_num)) != VC_CONTAINER_SUCCESS) + { + p_state->data_offset = STREAM_POSITION(p_ctx); + return status; + } + } + + p_state->chunk_size = p_state->chunk_data_left = chunk_size; + p_state->current_track_num = track_num; + } + + /* If there is data from another track skip past it */ + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK && + p_state->current_track_num != p_packet->track) + { + p_state->data_offset = STREAM_POSITION(p_ctx); + + AVI_SKIP_CHUNK(p_ctx, p_state->chunk_data_left); + LOG_DEBUG(p_ctx, "skipping track %d/%d as we are ignoring it", + p_state->current_track_num, p_ctx->tracks_num); + + track_module = p_ctx->tracks[p_packet->track]->priv->module; + + /* Handle disconnection from global state */ + if (p_state == &module->state && + p_ctx->tracks[p_state->current_track_num]->priv->module->chunk.state == &module->state) + { + /* Make a copy of the global state */ + LOG_DEBUG(p_ctx, "using local state on track %d", p_packet->track); + track_module->chunk.local_state = module->state; + track_module->chunk.state = &track_module->chunk.local_state; + } + + track_module->chunk.state->data_offset = STREAM_POSITION(p_ctx); + track_module->chunk.state->chunk_data_left = 0; + + return VC_CONTAINER_ERROR_CONTINUE; + } + + track_module = p_ctx->tracks[p_state->current_track_num]->priv->module; + + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + vc_container_assert(p_state->current_track_num == p_packet->track); + } + + LOG_DEBUG(p_ctx, "reading track %u chunk at time %"PRIi64"us, offset %"PRIu64, + p_state->current_track_num, track_module->chunk.time_pos, + track_module->chunk.offs); + if (p_state->extra_chunk_data_len) + track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED; + else + track_module->chunk.flags &= ~VC_CONTAINER_PACKET_FLAG_ENCRYPTED; + + if (p_packet) + { + p_packet->track = p_state->current_track_num; + p_packet->size = p_state->chunk_data_left + + p_state->extra_chunk_data_len; + p_packet->flags = track_module->chunk.flags; + + if (p_state->chunk_data_left == p_state->chunk_size) + { + p_packet->pts = track_module->chunk.time_pos; + if (track_module->sample_size == 0) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME; + } + else + { + p_packet->pts = VC_CONTAINER_TIME_UNKNOWN; + if (track_module->sample_size == 0) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + + p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; + } + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + SKIP_BYTES(p_ctx, p_state->chunk_data_left); + AVI_SYNC_CHUNK(p_ctx); + p_state->chunk_data_left = 0; + p_state->extra_chunk_data_len = 0; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + p_state->data_offset = STREAM_POSITION(p_ctx); + + LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset); + + return VC_CONTAINER_SUCCESS; + } + + if (p_packet) + { + uint8_t *data = p_packet->data; + uint32_t buffer_size = p_packet->buffer_size; + uint32_t size = 0; + uint32_t len; + + /* See if we need to insert extra data */ + if (p_state->extra_chunk_data_len) + { + len = MIN(buffer_size, p_state->extra_chunk_data_len); + memcpy(data, p_state->extra_chunk_data + p_state->extra_chunk_data_offs, len); + data += len; + buffer_size -= len; + size = len; + p_state->extra_chunk_data_len -= len; + p_state->extra_chunk_data_offs += len; + } + + /* Now try to read data into buffer */ + len = MIN(buffer_size, p_state->chunk_data_left); + READ_BYTES(p_ctx, data, len); + size += len; + p_state->chunk_data_left -= len; + p_packet->size = size; + + if (p_state->chunk_data_left) + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + + if (p_state->chunk_data_left == 0) + { + AVI_SYNC_CHUNK(p_ctx); + track_module->chunk.index++; + track_module->chunk.offs += p_state->chunk_size; + track_module->chunk.flags = 0; + track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); + } + + /* Update the track's position */ + p_state->data_offset = STREAM_POSITION(p_ctx); + + LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t position, pos; + AVI_TRACK_CHUNK_STATE_T chunk_state[AVI_TRACKS_MAX]; + AVI_TRACK_STREAM_STATE_T global_state; + unsigned seek_track_num, i; + + if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + LOG_DEBUG(p_ctx, "AVI seeking to %"PRIi64"us", *p_offset); + + /* Save current position and chunk state so we can restore it if we + hit an error whilst scanning index data */ + position = STREAM_POSITION(p_ctx); + for(i = 0; i < p_ctx->tracks_num; i++) + chunk_state[i] = p_ctx->tracks[i]->priv->module->chunk; + global_state = p_ctx->priv->module->state; + + /* Clear track state affected by a seek operation of any kind */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + p_ctx->tracks[i]->priv->module->chunk.index = INT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.offs = INT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.flags = 0; + p_ctx->tracks[i]->priv->module->chunk.time_pos = p_ctx->tracks[i]->priv->module->time_start; + p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->tracks[i]->priv->module->chunk.local_state; + p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_data_left = UINT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_size = UINT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.local_state.extra_chunk_data_len = 0; + p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = module->data_offset + 4; + } + + /* Clear the global state */ + p_ctx->priv->module->state.chunk_data_left = UINT64_C(0); + p_ctx->priv->module->state.chunk_size = UINT64_C(0); + p_ctx->priv->module->state.extra_chunk_data_len = 0; + p_ctx->priv->module->state.data_offset = module->data_offset + 4; + + /* Choose track to use for seeking, favor video tracks and tracks + that are enabled */ + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->is_enabled && + p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; + if(i == p_ctx->tracks_num) + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->is_enabled) break; + if(i == p_ctx->tracks_num) i = 0; + + LOG_DEBUG(p_ctx, "seek on track %d/%d", i, p_ctx->tracks_num); + seek_track_num = i; + + if (p_ctx->tracks[seek_track_num]->priv->module->index_offset) + { + LOG_DEBUG(p_ctx, "seeking using the super index"); + status = avi_scan_super_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + + /* As AVI chunks don't convey timestamp information, we need to scan all tracks + to the seek file position */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + if (p_ctx->tracks[i]->priv->module->index_offset && i != seek_track_num) + { + uint64_t track_pos; + int64_t track_time = *p_offset; + + status = avi_scan_super_index_chunk(p_ctx, i, &track_time, flags, &track_pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos; + } + } + } + else + { + LOG_DEBUG(p_ctx, "seeking using the legacy index"); + + /* The legacy index comes after data so it might not have been available at the + time the container was opened; if this is the case, see if we can find an index + now, if we can't, then there's no way we can proceed with the seek. */ + if(!module->index_offset) + { + uint32_t chunk_size; + + LOG_DEBUG(p_ctx, "no index offset, searching for one"); + + /* Locate data chunk and skip it */ + SEEK(p_ctx, module->data_offset); + AVI_SKIP_CHUNK(p_ctx, module->data_size); + /* Now search for the index */ + status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size); + if (status == VC_CONTAINER_SUCCESS) + { + /* Store offset to index data */ + module->index_offset = STREAM_POSITION(p_ctx); + module->index_size = chunk_size; + p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; + p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; + } + } + /* Check again, we may or may not have an index */ + if (!module->index_offset) + { + /* If there is no index and we are seeking to 0 we can assume the + correct location is the start of the data. Otherwise we are unable + to seek to a specified non-zero location without an index */ + if (*p_offset != INT64_C(0)) + { + LOG_DEBUG(p_ctx, "failed to find the legacy index, unable to seek"); + status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + goto error; + } + pos = module->data_offset; + } + else + { + LOG_DEBUG(p_ctx, "scanning the legacy index chunk"); + status = avi_scan_legacy_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + + for (i = 0; i < p_ctx->tracks_num; i++) + { + if (i != seek_track_num) + { + uint64_t track_pos = pos; + int64_t track_time = *p_offset; + + status = avi_scan_legacy_index_chunk(p_ctx, i, &track_time, flags, &track_pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos; + p_ctx->tracks[i]->priv->module->chunk.local_state.current_track_num = i; + } + } + } + } + + position = pos; + + /* Set the seek track's data offset */ + p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.data_offset = position; + p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.current_track_num = seek_track_num; + + /* Connect the earlier track(s) to the global state. Needs 2 passes */ + module->state.data_offset = INT64_MAX; + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset < + module->state.data_offset) + module->state = p_ctx->tracks[i]->priv->module->chunk.local_state; + } + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset == + module->state.data_offset) + p_ctx->tracks[i]->priv->module->chunk.state = &module->state; + } + + LOG_DEBUG(p_ctx, "seek to %"PRIi64", position %"PRIu64, *p_offset, pos); + + return SEEK(p_ctx, position); + +error: + p_ctx->priv->module->state = global_state; + for(i = 0; i < p_ctx->tracks_num; i++) + p_ctx->tracks[i]->priv->module->chunk = chunk_state[i]; + SEEK(p_ctx, position); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + free(module); + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint32_t chunk_size; + uint32_t peek_buf[3]; + unsigned int i; + uint32_t flags, num_streams; + int64_t offset; + + /* Check the RIFF chunk descriptor */ + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 12) != 12) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( peek_buf[0] != VC_FOURCC('R','I','F','F') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( peek_buf[2] != VC_FOURCC('A','V','I',' ') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We now know we are dealing with an AVI file + */ + SKIP_FOURCC(p_ctx, "RIFF ID"); + SKIP_U32(p_ctx, "fileSize"); + SKIP_FOURCC(p_ctx, "fileType"); + + /* Look for the 'hdrl' LIST (sub)chunk */ + status = avi_find_list(p_ctx, VC_FOURCC('h','d','r','l'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'hdrl' LIST not found"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + SKIP_FOURCC(p_ctx, "hdrl"); + + /* Now look for the 'avih' sub-chunk */ + status = avi_find_chunk(p_ctx, VC_FOURCC('a','v','i','h'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'avih' not found"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* Parse the 'avih' sub-chunk */ + SKIP_U32(p_ctx, "dwMicroSecPerFrame"); + SKIP_U32(p_ctx, "dwMaxBytesPerSec"); + SKIP_U32(p_ctx, "dwPaddingGranularity"); + flags = READ_U32(p_ctx, "dwFlags"); + SKIP_U32(p_ctx, "dwTotalFrames"); + SKIP_U32(p_ctx, "dwInitialFrames"); + num_streams = READ_U32(p_ctx, "dwStreams"); + SKIP_U32(p_ctx, "dwSuggestedBufferSize"); + SKIP_U32(p_ctx, "dwWidth"); + SKIP_U32(p_ctx, "dwHeight"); + SKIP_U32(p_ctx, "dwReserved0"); + SKIP_U32(p_ctx, "dwReserved1"); + SKIP_U32(p_ctx, "dwReserved2"); + SKIP_U32(p_ctx, "dwReserved3"); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + goto error; + + /* Allocate our context and tracks */ + if ((module = malloc(sizeof(*module))) == NULL) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + if (num_streams > AVI_TRACKS_MAX) + { + LOG_DEBUG(p_ctx, "cannot handle %u tracks, restricted to %d", num_streams, AVI_TRACKS_MAX); + num_streams = AVI_TRACKS_MAX; + } + + for (p_ctx->tracks_num = 0; p_ctx->tracks_num != num_streams; p_ctx->tracks_num++) + { + p_ctx->tracks[p_ctx->tracks_num] = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!p_ctx->tracks[p_ctx->tracks_num]) break; + } + if(p_ctx->tracks_num != num_streams) + { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + /* Try to read stream header list chunks ('strl') */ + for (i = 0; i != num_streams; ++i) + { + status = avi_read_stream_header_list(p_ctx, p_ctx->tracks[i], p_ctx->tracks[i]->priv->module); + if(status != VC_CONTAINER_SUCCESS) goto error; + } + + /* Look for the 'movi' LIST (sub)chunk */ + status = avi_find_list(p_ctx, VC_FOURCC('m','o','v','i'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'movi' LIST not found"); + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto error; + } + + /* Store offset to the start and size of data (the 'movi' LIST) */ + module->data_offset = STREAM_POSITION(p_ctx); + module->data_size = chunk_size; + + p_ctx->priv->pf_close = avi_reader_close; + p_ctx->priv->pf_read = avi_reader_read; + p_ctx->priv->pf_seek = avi_reader_seek; + + if (flags & AVIF_MUSTUSEINDEX) + { + LOG_DEBUG(p_ctx, "AVIF_MUSTUSEINDEX not supported, playback might not work properly"); + } + + /* If the stream is seekable, see if we can find an index (for at + least one of the tracks); even if we cannot find an index now, + one might become available later (e.g. when the stream grows + run-time), in that case we might want to report that we can seek + and re-search for the index again if or when we're requested to + seek. */ + if(STREAM_SEEKABLE(p_ctx)) + { + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK; + + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->module->index_offset) break; + + if (i < p_ctx->tracks_num) + { + p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; + if (flags & AVIF_TRUSTCKTYPE) + p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; + } + else + { + /* Skip data first */ + AVI_SKIP_CHUNK(p_ctx, module->data_size); + /* Now search for the index */ + status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size); + if (status == VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'idx1' found"); + /* Store offset to index data */ + module->index_offset = STREAM_POSITION(p_ctx); + module->index_size = chunk_size; + p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; + p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; + } + + /* Seek back to the start of the data */ + SEEK(p_ctx, module->data_offset); + } + } + + SKIP_FOURCC(p_ctx, "movi"); + + for (i = 0; i != num_streams; i++) + { + p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->priv->module->state; + } + p_ctx->priv->module->state.data_offset = STREAM_POSITION(p_ctx); + + /* Update the tracks to set their data offsets. This help with bad + interleaving, for example when there is all the video tracks followed + by all the audio tracks. It means we don't have to read through the + tracks we are not interested in when forcing a read from a given track, + as could be the case in the above example. If this fails we will fall + back to skipping track data. */ + offset = INT64_C(0); + avi_reader_seek(p_ctx, &offset, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_PRECISE); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + +error: + LOG_DEBUG(p_ctx, "error opening stream (%i)", status); + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) free(module); + p_ctx->priv->module = NULL; + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open avi_reader_open +#endif diff --git a/containers/avi/avi_writer.c b/containers/avi/avi_writer.c new file mode 100755 index 0000000..589ee13 --- /dev/null +++ b/containers/avi/avi_writer.c @@ -0,0 +1,1171 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#define CONTAINER_IS_LITTLE_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */ +#define AVIF_HASINDEX 0x00000010 +#define AVIF_TRUSTCKTYPE 0x00000800 +#define AVIIF_KEYFRAME 0x00000010 + +#define AVI_INDEX_OF_INDEXES 0x00 +#define AVI_INDEX_OF_CHUNKS 0x01 +#define AVI_INDEX_DELTAFRAME 0x80000000 + +#define AVI_INDEX_ENTRY_SIZE 16 +#define AVI_SUPER_INDEX_ENTRY_SIZE 16 +#define AVI_STD_INDEX_ENTRY_SIZE 8 +#define AVI_FRAME_BUFFER_SIZE 100000 + +#define AVI_TRACKS_MAX 3 + +#define AVI_AUDIO_CHUNK_SIZE_LIMIT 16384 /*< Watermark limit for data chunks when 'dwSampleSize' + is non-zero */ + +#define AVI_END_CHUNK(ctx) \ + do { \ + if(STREAM_POSITION(ctx) & 1) WRITE_U8(ctx, 0, "AVI_END_CHUNK"); \ + } while(0) + +#define AVI_PACKET_KEYFRAME (VC_CONTAINER_PACKET_FLAG_KEYFRAME | VC_CONTAINER_PACKET_FLAG_FRAME_END) +#define AVI_PACKET_IS_KEYFRAME(flags) (((flags) & AVI_PACKET_KEYFRAME) == AVI_PACKET_KEYFRAME) + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + uint32_t chunk_index; /**< index of current chunk */ + uint32_t chunk_offs; /**< current offset into bytestream consisting of all + chunks for this track */ + uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */ + uint32_t max_chunk_size; /**< largest chunk written so far */ + uint64_t index_offset; /**< Offset to the start of an OpenDML index for this track + i.e. 'indx' */ + uint32_t index_size; /**< Size of the OpenDML index for this track i.e. 'indx' */ +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX]; + VC_CONTAINER_WRITER_EXTRAIO_T null_io; /**< Null I/O for calculating chunk sizes, etc. */ + VC_CONTAINER_WRITER_EXTRAIO_T temp_io; /**< I/O for temporary storage of index data */ + int headers_written; + + uint32_t header_list_offset; /**< Offset to the header list chunk ('hdrl') */ + uint32_t header_list_size; /**< Size of the header list chunk ('hdrl') */ + uint32_t data_offset; /**< Offset to the start of data packets i.e. + the data in the AVI RIFF 'movi' list */ + uint64_t data_size; /**< Size of the chunk containing data packets */ + uint32_t index_offset; /**< Offset to the start of index data e.g. + the data in an 'idx1' list */ + unsigned current_track_num; /**< Number of track currently being written */ + uint32_t chunk_size; /**< Final size of the current chunk being written (if known) */ + uint32_t chunk_data_written; /**< Data written to the current chunk so far */ + uint8_t *avi_frame_buffer; /**< For accumulating whole frames when seeking isn't available. */ + VC_CONTAINER_PACKET_T frame_packet; /**< Packet header for whole frame. */ + + VC_CONTAINER_STATUS_T index_status; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Local Functions +******************************************************************************/ +static void avi_chunk_id_from_track_num( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T *p_chunk_id, unsigned int track_num ) +{ + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num]; + VC_CONTAINER_FOURCC_T chunk_id = 0; + char track_num_buf[3]; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + chunk_id = VC_FOURCC('0','0','d','c'); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + chunk_id = VC_FOURCC('0','0','w','b'); + else + { + /* Note that avi_writer_add_track should ensure this + can't happen */ + *p_chunk_id = VC_FOURCC('J','U','N','K'); return; + } + + snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num); + memcpy(&chunk_id, track_num_buf, 2); + + *p_chunk_id = chunk_id; +} + +/*****************************************************************************/ +static void avi_index_chunk_id_from_track_num(VC_CONTAINER_FOURCC_T *p_chunk_id, + unsigned int track_num ) +{ + VC_CONTAINER_FOURCC_T chunk_id = 0; + char track_num_buf[3]; + + chunk_id = VC_FOURCC('i','x','0','0'); + + snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num); + memcpy(((uint8_t*)&chunk_id) + 2, track_num_buf, 2); + + *p_chunk_id = chunk_id; +} + +/*****************************************************************************/ +static uint32_t avi_num_chunks( VC_CONTAINER_T *p_ctx ) +{ + unsigned int i; + uint32_t num_chunks = 0; + for (i = 0; i < p_ctx->tracks_num; i++) + num_chunks += p_ctx->tracks[i]->priv->module->chunk_index; + + return num_chunks; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_finish_data_chunk( VC_CONTAINER_T *p_ctx, uint32_t chunk_size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (chunk_size) + { + /* Rewrite the chunk size, this won't be efficient if it happens often */ + if (STREAM_SEEKABLE(p_ctx)) + { + SEEK(p_ctx, STREAM_POSITION(p_ctx) - chunk_size - 4); + WRITE_U32(p_ctx, chunk_size, "Chunk Size"); + SKIP_BYTES(p_ctx, chunk_size); + } + else + { + LOG_DEBUG(p_ctx, "warning, can't rewrite chunk size, data will be malformed"); + status = VC_CONTAINER_ERROR_FAILED; + } + } + + AVI_END_CHUNK(p_ctx); + + if (status != VC_CONTAINER_SUCCESS) status = STREAM_STATUS(p_ctx); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_index_entry( VC_CONTAINER_T *p_ctx, uint8_t track_num, + uint32_t chunk_size, int keyframe ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t deltaframe = keyframe ? 0 : AVI_INDEX_DELTAFRAME; + + vc_container_io_write_uint8(module->temp_io.io, track_num); + vc_container_io_write_be_uint32(module->temp_io.io, chunk_size | deltaframe); + + if (module->temp_io.io->status != VC_CONTAINER_SUCCESS) + { + module->index_status = module->temp_io.io->status; + LOG_DEBUG(p_ctx, "warning, couldn't store index data, index data will be incorrect"); + } + + return module->temp_io.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_read_index_entry( VC_CONTAINER_T *p_ctx, + unsigned int *p_track_num, uint32_t *p_chunk_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t chunk_size; + uint8_t track_num; + + track_num = vc_container_io_read_uint8(module->temp_io.io); + chunk_size = vc_container_io_read_be_uint32(module->temp_io.io); + + /* This shouldn't really happen if the temporary I/O is reliable */ + if (track_num >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED; + + *p_track_num = track_num; + *p_chunk_size = chunk_size; + + return module->temp_io.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_stream_format_chunk(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, uint32_t chunk_size) +{ + VC_CONTAINER_STATUS_T status; + + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','f'), "Chunk ID"); + WRITE_U32(p_ctx, chunk_size, "Chunk Size"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = vc_container_write_bitmapinfoheader(p_ctx, track->format); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = vc_container_write_waveformatex(p_ctx, track->format); + + if (status != VC_CONTAINER_SUCCESS) return status; + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_stream_header_chunk(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track) +{ + VC_CONTAINER_FOURCC_T fourcc_type = 0, fourcc_handler = 0; + uint32_t flags, scale = 0, rate = 0, div, start = 0, sample_size = 0; + uint16_t left = 0, right = 0, top = 0, bottom = 0; + uint32_t max_chunk_size, length = 0; + + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','h'), "Chunk ID"); + WRITE_U32(p_ctx, 56, "Chunk Size"); + + if (!track->is_enabled) + flags = 0; /* AVISF_DISABLED; FIXME: write_media should set this correctly! */ + else + flags = 0; + + if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + fourcc_type = VC_FOURCC('v','i','d','s'); + sample_size = 0; + scale = track->format->type->video.frame_rate_den; + rate = track->format->type->video.frame_rate_num; + if (rate == 0 || scale == 0) + { + LOG_DEBUG(p_ctx, "invalid video framerate (%d/%d)", rate, scale); + LOG_DEBUG(p_ctx, "using 30/1 (playback timing will almost certainly be incorrect)"); + scale = 1; rate = 30; + } + + top = track->format->type->video.y_offset; + left = track->format->type->video.x_offset; + bottom = track->format->type->video.y_offset + track->format->type->video.visible_height; + right = track->format->type->video.x_offset + track->format->type->video.visible_width; + } + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + fourcc_type = VC_FOURCC('a','u','d','s'); + sample_size = track->format->type->audio.block_align; + scale = 1; + + if (track->format->type->audio.block_align) + rate = (track->format->bitrate / track->format->type->audio.block_align) >> 3; + + if (rate == 0) + { + rate = track->format->type->audio.sample_rate ? track->format->type->audio.sample_rate : 32000; + LOG_DEBUG(p_ctx, "invalid audio rate, using %d (playback timing will almost certainly be incorrect)", + rate); + } + } + else + { + /* avi_writer_add_track should ensure this can't happen */ + vc_container_assert(0); + } + + fourcc_handler = codec_to_vfw_fourcc(track->format->codec); + + div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate); + scale /= div; + rate /= div; + + length = sample_size ? track->priv->module->chunk_offs : track->priv->module->chunk_index; + max_chunk_size = track->priv->module->max_chunk_size; + track->priv->module->sample_size = sample_size; + + WRITE_FOURCC(p_ctx, fourcc_type, "fccType"); + WRITE_FOURCC(p_ctx, fourcc_handler, "fccHandler"); + WRITE_U32(p_ctx, flags, "dwFlags"); + WRITE_U16(p_ctx, 0, "wPriority"); + WRITE_U16(p_ctx, 0, "wLanguage"); + WRITE_U32(p_ctx, 0, "dwInitialFrames"); + WRITE_U32(p_ctx, scale, "dwScale"); + WRITE_U32(p_ctx, rate, "dwRate"); + WRITE_U32(p_ctx, start, "dwStart"); + WRITE_U32(p_ctx, length, "dwLength"); + WRITE_U32(p_ctx, max_chunk_size, "dwSuggestedBufferSize"); + WRITE_U32(p_ctx, 0, "dwQuality"); + WRITE_U32(p_ctx, sample_size, "dwSampleSize"); + WRITE_U16(p_ctx, left, "rcFrame.left"); + WRITE_U16(p_ctx, top, "rcFrame.top"); + WRITE_U16(p_ctx, right, "rcFrame.right"); + WRITE_U16(p_ctx, bottom, "rcFrame.bottom"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned int index_track_num, + uint32_t index_size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t num_indices = 1; /* FIXME: support for multiple RIFF chunks (AVIX) */ + unsigned int i; + + if(module->null_io.refcount) + { + /* Assume that we're not actually writing the data, just want know the index chunk size */ + WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_indices * (int64_t)AVI_SUPER_INDEX_ENTRY_SIZE); + return STREAM_STATUS(p_ctx); + } + + if (track_module->index_offset) + WRITE_FOURCC(p_ctx, VC_FOURCC('i','n','d','x'), "Chunk ID"); + else + WRITE_FOURCC(p_ctx, VC_FOURCC('J','U','N','K'), "Chunk ID"); + + WRITE_U32(p_ctx, index_size, "Chunk Size"); + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num); + WRITE_U16(p_ctx, 4, "wLongsPerEntry"); + WRITE_U8(p_ctx, 0, "bIndexSubType"); + WRITE_U8(p_ctx, AVI_INDEX_OF_INDEXES, "bIndexType"); + WRITE_U32(p_ctx, num_indices, "nEntriesInUse"); + WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId"); + WRITE_U32(p_ctx, 0, "dwReserved0"); + WRITE_U32(p_ctx, 0, "dwReserved1"); + WRITE_U32(p_ctx, 0, "dwReserved2"); + + for (i = 0; i < num_indices; ++i) + { + uint64_t index_offset = track_module->index_offset; + uint32_t chunk_size = track_module->index_size; + uint32_t length = track_module->sample_size ? + track_module->chunk_offs : track_module->chunk_index; + WRITE_U64(p_ctx, index_offset, "qwOffset"); + WRITE_U32(p_ctx, chunk_size, "dwSize"); + WRITE_U32(p_ctx, length, "dwDuration"); + } + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_stream_header_list(VC_CONTAINER_T *p_ctx, + unsigned int track_num, uint32_t list_size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num]; + VC_CONTAINER_STATUS_T status; + uint32_t chunk_size = 0; + + WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); + WRITE_U32(p_ctx, list_size, "LIST Size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','l'), "Chunk ID"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + /* Write the stream header chunk ('strh') */ + status = avi_write_stream_header_chunk(p_ctx, track); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Write the stream format chunk ('strf') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_stream_format_chunk(p_ctx, track, 0); + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_stream_format_chunk(p_ctx, track, chunk_size); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* If the track has DRM data, write it into the 'strd' chunk (we don't write + write codec configuration data into 'strd') */ + if (track->priv->drmdata && track->priv->drmdata_size) + { + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','d'), "Chunk ID"); + WRITE_U32(p_ctx, track->priv->drmdata_size, "Chunk Size"); + WRITE_BYTES(p_ctx, track->priv->drmdata, track->priv->drmdata_size); + AVI_END_CHUNK(p_ctx); + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + } + + /* Write the super index chunk ('indx') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_super_index_chunk(p_ctx, track_num, 0); + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_super_index_chunk(p_ctx, track_num, chunk_size); + if (status != VC_CONTAINER_SUCCESS) return status; + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_avi_header_chunk(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t bitrate = 0, width = 0, height = 0, frame_interval = 0; + uint32_t flags, num_chunks = 0, max_video_chunk_size = 0; + uint32_t num_streams = p_ctx->tracks_num; + unsigned int i; + + for (i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module; + if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + width = track->format->type->video.width; + height = track->format->type->video.height; + if (track->format->type->video.frame_rate_num) + frame_interval = track->format->type->video.frame_rate_den * UINT64_C(1000000) / + track->format->type->video.frame_rate_num; + num_chunks = track_module->chunk_index; + max_video_chunk_size = track_module->max_chunk_size; + break; + } + } + + flags = (module->index_offset && module->index_status == VC_CONTAINER_SUCCESS) ? + (AVIF_HASINDEX | AVIF_TRUSTCKTYPE) : 0; + + WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','i','h'), "Chunk ID"); + WRITE_U32(p_ctx, 56, "Chunk Size"); + WRITE_U32(p_ctx, frame_interval, "dwMicroSecPerFrame"); + WRITE_U32(p_ctx, bitrate >> 3, "dwMaxBytesPerSec"); + WRITE_U32(p_ctx, 0, "dwPaddingGranularity"); + WRITE_U32(p_ctx, flags, "dwFlags"); + WRITE_U32(p_ctx, num_chunks, "dwTotalFrames"); + WRITE_U32(p_ctx, 0, "dwInitialFrames"); + WRITE_U32(p_ctx, num_streams, "dwStreams"); + WRITE_U32(p_ctx, max_video_chunk_size, "dwSuggestedBufferSize"); + WRITE_U32(p_ctx, width, "dwWidth"); + WRITE_U32(p_ctx, height, "dwHeight"); + WRITE_U32(p_ctx, 0, "dwReserved0"); + WRITE_U32(p_ctx, 0, "dwReserved1"); + WRITE_U32(p_ctx, 0, "dwReserved2"); + WRITE_U32(p_ctx, 0, "dwReserved3"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_header_list( VC_CONTAINER_T *p_ctx, uint32_t header_list_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + unsigned int i; + + WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); + WRITE_U32(p_ctx, header_list_size, "LIST Size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('h','d','r','l'), "Chunk ID"); + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + /* Write the main AVI header chunk ('avih') */ + if ((status = avi_write_avi_header_chunk(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + for (i = 0; i < p_ctx->tracks_num; i++) + { + uint32_t list_size = 0; + + /* Write a stream header list chunk ('strl') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_stream_header_list(p_ctx, i, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + list_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_stream_header_list(p_ctx, i, list_size); + if (status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_headers( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint32_t header_list_offset, header_list_size = 0; + + /* Write the header list chunk ('hdrl') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_header_list(p_ctx, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + header_list_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + header_list_offset = STREAM_POSITION(p_ctx); + status = avi_write_header_list(p_ctx, header_list_size); + if (status == VC_CONTAINER_SUCCESS && !module->header_list_offset) + { + module->header_list_offset = header_list_offset; + module->header_list_size = header_list_size; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_legacy_index_chunk( VC_CONTAINER_T *p_ctx, uint32_t index_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint32_t chunk_offset = 4; + unsigned int track_num; + + vc_container_assert(8 + avi_num_chunks(p_ctx) * INT64_C(16) <= (int64_t)ULONG_MAX); + + if(module->null_io.refcount) + { + /* Assume that we're not actually writing the data, + just want know the index size */ + WRITE_BYTES(p_ctx, NULL, 8 + avi_num_chunks(p_ctx) * (int64_t)AVI_INDEX_ENTRY_SIZE); + return STREAM_STATUS(p_ctx); + } + + module->index_offset = STREAM_POSITION(p_ctx); + + WRITE_FOURCC(p_ctx, VC_FOURCC('i','d','x','1'), "Chunk ID"); + WRITE_U32(p_ctx, index_size, "Chunk Size"); + + /* Scan through all written entries, convert to appropriate index format */ + vc_container_io_seek(module->temp_io.io, INT64_C(0)); + + while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS) + { + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size, flags; + + status = avi_read_index_entry(p_ctx, &track_num, &chunk_size); + if (status != VC_CONTAINER_SUCCESS) break; + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, track_num); + flags = (chunk_size & AVI_INDEX_DELTAFRAME) ? 0 : AVIIF_KEYFRAME; + chunk_size &= ~AVI_INDEX_DELTAFRAME; + + WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); + WRITE_U32(p_ctx, flags, "dwFlags"); + WRITE_U32(p_ctx, chunk_offset, "dwOffset"); + WRITE_U32(p_ctx, chunk_size, "dwSize"); + + chunk_offset += ((chunk_size + 1) & ~1) + 8; + } + + AVI_END_CHUNK(p_ctx); + + /* Note that currently, we might write a partial index but still set AVIF_HASINDEX */ + /* if ( STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS ) module->index_offset = 0 */ + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_standard_index_chunk( VC_CONTAINER_T *p_ctx, unsigned int index_track_num, + uint32_t index_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module; + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T chunk_id; + int64_t base_offset = module->data_offset + 12; + uint32_t num_chunks = track_module->chunk_index; + uint32_t chunk_offset = 4; + + vc_container_assert(32 + num_chunks * (int64_t)AVI_STD_INDEX_ENTRY_SIZE <= (int64_t)ULONG_MAX); + + if(module->null_io.refcount) + { + /* Assume that we're not actually writing the data, just want know the index chunk size */ + WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_chunks * INT64_C(8)); + return STREAM_STATUS(p_ctx); + } + + track_module->index_offset = STREAM_POSITION(p_ctx); + track_module->index_size = index_size ? (index_size - 8) : 0; + + avi_index_chunk_id_from_track_num(&chunk_id, index_track_num); + WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); + WRITE_U32(p_ctx, index_size, "Chunk Size"); + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num); + WRITE_U16(p_ctx, 2, "wLongsPerEntry"); + WRITE_U8(p_ctx, 0, "bIndexSubType"); + WRITE_U8(p_ctx, AVI_INDEX_OF_CHUNKS, "bIndexType"); + WRITE_U32(p_ctx, num_chunks, "nEntriesInUse"); + WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId"); + WRITE_U64(p_ctx, base_offset, "qwBaseOffset"); + WRITE_U32(p_ctx, 0, "dwReserved"); + + /* Scan through all written entries, convert to appropriate index format */ + vc_container_io_seek(module->temp_io.io, INT64_C(0)); + + while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS) + { + uint32_t chunk_size; + unsigned int track_num; + + status = avi_read_index_entry(p_ctx, &track_num, &chunk_size); + if (status != VC_CONTAINER_SUCCESS) break; + + if(track_num != index_track_num) continue; + + WRITE_U32(p_ctx, chunk_offset, "dwOffset"); + WRITE_U32(p_ctx, chunk_size, "dwSize"); + + chunk_offset += ((chunk_size + 1) & ~(1 | AVI_INDEX_DELTAFRAME)) + 12; + } + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_legacy_index_data( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t chunk_size = 0; + + /* Write the legacy index chunk ('idx1') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_legacy_index_chunk(p_ctx, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_legacy_index_chunk(p_ctx, chunk_size); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_standard_index_data( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t chunk_size = 0; + unsigned int i; + + /* Write the standard index chunks ('ix00') */ + for (i = 0; i < p_ctx->tracks_num; i++) + { + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_standard_index_chunk(p_ctx, i, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_standard_index_chunk(p_ctx, i, chunk_size); + if (status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static int64_t avi_calculate_file_size( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t filesize = 0; + int refcount; + + /* Start from current file position */ + filesize = STREAM_POSITION(p_ctx); + + refcount = vc_container_writer_extraio_enable(p_ctx, &module->null_io); + vc_container_assert(refcount == 0); /* Although perfectly harmless, we should + not be called with the null i/o enabled. */ + VC_CONTAINER_PARAM_UNUSED(refcount); + + do { + /* If we know what the final size of the chunk is going to be, + we can use that here to avoid writing a partial final packet */ + WRITE_BYTES(p_ctx, NULL, p_packet->frame_size ? p_packet->frame_size : p_packet->size); + AVI_END_CHUNK(p_ctx); + + /* Index entries for the chunk */ + WRITE_BYTES(p_ctx, NULL, AVI_INDEX_ENTRY_SIZE + AVI_STD_INDEX_ENTRY_SIZE); + + /* Current standard index data */ + if (avi_write_standard_index_data(p_ctx) != VC_CONTAINER_SUCCESS) break; + + /* Current legacy index data */ + status = avi_write_legacy_index_data(p_ctx); + if (status != VC_CONTAINER_SUCCESS) break; + } while(0); + + filesize += STREAM_POSITION(p_ctx); + + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + return filesize; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T avi_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; + + /* Check we have written headers before any data */ + if(!module->headers_written) + { + if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + module->headers_written = 1; + } + + /* Check that we have started the 'movi' list */ + if (!module->data_offset) + { + module->data_offset = STREAM_POSITION(p_ctx); + vc_container_assert(module->data_offset != INT64_C(0)); + + WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); + WRITE_U32(p_ctx, 0, "LIST Size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('m','o','v','i'), "Chunk ID"); + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + } + + /* If the container is passing in a frame from a new track but we + arent't finished with a chunk from another track we need to finish + that chunk first */ + if (module->chunk_data_written && p_packet->track != module->current_track_num) + { + track_module = p_ctx->tracks[module->current_track_num]->priv->module; + status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); + avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0); + track_module->chunk_index++; + track_module->chunk_offs += module->chunk_data_written; + track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); + module->chunk_data_written = 0; + if (status != VC_CONTAINER_SUCCESS) return status; + } + + /* Check we are not about to go over the limit of total number of chunks */ + if (avi_num_chunks(p_ctx) == (uint32_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + if(STREAM_SEEKABLE(p_ctx)) + { + /* Check we are not about to go over the maximum file size */ + if (avi_calculate_file_size(p_ctx, p_packet) >= (int64_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + /* FIXME: are we expected to handle this case or should it be picked up by the above layer? */ + vc_container_assert(!(module->chunk_data_written && (p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))); + + track = p_ctx->tracks[p_packet->track]; + track_module = p_ctx->tracks[p_packet->track]->priv->module; + module->current_track_num = p_packet->track; + + if (module->chunk_data_written == 0) + { + /* This is the first fragment of the chunk */ + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, p_packet->track); + + if (p_packet->frame_size) + { + /* We know what the final size of the chunk is going to be */ + chunk_size = module->chunk_size = p_packet->frame_size; + } + else + { + chunk_size = p_packet->size; + module->chunk_size = 0; + } + + WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); + if(STREAM_SEEKABLE(p_ctx) || p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) + { + /* If the output stream can seek we can fix up the frame size later, and if the + * packet holds the whole frame we won't need to, so write data straight out. */ + WRITE_U32(p_ctx, chunk_size, "Chunk Size"); + WRITE_BYTES(p_ctx, p_packet->data, p_packet->size); + } + else + { + vc_container_assert(module->avi_frame_buffer); + if(p_packet->size > AVI_FRAME_BUFFER_SIZE) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + module->frame_packet = *p_packet; + module->frame_packet.data = module->avi_frame_buffer; + memcpy(module->frame_packet.data, + p_packet->data, module->frame_packet.size); + } + + module->chunk_data_written = p_packet->size; + } + else + { + if(module->frame_packet.size > 0 && module->avi_frame_buffer) + { + if(module->frame_packet.size > 0) + { + if(module->frame_packet.size + p_packet->size > AVI_FRAME_BUFFER_SIZE) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + memcpy(module->frame_packet.data + module->frame_packet.size, + p_packet->data, p_packet->size); + module->frame_packet.size += p_packet->size; + } + } + else + { + WRITE_BYTES(p_ctx, p_packet->data, p_packet->size); + } + module->chunk_data_written += p_packet->size; + } + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + if ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) || + (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO && + track->format->type->audio.block_align && + module->chunk_data_written > AVI_AUDIO_CHUNK_SIZE_LIMIT)) + { + if(module->frame_packet.size > 0) + { + WRITE_U32(p_ctx, module->frame_packet.size, "Chunk Size"); + WRITE_BYTES(p_ctx, module->frame_packet.data, module->frame_packet.size); + p_packet->size = module->frame_packet.size; + module->frame_packet.size = 0; + } + + if (!module->chunk_size && module->chunk_data_written > p_packet->size) + { + /* The chunk size needs to be rewritten */ + status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); + } + else + { + status = avi_finish_data_chunk(p_ctx, 0); + } + + if(!STREAM_SEEKABLE(p_ctx)) + { + /* If we are streaming then flush to avoid delaying data transport. */ + vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_FLUSH); + } + + if(STREAM_SEEKABLE(p_ctx)) + { + /* Keep track of data written so we can check we don't exceed file size and also for doing + * index fix-ups, but only do this if we are writing to a seekable IO. */ + avi_write_index_entry(p_ctx, p_packet->track, module->chunk_data_written, AVI_PACKET_IS_KEYFRAME(p_packet->flags)); + } + track_module->chunk_index++; + track_module->chunk_offs += module->chunk_data_written; + track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); + module->chunk_data_written = 0; + + if (status != VC_CONTAINER_SUCCESS) return status; + } + + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + /* If we arent't finished with a chunk we need to finish it first */ + if (module->chunk_data_written) + { + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track_num]->priv->module; + status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, writing failed, last chunk truncated"); + } + avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0); + track_module->chunk_index++; + track_module->chunk_offs += module->chunk_data_written; + track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); + module->chunk_data_written = 0; + } + + if(STREAM_SEEKABLE(p_ctx)) + { + uint32_t filesize; + + /* Write standard index data before finalising the size of the 'movi' list */ + status = avi_write_standard_index_data(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + module->index_status = status; + LOG_DEBUG(p_ctx, "warning, writing standard index data failed, file will be malformed"); + } + + /* FIXME: support for multiple RIFF chunks (AVIX) */ + module->data_size = STREAM_POSITION(p_ctx) - module->data_offset - 8; + + /* Now write the legacy index */ + status = avi_write_legacy_index_data(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + module->index_status = status; + LOG_DEBUG(p_ctx, "warning, writing legacy index data failed, file will be malformed"); + } + + /* If we can, do the necessary fixups for values not know at the + time of writing chunk headers */ + + /* Rewrite the AVI RIFF chunk size */ + filesize = (uint32_t)STREAM_POSITION(p_ctx); + SEEK(p_ctx, 4); + WRITE_U32(p_ctx, filesize, "fileSize"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, rewriting 'fileSize' failed, file will be malformed"); + } + + /* Rewrite the header list chunk ('hdrl') */ + SEEK(p_ctx, module->header_list_offset); + status = avi_write_header_list(p_ctx, module->header_list_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, rewriting 'hdrl' failed, file will be malformed"); + } + + /* Rewrite the AVI RIFF 'movi' list size */ + SEEK(p_ctx, module->data_offset + 4); + WRITE_U32(p_ctx, module->data_size, "Chunk Size"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, rewriting 'movi' list size failed, file will be malformed"); + } + } + + vc_container_writer_extraio_delete(p_ctx, &module->null_io); + if(module->temp_io.io) vc_container_writer_extraio_delete(p_ctx, &module->temp_io); + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks_num = 0; + p_ctx->tracks = NULL; + + if(module->avi_frame_buffer) free(module->avi_frame_buffer); + free(module); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + + if (module->headers_written) return VC_CONTAINER_ERROR_FAILED; + + /* FIXME: should we check the format in more detail? */ + if((format->es_type != VC_CONTAINER_ES_TYPE_VIDEO && format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) || + format->codec == VC_CONTAINER_CODEC_UNKNOWN) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Allocate new track */ + if(p_ctx->tracks_num >= AVI_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status) goto error; + } + + status = vc_container_format_copy(track->format, format, format->extradata_size); + if(status) goto error; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + +error: + vc_container_free_track(p_ctx, track); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + { + VC_CONTAINER_ES_FORMAT_T *format = + (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + return avi_writer_add_track(p_ctx, format); + } + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + { + if(!module->headers_written) + { + if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + module->headers_written = 1; + return VC_CONTAINER_SUCCESS; + } + else + return VC_CONTAINER_ERROR_FAILED; + } + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ +VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = 0; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "avi") && strcasecmp(extension, "divx")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + + /* Create a null i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_null(p_ctx, &module->null_io); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(STREAM_SEEKABLE(p_ctx)) + { + /* Create a temporary i/o writer for storage of index data while we are writing */ + status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp_io); + if(status != VC_CONTAINER_SUCCESS) goto error; + } + else + { + module->avi_frame_buffer = malloc(AVI_FRAME_BUFFER_SIZE); + if(!module->avi_frame_buffer) + { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + } + module->frame_packet.size = 0; + + p_ctx->tracks = module->tracks; + + /* Write the RIFF chunk descriptor */ + WRITE_FOURCC(p_ctx, VC_FOURCC('R','I','F','F'), "RIFF ID"); + WRITE_U32(p_ctx, 0, "fileSize"); + WRITE_FOURCC(p_ctx, VC_FOURCC('A','V','I',' '), "fileType"); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + p_ctx->priv->pf_close = avi_writer_close; + p_ctx->priv->pf_write = avi_writer_write; + p_ctx->priv->pf_control = avi_writer_control; + + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "error opening stream"); + p_ctx->tracks_num = 0; + p_ctx->tracks = NULL; + if(module) + { + if(module->avi_frame_buffer) free(module->avi_frame_buffer); + free(module); + } + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open avi_writer_open +#endif diff --git a/containers/binary/CMakeLists.txt b/containers/binary/CMakeLists.txt new file mode 100755 index 0000000..c159b40 --- /dev/null +++ b/containers/binary/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_binary ${LIBRARY_TYPE} binary_reader.c) + +target_link_libraries(reader_binary containers) + +install(TARGETS reader_binary DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_binary ${LIBRARY_TYPE} binary_writer.c) + +target_link_libraries(writer_binary containers) + +install(TARGETS writer_binary DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/binary/binary_reader.c b/containers/binary/binary_reader.c new file mode 100755 index 0000000..0bcf389 --- /dev/null +++ b/containers/binary/binary_reader.c @@ -0,0 +1,267 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define DEFAULT_BLOCK_SIZE (1024*16) +/* Work-around for JPEG because our decoder expects that much at the start */ +#define DEFAULT_JPEG_BLOCK_SIZE (1024*80) + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + unsigned int default_block_size; + unsigned int block_size; + bool init; + + VC_CONTAINER_STATUS_T status; + +} VC_CONTAINER_MODULE_T; + +static struct +{ + const char *ext; + VC_CONTAINER_ES_TYPE_T type; + VC_CONTAINER_FOURCC_T codec; + +} extension_to_format_table[] = +{ + /* Audio */ + {"mp3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MPGA}, + {"aac", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A}, + {"adts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A}, + {"ac3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AC3}, + {"ec3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EAC3}, + {"amr", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRNB}, + {"awb", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRWB}, + {"evrc", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EVRC}, + {"dts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_DTS}, + + /* Video */ + {"m1v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP1V}, + {"m2v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP2V}, + {"m4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, + {"mp4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, + {"h263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, + {"263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, + {"h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, + {"264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, + {"mvc", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MVC}, + {"vc1l", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_WVC1}, + + /* Image */ + {"gif", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_GIF}, + {"jpg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG}, + {"jpeg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG}, + {"png", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PNG}, + {"ppm", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PPM}, + {"tga", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_TGA}, + {"bmp", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_BMP}, + + {"bin", 0, 0}, + {0, 0, 0} +}; + +static struct +{ + const char *ext; + VC_CONTAINER_ES_TYPE_T type; + VC_CONTAINER_FOURCC_T codec; + +} bin_extension_to_format_table[] = +{ + {"m4v.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, + {"263.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, + {"264.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, + {0, 0, 0} +}; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int size; + + if(module->status != VC_CONTAINER_SUCCESS) + return module->status; + + if(!module->block_size) + { + module->block_size = module->default_block_size; + module->init = 0; + } + + packet->size = module->block_size; + packet->dts = packet->pts = VC_CONTAINER_TIME_UNKNOWN; + if(module->init) packet->dts = packet->pts = 0; + packet->track = 0; + packet->flags = 0; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(p_ctx, module->block_size); + module->block_size -= size; + module->status = STREAM_STATUS(p_ctx); + return module->status; + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->block_size, packet->buffer_size); + size = READ_BYTES(p_ctx, packet->data, size); + module->block_size -= size; + packet->size = size; + + module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); + return module->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(module); + VC_CONTAINER_PARAM_UNUSED(offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + VC_CONTAINER_ES_TYPE_T es_type = 0; + VC_CONTAINER_FOURCC_T codec = 0; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check if the extension is supported */ + if(!extension || !vc_uri_path(p_ctx->priv->uri)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + for(i = 0; extension_to_format_table[i].ext; i++) + { + if(strcasecmp(extension, extension_to_format_table[i].ext)) + continue; + + es_type = extension_to_format_table[i].type; + codec = extension_to_format_table[i].codec; + break; + } + if(!extension_to_format_table[i].ext) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* If this a .bin file we look in our bin list */ + for(i = 0; !codec && bin_extension_to_format_table[i].ext; i++) + { + if(!strstr(vc_uri_path(p_ctx->priv->uri), bin_extension_to_format_table[i].ext) && + !strstr(extension, bin_extension_to_format_table[i].ext)) + continue; + + es_type = bin_extension_to_format_table[i].type; + codec = bin_extension_to_format_table[i].codec; + break; + } + if(!codec) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + p_ctx->tracks[0]->format->es_type = es_type; + p_ctx->tracks[0]->format->codec = codec; + p_ctx->tracks[0]->is_enabled = true; + module->default_block_size = DEFAULT_BLOCK_SIZE; + if(codec == VC_CONTAINER_CODEC_JPEG) + module->default_block_size = DEFAULT_JPEG_BLOCK_SIZE; + module->block_size = module->default_block_size; + module->init = 1; + + /* + * We now have all the information we really need to start playing the stream + */ + + p_ctx->priv->pf_close = binary_reader_close; + p_ctx->priv->pf_read = binary_reader_read; + p_ctx->priv->pf_seek = binary_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open binary_reader_open +#endif diff --git a/containers/binary/binary_writer.c b/containers/binary/binary_writer.c new file mode 100755 index 0000000..b4580ea --- /dev/null +++ b/containers/binary/binary_writer.c @@ -0,0 +1,160 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Supported extensions +******************************************************************************/ +static const char *extensions[] = +{ "mp3", "aac", "adts", "ac3", "ec3", "amr", "awb", "evrc", "dts", + "m1v", "m2v", "mp4v", "h263", "263", "h264", "264", "mvc", + "bin", 0 +}; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + WRITE_BYTES(p_ctx, packet->data, packet->size); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_writer_control( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_ES_FORMAT_T *format; + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_STATUS_T status; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= 1) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status != VC_CONTAINER_SUCCESS) + { + vc_container_free_track(p_ctx, track); + return status; + } + WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); + } + + vc_container_format_copy(track->format, format, format->extradata_size); + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + for(i = 0; extensions[i]; i++) + if(!strcasecmp(extension, extensions[i])) break; + if(!extensions[i]) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = &module->track; + + p_ctx->priv->pf_close = binary_writer_close; + p_ctx->priv->pf_write = binary_writer_write; + p_ctx->priv->pf_control = binary_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open binary_writer_open +#endif diff --git a/containers/containers.h b/containers/containers.h new file mode 100755 index 0000000..4b090c9 --- /dev/null +++ b/containers/containers.h @@ -0,0 +1,746 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_H +#define VC_CONTAINERS_H + +/** \file containers.h + * Public API for container readers and writers + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers_types.h" + +/** \defgroup VcContainerApi Container API + * API for container readers and writers */ +/* @{ */ + +/** Status codes returned by the container API */ +typedef enum +{ + VC_CONTAINER_SUCCESS = 0, /**< No error */ + VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED, /**< Format of container is not supported */ + VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED, /**< Format of container uses unsupported features */ + VC_CONTAINER_ERROR_FORMAT_INVALID, /**< Format of container is invalid */ + VC_CONTAINER_ERROR_CORRUPTED, /**< Container is corrupted */ + VC_CONTAINER_ERROR_URI_NOT_FOUND, /**< URI could not be found */ + VC_CONTAINER_ERROR_URI_OPEN_FAILED, /**< URI could not be opened */ + VC_CONTAINER_ERROR_OUT_OF_MEMORY, /**< Out of memory */ + VC_CONTAINER_ERROR_OUT_OF_SPACE, /**< Out of disk space (used when writing) */ + VC_CONTAINER_ERROR_OUT_OF_RESOURCES, /**< Out of resources (other than memory) */ + VC_CONTAINER_ERROR_EOS, /**< End of stream reached */ + VC_CONTAINER_ERROR_LIMIT_REACHED, /**< User defined limit reached (used when writing) */ + VC_CONTAINER_ERROR_BUFFER_TOO_SMALL, /**< Given buffer is too small for data to be copied */ + VC_CONTAINER_ERROR_INCOMPLETE_DATA, /**< Requested data is incomplete */ + VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE, /**< Container doesn't have any track */ + VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED, /**< Format of the track is not supported */ + VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION, /**< The requested operation is not supported */ + VC_CONTAINER_ERROR_INVALID_ARGUMENT, /**< The argument provided is invalid */ + VC_CONTAINER_ERROR_CONTINUE, /**< The requested operation was interrupted and needs to be tried again */ + VC_CONTAINER_ERROR_ABORTED, /**< The requested operation was aborted */ + VC_CONTAINER_ERROR_NOT_FOUND, /**< The requested data was not found */ + VC_CONTAINER_ERROR_DRM_NOT_AUTHORIZED, /**< The DRM was not authorized */ + VC_CONTAINER_ERROR_DRM_EXPIRED, /**< The DRM has expired */ + VC_CONTAINER_ERROR_DRM_FAILED, /**< Generic DRM error */ + VC_CONTAINER_ERROR_FAILED, /**< Generic error */ + VC_CONTAINER_ERROR_NOT_READY /**< The container was not yet able to carry out the operation. */ +} VC_CONTAINER_STATUS_T; + +/** Four Character Code type used to identify codecs, etc. */ +typedef uint32_t VC_CONTAINER_FOURCC_T; + +/** Type definition for language codes. + * Language are defined as ISO639 Alpha-3 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) */ +typedef uint8_t VC_CONTAINER_LANGUAGE_T[3]; + +/** Enumeration of the character encodings supported. */ +typedef enum { + VC_CONTAINER_CHAR_ENCODING_UNKNOWN = 0, /**< Encoding is unknown */ + VC_CONTAINER_CHAR_ENCODING_UTF8 /**< UTF8 encoding */ +} VC_CONTAINER_CHAR_ENCODING_T; + +/** \name Container Capabilities + * The following flags are exported by containers to describe their capabilities */ +/* @{ */ +/** Type definition for container capabilities */ +typedef uint32_t VC_CONTAINER_CAPABILITIES_T; +/** The container can seek */ +#define VC_CONTAINER_CAPS_CAN_SEEK 0x1 +/** Seeking is fast. The absence of this flag can be used as a hint to avoid seeking as much as possible */ +#define VC_CONTAINER_CAPS_SEEK_IS_FAST 0x2 +/** The container controls the pace at which it is reading the data */ +#define VC_CONTAINER_CAPS_CAN_CONTROL_PACE 0x4 +/** The container provides an index. This basically means that seeking will be precise and fast */ +#define VC_CONTAINER_CAPS_HAS_INDEX 0x8 +/** The container provides keyframe information */ +#define VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG 0x10 +/** The container supports adding tracks after TRACK_ADD_DONE control message has been sent */ +#define VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD 0x20 +/** The container supports forcing reading of a given track */ +#define VC_CONTAINER_CAPS_FORCE_TRACK 0x40 +/* @} */ + +/** \defgroup VcContainerMetadata Container Metadata + * Container metadata contains descriptive information which is associated with the multimedia data */ +/* @{ */ + +/** Enumeration of the different metadata keys available. */ +typedef enum { + /* Metadata of global scope */ + VC_CONTAINER_METADATA_KEY_TITLE = VC_FOURCC('t','i','t','l'), + VC_CONTAINER_METADATA_KEY_ARTIST = VC_FOURCC('a','r','t','i'), + VC_CONTAINER_METADATA_KEY_ALBUM = VC_FOURCC('a','l','b','m'), + VC_CONTAINER_METADATA_KEY_DESCRIPTION = VC_FOURCC('d','e','s','c'), + VC_CONTAINER_METADATA_KEY_YEAR = VC_FOURCC('y','e','a','r'), + VC_CONTAINER_METADATA_KEY_GENRE = VC_FOURCC('g','e','n','r'), + VC_CONTAINER_METADATA_KEY_TRACK = VC_FOURCC('t','r','a','k'), + VC_CONTAINER_METADATA_KEY_LYRICS = VC_FOURCC('l','y','r','x'), + + VC_CONTAINER_METADATA_KEY_UNKNOWN = 0 + +} VC_CONTAINER_METADATA_KEY_T; + +/** Definition of the metadata type. + * This type is used to store one element of metadata */ +typedef struct VC_CONTAINER_METADATA_T +{ + /** Identifier for the type of metadata the value refers to. + * Using an enum for the id will mean that a list of possible values will have to be + * defined and maintained. This might limit extensibility and customisation.\n + * Maybe it would be better to use a FOURCC or even a string here. */ + VC_CONTAINER_METADATA_KEY_T key; + + VC_CONTAINER_LANGUAGE_T language; /**< Language code for the metadata */ + VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding of the metadata */ + + /** Metadata value. This value is defined as null-terminated UTF-8 string.\n + * Do we want to support other types than strings (e.g. integer) ?\n + * We need an encoding conversion library! */ + char *value; + + /** Size of the memory area reserved for metadata value (including any + * terminating characters). */ + unsigned int size; +} VC_CONTAINER_METADATA_T; +/* @} */ + +/** \defgroup VcContainerESFormat Container Elementary Stream Format + * This describes the format of an elementary stream associated with a track */ +/* @{ */ + +/** Enumeration of the different types of elementary streams. + * This divides elementary streams into 4 big categories. */ +typedef enum +{ + VC_CONTAINER_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ + VC_CONTAINER_ES_TYPE_AUDIO, /**< Audio elementary stream */ + VC_CONTAINER_ES_TYPE_VIDEO, /**< Video elementary stream */ + VC_CONTAINER_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream (e.g. subtitles, overlays) */ + +} VC_CONTAINER_ES_TYPE_T; + +/** Definition of a video format. + * This describes the properties specific to a video stream */ +typedef struct VC_CONTAINER_VIDEO_FORMAT_T +{ + uint32_t width; /**< Width of the frame */ + uint32_t height; /**< Height of the frame */ + uint32_t visible_width; /**< Width of the visible area of the frame */ + uint32_t visible_height; /**< Height of the visible area of the frame */ + uint32_t x_offset; /**< Offset to the start of the visible width */ + uint32_t y_offset; /**< Offset to the start of the visible height */ + uint32_t frame_rate_num; /**< Frame rate numerator */ + uint32_t frame_rate_den; /**< Frame rate denominator */ + uint32_t par_num; /**< Pixel aspect ratio numerator */ + uint32_t par_den; /**< Pixel aspect ratio denominator */ +} VC_CONTAINER_VIDEO_FORMAT_T; + +/** Enumeration for the different channel locations */ +typedef enum +{ + VC_CONTAINER_AUDIO_CHANNEL_LEFT = 0, /**< Left channel */ + VC_CONTAINER_AUDIO_CHANNEL_RIGHT, /**< Right channel */ + VC_CONTAINER_AUDIO_CHANNEL_CENTER, /**< Center channel */ + VC_CONTAINER_AUDIO_CHANNEL_LOW_FREQUENCY, /**< Low frequency channel */ + VC_CONTAINER_AUDIO_CHANNEL_BACK_LEFT, /**< Back left channel */ + VC_CONTAINER_AUDIO_CHANNEL_BACK_RIGHT, /**< Back right channel */ + VC_CONTAINER_AUDIO_CHANNEL_BACK_CENTER, /**< Back center channel */ + VC_CONTAINER_AUDIO_CHANNEL_SIDE_LEFT, /**< Side left channel */ + VC_CONTAINER_AUDIO_CHANNEL_SIDE_RIGHT, /**< Side right channel */ + + VC_CONTAINER_AUDIO_CHANNELS_MAX = 32 /**< Maximum number of channels supported */ + +} VC_CONTAINER_AUDIO_CHANNEL_T; + +/** \name Audio format flags + * \anchor audioformatflags + * The following flags describe properties of an audio stream */ +/* @{ */ +#define VC_CONTAINER_AUDIO_FORMAT_FLAG_CHANNEL_MAPPING 0x1 /**< Channel mapping available */ +/* @} */ + +/** Definition of an audio format. + * This describes the properties specific to an audio stream */ +typedef struct VC_CONTAINER_AUDIO_FORMAT_T +{ + uint32_t channels; /**< Number of audio channels */ + uint32_t sample_rate; /**< Sample rate */ + + uint32_t bits_per_sample; /**< Bits per sample */ + uint32_t block_align; /**< Size of a block of data */ + + uint32_t flags; /**< Flags describing the audio format. + * See \ref audioformatflags "Audio format flags". */ + + /** Mapping of the channels in order of appearance */ + VC_CONTAINER_AUDIO_CHANNEL_T channel_mapping[VC_CONTAINER_AUDIO_CHANNELS_MAX]; + + uint16_t gap_delay; /**< Delay introduced by the encoder. Used for gapless playback */ + uint16_t gap_padding; /**< Padding introduced by the encoder. Used for gapless playback */ + + /** Replay gain information. First element is the track information and the second + * is the album information. */ + struct { + float peak; /**< Peak value (full range is 1.0) */ + float gain; /**< Gain value in dB */ + } replay_gain[2]; + +} VC_CONTAINER_AUDIO_FORMAT_T; + +/** Definition of a subpicture format. + * This describes the properties specific to a subpicture stream */ +typedef struct VC_CONTAINER_SUBPICTURE_FORMAT_T +{ + VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding for text based subpicture formats */ + uint32_t x_offset; /**< Width offset to the start of the subpicture */ + uint32_t y_offset; /**< Height offset to the start of the subpicture */ +} VC_CONTAINER_SUBPICTURE_FORMAT_T; + +/** \name Elementary stream format flags + * \anchor esformatflags + * The following flags describe properties of an elementary stream */ +/* @{ */ +#define VC_CONTAINER_ES_FORMAT_FLAG_FRAMED 0x1 /**< Elementary stream is framed */ +/* @} */ + +/** Definition of the type specific format. + * This describes the type specific information of the elementary stream. */ +typedef union +{ + VC_CONTAINER_AUDIO_FORMAT_T audio; /**< Audio specific information */ + VC_CONTAINER_VIDEO_FORMAT_T video; /**< Video specific information */ + VC_CONTAINER_SUBPICTURE_FORMAT_T subpicture; /**< Subpicture specific information */ +} VC_CONTAINER_ES_SPECIFIC_FORMAT_T; + +/** Definition of an elementary stream format */ +typedef struct VC_CONTAINER_ES_FORMAT_T +{ + VC_CONTAINER_ES_TYPE_T es_type; /**< Type of the elementary stream */ + VC_CONTAINER_FOURCC_T codec; /**< Coding of the elementary stream */ + VC_CONTAINER_FOURCC_T codec_variant; /**< If set, indicates a variant of the coding */ + + VC_CONTAINER_ES_SPECIFIC_FORMAT_T *type; /**< Type specific information for the elementary stream */ + + uint32_t bitrate; /**< Bitrate */ + + VC_CONTAINER_LANGUAGE_T language; /**< Language code for the elementary stream */ + uint32_t group_id; /**< ID of the group this elementary stream belongs to */ + + uint32_t flags; /**< Flags describing the properties of an elementary stream. + * See \ref esformatflags "Elementary stream format flags". */ + + unsigned int extradata_size; /**< Size of the codec specific data */ + uint8_t *extradata; /**< Codec specific data */ + +} VC_CONTAINER_ES_FORMAT_T; +/* @} */ + +/** \defgroup VcContainerPacket Container Packet + * A container packet is the unit of data that is being read from or written to a container */ +/* @{ */ + +/** Structure describing a data packet */ +typedef struct VC_CONTAINER_PACKET_T +{ + struct VC_CONTAINER_PACKET_T *next; /**< Used to build lists of packets */ + uint8_t *data; /**< Pointer to the buffer containing the actual data for the packet */ + unsigned int buffer_size; /**< Size of the p_data buffer. This is used to indicate how much data can be read in p_data */ + unsigned int size; /**< Size of the data contained in p_data */ + unsigned int frame_size; /**< If set, indicates the size of the frame this packet belongs to */ + int64_t pts; /**< Presentation Timestamp of the packet */ + int64_t dts; /**< Decoding Timestamp of the packet */ + uint64_t num; /**< Number of this packet */ + uint32_t track; /**< Track associated with this packet */ + uint32_t flags; /**< Flags associated with this packet */ + + void *user_data; /**< Field reserved for use by the client */ + void *framework_data; /**< Field reserved for use by the framework */ + +} VC_CONTAINER_PACKET_T; + +/** \name Container Packet Flags + * The following flags describe properties of the data packet */ +/* @{ */ +#define VC_CONTAINER_PACKET_FLAG_KEYFRAME 0x01 /**< Packet is a keyframe */ +#define VC_CONTAINER_PACKET_FLAG_FRAME_START 0x02 /**< Packet starts a frame */ +#define VC_CONTAINER_PACKET_FLAG_FRAME_END 0x04 /**< Packet ends a frame */ +#define VC_CONTAINER_PACKET_FLAG_FRAME 0x06 /**< Packet contains only complete frames */ +#define VC_CONTAINER_PACKET_FLAG_DISCONTINUITY 0x08 /**< Packet comes after a discontinuity in the stream. Decoders might have to be flushed */ +#define VC_CONTAINER_PACKET_FLAG_ENCRYPTED 0x10 /**< Packet contains DRM encrypted data */ +#define VC_CONTAINER_PACKET_FLAG_CONFIG 0x20 /**< Packet contains stream specific config data */ +/* @} */ + +/** \name Special Unknown Time Value + * This is the special value used to signal that a timestamp is not known */ +/* @{ */ +#define VC_CONTAINER_TIME_UNKNOWN (INT64_C(1)<<63) /**< Special value for signalling that time is not known */ +/* @} */ + +/* @} */ + +/** \name Track flags + * \anchor trackflags + * The following flags describe properties of a track */ +/* @{ */ +#define VC_CONTAINER_TRACK_FLAG_CHANGED 0x1 /**< Track definition has changed */ +/* @} */ + +/** Definition of the track type */ +typedef struct VC_CONTAINER_TRACK_T +{ + struct VC_CONTAINER_TRACK_PRIVATE_T *priv; /**< Private member used by the implementation */ + uint32_t is_enabled; /**< Flag to specify if the track is enabled */ + uint32_t flags; /**< Flags describing the properties of a track. + * See \ref trackflags "Track flags". */ + + VC_CONTAINER_ES_FORMAT_T *format; /**< Format of the elementary stream contained in the track */ + + unsigned int meta_num; /**< Number of metadata elements associated with the track */ + VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the track */ + +} VC_CONTAINER_TRACK_T; + +/** Definition of the DRM type */ +typedef struct VC_CONTAINER_DRM_T +{ + VC_CONTAINER_FOURCC_T format; /**< Four character code describing the format of the DRM in use */ + unsigned int views_max; /**< Maximum number of views allowed */ + unsigned int views_current; /**< Current number of views */ + +} VC_CONTAINER_DRM_T; + +/** Type definition for the progress reporting function. This function will be called regularly + * by the container during a call which blocks for too long and will report the progress of the + * operation as an estimated total length in microseconds and a percentage done. + * Returning anything else than VC_CONTAINER_SUCCESS in this function will abort the current + * operation. */ +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_PROGRESS_REPORT_FUNC_T)(void *userdata, + int64_t length, unsigned int percentage_done); + +/** \name Container Events + * The following flags are exported by containers to notify the application of events */ +/* @{ */ +/** Type definition for container events */ +typedef uint32_t VC_CONTAINER_EVENTS_T; +#define VC_CONTAINER_EVENT_TRACKS_CHANGE 1 /**< Track information has changed */ +#define VC_CONTAINER_EVENT_METADATA_CHANGE 2 /**< Metadata has changed */ +/* @} */ + +/** Definition of the container context */ +typedef struct VC_CONTAINER_T +{ + struct VC_CONTAINER_PRIVATE_T *priv; /**< Private member used by the implementation */ + + VC_CONTAINER_EVENTS_T events; /**< Events generated by the container */ + VC_CONTAINER_CAPABILITIES_T capabilities; /**< Capabilities exported by the container */ + + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress; /**< Progress report function pointer */ + void *progress_userdata; /**< Progress report user data */ + + int64_t duration; /**< Duration of the media in microseconds */ + int64_t position; /**< Current time position into the media */ + int64_t size; /**< Size of the media in bytes */ + + unsigned int tracks_num; /**< Number of tracks available */ + /** Pointer to an array of pointers to track elements. + * The reasoning for using a pointer to pointers here is to allow us to extend + * VC_CONTAINER_TRACK_T without losing binary backward compatibility. */ + VC_CONTAINER_TRACK_T **tracks; + + unsigned int meta_num; /**< Number of metadata elements associated with the container */ + VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the container */ + + VC_CONTAINER_DRM_T *drm; /**< Description used for DRM protected content */ + +} VC_CONTAINER_T; + +/** Forward declaration of a container input / output context. + * This structure defines the context for a container io instance */ +typedef struct VC_CONTAINER_IO_T VC_CONTAINER_IO_T; + +/** Opens the media container pointed to by the URI for reading. + * This will create an an instance of a container reader and its associated context. + * The context returned will also be filled with the information retrieved from the media. + * + * If the media isn't accessible or recognized, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param psz_uri Unified Resource Identifier pointing to the media container + * \param status Returns the status of the operation + * \param pf_progress User provided function pointer to a progress report function. Can be set to + * null if no progress report is wanted. This function will be used during + * the whole lifetime of the instance (i.e. it will be used during + * open / seek / close) + * \param progress_userdata User provided pointer that will be passed during the progress report + * function call. + * \return A pointer to the context of the new instance of the + * container reader. Returns NULL on failure. + */ +VC_CONTAINER_T *vc_container_open_reader( const char *psz_uri, VC_CONTAINER_STATUS_T *status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); + +/** Opens for reading the media container pointed to by the container i/o. + * This will create an an instance of a container reader and its associated context. + * The context returned will also be filled with the information retrieved from the media. + * + * If the media isn't accessible or recognized, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param p_io Instance of the container i/o to use + * \param psz_uri Unified Resource Identifier pointing to the media container (optional) + * \param status Returns the status of the operation + * \param pf_progress User provided function pointer to a progress report function. Can be set to + * null if no progress report is wanted. This function will be used during + * the whole lifetime of the instance (i.e. it will be used during + * open / seek / close) + * \param progress_userdata User provided pointer that will be passed during the progress report + * function call. + * \return A pointer to the context of the new instance of the + * container reader. Returns NULL on failure. + */ +VC_CONTAINER_T *vc_container_open_reader_with_io( VC_CONTAINER_IO_T *p_io, + const char *psz_uri, VC_CONTAINER_STATUS_T *status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); + +/** Opens the media container pointed to by the URI for writing. + * This will create an an instance of a container writer and its associated context. + * The context returned will be initialised to sensible values. + * + * The application will need to add all the media tracks using \ref vc_container_control before + * it starts writing data using \ref vc_container_write. + * + * If the media isn't accessible or recognized, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param psz_uri Unified Resource Identifier pointing to the media container + * \param status Returns the status of the operation + * \param pf_progress User provided function pointer to a progess report function. Can be set to + * null if no progress report is wanted. + * \param progress_userdata User provided pointer that will be passed during the progress report + * function call. + * \return A pointer to the context of the new instance of the + * container writer. Returns NULL on failure. + */ +VC_CONTAINER_T *vc_container_open_writer( const char *psz_uri, VC_CONTAINER_STATUS_T *status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); + +/** Closes an instance of a container reader / writer. + * This will free all the resources associated with the context. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *context ); + +/** \name Container read flags + * The following flags can be passed during a read call */ +/* @{ */ +/** Type definition for the read flags */ +typedef uint32_t VC_CONTAINER_READ_FLAGS_T; +/** Ask the container to only return information on the next packet without reading it */ +#define VC_CONTAINER_READ_FLAG_INFO 1 +/** Ask the container to skip the next packet */ +#define VC_CONTAINER_READ_FLAG_SKIP 2 +/** Force the container to read data from the specified track */ +#define VC_CONTAINER_READ_FLAG_FORCE_TRACK 4 +/* @} */ + +/** Reads a data packet from a container reader. + * By default, the reader will read whatever packet comes next in the container and update the + * given \ref VC_CONTAINER_PACKET_T structure with this packet's information. + * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n + * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the + * following packet but not its actual data. The data can be retreived later by issuing another + * read request.\n + * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the + * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting + * to reading the packet which comes next in the container.\n + * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case + * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure + * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given.\n + * A combination of all these flags can be used. + * + * \param context Pointer to the context of the reader to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * This needs to be partially filled before the call (buffer, buffer_size) + * \param flags Flags controlling the read operation + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags ); + +/** Writes a data packet to a container writer. + * + * \param context Pointer to the context of the writer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet ); + +/** Definition of the different seek modes */ +typedef enum +{ + /** The offset provided for seeking is an absolute time offset in microseconds */ + VC_CONTAINER_SEEK_MODE_TIME = 0, + /** The offset provided for seeking is a percentage (Q32 ?) */ + VC_CONTAINER_SEEK_MODE_PERCENT + +} VC_CONTAINER_SEEK_MODE_T; + +/** \name Container Seek Flags + * The following flags control seek operations */ +/* @{ */ +/** Type definition for the seek flags */ +typedef uint32_t VC_CONTAINER_SEEK_FLAGS_T; +/** Choose precise seeking even if slower */ +#define VC_CONTAINER_SEEK_FLAG_PRECISE 0x1 +/** By default a seek will always seek to the keyframe which comes just before the requested + * position. This flag allows the caller to force the container to seek to the keyframe which + * comes just after the requested position. */ +#define VC_CONTAINER_SEEK_FLAG_FORWARD 0x2 +/* @} */ + +/** Seek into a container reader. + * + * \param context Pointer to the context of the reader to use + * \param offset Offset to seek to. Used as an input as well as output value. + * \param mode Seeking mode requested. + * \param flags Flags affecting the seeking operation. + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *context, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags); + +/** Performance statistics. + */ +/** The maximum number of bins a statistics value is held in */ +#define VC_CONTAINER_STATS_BINS 10 + +/** This type is used to represent multiple values of a statistic. + */ +typedef struct VC_CONTAINER_STATS_T +{ + /** The number of places to right shift count before using. Resulting values + * of zero are rounded to 1. */ + uint32_t shift; + + /** We store VC_CONTAINER_STATS_BINS+1 records, in sorted order of numpc. + * At least one will be invalid and all zero. We combine adjacent records + * as necessary. */ + struct { + /** Sum of count. For single value statistics this is the freqency, for paired statistics + * this is the number of bytes written. */ + uint32_t count; + /** Number of count. For single value statistics this is the total value, for paired statistics + * this is the total length of time. */ + uint32_t num; + /** Number>>shift per count. Stored to save recalculation. */ + uint32_t numpc; + } record[VC_CONTAINER_STATS_BINS+1]; +} VC_CONTAINER_STATS_T; + +/** This type represents the statistics saved by the io layer. */ +typedef struct VC_CONTAINER_WRITE_STATS_T +{ + /** This logs the number of bytes written in count, and the microseconds taken to write + * in num. */ + VC_CONTAINER_STATS_T write; + /** This logs the length of time the write function has to wait for the asynchronous task. */ + VC_CONTAINER_STATS_T wait; + /** This logs the length of time that we wait for a flush command to complete. */ + VC_CONTAINER_STATS_T flush; +} VC_CONTAINER_WRITE_STATS_T; + + +/** Control operations which can be done on containers. */ +typedef enum +{ + /** Adds a new track to the list of tracks. This should be used by writers to create + * their list of tracks.\n + * Arguments:\n + * arg1= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n + * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ + VC_CONTAINER_CONTROL_TRACK_ADD = 0, + + /** Specifies that we're done adding new tracks. This is optional but can be used by writers + * to trigger the writing of the container header early. If this isn't used, the header will be + * written when the first data packet is received.\n + * No arguments.\n + * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ + VC_CONTAINER_CONTROL_TRACK_ADD_DONE, + + /** Change the format of a track in the list of tracks. This should be used by writers to modify + * the format of a track at run-time.\n + * Arguments:\n + * arg1= unsigned int: index of track to change\n + * arg2= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n + * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ + VC_CONTAINER_CONTROL_TRACK_CHANGE, + + /** Deletes a track from the list of tracks. This should be used by writers to delete tracks + * during run-time. Note that vc_container_close will automatically delete all track so it + * isn't necessary to call this before closing a writer.\n + * Arguments:\n + * arg1= index of the track to delete */ + VC_CONTAINER_CONTROL_TRACK_DEL, + + /** Activate the playback of DRM protected content.\n + * No arguments.\n + * return= one of the VC_CONTAINER_ERROR_DRM error codes if content can't be played */ + VC_CONTAINER_CONTROL_DRM_PLAY, + + /** TBD */ + VC_CONTAINER_CONTROL_METADATA_ADD, + /** TBD */ + VC_CONTAINER_CONTROL_METADATA_CHANGE, + /** TBD */ + VC_CONTAINER_CONTROL_METADATA_DEL, + + /** TBD */ + VC_CONTAINER_CONTROL_CHAPTER_ADD, + /** TBD */ + VC_CONTAINER_CONTROL_CHAPTER_DEL, + + /** Set a maximum size for files generated by writers.\n + * Arguments:\n + * arg1= uint64_t: maximum size */ + VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE, + + /** Enables/disabled performance statistic gathering.\n + * Arguments:\n + * arg1= bool: enable or disable */ + VC_CONTAINER_CONTROL_SET_IO_PERF_STATS, + + /** Collects performance statistics.\n + * Arguments:\n + * arg1= VC_CONTAINER_WRITE_STATS_T *: */ + VC_CONTAINER_CONTROL_GET_IO_PERF_STATS, + + /** HACK.\n + * Arguments:\n + * arg1= void (*)(void *): callback function + * arg1= void *: opaque pointer to pass during the callback */ + VC_CONTAINER_CONTROL_SET_IO_BUFFER_FULL_CALLBACK, + + /** Set the I/O read buffer size to be used.\n + * Arguments:\n + * arg1= uint32_t: New buffer size in bytes*/ + VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, + + /** Set the timeout on I/O read operations, if applicable.\n + * Arguments:\n + * arg1= uint32_t: New timeout in milliseconds, or VC_CONTAINER_READ_TIMEOUT_BLOCK */ + VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, + + /** Set the timestamp base.\n + * The timestamp passed equates to time zero for the stream.\n + * Arguments:\n + * arg1= uint32_t: Timestamp base in stream clock units. */ + VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, + + /** Set the next expected sequence number for the stream.\n + * Arguments:\n + * arg1= uint32_t: Next expected sequence number. */ + VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, + + /** Set the source ID for the container.\n + * Arguments:\n + * arg1= uint32_t: Source identifier. */ + VC_CONTAINER_CONTROL_SET_SOURCE_ID, + + /** Arguments:\n + * arg1= void *: metadata buffer + * arg2= unsigned long: length of metadata in bytes */ + VC_CONTAINER_CONTROL_GET_DRM_METADATA, + + /** Arguments:\n + * arg1= unsigned long: track number + * arg2= VC_CONTAINER_FOURCC_T : drm type + * arg3= void *: encryption configuration parameters. + * arg4= unsigned long: configuration data length */ + VC_CONTAINER_CONTROL_ENCRYPT_TRACK, + + /** Causes the io to be flushed.\n + * Arguments: none */ + VC_CONTAINER_CONTROL_IO_FLUSH, + + /** Request the container reader to packetize data for the specified track. + * Arguments:\n + * arg1= unsigned long: track number + * arg2= VC_CONTAINER_FOURCC_T: codec variant to output */ + VC_CONTAINER_CONTROL_TRACK_PACKETIZE, + + /** Private user extensions must be above this number */ + VC_CONTAINER_CONTROL_USER_EXTENSIONS = 0x1000 + +} VC_CONTAINER_CONTROL_T; + +/** Used with the VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS control to indicate the read shall + * block until either data is available, or an error occurs. + */ +#define VC_CONTAINER_READ_TIMEOUT_BLOCK (uint32_t)(-1) + +/** Extensible control function for container readers and writers. + * This function takes a variable number of arguments which will depend on the specific operation. + * + * \param context Pointer to the VC_CONTAINER_T context to use + * \param operation The requested operation + * \return the status of the operation. Can be \ref VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION + * if the operation is not supported or implemented by the container. + */ +VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, ... ); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_H */ diff --git a/containers/containers_codecs.h b/containers/containers_codecs.h new file mode 100755 index 0000000..3a6bc4b --- /dev/null +++ b/containers/containers_codecs.h @@ -0,0 +1,214 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_CODECS_H +#define VC_CONTAINERS_CODECS_H + +/** \file containers_codecs.h + * Codec helpers + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers_types.h" + +/* Video */ +#define VC_CONTAINER_CODEC_MP1V VC_FOURCC('m','p','1','v') +#define VC_CONTAINER_CODEC_MP2V VC_FOURCC('m','p','2','v') +#define VC_CONTAINER_CODEC_MP4V VC_FOURCC('m','p','4','v') +#define VC_CONTAINER_CODEC_DIV3 VC_FOURCC('d','i','v','3') +#define VC_CONTAINER_CODEC_DIV4 VC_FOURCC('d','i','v','4') +#define VC_CONTAINER_CODEC_H263 VC_FOURCC('h','2','6','3') +#define VC_CONTAINER_CODEC_H264 VC_FOURCC('h','2','6','4') +#define VC_CONTAINER_CODEC_MVC VC_FOURCC('m','v','c',' ') +#define VC_CONTAINER_CODEC_WMV1 VC_FOURCC('w','m','v','1') +#define VC_CONTAINER_CODEC_WMV2 VC_FOURCC('w','m','v','2') +#define VC_CONTAINER_CODEC_WMV3 VC_FOURCC('w','m','v','3') +#define VC_CONTAINER_CODEC_WVC1 VC_FOURCC('w','v','c','1') +#define VC_CONTAINER_CODEC_WMVA VC_FOURCC('w','m','v','a') +#define VC_CONTAINER_CODEC_MJPEG VC_FOURCC('m','j','p','g') +#define VC_CONTAINER_CODEC_MJPEGA VC_FOURCC('m','j','p','a') +#define VC_CONTAINER_CODEC_MJPEGB VC_FOURCC('m','j','p','b') +#define VC_CONTAINER_CODEC_THEORA VC_FOURCC('t','h','e','o') +#define VC_CONTAINER_CODEC_VP3 VC_FOURCC('v','p','3',' ') +#define VC_CONTAINER_CODEC_VP6 VC_FOURCC('v','p','6',' ') +#define VC_CONTAINER_CODEC_VP7 VC_FOURCC('v','p','7',' ') +#define VC_CONTAINER_CODEC_VP8 VC_FOURCC('v','p','8',' ') +#define VC_CONTAINER_CODEC_RV10 VC_FOURCC('r','v','1','0') +#define VC_CONTAINER_CODEC_RV20 VC_FOURCC('r','v','2','0') +#define VC_CONTAINER_CODEC_RV30 VC_FOURCC('r','v','3','0') +#define VC_CONTAINER_CODEC_RV40 VC_FOURCC('r','v','4','0') +#define VC_CONTAINER_CODEC_AVS VC_FOURCC('a','v','s',' ') +#define VC_CONTAINER_CODEC_SPARK VC_FOURCC('s','p','r','k') +#define VC_CONTAINER_CODEC_DIRAC VC_FOURCC('d','r','a','c') + +#define VC_CONTAINER_CODEC_YUV VC_FOURCC('y','u','v',' ') +#define VC_CONTAINER_CODEC_I420 VC_FOURCC('I','4','2','0') +#define VC_CONTAINER_CODEC_YV12 VC_FOURCC('Y','V','1','2') +#define VC_CONTAINER_CODEC_I422 VC_FOURCC('I','4','2','2') +#define VC_CONTAINER_CODEC_YUYV VC_FOURCC('Y','U','Y','V') +#define VC_CONTAINER_CODEC_YVYU VC_FOURCC('Y','V','Y','U') +#define VC_CONTAINER_CODEC_UYVY VC_FOURCC('U','Y','V','Y') +#define VC_CONTAINER_CODEC_VYUY VC_FOURCC('V','Y','U','Y') +#define VC_CONTAINER_CODEC_NV12 VC_FOURCC('N','V','1','2') +#define VC_CONTAINER_CODEC_NV21 VC_FOURCC('N','V','2','1') +#define VC_CONTAINER_CODEC_ARGB VC_FOURCC('A','R','G','B') +#define VC_CONTAINER_CODEC_RGBA VC_FOURCC('R','G','B','A') +#define VC_CONTAINER_CODEC_ABGR VC_FOURCC('A','B','G','R') +#define VC_CONTAINER_CODEC_BGRA VC_FOURCC('B','G','R','A') +#define VC_CONTAINER_CODEC_RGB16 VC_FOURCC('R','G','B','2') +#define VC_CONTAINER_CODEC_RGB24 VC_FOURCC('R','G','B','3') +#define VC_CONTAINER_CODEC_RGB32 VC_FOURCC('R','G','B','4') +#define VC_CONTAINER_CODEC_BGR16 VC_FOURCC('B','G','R','2') +#define VC_CONTAINER_CODEC_BGR24 VC_FOURCC('B','G','R','3') +#define VC_CONTAINER_CODEC_BGR32 VC_FOURCC('B','G','R','4') +#define VC_CONTAINER_CODEC_YUVUV128 VC_FOURCC('S','A','N','D') + +#define VC_CONTAINER_CODEC_JPEG VC_FOURCC('j','p','e','g') +#define VC_CONTAINER_CODEC_PNG VC_FOURCC('p','n','g',' ') +#define VC_CONTAINER_CODEC_GIF VC_FOURCC('g','i','f',' ') +#define VC_CONTAINER_CODEC_PPM VC_FOURCC('p','p','m',' ') +#define VC_CONTAINER_CODEC_TGA VC_FOURCC('t','g','a',' ') +#define VC_CONTAINER_CODEC_BMP VC_FOURCC('b','m','p',' ') + +/* Audio */ +#define VC_CONTAINER_CODEC_PCM_UNSIGNED_BE VC_FOURCC('P','C','M','U') +#define VC_CONTAINER_CODEC_PCM_UNSIGNED_LE VC_FOURCC('p','c','m','u') +#define VC_CONTAINER_CODEC_PCM_SIGNED_BE VC_FOURCC('P','C','M','S') +#define VC_CONTAINER_CODEC_PCM_SIGNED_LE VC_FOURCC('p','c','m','s') +#define VC_CONTAINER_CODEC_PCM_FLOAT_BE VC_FOURCC('P','C','M','F') +#define VC_CONTAINER_CODEC_PCM_FLOAT_LE VC_FOURCC('p','c','m','f') +/* Defines for native endianness */ +#ifdef VC_CONTAINER_IS_BIG_ENDIAN +#define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_BE +#define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_BE +#define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_BE +#else +#define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_LE +#define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_LE +#define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_LE +#endif + +#define VC_CONTAINER_CODEC_MPGA VC_FOURCC('m','p','g','a') +#define VC_CONTAINER_CODEC_MP4A VC_FOURCC('m','p','4','a') +#define VC_CONTAINER_CODEC_ALAW VC_FOURCC('a','l','a','w') +#define VC_CONTAINER_CODEC_MULAW VC_FOURCC('u','l','a','w') +#define VC_CONTAINER_CODEC_ADPCM_MS VC_FOURCC('m','s',0x0,0x2) +#define VC_CONTAINER_CODEC_ADPCM_IMA_MS VC_FOURCC('m','s',0x0,0x1) +#define VC_CONTAINER_CODEC_ADPCM_SWF VC_FOURCC('a','s','w','f') +#define VC_CONTAINER_CODEC_WMA1 VC_FOURCC('w','m','a','1') +#define VC_CONTAINER_CODEC_WMA2 VC_FOURCC('w','m','a','2') +#define VC_CONTAINER_CODEC_WMAP VC_FOURCC('w','m','a','p') +#define VC_CONTAINER_CODEC_WMAL VC_FOURCC('w','m','a','l') +#define VC_CONTAINER_CODEC_WMAV VC_FOURCC('w','m','a','v') +#define VC_CONTAINER_CODEC_AMRNB VC_FOURCC('a','m','r','n') +#define VC_CONTAINER_CODEC_AMRWB VC_FOURCC('a','m','r','w') +#define VC_CONTAINER_CODEC_AMRWBP VC_FOURCC('a','m','r','p') +#define VC_CONTAINER_CODEC_AC3 VC_FOURCC('a','c','3',' ') +#define VC_CONTAINER_CODEC_EAC3 VC_FOURCC('e','a','c','3') +#define VC_CONTAINER_CODEC_DTS VC_FOURCC('d','t','s',' ') +#define VC_CONTAINER_CODEC_MLP VC_FOURCC('m','l','p',' ') +#define VC_CONTAINER_CODEC_FLAC VC_FOURCC('f','l','a','c') +#define VC_CONTAINER_CODEC_VORBIS VC_FOURCC('v','o','r','b') +#define VC_CONTAINER_CODEC_SPEEX VC_FOURCC('s','p','x',' ') +#define VC_CONTAINER_CODEC_ATRAC3 VC_FOURCC('a','t','r','3') +#define VC_CONTAINER_CODEC_ATRACX VC_FOURCC('a','t','r','x') +#define VC_CONTAINER_CODEC_ATRACL VC_FOURCC('a','t','r','l') +#define VC_CONTAINER_CODEC_MIDI VC_FOURCC('m','i','d','i') +#define VC_CONTAINER_CODEC_EVRC VC_FOURCC('e','v','r','c') +#define VC_CONTAINER_CODEC_NELLYMOSER VC_FOURCC('n','e','l','y') +#define VC_CONTAINER_CODEC_QCELP VC_FOURCC('q','c','e','l') + +/* Text */ +#define VC_CONTAINER_CODEC_TEXT VC_FOURCC('t','e','x','t') +#define VC_CONTAINER_CODEC_SSA VC_FOURCC('s','s','a',' ') +#define VC_CONTAINER_CODEC_USF VC_FOURCC('u','s','f',' ') +#define VC_CONTAINER_CODEC_VOBSUB VC_FOURCC('v','s','u','b') + +#define VC_CONTAINER_CODEC_UNKNOWN VC_FOURCC('u','n','k','n') + +/* Codec variants */ + +/** ISO 14496-10 Annex B byte stream format */ +#define VC_CONTAINER_VARIANT_H264_DEFAULT 0 +/** ISO 14496-15 AVC format (used in mp4/mkv and other containers) */ +#define VC_CONTAINER_VARIANT_H264_AVC1 VC_FOURCC('a','v','c','C') +/** Implicitly delineated NAL units without emulation prevention */ +#define VC_CONTAINER_VARIANT_H264_RAW VC_FOURCC('r','a','w',' ') + +/** MPEG 1/2 Audio - Layer unknown */ +#define VC_CONTAINER_VARIANT_MPGA_DEFAULT 0 +/** MPEG 1/2 Audio - Layer 1 */ +#define VC_CONTAINER_VARIANT_MPGA_L1 VC_FOURCC('l','1',' ',' ') +/** MPEG 1/2 Audio - Layer 2 */ +#define VC_CONTAINER_VARIANT_MPGA_L2 VC_FOURCC('l','2',' ',' ') +/** MPEG 1/2 Audio - Layer 3 */ +#define VC_CONTAINER_VARIANT_MPGA_L3 VC_FOURCC('l','3',' ',' ') + +/** Converts a WaveFormat ID into a VC_CONTAINER_FOURCC_T. + * + * \param waveformat_id WaveFormat ID to convert + * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. + */ +VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id); + +/** Converts a VC_CONTAINER_FOURCC_T into a WaveFormat ID. + * + * \param codec VC_CONTAINER_FOURCC_T to convert + * \return a valid WaveFormat ID of 0 if no mapping was found. + */ +uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec); + +/** Tries to convert a generic fourcc into a VC_CONTAINER_FOURCC_T. + * + * \param fourcc fourcc to convert + * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. + */ +VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc); + +uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec); + +/** Tries to convert VideoForWindows fourcc into a VC_CONTAINER_FOURCC_T. + * + * \param fourcc vfw fourcc to convert + * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. + */ +VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc); + +/** Tries to convert a VC_CONTAINER_FOURCC_T into a VideoForWindows fourcc. + * + * \param codec VC_CONTAINER_FOURCC_T to convert + * \return a valid vfw fourcc or 0 if no mapping was found. + */ +uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec); + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_CODECS_H */ diff --git a/containers/containers_types.h b/containers/containers_types.h new file mode 100755 index 0000000..4cf6666 --- /dev/null +++ b/containers/containers_types.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_TYPES_H +#define VC_CONTAINERS_TYPES_H + +/** \file containers_types.h + * Definition of types used by the containers API + */ + +#include +#include +#include +#include + +#if defined( __STDC__ ) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#include + +#elif defined( __GNUC__ ) +#include +#include + +#elif defined(_MSC_VER) +#include +#if (_MSC_VER < 1700) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif +#define PRIu16 "u" +#define PRIu32 "u" +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" + +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +#endif + +/* C99 64bits integers */ +#ifndef INT64_C +# define INT64_C(value) value##LL +# define UINT64_C(value) value##ULL +#endif + +/* C99 boolean */ +#ifndef __cplusplus +#ifndef bool +# define bool int +#endif +#ifndef true +# define true 1 +#endif +#ifndef false +# define false 0 +#endif +#endif /* __cplusplus */ + +/* FIXME: should be different for big endian */ +#define VC_FOURCC(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24)) + +#endif /* VC_CONTAINERS_TYPES_H */ diff --git a/containers/core/containers.c b/containers/core/containers.c new file mode 100755 index 0000000..3693e35 --- /dev/null +++ b/containers/core/containers.c @@ -0,0 +1,637 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_filters.h" +#include "containers/core/containers_loader.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" + +#define WRITER_SPACE_SAFETY_MARGIN (10*1024) +#define PACKETIZER_BUFFER_SIZE (32*1024) + +/*****************************************************************************/ +VC_CONTAINER_T *vc_container_open_reader_with_io( struct VC_CONTAINER_IO_T *io, + const char *uri, VC_CONTAINER_STATUS_T *p_status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_T *p_ctx = 0; + const char *extension; + + VC_CONTAINER_PARAM_UNUSED(pfn_progress); + VC_CONTAINER_PARAM_UNUSED(progress_userdata); + VC_CONTAINER_PARAM_UNUSED(uri); + + /* Sanity check the i/o */ + if (!io || !io->pf_read || !io->pf_seek) + { + LOG_ERROR(0, "invalid i/o instance: %p", io); + status = VC_CONTAINER_ERROR_INVALID_ARGUMENT; + goto error; + } + + /* Allocate our context before trying out the different readers / writers */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm)); + p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1); + p_ctx->priv->verbosity = vc_container_log_get_default_verbosity(); + p_ctx->drm = (VC_CONTAINER_DRM_T *)(p_ctx->priv + 1); + p_ctx->size = io->size; + p_ctx->priv->io = io; + p_ctx->priv->uri = io->uri_parts; + + /* If the uri has an extension, use it as a hint when loading the container */ + extension = vc_uri_path_extension(p_ctx->priv->uri); + /* If the user has specified a container, then use that instead */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + status = vc_container_load_reader(p_ctx, extension); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), + VC_FOURCC('u','n','k','n'), p_ctx, &status); + if (status != VC_CONTAINER_SUCCESS) + { + /* Some other problem occurred aside from the filter not being appropriate or + the stream not needing it. */ + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + + /* Report no DRM and continue as normal */ + p_ctx->drm = NULL; + status = VC_CONTAINER_SUCCESS; + } + +end: + if(p_status) *p_status = status; + return p_ctx; + +error: + if (p_ctx) + { + p_ctx->priv->io = NULL; /* The i/o doesn't belong to us */ + vc_container_close(p_ctx); + p_ctx = NULL; + } + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_T *vc_container_open_reader( const char *uri, VC_CONTAINER_STATUS_T *p_status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) +{ + VC_CONTAINER_IO_T *io; + VC_CONTAINER_T *ctx; + + /* Start by opening the URI */ + io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_READ, p_status ); + if (!io) + return 0; + + ctx = vc_container_open_reader_with_io( io, uri, p_status, pfn_progress, progress_userdata); + if (!ctx) + vc_container_io_close(io); + return ctx; +} + +/*****************************************************************************/ +VC_CONTAINER_T *vc_container_open_writer( const char *uri, VC_CONTAINER_STATUS_T *p_status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_T *p_ctx = 0; + VC_CONTAINER_IO_T *io; + const char *extension; + VC_CONTAINER_PARAM_UNUSED(pfn_progress); + VC_CONTAINER_PARAM_UNUSED(progress_userdata); + + /* Start by opening the URI */ + io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status ); + if(!io) goto error; + + /* Make sure we have enough space available to start writing */ + if(io->max_size && io->max_size < WRITER_SPACE_SAFETY_MARGIN) + { + LOG_DEBUG(p_ctx, "not enough space available to open a writer"); + status = VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + goto error; + } + + /* Allocate our context before trying out the different readers / writers */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1); + p_ctx->priv->verbosity = vc_container_log_get_default_verbosity(); + p_ctx->priv->io = io; + p_ctx->priv->uri = io->uri_parts; + io = NULL; /* io now owned by the context */ + + /* If the uri has an extension, use it as a hint when loading the container */ + extension = vc_uri_path_extension(p_ctx->priv->uri); + /* If the user has specified a container, then use that instead */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + status = vc_container_load_writer(p_ctx, extension); + if(status != VC_CONTAINER_SUCCESS) goto error; + + end: + if(p_status) *p_status = status; + return p_ctx; + +error: + if(io) vc_container_io_close(io); + if (p_ctx) vc_container_close(p_ctx); + p_ctx = NULL; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *p_ctx ) +{ + unsigned int i; + + if(!p_ctx) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->packetizer) + vc_packetizer_close(p_ctx->tracks[i]->priv->packetizer); + if(p_ctx->priv->packetizer_buffer) free(p_ctx->priv->packetizer_buffer); + if(p_ctx->priv->drm_filter) vc_container_filter_close(p_ctx->priv->drm_filter); + if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); + if(p_ctx->priv->io) vc_container_io_close(p_ctx->priv->io); + if(p_ctx->priv->module_handle) vc_container_unload(p_ctx); + for(i = 0; i < p_ctx->meta_num; i++) free(p_ctx->meta[i]); + if(p_ctx->meta_num) free(p_ctx->meta); + p_ctx->meta_num = 0; + free(p_ctx); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T container_read_packet( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_STATUS_T status; + + while(1) + { + status = p_ctx->priv->pf_read(p_ctx, p_packet, flags); + if(status == VC_CONTAINER_ERROR_CONTINUE) + continue; + + if(!p_packet || (flags & VC_CONTAINER_READ_FLAG_SKIP)) + return status; /* We've just been requested to skip the data */ + + if(status != VC_CONTAINER_SUCCESS) + return status; + + /* Skip data from out of bounds tracks, disabled tracks or packets that are encrypted + and cannot be decrypted */ + if(p_packet->track >= p_ctx->tracks_num || + !p_ctx->tracks[p_packet->track]->is_enabled || + ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_ENCRYPTED) && !p_ctx->priv->drm_filter)) + { + if(flags & VC_CONTAINER_READ_FLAG_INFO) + status = p_ctx->priv->pf_read(p_ctx, p_packet, VC_CONTAINER_READ_FLAG_SKIP); + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_CONTINUE) + continue; + } + if(status != VC_CONTAINER_SUCCESS) + return status; + + if(p_ctx->priv->drm_filter) + status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet); + + break; + } + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_CONTINUE; + VC_PACKETIZER_FLAGS_T packetizer_flags = 0; + VC_PACKETIZER_T *packetizer; + uint32_t force = flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK; + unsigned int i; + + if(!p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(!p_packet && (flags & VC_CONTAINER_READ_FLAG_INFO)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(p_packet && !p_packet->data && !(flags & (VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_SKIP))) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && + (!p_packet || p_packet->track >= p_ctx->tracks_num || !p_ctx->tracks[p_packet->track]->is_enabled)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + /* Always having a packet structure to work with simplifies things */ + if(!p_packet) + p_packet = &p_ctx->priv->packetizer_packet; + + /* Simple/Fast case first */ + if(!p_ctx->priv->packetizing) + { + status = container_read_packet( p_ctx, p_packet, flags ); + goto end; + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + packetizer_flags |= VC_PACKETIZER_FLAG_INFO; + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + packetizer_flags |= VC_PACKETIZER_FLAG_SKIP; + + /* Loop through all the packetized tracks first to see if we've got any + * data to consume there */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_PACKETIZER_T *packetizer = p_ctx->tracks[i]->priv->packetizer; + if(!p_ctx->tracks[i]->is_enabled || !packetizer || + (force && i != p_packet->track)) + continue; + + status = vc_packetizer_read(packetizer, p_packet, packetizer_flags); + p_packet->track = i; + if(status == VC_CONTAINER_SUCCESS) + break; + } + if(i < p_ctx->tracks_num) /* We've got some data */ + goto end; + + /* Let's go and read some data from the actual container */ + while(1) + { + VC_CONTAINER_PACKET_T *tmp = &p_ctx->priv->packetizer_packet; + tmp->track = p_packet->track; + + /* Let's check what the container has to offer */ + status = container_read_packet( p_ctx, tmp, force|VC_PACKETIZER_FLAG_INFO ); + if(status != VC_CONTAINER_SUCCESS) + return status; + + if(!p_ctx->tracks[tmp->track]->priv->packetizer) + { + status = container_read_packet( p_ctx, p_packet, flags ); + break; + } + + /* Feed data from the container onto the packetizer */ + packetizer = p_ctx->tracks[tmp->track]->priv->packetizer; + + tmp->data = p_ctx->priv->packetizer_buffer; + tmp->buffer_size = PACKETIZER_BUFFER_SIZE; + tmp->size = 0; + status = container_read_packet( p_ctx, tmp, force ); + if(status != VC_CONTAINER_SUCCESS) + return status; + + p_packet->track = tmp->track; + vc_packetizer_push(packetizer, tmp); + vc_packetizer_pop(packetizer, &tmp, VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT); + + status = vc_packetizer_read(packetizer, p_packet, packetizer_flags); + if(status == VC_CONTAINER_SUCCESS) + break; + } + + end: + if(status != VC_CONTAINER_SUCCESS) + return status; + + if(p_packet && p_packet->dts > p_ctx->position) + p_ctx->position = p_packet->dts; + if(p_packet && p_packet->pts > p_ctx->position) + p_ctx->position = p_packet->pts; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_STATUS_T status; + void * p_metadata_buffer = NULL; + uint32_t metadata_length = 0; + + /* TODO: check other similar argument errors and non-stateless errors */ + if (!p_packet || !p_packet->data || p_packet->track >= p_ctx->tracks_num) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + /* Check for a previous error */ + if(p_ctx->priv->status != VC_CONTAINER_SUCCESS && p_ctx->priv->status != VC_CONTAINER_ERROR_NOT_READY) + return p_ctx->priv->status; + + /* Check we have enough space to write the data */ + if(p_ctx->priv->max_size && + p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN > p_ctx->priv->max_size) + {status = VC_CONTAINER_ERROR_LIMIT_REACHED; goto end;} + if(p_ctx->priv->io->max_size && + p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN + + (p_ctx->priv->tmp_io ? p_ctx->priv->tmp_io->offset : 0) > p_ctx->priv->io->max_size) + {status = VC_CONTAINER_ERROR_OUT_OF_SPACE; goto end;} + + /* If a filter is created, then send the packet to the filter for encryption. */ + if(p_ctx->priv->drm_filter) + { + status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet); + + if(status == VC_CONTAINER_SUCCESS) + { + /* Get the encryption metadata and send it to the output first. */ + if(vc_container_control(p_ctx, VC_CONTAINER_CONTROL_GET_DRM_METADATA, + &p_metadata_buffer, &metadata_length) == VC_CONTAINER_SUCCESS && metadata_length > 0) + { + /* Make a packet up with the metadata in the payload and write it. */ + VC_CONTAINER_PACKET_T metadata_packet; + metadata_packet.data = p_metadata_buffer; + metadata_packet.buffer_size = metadata_length; + metadata_packet.size = metadata_length; + metadata_packet.frame_size = p_packet->frame_size + metadata_length; + metadata_packet.pts = p_packet->pts; + metadata_packet.dts = p_packet->dts; + metadata_packet.num = p_packet->num; + metadata_packet.track = p_packet->track; + /* As this packet is written first, we must transfer any frame start + flag from the following packet. Also, this packet can never have the end flag set. */ + metadata_packet.flags = p_packet->flags & ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + p_packet->pts = p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START; + if(p_ctx->priv->pf_write(p_ctx, &metadata_packet) != VC_CONTAINER_SUCCESS) goto end; + } + } + else if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + { + /* Encryption was appropriate but a problem has occurred. Skip the write of data + to the io and return the status to the caller. */ + goto end; + } + } + + status = p_ctx->priv->pf_write(p_ctx, p_packet); + + end: + p_ctx->priv->status = status; + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status; + int64_t offset = *p_offset; + unsigned int i; + + /* Reset all packetizers */ + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->packetizer) + vc_packetizer_reset(p_ctx->tracks[i]->priv->packetizer); + + status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags); + + /* */ + if(status == VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && + *p_offset < offset) + { + LOG_DEBUG(p_ctx, "container didn't seek forward as requested (%"PRIi64"/%"PRIi64")", + *p_offset, offset); + for(i = 1; i <= 5 && *p_offset < offset; i++) + { + *p_offset = offset + i * i * 100000; + status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags); + if(status != VC_CONTAINER_SUCCESS) break; + } + } + + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + va_list args; + + va_start( args, operation ); + + if(operation == VC_CONTAINER_CONTROL_ENCRYPT_TRACK) + { + VC_CONTAINER_FOURCC_T type = va_arg(args, VC_CONTAINER_FOURCC_T); + + uint32_t track_num = va_arg(args, uint32_t); + void *p_drm_data = va_arg(args, void *); + int drm_data_size = va_arg(args, uint32_t); + + if(!p_ctx->priv->drm_filter && track_num < p_ctx->tracks_num) + { + VC_CONTAINER_TRACK_T * p_track = p_ctx->tracks[track_num]; + + if ((status = vc_container_track_allocate_drmdata(p_ctx, p_track, drm_data_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'drm data' (%d bytes)", drm_data_size); + goto end; + } + memcpy(p_track->priv->drmdata, p_drm_data, drm_data_size); + + /* Track encryption enabled and no filter - create one now. */ + p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), type, p_ctx, &status); + if(status != VC_CONTAINER_SUCCESS) + goto end; + + status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, track_num); + } + } + else if(operation == VC_CONTAINER_CONTROL_GET_DRM_METADATA) + { + void *p_drm_data = va_arg(args, void *); + int *drm_data_size = va_arg(args, int *); + status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, p_drm_data, drm_data_size); + } + + if(status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION && p_ctx->priv->pf_control) + status = p_ctx->priv->pf_control(p_ctx, operation, args); + + /* If the request has already been handled then we're done */ + if(status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + goto end; + + switch(operation) + { + case VC_CONTAINER_CONTROL_DRM_PLAY: + if (p_ctx->priv->drm_filter) + status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, args); + break; + + case VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE: + p_ctx->priv->max_size = (uint64_t)va_arg( args, uint64_t ); + if(p_ctx->priv->io->max_size && + p_ctx->priv->max_size > p_ctx->priv->io->max_size) + p_ctx->priv->max_size = p_ctx->priv->io->max_size; + status = VC_CONTAINER_SUCCESS; + break; + + case VC_CONTAINER_CONTROL_TRACK_PACKETIZE: + { + unsigned int track_num = va_arg(args, unsigned int); + VC_CONTAINER_FOURCC_T fourcc = va_arg(args, VC_CONTAINER_FOURCC_T); + VC_CONTAINER_TRACK_T *p_track; + + if(track_num >= p_ctx->tracks_num) + { + status = VC_CONTAINER_ERROR_INVALID_ARGUMENT; + break; + } + if(p_ctx->tracks[track_num]->priv->packetizer) + { + status = VC_CONTAINER_SUCCESS; + break; + } + + p_track = p_ctx->tracks[track_num]; + p_track->priv->packetizer = vc_packetizer_open( p_track->format, fourcc, &status ); + if(!p_track->priv->packetizer) + break; + + if(!p_ctx->priv->packetizer_buffer) + { + p_ctx->priv->packetizer_buffer = malloc(PACKETIZER_BUFFER_SIZE); + if(!p_ctx->priv->packetizer_buffer) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + vc_packetizer_close(p_track->priv->packetizer); + p_track->priv->packetizer = NULL; + break; + } + } + + vc_container_format_copy(p_track->format, p_track->priv->packetizer->out, + p_track->format->extradata_size); + p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + p_ctx->priv->packetizing = true; + } + break; + + default: break; + } + + if (status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + status = vc_container_io_control_list(p_ctx->priv->io, operation, args); + + end: + va_end( args ); + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size ) +{ + VC_CONTAINER_TRACK_T *p_ctx; + unsigned int size; + VC_CONTAINER_PARAM_UNUSED(context); + + size = sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->format) + + sizeof(*p_ctx->format->type) + extra_size; + + p_ctx = malloc(size); + if(!p_ctx) return 0; + + memset(p_ctx, 0, size); + p_ctx->priv = (VC_CONTAINER_TRACK_PRIVATE_T *)(p_ctx + 1); + p_ctx->format = (VC_CONTAINER_ES_FORMAT_T *)(p_ctx->priv + 1); + p_ctx->format->type = (void *)(p_ctx->format + 1); + p_ctx->priv->module = (struct VC_CONTAINER_TRACK_MODULE_T *)(p_ctx->format->type + 1); + return p_ctx; +} + +/*****************************************************************************/ +void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track ) +{ + VC_CONTAINER_PARAM_UNUSED(context); + if(p_track) + { + if(p_track->priv->extradata) free(p_track->priv->extradata); + if(p_track->priv->drmdata) free(p_track->priv->drmdata); + free(p_track); + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size ) +{ + VC_CONTAINER_PARAM_UNUSED(context); + + /* Sanity check the size of the extra data */ + if(extra_size > 100*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Check if we need to allocate a buffer */ + if(extra_size > p_track->priv->extradata_size) + { + p_track->priv->extradata_size = 0; + if(p_track->priv->extradata) free(p_track->priv->extradata); + p_track->priv->extradata = malloc(extra_size); + p_track->format->extradata = p_track->priv->extradata; + if(!p_track->priv->extradata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + p_track->priv->extradata_size = extra_size; + } + p_track->format->extradata = p_track->priv->extradata; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int size ) +{ + VC_CONTAINER_PARAM_UNUSED(context); + + /* Sanity check the size of the drm data */ + if(size > 200*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Check if we need to allocate a buffer */ + if(size > p_track->priv->drmdata_size) + { + p_track->priv->drmdata_size = 0; + if(p_track->priv->drmdata) free(p_track->priv->drmdata); + p_track->priv->drmdata = malloc(size); + if(!p_track->priv->drmdata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + p_track->priv->drmdata_size = size; + } + + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/core/containers_bits.c b/containers/core/containers_bits.c new file mode 100755 index 0000000..30f8650 --- /dev/null +++ b/containers/core/containers_bits.c @@ -0,0 +1,490 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "containers/core/containers_bits.h" +#include "containers/core/containers_common.h" + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +#include "containers/core/containers_logging.h" +#endif + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +/** String used for indentation. If more spaces are needed, just add them. */ +#define INDENT_SPACES_STRING "> " +#define INDENT_SPACES_LENGTH (sizeof(INDENT_SPACES_STRING) - 1) +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + +/**************************************************************************//** + * Returns a string that indicates whether the bit stream is valid or not. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return A string indicating the validity of the stream. + */ +static const char * vc_container_bits_valid_str( VC_CONTAINER_BITS_T *bit_stream ) +{ + return vc_container_bits_valid(bit_stream) ? "" : " - stream invalid"; +} + +/**************************************************************************//** + * Returns a string of spaces the length of which is determined by the + * parameter. + * The length is limited to a certain size, above which a greater than symbol + * prefixes the maximum number of spaces. + * + * \param length The required length of the string. + * \return A string indicating the validity of the stream. + */ +static const char * vc_container_bits_indent_str(uint32_t length) +{ + uint32_t str_length = length; + + if (str_length > INDENT_SPACES_LENGTH) + str_length = INDENT_SPACES_LENGTH; + + return INDENT_SPACES_STRING + (INDENT_SPACES_LENGTH - str_length); +} + +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ + +/**************************************************************************//** + * Returns the number of consecutive zero bits in the stream. + * the zero bits are terminated either by a one bit, or the end of the stream. + * In the former case, the zero bits and the terminating one bit are removed + * from the stream. + * In the latter case, the stream becomes invalid. The stream also becomes + * invalid if there are not as many bits after the one bit as zero bits before + * it. + * If the stream is already or becomes invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The number of consecutive zero bits, or zero if the stream is + * invalid. + */ +static uint32_t vc_container_bits_get_leading_zero_bits( VC_CONTAINER_BITS_T *bit_stream ) +{ + uint32_t leading_zero_bits; + uint32_t bits_left = vc_container_bits_available(bit_stream); + uint32_t bits; + uint8_t mask, current_byte; + + if (!bits_left) + return vc_container_bits_invalidate(bit_stream); + + /* Cache 'bits' field to avoid repeated pointer access */ + bits = bit_stream->bits; + if (bits) + { + current_byte = *bit_stream->buffer; + mask = 1 << (bits - 1); + } else { + /* Initialize variables to placate the compiler */ + current_byte = 0; + mask = 0; + } + + /* Scan for the first one bit, counting the number of zeroes. This gives the + * number of further bits after the one that are part of the value. See + * section 9.1 of ITU-T REC H.264 201003 for more details. */ + + for (leading_zero_bits = 0; leading_zero_bits < bits_left; leading_zero_bits++) + { + if (!bits) + { + if (!bit_stream->bytes) + return vc_container_bits_invalidate(bit_stream); + bit_stream->bytes--; + current_byte = *(++bit_stream->buffer); + bits = 8; + mask = 0x80; + } + + bits--; + bits_left--; + if (current_byte & mask) + break; /* Found the marker bit */ + + mask >>= 1; + } + + /* Check enough bits are left in the stream for the value. */ + if (leading_zero_bits > bits_left) + return vc_container_bits_invalidate(bit_stream); + + /* Return cached value of bits to the stream */ + bit_stream->bits = bits; + + return leading_zero_bits; +} + +/***************************************************************************** +Functions exported as part of the bit stream API + *****************************************************************************/ + +/*****************************************************************************/ +void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, + const uint8_t *buffer, + uint32_t available) +{ + vc_container_assert(buffer && (buffer != (const uint8_t *)1)); + + /* Start with buffer pointing at the previous byte with no bits available + * to make the mathematics easier */ + bit_stream->buffer = buffer - 1; + bit_stream->bytes = available; + bit_stream->bits = 0; +} + +/*****************************************************************************/ +uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream ) +{ + bit_stream->buffer = NULL; + return 0; +} + +/*****************************************************************************/ +bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream) +{ + return (bit_stream->buffer != NULL); +} + +/*****************************************************************************/ +void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream) +{ + bit_stream->bytes = 0; + bit_stream->bits = 0; +} + +/*****************************************************************************/ +const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream) +{ + const uint8_t *buffer = bit_stream->buffer; + + /* Only valid on byte boundaries, where buffer pointer has not been moved yet */ + vc_container_assert(!bit_stream->bits); + + return buffer ? (buffer + 1) : NULL; +} + +/*****************************************************************************/ +void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, + const VC_CONTAINER_BITS_T *src) +{ + memcpy(dst, src, sizeof(VC_CONTAINER_BITS_T)); +} + +/*****************************************************************************/ +uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream) +{ + if (!bit_stream->buffer) + return 0; + return (bit_stream->bytes << 3) + bit_stream->bits; +} + +/*****************************************************************************/ +uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream) +{ + if (!bit_stream->buffer) + return 0; + + vc_container_assert(!bit_stream->bits); + + return vc_container_bits_available(bit_stream) >> 3; +} + +/*****************************************************************************/ +void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bits_to_skip) +{ + uint32_t have_bits; + uint32_t new_bytes; + + have_bits = vc_container_bits_available(bit_stream); + if (have_bits < bits_to_skip) + { + vc_container_bits_invalidate(bit_stream); + return; + } + + have_bits -= bits_to_skip; + new_bytes = have_bits >> 3; + bit_stream->bits = have_bits & 7; + bit_stream->buffer += (bit_stream->bytes - new_bytes); + bit_stream->bytes = new_bytes; +} + +/*****************************************************************************/ +void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bytes_to_skip) +{ + /* Only valid on byte boundaries */ + vc_container_assert(!bit_stream->bits); + + vc_container_bits_skip(bit_stream, bytes_to_skip << 3); +} + +/*****************************************************************************/ +void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bytes_to_reduce) +{ + if (bit_stream->bytes >= bytes_to_reduce) + bit_stream->bytes -= bytes_to_reduce; + else + vc_container_bits_invalidate(bit_stream); +} + +/*****************************************************************************/ +void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bytes_to_copy, + uint8_t *dst) +{ + vc_container_assert(!bit_stream->bits); + + if (bit_stream->bytes < bytes_to_copy) + { + /* Not enough data */ + vc_container_bits_invalidate(bit_stream); + return; + } + + /* When the number of bits is zero, the next byte to take is at buffer + 1 */ + memcpy(dst, bit_stream->buffer + 1, bytes_to_copy); + bit_stream->buffer += bytes_to_copy; + bit_stream->bytes -= bytes_to_copy; +} + +/*****************************************************************************/ +uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, + uint32_t value_bits) +{ + uint32_t value = 0; + uint32_t needed = value_bits; + uint32_t bits; + + vc_container_assert(value_bits <= 32); + + if (needed > vc_container_bits_available(bit_stream)) + return vc_container_bits_invalidate(bit_stream); + + bits = bit_stream->bits; + while (needed) + { + uint32_t take; + + if (!bits) + { + bit_stream->bytes--; + bit_stream->buffer++; + bits = 8; + } + + take = bits; + if (needed < take) take = needed; + + bits -= take; + needed -= take; + + value <<= take; + if (take == 8) + value |= *bit_stream->buffer; /* optimize whole byte case */ + else + value |= (*bit_stream->buffer >> bits) & ((1 << take) - 1); + } + + bit_stream->bits = bits; + return value; +} + +/*****************************************************************************/ +void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) +{ + vc_container_bits_skip(bit_stream, vc_container_bits_get_leading_zero_bits(bit_stream)); +} + +/*****************************************************************************/ +uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) +{ + uint32_t leading_zero_bits; + uint32_t codeNum; + + leading_zero_bits = vc_container_bits_get_leading_zero_bits(bit_stream); + + /* Anything bigger than 32 bits is definitely overflow */ + if (leading_zero_bits > 32) + return vc_container_bits_invalidate(bit_stream); + + codeNum = vc_container_bits_read_u32(bit_stream, leading_zero_bits); + + if (leading_zero_bits == 32) + { + /* If codeNum is non-zero, it would need 33 bits, so is also overflow */ + if (codeNum) + return vc_container_bits_invalidate(bit_stream); + + return 0xFFFFFFFF; + } + + return codeNum + (1 << leading_zero_bits) - 1; +} + +/*****************************************************************************/ +int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) +{ + uint32_t uval; + + uval = vc_container_bits_read_u32_exp_golomb(bit_stream); + + /* The signed Exp-Golomb code 0xFFFFFFFF cannot be represented as a signed 32-bit + * integer, because it should be one larger than the largest positive value. */ + if (uval == 0xFFFFFFFF) + return vc_container_bits_invalidate(bit_stream); + + /* Definition of conversion is + * s = ((-1)^(u + 1)) * Ceil(u / 2) + * where '^' is power, but this should be equivalent */ + return ((int32_t)((uval & 1) << 1) - 1) * (int32_t)((uval >> 1) + (uval & 1)); +} + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + +/*****************************************************************************/ +void vc_container_bits_log(VC_CONTAINER_T *p_ctx, + uint32_t indent, + const char *txt, + VC_CONTAINER_BITS_T *bit_stream, + VC_CONTAINER_BITS_LOG_OP_T op, + uint32_t length) +{ + const char *valid_str = vc_container_bits_valid_str(bit_stream); + const char *indent_str = vc_container_bits_indent_str(indent); + + switch (op) + { + case VC_CONTAINER_BITS_LOG_SKIP: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bits skipped%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_SKIP_BYTES: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes skipped%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_COPY_BYTES: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes copied%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_REDUCE_BYTES: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes reduced%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_EG_SKIP: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: Exp-Golomb value skipped%s", indent_str, txt, valid_str); + break; + default: + /* Unexpected operation. Check bit stream logging macros */ + vc_container_assert(0); + } +} + +/*****************************************************************************/ +uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, + uint32_t indent, + const char *txt, + VC_CONTAINER_BITS_T *bit_stream, + VC_CONTAINER_BITS_LOG_OP_T op, + uint32_t length, + uint32_t value) +{ + const char *valid_str = vc_container_bits_valid_str(bit_stream); + const char *indent_str = vc_container_bits_indent_str(indent); + + switch (op) + { + case VC_CONTAINER_BITS_LOG_U8: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%02x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_U16: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%04x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_U32: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_EG_U32: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) unsigned Exp-Golomb%s", indent_str, txt, value, value, valid_str); + break; + default: + /* Unexpected operation. Check bit stream logging macros */ + vc_container_assert(0); + } + + return value; +} + +/*****************************************************************************/ +int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, + uint32_t indent, + const char *txt, + VC_CONTAINER_BITS_T *bit_stream, + VC_CONTAINER_BITS_LOG_OP_T op, + uint32_t length, + int32_t value) +{ + const char *valid_str = vc_container_bits_valid_str(bit_stream); + const char *indent_str = vc_container_bits_indent_str(indent); + + VC_CONTAINER_PARAM_UNUSED(length); + + switch (op) + { + case VC_CONTAINER_BITS_LOG_EG_S32: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%d) signed Exp-Golomb%s", indent_str, txt, value, value, valid_str); + break; + default: + /* Unexpected operation. Check bit stream logging macros */ + vc_container_assert(0); + } + + return value; +} + +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ diff --git a/containers/core/containers_bits.h b/containers/core/containers_bits.h new file mode 100755 index 0000000..10f1cd1 --- /dev/null +++ b/containers/core/containers_bits.h @@ -0,0 +1,344 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_CONTAINERS_BITS_H +#define VC_CONTAINERS_BITS_H + +#include "containers/containers.h" + +/** Bit stream structure + * Value are read from the buffer, taking bits from MSB to LSB in sequential + * bytes until the number of bit and the number of bytes runs out. */ +typedef struct vc_container_bits_tag +{ + const uint8_t *buffer; /**< Buffer from which to take bits */ + uint32_t bytes; /**< Number of bytes available from buffer */ + uint32_t bits; /**< Number of bits available at current pointer */ +} VC_CONTAINER_BITS_T; + +/** Initialise a bit stream object. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object to initialise. + * \param buffer Pointer to the start of the byte buffer. + * \param available Number of bytes in the bit stream. + */ +void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, const uint8_t *buffer, uint32_t available); + +/** Invalidates the bit stream. + * Also returns zero, because it allows callers that need to invalidate and + * immediately return zero to do so in a single statement. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return Zero, always. + */ +uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream ); + +/** Returns true if the bit stream is currently valid. + * The stream becomes invalid when a read or skip operation goe beyond the end + * of the stream. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return True if the stream is valid, false if it is invalid. + */ +bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream); + +/** Reset a valid bit stream object to appear empty. + * Once a stream has become invalid, reset has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + */ +void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream); + +/** Return the current byte pointer for the bit stream. + * + * \pre bit_stream is not NULL. + * \pre The stream is on a byte boundary. + * + * \param bit_stream The bit stream object. + * \return The current byte pointer, or NULL if the stream is invalid. + */ +const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream); + +/** Copy one bit stream to another. + * If the source stream is invalid, the destination one will become so as well. + * + * \pre Neither bit stream is NULL. + * + * \param dst The destination bit stream object. + * \param src The source bit stream object. + */ +void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, const VC_CONTAINER_BITS_T *src); + +/** Return the number of bits left to take from the stream. + * If the stream is invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The number of bits left to take. + */ +uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream); + +/** Return the number of bytes left to take from the stream. + * If the stream is invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The number of bytes left to take. + */ +uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream); + +/** Skip past a number of bits in the stream. + * If bits_to_skip is greater than the number of bits available in the stream, + * the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \param bits_to_skip The number of bits to skip. + */ +void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, uint32_t bits_to_skip); + +/** Skip past a number of bytes in the stream. + * If bytes_to_skip is greater than the number of bytes available in the stream, + * the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * \pre The stream is on a byte boundary. + * + * \param bit_stream The bit stream object. + * \param bytes_to_skip The number of bytes to skip. + */ +void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_skip); + +/** Reduce the length of the bit stream by a number of bytes. + * This reduces the number of bits/bytes available without changing the current + * position in the stream. If bytes_to_reduce is greater than the number of + * bytes available in the stream, the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \param bytes_to_reduce The number of bytes by which to reduce the stream. + */ +void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_reduce); + +/** Copies a number of bytes from the stream to a byte buffer. + * If the stream is or becomes invalid, no data is copied. + * + * \pre bit_stream is not NULL. + * \pre The stream is on a byte boundary. + * + * \param bit_stream The bit stream object. + * \param bytes_to_copy The number of bytes to copy. + * \param dst The byte buffer destination. + */ +void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_copy, uint8_t *dst); + +/** Returns the next value_bits from the stream. The last bit will be the least + * significant bit in the returned value. + * If value_bits is greater than the number of bits available in the stream, + * the stream becomes invalid. + * If the stream is invalid, or becomes invalid while reading the value, zero + * is returned. + * + * \pre bit_stream is not NULL. + * \pre value_bits is not larger than 32. + * + * \param bit_stream The bit stream object. + * \param value_bits The number of bits to retrieve. + * \return The value read from the stream, or zero if the stream is invalid. + */ +uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, uint32_t value_bits); + +/** Skips the next Exp-Golomb value in the stream. + * See section 9.1 of ITU-T REC H.264 201003 for details. + * If there are not enough bits in the stream to complete an Exp-Golomb value, + * the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + */ +void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); + +/** Returns the next unsigned Exp-Golomb value from the stream. + * See section 9.1 of ITU-T REC H.264 201003 for details. + * If there are not enough bits in the stream to complete an Exp-Golomb value, + * the stream becomes invalid. + * If the next unsigned Exp-Golomb value in the stream is larger than 32 bits, + * or the stream is or becomes invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The next unsigned value from the stream, or zero on error. + */ +uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); + +/** Returns the next signed Exp-Golomb value from the stream. + * See section 9.1.1 of ITU-T REC H.264 201003 for details. + * If there are not enough bits in the stream to complete an Exp-Golomb value, + * the stream becomes invalid. + * If the next signed Exp-Golomb value in the stream is larger than 32 bits, + * or the stream is or becomes invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The next signed value from the stream, or zero on error. + */ +int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); + +/****************************************************************************** + * Macros reduce function name length and enable logging of some operations * + ******************************************************************************/ +#define BITS_INIT(ctx, bits, buffer, available) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_init(bits, buffer, available)) +#define BITS_INVALIDATE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_invalidate(bits)) +#define BITS_VALID(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_valid(bits)) +#define BITS_RESET(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_reset(bits)) +#define BITS_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_available(bits)) +#define BITS_BYTES_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_bytes_available(bits)) +#define BITS_CURRENT_POINTER(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_current_pointer(bits)) +#define BITS_COPY_STREAM(ctx, dst, src) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_copy_stream(dst, src)) + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + +typedef enum { + VC_CONTAINER_BITS_LOG_SKIP, + VC_CONTAINER_BITS_LOG_SKIP_BYTES, + VC_CONTAINER_BITS_LOG_U8, + VC_CONTAINER_BITS_LOG_U16, + VC_CONTAINER_BITS_LOG_U32, + VC_CONTAINER_BITS_LOG_COPY_BYTES, + VC_CONTAINER_BITS_LOG_REDUCE_BYTES, + VC_CONTAINER_BITS_LOG_EG_SKIP, + VC_CONTAINER_BITS_LOG_EG_U32, + VC_CONTAINER_BITS_LOG_EG_S32, +} VC_CONTAINER_BITS_LOG_OP_T; + +/** Logs an operation with void return. + * + * \pre None of p_ctx, txt or bit_stream are NULL. + * + * \param p_ctx Container context. + * \param indent Indent level. + * \param txt Description of what is being read. + * \param bit_stream The bit stream object. + * \param op The operation just performed. + * \param length The length of the operation. + */ +void vc_container_bits_log(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length); + +/** Logs an operation with unsigned 32-bit integer return. + * + * \pre None of p_ctx, txt or bit_stream are NULL. + * + * \param p_ctx Container context. + * \param indent Indent level. + * \param txt Description of what is being read. + * \param bit_stream The bit stream object. + * \param op The operation just performed. + * \param length The length of the operation. + * \param value The value returned by the operation. + * \return The unsigned 32-bit integer value passed in. + */ +uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, uint32_t value); + +/** Logs an operation with signed 32-bit integer return. + * + * \pre None of p_ctx, txt or bit_stream are NULL. + * + * \param p_ctx Container context. + * \param indent Indent level. + * \param txt Description of what is being read. + * \param bit_stream The bit stream object. + * \param op The operation just performed. + * \param length The length of the operation. + * \param value The value returned by the operation. + * \return The signed 32-bit integer value passed in. + */ +int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, int32_t value); + +#ifndef BITS_LOG_INDENT +# ifndef CONTAINER_HELPER_LOG_INDENT +# define BITS_LOG_INDENT(ctx) 0 +# else +# define BITS_LOG_INDENT(ctx) ((ctx)->priv->io->module ? CONTAINER_HELPER_LOG_INDENT(a) : 0) +# endif +#endif + +#define BITS_SKIP(ctx, bits, length, txt) (vc_container_bits_skip(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP, length)) +#define BITS_SKIP_BYTES(ctx, bits, length, txt) (vc_container_bits_skip_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP_BYTES, length)) + +#define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U8, length, vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U16, length, vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U32(ctx, bits, length, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U32, length, vc_container_bits_read_u32(bits, length)) + +#define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (vc_container_bits_copy_bytes(bits, length, dst), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_COPY_BYTES, length)) + +#define BITS_REDUCE_BYTES(ctx, bits, length, txt) (vc_container_bits_reduce_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_REDUCE_BYTES, length)) + +#define BITS_SKIP_EXP(ctx, bits, txt) (vc_container_bits_skip_exp_golomb(bits), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_SKIP, 0)) + +#define BITS_READ_S32_EXP(ctx, bits, txt) vc_container_bits_log_s32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_S32, 0, vc_container_bits_read_s32_exp_golomb(bits)) +#define BITS_READ_U32_EXP(ctx, bits, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_U32, 0, vc_container_bits_read_u32_exp_golomb(bits)) + +#else /* ENABLE_CONTAINERS_LOG_FORMAT */ + +#define BITS_SKIP(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip(bits, length)) +#define BITS_SKIP_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_bytes(bits, length)) + +#define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U32(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) + +#define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_copy_bytes(bits, length, dst)) + +#define BITS_REDUCE_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_reduce_bytes(bits, length)) + +#define BITS_SKIP_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_exp_golomb(bits)) + +#define BITS_READ_S32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_s32_exp_golomb(bits)) +#define BITS_READ_U32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32_exp_golomb(bits)) + +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ + +#endif /* VC_CONTAINERS_BITS_H */ diff --git a/containers/core/containers_bytestream.h b/containers/core/containers_bytestream.h new file mode 100755 index 0000000..95b3a61 --- /dev/null +++ b/containers/core/containers_bytestream.h @@ -0,0 +1,389 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_BYTESTREAM_H +#define VC_CONTAINERS_BYTESTREAM_H + +/** \file + * Utility functions to provide a byte stream out of a list of container packets + */ + +typedef struct VC_CONTAINER_BYTESTREAM_T +{ + VC_CONTAINER_PACKET_T *first; /**< first packet in the chain */ + VC_CONTAINER_PACKET_T **last; /**< last packet in the chain */ + + VC_CONTAINER_PACKET_T *current; /**< packet containing the current read pointer */ + size_t current_offset; /**< position of current packet (in bytes) */ + size_t offset; /**< position within the current packet */ + + size_t bytes; /**< Number of bytes available in the bytestream */ + +} VC_CONTAINER_BYTESTREAM_T; + +/*****************************************************************************/ +STATIC_INLINE void bytestream_init( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + stream->first = stream->current = NULL; + stream->last = &stream->first; + stream->offset = stream->current_offset = stream->bytes = 0; +} + +STATIC_INLINE void bytestream_push( VC_CONTAINER_BYTESTREAM_T *stream, + VC_CONTAINER_PACKET_T *packet ) +{ + *stream->last = packet; + stream->last = &packet->next; + packet->next = NULL; + if( !stream->current ) stream->current = packet; + stream->bytes += packet->size; +} + +STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_pop( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + VC_CONTAINER_PACKET_T *packet = stream->first; + + if( stream->current == packet ) + return NULL; + vc_container_assert(packet); + + stream->bytes -= packet->size; + stream->current_offset -= packet->size; + stream->first = packet->next; + if( !stream->first ) + stream->last = &stream->first; + return packet; +} + +STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_get_packet( VC_CONTAINER_BYTESTREAM_T *stream, size_t *offset ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + size_t off = stream->offset; + + while(packet && packet->size == off) + { + packet = packet->next; + off = 0; + } + + if (offset) + *offset = off; + + return packet; +} + +STATIC_INLINE bool bytestream_skip_packet( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( packet ) + { + stream->current = packet->next; + stream->current_offset += (packet->size - stream->offset); + stream->offset = 0; + } + + return !!packet; +} + +STATIC_INLINE void bytestream_get_timestamps( VC_CONTAINER_BYTESTREAM_T *stream, int64_t *pts, int64_t *dts, bool b_same ) +{ + VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, 0 ); + + if(packet) + { + if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts; + if(pts) *pts = packet->pts; + if(dts) *dts = packet->dts; + + packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; + return; + } + + if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN; + if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN; +} + +STATIC_INLINE void bytestream_get_timestamps_and_offset( VC_CONTAINER_BYTESTREAM_T *stream, + int64_t *pts, int64_t *dts, size_t *offset, bool b_same ) +{ + VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, offset ); + + if(packet) + { + if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts; + if(pts) *pts = packet->pts; + if(dts) *dts = packet->dts; + + packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; + return; + } + + if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN; + if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN; +} + +STATIC_INLINE size_t bytestream_size( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + return stream->bytes - stream->current_offset - stream->offset; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip( VC_CONTAINER_BYTESTREAM_T *stream, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t offset, bytes = 0, skip; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + for( packet = stream->current, offset = stream->offset; ; + packet = packet->next, offset = 0 ) + { + if( packet->size - offset >= size) + break; + + skip = packet->size - offset; + bytes += skip; + size -= skip; + } + + stream->current = packet; + stream->current_offset += stream->offset - offset + bytes; + stream->offset = offset + size; + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_get( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t offset, bytes = 0, copy; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + for( packet = stream->current, offset = stream->offset; ; + packet = packet->next, offset = 0 ) + { + if( packet->size - offset >= size) + break; + + copy = packet->size - offset; + memcpy( data, packet->data + offset, copy ); + bytes += copy; + data += copy; + size -= copy; + } + + memcpy( data, packet->data + offset, size ); + stream->current = packet; + stream->current_offset += stream->offset - offset + bytes; + stream->offset = offset + size; + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t offset, copy; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + for( packet = stream->current, offset = stream->offset; ; + packet = packet->next, offset = 0 ) + { + if( packet->size - offset >= size) + break; + + copy = packet->size - offset; + memcpy( data, packet->data + offset, copy ); + data += copy; + size -= copy; + } + + memcpy( data, packet->data + offset, size ); + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek_at( VC_CONTAINER_BYTESTREAM_T *stream, + size_t peek_offset, uint8_t *data, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t copy; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < peek_offset + size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + peek_offset += stream->offset; + + /* Find the right place */ + for( packet = stream->current; ; packet = packet->next ) + { + if( packet->size > peek_offset ) + break; + + peek_offset -= packet->size; + } + + /* Copy the data */ + for( ; ; packet = packet->next, peek_offset = 0 ) + { + if( packet->size - peek_offset >= size) + break; + + copy = packet->size - peek_offset; + memcpy( data, packet->data + peek_offset, copy ); + data += copy; + size -= copy; + } + + memcpy( data, packet->data + peek_offset, size ); + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip_byte( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( !packet ) + return VC_CONTAINER_ERROR_EOS; + + /* Fast path first */ + if( packet->size - stream->offset ) + { + stream->offset++; + return VC_CONTAINER_SUCCESS; + } + + return bytestream_skip( stream, 1 ); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T packet_peek_byte( VC_CONTAINER_BYTESTREAM_T *stream, + uint8_t *data ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( !packet ) + return VC_CONTAINER_ERROR_EOS; + + /* Fast path first */ + if( packet->size - stream->offset ) + { + *data = packet->data[stream->offset]; + return VC_CONTAINER_SUCCESS; + } + + return bytestream_peek( stream, data, 1 ); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T packet_get_byte( VC_CONTAINER_BYTESTREAM_T *stream, + uint8_t *data ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( !packet ) + return VC_CONTAINER_ERROR_EOS; + + /* Fast path first */ + if( packet->size - stream->offset ) + { + *data = packet->data[stream->offset]; + stream->offset++; + return VC_CONTAINER_SUCCESS; + } + + return bytestream_get( stream, data, 1 ); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_find_startcode( VC_CONTAINER_BYTESTREAM_T *stream, + size_t *search_offset, const uint8_t *startcode, unsigned int length ) +{ + VC_CONTAINER_PACKET_T *packet, *backup_packet = NULL; + size_t position, start_offset = position = *search_offset; + size_t offset, backup_offset = 0; + unsigned int match = 0; + + if( stream->bytes - stream->current_offset - stream->offset < start_offset + length ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + /* Find the right place */ + for( packet = stream->current, offset = stream->offset; + packet != NULL; packet = packet->next, offset = 0 ) + { + if( packet->size - offset > start_offset) + break; + + start_offset -= (packet->size - offset); + } + + /* Start the search for the start code. + * To make things simple we try to find a match one byte at a time. */ + for( offset += start_offset; + packet != NULL; packet = packet->next, offset = 0 ) + { + for( ; offset < packet->size; offset++ ) + { + if( packet->data[offset] != startcode[match] ) + { + if ( match ) /* False positive */ + { + packet = backup_packet; + offset = backup_offset; + match = 0; + } + position++; + continue; + } + + /* We've got a match */ + if( !match++ ) + { + backup_packet = packet; + backup_offset = offset; + } + + if( match == length ) + { + /* We have the full start code it */ + *search_offset = position; + return VC_CONTAINER_SUCCESS; + } + } + } + + *search_offset = position; + return VC_CONTAINER_ERROR_EOS; /* No luck in finding the start code */ +} + +#endif /* VC_CONTAINERS_BYTESTREAM_H */ diff --git a/containers/core/containers_codecs.c b/containers/core/containers_codecs.c new file mode 100755 index 0000000..16e1593 --- /dev/null +++ b/containers/core/containers_codecs.c @@ -0,0 +1,209 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/containers_codecs.h" +#include "containers/core/containers_utils.h" + +/*****************************************************************************/ +static struct { + VC_CONTAINER_FOURCC_T codec; + uint16_t id; +} codec_to_wf_table[] = +{ + {VC_CONTAINER_CODEC_PCM_SIGNED_LE, WAVE_FORMAT_PCM}, + {VC_CONTAINER_CODEC_ALAW, WAVE_FORMAT_ALAW}, + {VC_CONTAINER_CODEC_MULAW, WAVE_FORMAT_MULAW}, + {VC_CONTAINER_CODEC_ADPCM_MS, WAVE_FORMAT_ADPCM}, + {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEG}, + {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEGLAYER3}, + {VC_CONTAINER_CODEC_WMA1, WAVE_FORMAT_WMAUDIO1}, + {VC_CONTAINER_CODEC_WMA2, WAVE_FORMAT_WMAUDIO2}, + {VC_CONTAINER_CODEC_WMAP, WAVE_FORMAT_WMAUDIOPRO}, + {VC_CONTAINER_CODEC_WMAL, WAVE_FORMAT_WMAUDIO_LOSSLESS}, + {VC_CONTAINER_CODEC_WMAV, WAVE_FORMAT_WMAUDIO_VOICE}, + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DVM}, + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DOLBY_AC3_SPDIF}, /**< AC-3 padded for S/PDIF */ + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_RAW_SPORT}, /**< AC-3 padded for S/PDIF */ + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_ESST_AC3}, /**< AC-3 padded for S/PDIF */ + {VC_CONTAINER_CODEC_EAC3, WAVE_FORMAT_DVM}, + {VC_CONTAINER_CODEC_DTS, WAVE_FORMAT_DTS}, +#if 0 + {CODEC_G726, WAVE_FORMAT_G726_ADPCM}, + {CODEC_G726, WAVE_FORMAT_DF_G726}, + {CODEC_G726, WAVE_FORMAT_G726ADPCM}, + {CODEC_G726, WAVE_FORMAT_PANASONIC_G726}, +#endif + {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_AAC}, + {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_MP4A}, + {VC_CONTAINER_CODEC_ATRAC3, WAVE_FORMAT_SONY_SCX}, + {VC_CONTAINER_CODEC_UNKNOWN, WAVE_FORMAT_UNKNOWN} +}; + +VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id) +{ + unsigned int i; + for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_wf_table[i].id == waveformat_id) break; + return codec_to_wf_table[i].codec; +} + +uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec) +{ + unsigned int i; + for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_wf_table[i].codec == codec) break; + return codec_to_wf_table[i].id; +} + +static struct { + VC_CONTAINER_FOURCC_T codec; + uint32_t fourcc; +} codec_to_vfw_table[] = +{ +#if defined(ENABLE_CONTAINERS_STANDALONE) || !defined(NDEBUG) + /* We are legally required to not play DivX in RELEASE mode. See Jira SW-3138 */ + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('D','I','V','3')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('d','i','v','3')}, + {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('D','I','V','4')}, + {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('d','i','v','4')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','X','5','0')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','I','V','X')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('d','i','v','x')}, +#endif /* ENABLE_CONTAINERS_STANDALONE || !NDEBUG */ + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('X','V','I','D')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('x','v','i','d')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')}, + {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('W','V','C','1')}, + {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('w','v','c','1')}, + {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('w','m','v','a')}, + {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('W','M','V','A')}, + {VC_CONTAINER_CODEC_VP6, VC_FOURCC('V','P','6','F')}, + {VC_CONTAINER_CODEC_VP6, VC_FOURCC('v','p','6','f')}, + {VC_CONTAINER_CODEC_VP7, VC_FOURCC('V','P','7','0')}, + {VC_CONTAINER_CODEC_VP7, VC_FOURCC('v','p','7','0')}, + {VC_CONTAINER_CODEC_H263, VC_FOURCC('H','2','6','3')}, + {VC_CONTAINER_CODEC_H263, VC_FOURCC('h','2','6','3')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('H','2','6','4')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('h','2','6','4')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('A','V','C','1')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('a','v','c','1')}, + {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('F','L','V','1')}, + {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('f','l','v','1')}, + {VC_CONTAINER_CODEC_UNKNOWN, 0} +}; + +VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc) +{ + unsigned int i; + for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_vfw_table[i].fourcc == fourcc) break; + + if(codec_to_vfw_table[i].codec == VC_CONTAINER_CODEC_UNKNOWN) + return fourcc; + + return codec_to_vfw_table[i].codec; +} + +uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec) +{ + unsigned int i; + for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_vfw_table[i].codec == codec) break; + + return codec_to_vfw_table[i].fourcc; +} + +static struct { + VC_CONTAINER_FOURCC_T codec; + uint32_t fourcc; +} codec_to_fourcc_table[] = +{ + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')}, + {VC_CONTAINER_CODEC_UNKNOWN, 0} +}; + +VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc) +{ + unsigned int i; + for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_fourcc_table[i].fourcc == fourcc) break; + + return codec_to_fourcc_table[i].codec; +} + +uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec) +{ + unsigned int i; + for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_fourcc_table[i].codec == codec) break; + + return codec_to_fourcc_table[i].fourcc; +} diff --git a/containers/core/containers_common.h b/containers/core/containers_common.h new file mode 100755 index 0000000..eb18671 --- /dev/null +++ b/containers/core/containers_common.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_COMMON_H +#define VC_CONTAINERS_COMMON_H + +/** \file containers_common.h + * Common definitions for containers infrastructure + */ + +#ifndef ENABLE_CONTAINERS_STANDALONE +# include "vcos.h" +# define vc_container_assert(a) vcos_assert(a) +#else +# include "assert.h" +# define vc_container_assert(a) assert(a) +#endif /* ENABLE_CONTAINERS_STANDALONE */ + +#ifndef countof +# define countof(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef _MSC_VER +# define strcasecmp stricmp +# define strncasecmp strnicmp +#endif + +#define STATIC_INLINE static __inline +#define VC_CONTAINER_PARAM_UNUSED(a) (void)(a) + +#if defined(__HIGHC__) && !defined(strcasecmp) +# define strcasecmp(a,b) _stricmp(a,b) +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2) +# define VC_CONTAINER_CONSTRUCTOR(func) void __attribute__((constructor,used)) func(void) +# define VC_CONTAINER_DESTRUCTOR(func) void __attribute__((destructor,used)) func(void) +#else +# define VC_CONTAINER_CONSTRUCTOR(func) void func(void) +# define VC_CONTAINER_DESTRUCTOR(func) void func(void) +#endif + +#endif /* VC_CONTAINERS_COMMON_H */ diff --git a/containers/core/containers_filters.c b/containers/core/containers_filters.c new file mode 100755 index 0000000..a5bc0b7 --- /dev/null +++ b/containers/core/containers_filters.c @@ -0,0 +1,222 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_filters.h" + +#if !defined(ENABLE_CONTAINERS_STANDALONE) + #include "vcos_dlfcn.h" + #define DL_SUFFIX VCOS_SO_EXT + #ifndef DL_PATH_PREFIX + #define DL_PATH_PREFIX "" + #endif +#endif + +typedef struct VC_CONTAINER_FILTER_PRIVATE_T +{ + /** Pointer to the container filter code and symbols */ + void *handle; +} VC_CONTAINER_FILTER_PRIVATE_T; + +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_FILTER_OPEN_FUNC_T)(VC_CONTAINER_FILTER_T*, VC_CONTAINER_FOURCC_T); + +static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name); +static void unload_library(void *handle); + +static struct { + VC_CONTAINER_FOURCC_T filter; + const char *name; +} filter_to_name_table[] = +{ + {VC_FOURCC('d','r','m',' '), "divx"}, + {VC_FOURCC('d','r','m',' '), "hdcp2"}, + {0, NULL} +}; + +static VC_CONTAINER_STATUS_T vc_container_filter_load(VC_CONTAINER_FILTER_T *p_ctx, + VC_CONTAINER_FOURCC_T filter, + VC_CONTAINER_FOURCC_T type) +{ + void *handle = NULL; + VC_CONTAINER_FILTER_OPEN_FUNC_T func; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + unsigned int i; + + for(i = 0; filter_to_name_table[i].filter; ++i) + { + if (filter_to_name_table[i].filter == filter) + { + if ((func = load_library(&handle, filter, filter_to_name_table[i].name)) != NULL) + { + status = (*func)(p_ctx, type); + if(status == VC_CONTAINER_SUCCESS) break; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) break; + } + } + } + + p_ctx->priv->handle = handle; + return status; +} + +static void vc_container_filter_unload(VC_CONTAINER_FILTER_T *p_ctx) +{ + unload_library(p_ctx->priv->handle); + p_ctx->priv->handle = NULL; +} + +/*****************************************************************************/ +VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter, + VC_CONTAINER_FOURCC_T type, + VC_CONTAINER_T *p_container, + VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + VC_CONTAINER_FILTER_T *p_ctx = 0; + VC_CONTAINER_FILTER_PRIVATE_T *priv = 0; + + /* Allocate our context before trying out the different filter modules */ + p_ctx = malloc(sizeof(*p_ctx) + sizeof(*priv)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*priv)); + p_ctx->priv = priv = (VC_CONTAINER_FILTER_PRIVATE_T *)&p_ctx[1]; + p_ctx->container = p_container; + + status = vc_container_filter_load(p_ctx, filter, type); + if(status != VC_CONTAINER_SUCCESS) goto error; + + end: + if(p_status) *p_status = status; + return p_ctx; + + error: + if(p_ctx) free(p_ctx); + p_ctx = 0; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *p_ctx ) +{ + if (p_ctx) + { + if(p_ctx->pf_close) p_ctx->pf_close(p_ctx); + if(p_ctx->priv && p_ctx->priv->handle) vc_container_filter_unload(p_ctx); + free(p_ctx); + } + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_filter_process( VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_STATUS_T status; + status = p_ctx->pf_process(p_ctx, p_packet); + return status; +} + +VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + va_list args; + + va_start( args, operation ); + if(p_ctx->pf_control) + status = p_ctx->pf_control(p_ctx, operation, args); + va_end( args ); + + return status; +} + +static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name) +{ + VC_CONTAINER_FILTER_OPEN_FUNC_T func = NULL; +#ifdef ENABLE_CONTAINERS_STANDALONE + VC_CONTAINER_PARAM_UNUSED(handle); + VC_CONTAINER_PARAM_UNUSED(filter); + VC_CONTAINER_PARAM_UNUSED(name); +#else + char *dl_name, *entrypt_name; + const char *entrypt_name_short = "filter_open"; + char filter_[6], *ptr; + void *dl_handle; + int dl_name_len; + int entrypt_name_len; + + snprintf(filter_, sizeof(filter_), "%4.4s", (const char*)&filter); + ptr = strchr(filter_, '\0'); + while (ptr > filter_ && isspace(*--ptr)) *ptr = '\0'; + strncat(filter_, "_", 1); + + dl_name_len = strlen(DL_PATH_PREFIX) + strlen(filter_) + strlen(name) + strlen(DL_SUFFIX) + 1; + dl_name = malloc(dl_name_len); + if (!dl_name) return NULL; + + entrypt_name_len = strlen(name) + 1 + strlen(filter_) + strlen(entrypt_name_short) + 1; + entrypt_name = malloc(entrypt_name_len); + if (!entrypt_name) + { + free(dl_name); + return NULL; + } + + snprintf(dl_name, dl_name_len, "%s%s%s%s", DL_PATH_PREFIX, filter_, name, DL_SUFFIX); + snprintf(entrypt_name, entrypt_name_len, "%s_%s%s", name, filter_, entrypt_name_short); + + if ((dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL) + { + /* Try generic entrypoint name before the mangled, full name */ + func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name_short); + if (!func) func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); + /* Only return handle if symbol found */ + if (func) + *handle = dl_handle; + else + vcos_dlclose(dl_handle); + } + + free(dl_name); + free(entrypt_name); +#endif + return func; +} + +static void unload_library(void *handle) +{ +#ifdef ENABLE_CONTAINERS_STANDALONE + VC_CONTAINER_PARAM_UNUSED(handle); +#else + vcos_dlclose(handle); +#endif +} diff --git a/containers/core/containers_filters.h b/containers/core/containers_filters.h new file mode 100755 index 0000000..9026274 --- /dev/null +++ b/containers/core/containers_filters.h @@ -0,0 +1,110 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_FILTERS_H +#define VC_CONTAINERS_FILTERS_H + +/** \file containers_filters.h + * Interface definition for the filter abstraction used by the container + * common layer */ +#include + +#include "containers/containers.h" + +/** \defgroup VcContainerFilterApi Container Filter API */ +/* @{ */ + +/** Container Filter Context. + * This structure defines the context for a container filter instance */ +typedef struct VC_CONTAINER_FILTER_T +{ + /** Pointer to container instance */ + struct VC_CONTAINER_T *container; + /** Pointer to information private to the container filter instance */ + struct VC_CONTAINER_FILTER_PRIVATE_T *priv; + /** Pointer to information private to the container filter module */ + struct VC_CONTAINER_FILTER_MODULE_T *module; + + /** \note the following list of function pointers should not be used directly. + * They defines the interface for implementing container filter modules and are + * filled in by the container filter modules themselves. */ + + /** \private + * Function pointer to close and free all resources allocated by a + * container filter module */ + VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_FILTER_T *filter); + + /** \private + * Function pointer to filter a data packet using a container filter module */ + VC_CONTAINER_STATUS_T (*pf_process)(struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_PACKET_T *p_packet); + + /** \private + * Function pointer to control container filter module */ + VC_CONTAINER_STATUS_T (*pf_control)( struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_CONTROL_T operation, va_list args ); + +} VC_CONTAINER_FILTER_T; + +/** Opens a container filter using a four character code describing the filter. + * This will create an instance of the container filter. + * + * \param filter Four Character Code describing the filter + * \param type Four Character Code describing the subtype - indicated whether filter is encrypt or decrypt + * \param container Pointer to the container instance + * \param status Returns the status of the operation + * \return If successful, this returns a pointer to the new instance + * of the container filter. Returns NULL on failure. + */ +VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter, + VC_CONTAINER_FOURCC_T type, + VC_CONTAINER_T *container, + VC_CONTAINER_STATUS_T *status ); + +/** Closes an instance of a container filter. + * \param context Pointer to the VC_CONTAINER_FILTER_T context of the instance to close + * \return VC_CONTAINER_SUCCESS on success. + */ +VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *context ); + +/** Filter a data packet using a container filter module. + * \param context Pointer to the VC_CONTAINER_FILTER_T instance to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure to process + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_filter_process(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_PACKET_T *p_packet); + +/** Extensible control function for container filter modules. +* This function takes a variable number of arguments which will depend on the specific operation. +* +* \param context Pointer to the VC_CONTAINER_FILTER_T instance to use +* \param operation The requested operation +* \param args Arguments for the operation +* \return the status of the operation +*/ +VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_CONTROL_T operation, ... ); + +/* @} */ + +#endif /* VC_CONTAINERS_FILTERS_H */ diff --git a/containers/core/containers_index.c b/containers/core/containers_index.c new file mode 100755 index 0000000..7edebfa --- /dev/null +++ b/containers/core/containers_index.c @@ -0,0 +1,192 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_index.h" + +typedef struct { + int64_t file_offset; + int64_t time; +} VC_CONTAINER_INDEX_POS_T; + +struct VC_CONTAINER_INDEX_T { + int len; // log2 of length of entry array + int next; // next array entry to write into + int gap; // log2 of the passes through entry array to build the full list + int mgap; // len-gap, stored for convenience + int count; // number of calls to index_add since last entry added + int max_count; // log2 of the number of calls to discard between each entry added + int64_t max_time; // time of the latest entry + VC_CONTAINER_INDEX_POS_T entry[0]; // array of position/time pairs +}; + +// We have a fixed length list, and when it is full we want to discard half the entries. +// This is done without coping data by mapping the entry number to the index to the array, +// in the following way: +// Length is a power of two, so the entry number is a fixed constant bit width. The highest gap +// bits are used as a direct offset into the array (o), the lowest mgap bits are right shifted by +// gap to increment this (S). Each time we double the number of passes through the actual array. +// So if len=3, we start off with mgap=3, gap=0, we have a single pass with the trivial mapping: +// |S|S|S| [0 1 2 3 4 5 6 7] +// when this is full we change to mgap=2, gap=1, so we iterate this way: +// |o|S|S| [0 2 4 6] [1 3 5 7] +// when this is full we change to mgap=1, gap=2 +// |o|o|S| [0 4] [1 5] [2 6] [3 7] +// when this is full we change to this, which is equivalent to where we started +// |o|o|o| [0] [1] [2] [3] [4] [5] [6] [7] + +#define ENTRY(x, i) ((x)->gap == 0 ? (i) : ((i)>>(x)->mgap) + (((i) & ((1<<(x)->mgap)-1)) << (x)->gap)) + +VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + VC_CONTAINER_INDEX_T *id = NULL; + int len = 0; + + if(length < 16) length = 16; + if(length > 4096) length = 4096; + while((length >>= 1) != 0) + len++; + + id = malloc(sizeof(VC_CONTAINER_INDEX_T) + (sizeof(VC_CONTAINER_INDEX_POS_T)<len = id->mgap = len; + + *index = id; + return VC_CONTAINER_SUCCESS; + + error: + return status; +} + +VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index ) +{ + if(index == NULL) + return VC_CONTAINER_ERROR_FAILED; + + free(index); + return VC_CONTAINER_SUCCESS; +} + +VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset ) +{ + if(index == NULL) + return VC_CONTAINER_ERROR_FAILED; + + // reject entries if they are in part of the time covered + if(index->next != 0 && time <= index->max_time) + return VC_CONTAINER_SUCCESS; + + index->count++; + if(index->count == (1<max_count)) + { + int entry; + if(index->next == (1<len)) + { + // New entry doesn't fit, we discard every other index record + // by changing how we map index positions to array entry indexes. + index->next >>= 1; + index->gap++; + index->mgap--; + index->max_count++; + + if(index->gap == index->len) + { + index->gap = 0; + index->mgap = index->len; + } + } + + entry = ENTRY(index, index->next); + + index->entry[entry].file_offset = file_offset; + index->entry[entry].time = time; + index->count = 0; + index->next++; + index->max_time = time; + } + + return VC_CONTAINER_SUCCESS; +} + +VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past ) +{ + int guess, start, end, entry; + + if(index == NULL || index->next == 0) + return VC_CONTAINER_ERROR_FAILED; + + guess = start = 0; + end = index->next-1; + + *past = *time > index->max_time; + + while(end-start > 1) + { + int64_t gtime; + guess = (start+end)>>1; + gtime = index->entry[ENTRY(index, guess)].time; + + if(*time < gtime) + end = guess; + else if(*time > gtime) + start = guess; + else + break; + } + + if (*time != index->entry[ENTRY(index, guess)].time) + { + if(later) + { + if(*time <= index->entry[ENTRY(index, start)].time) + guess = start; + else + guess = end; + } + else + { + if(*time >= index->entry[ENTRY(index, end)].time) + guess = end; + else + guess = start; + } + } + + entry = ENTRY(index, guess); + *time = index->entry[entry].time; + *file_offset = index->entry[entry].file_offset; + + return VC_CONTAINER_SUCCESS; +} + diff --git a/containers/core/containers_index.h b/containers/core/containers_index.h new file mode 100755 index 0000000..f0c49c3 --- /dev/null +++ b/containers/core/containers_index.h @@ -0,0 +1,82 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_INDEX_H +#define VC_CONTAINERS_INDEX_H + +/** \file containers_index.h + * Definition of index utilitie for containers. Creates and maintains an + * index of file offsets and times, and is able to suggest a file position + * to seek to achieve a given time target. Useful for container formats + * that don't include an index. + */ + +#include "containers/containers.h" + +struct VC_CONTAINER_INDEX_T; +typedef struct VC_CONTAINER_INDEX_T VC_CONTAINER_INDEX_T; + +/** + * Creates an index with a suggested number of entries. + * @param index Pointer to created index will be filled here on success. + * @param length Suggested length of index. + * @return Status code + */ +VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length ); + + +/** + * Frees an index. + * @param index Pointer to valid index. + * @return Status code. + */ +VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index ); + + +/** + * Adds an entry to the index. If the index is full then some stored records will be + * discarded. + * @param index Pointer to a valid index. + * @param time Timestamp of new index entry. + * @param file_offset File offset for new index entry. + * @return Status code + */ +VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset ); + + +/** + * Retrieves the best entry for the supplied time offset. + * @param index Pointer to valid index. + * @param later If true, the selected entry is the earliest retained entry with a greater or equal timestamp. + * If false, the selected entry is the latest retained entry with an earlier or equal timestamp. + * @param time The requested time. On success, this is filled in with the time of the selected entry. + * @param file_offset On success, this is filled in with the file offset of the selected entry. + * @param past Set if the requested time is after the last entry in the index. + * @return Status code. + */ +VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past ); + +#endif /* VC_CONTAINERS_WRITER_UTILS_H */ diff --git a/containers/core/containers_io.c b/containers/core/containers_io.c new file mode 100755 index 0000000..907a2a0 --- /dev/null +++ b/containers/core/containers_io.c @@ -0,0 +1,1088 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_uri.h" + +#define MAX_NUM_CACHED_AREAS 16 +#define MAX_NUM_MEMORY_AREAS 4 +#define NUM_TMP_MEMORY_AREAS 2 +#define MEM_CACHE_READ_MAX_SIZE (32*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_WRITE_MAX_SIZE (128*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_TMP_MAX_SIZE (32*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_ALIGNMENT (1*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_AREA_READ_MAX_SIZE (4*1024*1024) /* Needs to be a power of 2 */ + +typedef struct VC_CONTAINER_IO_PRIVATE_CACHE_T +{ + int64_t start; /**< Offset to the start of the cached area in the stream */ + int64_t end; /**< Offset to the end of the cached area in the stream */ + + int64_t offset; /**< Offset of the currently cached data in the stream */ + size_t size; /**< Size of the cached area */ + bool dirty; /**< Whether the cache is dirty and needs to be written back */ + + size_t position; /**< Current position in the cache */ + + uint8_t *buffer; /**< Pointer to the start of the valid cache area */ + uint8_t *buffer_end; /**< Pointer to the end of the cache */ + + unsigned int mem_max_size; /**< Maximum size of the memory cache */ + unsigned int mem_size; /**< Size of the memory cache */ + uint8_t *mem; /**< Pointer to the memory cache */ + + VC_CONTAINER_IO_T *io; + +} VC_CONTAINER_IO_PRIVATE_CACHE_T; + +typedef struct VC_CONTAINER_IO_PRIVATE_T +{ + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache; /**< Current cache */ + + unsigned int caches_num; + VC_CONTAINER_IO_PRIVATE_CACHE_T caches; + + unsigned int cached_areas_num; + VC_CONTAINER_IO_PRIVATE_CACHE_T cached_areas[MAX_NUM_CACHED_AREAS]; + + int64_t actual_offset; + + struct VC_CONTAINER_IO_ASYNC_T *async_io; + +} VC_CONTAINER_IO_PRIVATE_T; + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_http_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset); + +static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size ); +static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size ); +static VC_CONTAINER_STATUS_T vc_container_io_cache_seek( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset ); +static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ); +static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ); + +static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T * ); +static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx ); +static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ); +static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ); +static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ); +static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ); + +/*****************************************************************************/ +static VC_CONTAINER_IO_T *vc_container_io_open_core( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_IO_CAPABILITIES_T capabilities, + bool b_open, VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_T *p_ctx = 0; + VC_CONTAINER_IO_PRIVATE_T *private = 0; + unsigned int uri_length, caches = 0, cache_max_size, num_areas = MAX_NUM_MEMORY_AREAS; + + /* XXX */ + uri_length = strlen(uri) + 1; + + /* Allocate our context before trying out the different io modules */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*private) + uri_length); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*private) + uri_length ); + p_ctx->priv = private = (VC_CONTAINER_IO_PRIVATE_T *)&p_ctx[1]; + p_ctx->uri = (char *)&private[1]; + memcpy((char *)p_ctx->uri, uri, uri_length); + p_ctx->uri_parts = vc_uri_create(); + if(!p_ctx->uri_parts) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + vc_uri_parse(p_ctx->uri_parts, uri); + + if (b_open) + { + /* Open the actual i/o module */ + status = vc_container_io_null_open(p_ctx, uri, mode); + if(status) status = vc_container_io_net_open(p_ctx, uri, mode); + if(status) status = vc_container_io_pktfile_open(p_ctx, uri, mode); +#ifdef ENABLE_CONTAINER_IO_HTTP + if(status) status = vc_container_io_http_open(p_ctx, uri, mode); +#endif + if(status) status = vc_container_io_file_open(p_ctx, uri, mode); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(!p_ctx->pf_seek || (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)) + { + p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_CANT_SEEK; + p_ctx->pf_seek = io_seek_not_seekable; + } + } + else + { + /* We're only creating an empty container i/o */ + p_ctx->capabilities = capabilities; + } + + if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_NO_CACHING) + caches = 1; + + if(mode == VC_CONTAINER_IO_MODE_WRITE) cache_max_size = MEM_CACHE_WRITE_MAX_SIZE; + else cache_max_size = MEM_CACHE_READ_MAX_SIZE; + + if(mode == VC_CONTAINER_IO_MODE_WRITE && + vc_uri_path_extension(p_ctx->uri_parts) && + !strcasecmp(vc_uri_path_extension(p_ctx->uri_parts), "tmp")) + { + caches = 1; + cache_max_size = MEM_CACHE_TMP_MAX_SIZE; + num_areas = NUM_TMP_MEMORY_AREAS; + } + + /* Check if the I/O needs caching */ + if(caches) + { + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->caches; + cache->mem_max_size = cache_max_size; + cache->mem_size = cache->mem_max_size; + cache->io = p_ctx; + cache->mem = malloc(p_ctx->priv->caches.mem_size); + if(cache->mem) + { + cache->buffer = cache->mem; + cache->buffer_end = cache->mem + cache->mem_size; + p_ctx->priv->caches_num = 1; + } + } + + if(p_ctx->priv->caches_num) + p_ctx->priv->cache = &p_ctx->priv->caches; + + + /* Try to start an asynchronous io if we're in write mode and we've got at least 2 cache memory areas */ + if(mode == VC_CONTAINER_IO_MODE_WRITE && p_ctx->priv->cache && num_areas >= 2) + p_ctx->priv->async_io = async_io_start( p_ctx, num_areas, 0 ); + + end: + if(p_status) *p_status = status; + return p_ctx; + + error: + if(p_ctx) vc_uri_release(p_ctx->uri_parts); + if(p_ctx) free(p_ctx); + p_ctx = 0; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_STATUS_T *p_status ) +{ + return vc_container_io_open_core( uri, mode, 0, true, p_status ); +} + +/*****************************************************************************/ +VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_IO_CAPABILITIES_T capabilities, + VC_CONTAINER_STATUS_T *p_status ) +{ + return vc_container_io_open_core( uri, mode, capabilities, false, p_status ); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *p_ctx ) +{ + unsigned int i; + + if(p_ctx) + { + if(p_ctx->priv) + { + if(p_ctx->priv->caches_num) + { + if(p_ctx->priv->caches.dirty) + vc_container_io_cache_flush( p_ctx, &p_ctx->priv->caches, 1 ); + } + + if(p_ctx->priv->async_io) + async_io_stop( p_ctx->priv->async_io ); + else if(p_ctx->priv->caches_num) + free(p_ctx->priv->caches.mem); + + for(i = 0; i < p_ctx->priv->cached_areas_num; i++) + free(p_ctx->priv->cached_areas[i].mem); + + if(p_ctx->pf_close) + p_ctx->pf_close(p_ctx); + } + vc_uri_release(p_ctx->uri_parts); + free(p_ctx); + } + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +size_t vc_container_io_peek(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret; + + if(p_ctx->priv->cache) + { + /* FIXME: do something a bit more clever than this */ + int64_t offset = p_ctx->offset; + ret = vc_container_io_read(p_ctx, buffer, size); + vc_container_io_seek(p_ctx, offset); + return ret; + } + + if (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) + return 0; + + ret = p_ctx->pf_read(p_ctx, buffer, size); + p_ctx->pf_seek(p_ctx, p_ctx->offset); + return ret; +} + +/*****************************************************************************/ +size_t vc_container_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret; + + if(p_ctx->priv->cache) + ret = vc_container_io_cache_read( p_ctx, p_ctx->priv->cache, (uint8_t*)buffer, size ); + else + { + ret = p_ctx->pf_read(p_ctx, buffer, size); + p_ctx->priv->actual_offset += ret; + } + + p_ctx->offset += ret; + return ret; +} + +/*****************************************************************************/ +size_t vc_container_io_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + int32_t ret; + + if(p_ctx->priv->cache) + ret = vc_container_io_cache_write( p_ctx, p_ctx->priv->cache, (const uint8_t*)buffer, size ); + else + { + ret = p_ctx->pf_write(p_ctx, buffer, size); + p_ctx->priv->actual_offset += ret; + } + + p_ctx->offset += ret; + return ret < 0 ? 0 : ret; +} + +/*****************************************************************************/ +size_t vc_container_io_skip(VC_CONTAINER_IO_T *p_ctx, size_t size) +{ + if(!size) return 0; + + if(size < 8) + { + uint8_t value[8]; + return vc_container_io_read(p_ctx, value, size); + } + + if(p_ctx->priv->cache) + { + if(vc_container_io_cache_seek(p_ctx, p_ctx->priv->cache, p_ctx->offset + size)) return 0; + p_ctx->offset += size; + return size; + } + + if(vc_container_io_seek(p_ctx, p_ctx->offset + size)) return 0; + return size; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_STATUS_T status; + unsigned int i; + + /* Check if the requested position is in one of the cached areas */ + for(i = 0; i < p_ctx->priv->cached_areas_num; i++) + { + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->cached_areas[i]; + if(offset >= cache->start && offset < cache->end) + { + p_ctx->priv->cache = cache; + break; + } + } + if(i == p_ctx->priv->cached_areas_num) + p_ctx->priv->cache = p_ctx->priv->caches_num ? &p_ctx->priv->caches : 0; + + if(p_ctx->priv->cache) + { + status = vc_container_io_cache_seek( p_ctx, p_ctx->priv->cache, offset ); + if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset; + return status; + } + + if(p_ctx->status == VC_CONTAINER_SUCCESS && + offset == p_ctx->offset) return VC_CONTAINER_SUCCESS; + + status = p_ctx->pf_seek(p_ctx, offset); + if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset; + p_ctx->priv->actual_offset = p_ctx->offset; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv; + + vc_container_assert(offset >= private->actual_offset); + if(offset == private->actual_offset) return VC_CONTAINER_SUCCESS; + + if(offset < private->actual_offset) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return p_ctx->status; + } + + offset -= private->actual_offset; + while(offset && !p_ctx->status) + { + uint8_t value[64]; + unsigned int ret, size = MIN(offset, 64); + ret = p_ctx->pf_read(p_ctx, value, size); + if(ret != size) p_ctx->status = VC_CONTAINER_ERROR_EOS; + offset -= ret; + } + return p_ctx->status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, va_list args) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if (context->pf_control) + status = context->pf_control(context, operation, args); + + /* Option to add generic I/O control here */ + + if(operation == VC_CONTAINER_CONTROL_IO_FLUSH && context->priv->cache) + { + status = VC_CONTAINER_SUCCESS; + (void)vc_container_io_cache_flush( context, context->priv->cache, 1 ); + } + + if(operation == VC_CONTAINER_CONTROL_SET_IO_PERF_STATS && context->priv->async_io) + { + status = VC_CONTAINER_SUCCESS; + async_io_stats_initialise(context->priv->async_io, va_arg(args, int)); + } + + if(operation == VC_CONTAINER_CONTROL_GET_IO_PERF_STATS && context->priv->async_io) + { + status = VC_CONTAINER_SUCCESS; + async_io_stats_get(context->priv->async_io, va_arg(args, VC_CONTAINER_WRITE_STATS_T *)); + } + + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, ...) +{ + VC_CONTAINER_STATUS_T result; + va_list args; + + va_start(args, operation); + result = vc_container_io_control_list(context, operation, args); + va_end(args); + + return result; +} + +/*****************************************************************************/ +size_t vc_container_io_cache(VC_CONTAINER_IO_T *p_ctx, size_t size) +{ + VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv; + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, *main_cache; + VC_CONTAINER_STATUS_T status; + + /* Sanity checking */ + if(private->cached_areas_num >= MAX_NUM_CACHED_AREAS) return 0; + + cache = &private->cached_areas[private->cached_areas_num]; + cache->start = p_ctx->offset; + cache->end = cache->start + size; + cache->offset = p_ctx->offset; + cache->position = 0; + cache->size = 0; + cache->io = p_ctx; + + /* Set the size of the cache area depending on the capabilities of the i/o */ + if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) + cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE; + else if((p_ctx->capabilities & VC_CONTAINER_IO_CAPS_SEEK_SLOW) && + size <= MEM_CACHE_AREA_READ_MAX_SIZE) + cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE; + else + cache->mem_max_size = MEM_CACHE_READ_MAX_SIZE; + + cache->mem_size = size; + if(cache->mem_size > cache->mem_max_size) cache->mem_size = cache->mem_max_size; + cache->mem = malloc(cache->mem_size); + + cache->buffer = cache->mem; + cache->buffer_end = cache->mem + cache->mem_size; + + if(!cache->mem) return 0; + private->cached_areas_num++; + + /* Copy any data we've got in the current cache into the new cache */ + main_cache = p_ctx->priv->cache; + if(main_cache && main_cache->position < main_cache->size) + { + cache->size = main_cache->size - main_cache->position; + if(cache->size > cache->mem_size) cache->size = cache->mem_size; + memcpy(cache->buffer, main_cache->buffer + main_cache->position, cache->size); + main_cache->position += cache->size; + } + + /* Read the rest of the cache directly from the stream */ + if(cache->mem_size > cache->size) + { + size_t ret = cache->io->pf_read(cache->io, cache->buffer + cache->size, + cache->mem_size - cache->size); + cache->size += ret; + cache->io->priv->actual_offset = cache->offset + cache->size; + } + + status = vc_container_io_seek(p_ctx, cache->end); + if(status != VC_CONTAINER_SUCCESS) + return 0; + + if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) + return cache->size; + else + return size; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) +{ + size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); + + if(ret) return 0; /* TODO what should we do there ? */ + + if(p_ctx->priv->actual_offset != cache->offset) + { + if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) + return 0; + } + + ret = cache->io->pf_read(cache->io, cache->buffer, cache->buffer_end - cache->buffer); + cache->size = ret; + cache->position = 0; + cache->io->priv->actual_offset = cache->offset + ret; + return ret; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_refill_bypass( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *buffer, size_t size ) +{ + size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); + + if(ret) return 0; /* TODO what should we do there ? */ + + if(p_ctx->priv->actual_offset != cache->offset) + { + if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) + return 0; + } + + ret = cache->io->pf_read(cache->io, buffer, size); + cache->size = cache->position = 0; + cache->offset += ret; + cache->io->priv->actual_offset = cache->offset; + return ret; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size ) +{ + size_t read = 0, bytes, ret; + + while(size) + { + bytes = cache->size - cache->position; /* Bytes left in cache */ + +#if 1 // FIXME Only if stream is seekable + /* Try to read directly from the stream if the cache just gets in the way */ + if(!bytes && size > cache->mem_size) + { + bytes = cache->mem_size; + ret = vc_container_io_cache_refill_bypass( p_ctx, cache, data + read, bytes); + read += ret; + + if(ret != bytes) /* We didn't read as many bytes as we had hoped */ + goto end; + + size -= bytes; + continue; + } +#endif + + /* Refill the cache if it is empty */ + if(!bytes) bytes = vc_container_io_cache_refill( p_ctx, cache ); + if(!bytes) goto end; + + /* We do have some data in the cache so override the status */ + p_ctx->status = VC_CONTAINER_SUCCESS; + + /* Read data directly from the cache */ + if(bytes > size) bytes = size; + memcpy(data + read, cache->buffer + cache->position, bytes); + cache->position += bytes; + read += bytes; + size -= bytes; + } + + end: + vc_container_assert(cache->offset + cache->position == p_ctx->offset + read); + return read; +} + +/*****************************************************************************/ +static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size ) +{ + int32_t written = 0; + size_t bytes, ret; + + /* If we do not have a write cache then we need to flush it */ + if(cache->size && !cache->dirty) + { + ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); + if(ret) return -(int32_t)ret; + } + + while(size) + { + bytes = (cache->buffer_end - cache->buffer) - cache->position; /* Space left in cache */ + + /* Flush the cache if it is full */ + if(!bytes) + { + /* Cache full, flush it */ + ret = vc_container_io_cache_flush( p_ctx, cache, 0 ); + if(ret) + { + written -= ret; + return written; + } + continue; + } + + if(bytes > size) bytes = size; + + if(!p_ctx->priv->async_io && bytes == cache->mem_size) + { + /* Write directly from the buffer */ + ret = cache->io->pf_write(cache->io, data + written, bytes); + cache->offset += ret; + cache->io->priv->actual_offset += ret; + } + else + { + /* Write in the cache */ + memcpy(cache->buffer + cache->position, data + written, bytes); + cache->position += bytes; + cache->dirty = 1; + ret = bytes; + } + + written += ret; + if(ret != bytes) goto end; + + size -= bytes; + } + + end: + vc_container_assert(cache->offset + (int64_t)cache->position == p_ctx->offset + written); + if(cache->position > cache->size) cache->size = cache->position; + return written; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T vc_container_io_cache_seek(VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset) +{ + VC_CONTAINER_STATUS_T status; + size_t shift, ret; + + /* Check if the seek position is within our cache */ + if(offset >= cache->offset && offset < cache->offset + (int64_t)cache->size) + { + cache->position = offset - cache->offset; + return VC_CONTAINER_SUCCESS; + } + + shift = cache->buffer - cache->mem; + if(!cache->dirty && shift && cache->size && + offset >= cache->offset - (int64_t)shift && offset < cache->offset) + { + /* We need to refill the partial bit of the cache that we didn't take care of last time */ + status = cache->io->pf_seek(cache->io, cache->offset - shift); + if(status != VC_CONTAINER_SUCCESS) return status; + cache->offset -= shift; + cache->buffer -= shift; + + ret = cache->io->pf_read(cache->io, cache->buffer, shift); + vc_container_assert(ret == shift); /* FIXME: ret must = shift */ + cache->size += shift; + cache->position = offset - cache->offset; + cache->io->priv->actual_offset = cache->offset + ret; + return VC_CONTAINER_SUCCESS; + } + + if(cache->dirty) vc_container_io_cache_flush( p_ctx, cache, 1 ); + // FIXME: what if all the data couldn't be flushed ? + + if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, 1 ); + + status = cache->io->pf_seek(cache->io, offset); + if(status != VC_CONTAINER_SUCCESS) return status; + + vc_container_io_cache_flush( p_ctx, cache, 1 ); + + cache->offset = offset; + cache->io->priv->actual_offset = offset; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) +{ + size_t ret = 0, shift; + + if(cache->position > cache->size) cache->size = cache->position; + + if(cache->dirty && cache->size) + { + if(p_ctx->priv->actual_offset != cache->offset) + { + if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, complete ); + + if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) + return 0; + } + + if(p_ctx->priv->async_io) + { + ret = async_io_write( p_ctx->priv->async_io, cache ); + if(async_io_wait_complete( p_ctx->priv->async_io, cache, complete ) != VC_CONTAINER_SUCCESS) + ret = 0; + } + else + ret = cache->io->pf_write(cache->io, cache->buffer, cache->size); + + cache->io->priv->actual_offset = cache->offset + ret; + ret = cache->position - ret; + } + cache->dirty = 0; + + cache->offset += cache->size; + if(cache->mem_size == cache->mem_max_size) + { + shift = cache->offset &(MEM_CACHE_ALIGNMENT-1); + cache->buffer = cache->mem + shift; + } + + cache->position = cache->size = 0; + return ret; +} + +/***************************************************************************** + * Asynchronous I/O. + * This is here to keep the I/O as busy as possible by allowing the writer + * to continue its work while the I/O is taking place in the background. + *****************************************************************************/ + +#ifdef ENABLE_CONTAINERS_ASYNC_IO +#include "vcos.h" + +#define NUMPC(c,n,s) ((c) < (1<<(s)) ? (n) : ((n) / (c >> (s)))) + +static void stats_initialise(VC_CONTAINER_STATS_T *st, uint32_t shift) +{ + memset(st, 0, sizeof(VC_CONTAINER_STATS_T)); + st->shift = shift; +} + +static void stats_add_value(VC_CONTAINER_STATS_T *st, uint32_t count, uint32_t num) +{ + uint32_t numpc; + int i, j; + + if(count == 0) + return; + + numpc = NUMPC(count, num, st->shift); + // insert in the right place + i=0; + while(i < VC_CONTAINER_STATS_BINS && st->record[i].count != 0 && st->record[i].numpc > numpc) + i++; + + if(st->record[i].count != 0 && st->record[i].numpc == numpc) + { + // equal numpc, can merge now + st->record[i].count += count; + st->record[i].num += num; + } + else + { + // shift higher records up + for(j=VC_CONTAINER_STATS_BINS; j>i; j--) + st->record[j] = st->record[j-1]; + + // write record in + st->record[i].count = count; + st->record[i].num = num; + st->record[i].numpc = numpc; + + // if full, join the two closest records + if(st->record[VC_CONTAINER_STATS_BINS].count) + { + uint32_t min_diff = 0; + j = -1; + + // find closest, based on difference between numpc + for(i=0; irecord[i].numpc - st->record[i+1].numpc; + if(j == -1 || diff < min_diff) + { + j = i; + min_diff = diff; + } + } + + // merge these records + st->record[j].count += st->record[j+1].count; + st->record[j].num += st->record[j+1].num; + st->record[j].numpc = NUMPC(st->record[j].count, st->record[j].num, st->shift); + + // shift down higher records + while(++j < VC_CONTAINER_STATS_BINS) + st->record[j] = st->record[j+1]; + + // zero the free top record + st->record[VC_CONTAINER_STATS_BINS].count = 0; + st->record[VC_CONTAINER_STATS_BINS].num = 0; + st->record[VC_CONTAINER_STATS_BINS].numpc = 0; + } + } +} + +typedef struct VC_CONTAINER_IO_ASYNC_T +{ + VC_CONTAINER_IO_T *io; + VCOS_THREAD_T thread; + VCOS_SEMAPHORE_T spare_sema; + VCOS_SEMAPHORE_T queue_sema; + VCOS_EVENT_T wake_event; + int quit; + + unsigned int num_area; + uint8_t *mem[MAX_NUM_MEMORY_AREAS]; /**< Base address of memory areas */ + uint8_t *buffer[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, pointer to start of valid cache area */ + size_t size[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, size of valid area to write */ + unsigned int cur_area; + + unsigned char stack[3000]; + int error; + + int stats_enable; + VC_CONTAINER_WRITE_STATS_T stats; + +} VC_CONTAINER_IO_ASYNC_T; + +/*****************************************************************************/ +static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ) +{ + ctx->stats_enable = enable; + stats_initialise(&ctx->stats.write, 8); + stats_initialise(&ctx->stats.wait, 0); + stats_initialise(&ctx->stats.flush, 0); +} + +static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ) +{ + *stats = ctx->stats; +} + +static void *async_io_thread(VOID *argv) +{ + VC_CONTAINER_IO_ASYNC_T *ctx = argv; + unsigned int write_area = 0; + + while (1) + { + unsigned long time = 0; + + vcos_event_wait(&ctx->wake_event); + if(ctx->quit) break; + + while(vcos_semaphore_trywait(&ctx->queue_sema) == VCOS_SUCCESS) + { + uint8_t *buffer = ctx->buffer[write_area]; + size_t size = ctx->size[write_area]; + + if(ctx->stats_enable) + time = vcos_getmicrosecs(); + + if(ctx->io->pf_write(ctx->io, buffer, size) != size) + ctx->error = 1; + + if(ctx->stats_enable) + stats_add_value(&ctx->stats.write, size, vcos_getmicrosecs() - time); + + /* Signal that the write is done */ + vcos_semaphore_post(&ctx->spare_sema); + + if(++write_area == ctx->num_area) + write_area = 0; + } + } + + return NULL; +} + +static int async_io_write( VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) +{ + unsigned long time = 0; + unsigned int offset; + + if(ctx->stats_enable) + time = vcos_getmicrosecs(); + + /* post the current area */ + ctx->buffer[ctx->cur_area] = cache->buffer; + ctx->size[ctx->cur_area] = cache->size; + vcos_semaphore_post(&ctx->queue_sema); + vcos_event_signal(&ctx->wake_event); + + /* now we need to grab another area */ + vcos_semaphore_wait(&ctx->spare_sema); + if(++ctx->cur_area == ctx->num_area) + ctx->cur_area = 0; + + if(ctx->stats_enable) + stats_add_value(&ctx->stats.wait, 1, vcos_getmicrosecs() - time); + + /* alter cache mem to point to the new cur_area */ + offset = cache->buffer - cache->mem; + cache->mem = ctx->mem[ctx->cur_area]; + cache->buffer = cache->mem + offset; + cache->buffer_end = cache->mem + cache->mem_size; + + return ctx->error ? 0 : cache->size; +} + +static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) +{ + unsigned int time = 0; + + if(ctx->stats_enable) + time = vcos_getmicrosecs(); + + if(complete) + { + int num; + /* Need to make sure that all memory areas have been written out, so should have num-1 spare */ + for(num=0; numnum_area-1; num++) + vcos_semaphore_wait(&ctx->spare_sema); + + for(num=0; numnum_area-1; num++) + vcos_semaphore_post(&ctx->spare_sema); + } + else + { + /* Need to make sure we can acquire one memory area */ + vcos_semaphore_wait(&ctx->spare_sema); + vcos_semaphore_post(&ctx->spare_sema); + } + + if(ctx->stats_enable) + stats_add_value(&ctx->stats.flush, 1, vcos_getmicrosecs() - time); + + return ctx->error ? VC_CONTAINER_ERROR_FAILED : VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status ) +{ + VC_CONTAINER_IO_ASYNC_T *ctx = 0; + VCOS_UNSIGNED pri = 0; + + /* Allocate our context */ + ctx = malloc(sizeof(*ctx)); + if(!ctx) goto error_spare_sema; + memset(ctx, 0, sizeof(*ctx)); + ctx->io = io; + + ctx->mem[0] = io->priv->cache->mem; + + for(ctx->num_area = 1; ctx->num_area < num_areas; ctx->num_area++) + { + ctx->mem[ctx->num_area] = malloc(io->priv->cache->mem_size); + if(!ctx->mem[ctx->num_area]) + break; + } + + if(ctx->num_area == 1) // no real benefit in asynchronous writes + goto error_spare_sema; + + async_io_stats_initialise(ctx, 0); + + if(vcos_semaphore_create(&ctx->spare_sema, "async_spare_sem", ctx->num_area-1) != VCOS_SUCCESS) + goto error_spare_sema; + + if(vcos_semaphore_create(&ctx->queue_sema, "async_queue_sem", 0) != VCOS_SUCCESS) + goto error_queue_sema; + + if (vcos_event_create(&ctx->wake_event, "async_wake_event") != VCOS_SUCCESS) + goto error_event; + + // run this thread at a slightly higher priority than the calling thread - that means that + // we prefer to write to the SD card rather than filling the memory buffer. + pri = vcos_thread_get_priority(vcos_thread_current()); + if(vcos_thread_create_classic(&ctx->thread, "async_io", async_io_thread, ctx, + ctx->stack, sizeof(ctx->stack), pri-1, 10, VCOS_START) != VCOS_SUCCESS) + goto error_thread; + + if(status) *status = VC_CONTAINER_SUCCESS; + return ctx; + + error_thread: + vcos_event_delete(&ctx->wake_event); + error_event: + vcos_semaphore_delete(&ctx->queue_sema); + error_queue_sema: + vcos_semaphore_delete(&ctx->spare_sema); + error_spare_sema: + if(ctx) free(ctx); + if(status) *status = VC_CONTAINER_ERROR_FAILED; + return 0; +} + +static VC_CONTAINER_STATUS_T async_io_stop( VC_CONTAINER_IO_ASYNC_T *ctx ) +{ + /* Block if a write operation is already in progress */ + //vcos_semaphore_wait(&ctx->sema); + // XXX block until all done + + ctx->quit = 1; + vcos_event_signal(&ctx->wake_event); + vcos_thread_join(&ctx->thread,NULL); + vcos_event_delete(&ctx->wake_event); + vcos_semaphore_delete(&ctx->queue_sema); + vcos_semaphore_delete(&ctx->spare_sema); + + while(ctx->num_area > 0) + free(ctx->mem[--ctx->num_area]); + + free(ctx); + return VC_CONTAINER_SUCCESS; +} +#else + +static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status ) +{ + VC_CONTAINER_PARAM_UNUSED(io); + VC_CONTAINER_PARAM_UNUSED(num_areas); + if(status) *status = VC_CONTAINER_ERROR_FAILED; + return 0; +} + +static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(cache); + return 0; +} + +static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(cache); + VC_CONTAINER_PARAM_UNUSED(complete); + return 0; +} + +static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + return VC_CONTAINER_SUCCESS; +} + +static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(enable); +} + +static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(stats); +} + + +#endif diff --git a/containers/core/containers_io.h b/containers/core/containers_io.h new file mode 100755 index 0000000..8cd4407 --- /dev/null +++ b/containers/core/containers_io.h @@ -0,0 +1,229 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_IO_H +#define VC_CONTAINERS_IO_H + +/** \file containers_io.h + * Interface definition for the input / output abstraction layer used by container + * readers and writers */ +#include "containers/containers.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup VcContainerIoApi Container I/O API */ +/* @{ */ + +/** Container io opening mode. + * This is used to specify whether a reader or writer is requested. + */ +typedef enum { + VC_CONTAINER_IO_MODE_READ = 0, /**< Container io opened in reading mode */ + VC_CONTAINER_IO_MODE_WRITE = 1 /**< Container io opened in writing mode */ +} VC_CONTAINER_IO_MODE_T; + +/** \name Container I/O Capabilities + * The following flags are exported by container i/o modules to describe their capabilities */ +/* @{ */ +/** Type definition for container I/O capabilities */ +typedef uint32_t VC_CONTAINER_IO_CAPABILITIES_T; +/** Seeking is not supported */ +#define VC_CONTAINER_IO_CAPS_CANT_SEEK 0x1 +/** Seeking is slow and should be avoided */ +#define VC_CONTAINER_IO_CAPS_SEEK_SLOW 0x2 +/** The I/O doesn't provide any caching of the data */ +#define VC_CONTAINER_IO_CAPS_NO_CACHING 0x4 +/* @} */ + +/** Container Input / Output Context. + * This structure defines the context for a container io instance */ +struct VC_CONTAINER_IO_T +{ + /** Pointer to information private to the container io instance */ + struct VC_CONTAINER_IO_PRIVATE_T *priv; + + /** Pointer to information private to the container io module */ + struct VC_CONTAINER_IO_MODULE_T *module; + + /** Uniform Resource Identifier for the stream to open. + * This is a string encoded in UTF-8 which follows the syntax defined in + * RFC2396 (http://tools.ietf.org/html/rfc2396). */ + char *uri; + + /** Pre-parsed URI */ + struct VC_URI_PARTS_T *uri_parts; + + /** Current offset into the i/o stream */ + int64_t offset; + + /** Current size of the i/o stream (0 if unknown). This size might grow during the + * lifetime of the i/o instance (for instance when doing progressive download). */ + int64_t size; + + /** Capabilities of the i/o stream */ + VC_CONTAINER_IO_CAPABILITIES_T capabilities; + + /** Status of the i/o stream */ + VC_CONTAINER_STATUS_T status; + + /** Maximum size allowed for this i/o stream (0 if unknown). This is used during writing + * to limit the size of the stream to below this value. */ + int64_t max_size; + + /** \note the following list of function pointers should not be used directly. + * They defines the interface for implementing container io modules and are filled in + * by the container modules themselves. */ + + /** \private + * Function pointer to close and free all resources allocated by a + * container io module */ + VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_IO_T *io); + + /** \private + * Function pointer to read or skip data from container io module */ + size_t (*pf_read)(struct VC_CONTAINER_IO_T *io, void *buffer, size_t size); + + /** \private + * Function pointer to write data to a container io module */ + size_t (*pf_write)(struct VC_CONTAINER_IO_T *io, const void *buffer, size_t size); + + /** \private + * Function pointer to seek into a container io module */ + VC_CONTAINER_STATUS_T (*pf_seek)(struct VC_CONTAINER_IO_T *io, int64_t offset); + + /** \private + * Function pointer to perform a control operation on a container io module */ + VC_CONTAINER_STATUS_T (*pf_control)(struct VC_CONTAINER_IO_T *io, + VC_CONTAINER_CONTROL_T operation, va_list args); + +}; + +/** Opens an i/o stream pointed to by a URI. + * This will create an instance of the container i/o module. + * + * \param uri Uniform Resource Identifier pointing to the multimedia container + * \param mode Mode in which the i/o stream will be opened + * \param status Returns the status of the operation + * \return If successful, this returns a pointer to the new instance + * of the i/o module. Returns NULL on failure. + */ +VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_STATUS_T *status ); + +/** Creates an empty i/o stream. The i/o function pointers will have to be set + * by the caller before the i/o gets used. + * This will create an instance of the container i/o module. + * + * \param uri Uniform Resource Identifier pointing to the multimedia container + * \param mode Mode in which the i/o stream will be opened + * \param capabilities Flags indicating the capabilities of the i/o + * \param status Returns the status of the operation + * \return If successful, this returns a pointer to the new instance + * of the i/o module. Returns NULL on failure. + */ +VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_IO_CAPABILITIES_T capabilities, + VC_CONTAINER_STATUS_T *p_status ); + +/** Closes an instance of a container i/o module. + * \param context Pointer to the VC_CONTAINER_IO_T context of the instance to close + * \return VC_CONTAINER_SUCCESS on success. + */ +VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *context ); + +/** Read data from an i/o stream without advancing the read position within the stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param buffer Pointer to the buffer where the data will be read + * \param size Number of bytes to read + * \return The size of the data actually read. + */ +size_t vc_container_io_peek(VC_CONTAINER_IO_T *context, void *buffer, size_t size); + +/** Read data from an i/o stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param buffer Pointer to the buffer where the data will be read + * \param size Number of bytes to read + * \return The size of the data actually read. + */ +size_t vc_container_io_read(VC_CONTAINER_IO_T *context, void *buffer, size_t size); + +/** Skip data in an i/o stream without reading it. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param size Number of bytes to skip + * \return The size of the data actually skipped. + */ +size_t vc_container_io_skip(VC_CONTAINER_IO_T *context, size_t size); + +/** Write data to an i/o stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param buffer Pointer to the buffer containing the data to write + * \param size Number of bytes to write + * \return The size of the data actually written. + */ +size_t vc_container_io_write(VC_CONTAINER_IO_T *context, const void *buffer, size_t size); + +/** Seek into an i/o stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param offset Absolute file offset to seek to + * \return Status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *context, int64_t offset); + +/** Perform control operation on an i/o stream (va_list). + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param operation Control operation to be performed + * \param args Additional arguments for the operation + * \return Status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, + VC_CONTAINER_CONTROL_T operation, va_list args); + +/** Perform control operation on an i/o stream (varargs). + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param operation Control operation to be performed + * \param ... Additional arguments for the operation + * \return Status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, + VC_CONTAINER_CONTROL_T operation, ...); + +/** Cache the pointed region of the i/o stream (from current position). + * This will allow future seeking into the specified region even on non-seekable streams. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param size Size of the region to cache + * \return Status of the operation + */ +size_t vc_container_io_cache(VC_CONTAINER_IO_T *context, size_t size); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_HELPERS_H */ diff --git a/containers/core/containers_io_helpers.c b/containers/core/containers_io_helpers.c new file mode 100755 index 0000000..88e08cc --- /dev/null +++ b/containers/core/containers_io_helpers.c @@ -0,0 +1,244 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_logging.h" + +void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...) +{ + char debug_string[512]; + va_list args; + int result; + + if(indent >= (int)sizeof(debug_string)) return; + memset(debug_string, ' ', indent); + + va_start( args, format ); + result = vsnprintf(debug_string + indent, sizeof(debug_string) - indent, format, args); + va_end( args ); + + if(result <= 0) return; + + vc_container_log(ctx, VC_CONTAINER_LOG_FORMAT, debug_string); + fflush(0); +} + +uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + + if(type == LOG_FORMAT_TYPE_HEX) + vc_container_helper_format_debug(ctx, indent, "%s: 0x%"PRIx64, name, value); + else + vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); + return value; +} + +uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, + const char *name, uint8_t *buffer, int indent, int b_skip) +{ + int64_t offset = STREAM_POSITION(ctx); + uint64_t value = 0; + GUID_T guid; + + if(type == LOG_FORMAT_TYPE_STRING || + type == LOG_FORMAT_TYPE_STRING_UTF16_LE || + type == LOG_FORMAT_TYPE_STRING_UTF16_BE) + { + uint8_t stringbuf[256]; + char utf8buf[256]; + int stringsize = sizeof(stringbuf) - 2; + + if(!buffer) + { + buffer = stringbuf; + if(size < stringsize) stringsize = size; + } + else stringsize = size; + + value = vc_container_io_read(ctx->priv->io, buffer, stringsize); + + if(!utf8_from_charset(type == LOG_FORMAT_TYPE_STRING ? "UTF8" : "UTF16-LE", + utf8buf, sizeof(utf8buf), buffer, stringsize)) + vc_container_helper_format_debug(ctx, indent, "%s: \"%s\"", name, utf8buf); + else + vc_container_helper_format_debug(ctx, indent, "%s: (could not read)", name); + + if(size - stringsize) + value += vc_container_io_skip(ctx->priv->io, size - stringsize); + return value; + } + + if(type == LOG_FORMAT_TYPE_UINT_LE) + { + switch(size) + { + case 1: value = vc_container_io_read_uint8(ctx->priv->io); break; + case 2: value = vc_container_io_read_le_uint16(ctx->priv->io); break; + case 3: value = vc_container_io_read_le_uint24(ctx->priv->io); break; + case 4: value = vc_container_io_read_le_uint32(ctx->priv->io); break; + case 5: value = vc_container_io_read_le_uint40(ctx->priv->io); break; + case 6: value = vc_container_io_read_le_uint48(ctx->priv->io); break; + case 7: value = vc_container_io_read_le_uint56(ctx->priv->io); break; + case 8: value = vc_container_io_read_le_uint64(ctx->priv->io); break; + } + } + else if(type == LOG_FORMAT_TYPE_UINT_BE) + { + switch(size) + { + case 1: value = vc_container_io_read_uint8(ctx->priv->io); break; + case 2: value = vc_container_io_read_be_uint16(ctx->priv->io); break; + case 3: value = vc_container_io_read_be_uint24(ctx->priv->io); break; + case 4: value = vc_container_io_read_be_uint32(ctx->priv->io); break; + case 5: value = vc_container_io_read_be_uint40(ctx->priv->io); break; + case 6: value = vc_container_io_read_be_uint48(ctx->priv->io); break; + case 7: value = vc_container_io_read_be_uint56(ctx->priv->io); break; + case 8: value = vc_container_io_read_be_uint64(ctx->priv->io); break; + } + } + else if(type == LOG_FORMAT_TYPE_FOURCC) + { + value = vc_container_io_read_fourcc(ctx->priv->io); + } + else if(type == LOG_FORMAT_TYPE_GUID) + { + value = vc_container_io_read(ctx->priv->io, &guid, 16); + } + else + { + vc_container_assert(0); + return 0; + } + + if(type == LOG_FORMAT_TYPE_GUID) + { + if(value == 16) + { + vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + name, guid.word0, guid.short0, guid.short1, + guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3], + guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]); + if(buffer) memcpy(buffer, &guid, sizeof(guid)); + } + } + else if(type == LOG_FORMAT_TYPE_FOURCC) + { + uint32_t val = value; + vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&val); + } + else + { + vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); + } + + if(b_skip) value = (STREAM_POSITION(ctx) - offset) != size; + return value; +} + +VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, + const char *name, uint64_t value, const uint8_t *buffer, int indent, int silent) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if(type == LOG_FORMAT_TYPE_STRING) + { + value = vc_container_io_write(ctx->priv->io, buffer, size); + if(!silent) + vc_container_helper_format_debug(ctx, indent, "%s: \"%ls\"", name, buffer); + return value == (uint64_t)size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; + } + + if(type == LOG_FORMAT_TYPE_UINT_LE) + { + switch(size) + { + case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break; + case 2: status = vc_container_io_write_le_uint16(ctx->priv->io, (uint16_t)value); break; + case 3: status = vc_container_io_write_le_uint24(ctx->priv->io, (uint32_t)value); break; + case 4: status = vc_container_io_write_le_uint32(ctx->priv->io, (uint32_t)value); break; + case 8: status = vc_container_io_write_le_uint64(ctx->priv->io, value); break; + } + } + else if(type == LOG_FORMAT_TYPE_UINT_BE) + { + switch(size) + { + case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break; + case 2: status = vc_container_io_write_be_uint16(ctx->priv->io, (uint16_t)value); break; + case 3: status = vc_container_io_write_be_uint24(ctx->priv->io, (uint32_t)value); break; + case 4: status = vc_container_io_write_be_uint32(ctx->priv->io, (uint32_t)value); break; + case 8: status = vc_container_io_write_be_uint64(ctx->priv->io, value); break; + } + } + else if(type == LOG_FORMAT_TYPE_FOURCC) + { + status = vc_container_io_write_fourcc(ctx->priv->io, (uint32_t)value); + } + else if(type == LOG_FORMAT_TYPE_GUID) + { + value = vc_container_io_write(ctx->priv->io, buffer, 16); + } + else + { + vc_container_assert(0); + return 0; + } + + if(status) + { + vc_container_helper_format_debug(ctx, indent, "write failed for %s", name); + return status; + } + + if(!silent) + { + if (type == LOG_FORMAT_TYPE_FOURCC) + { + vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&value); + } + else if(type == LOG_FORMAT_TYPE_GUID) + { + GUID_T guid; + memcpy(&guid, buffer, sizeof(guid)); + vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + name, guid.word0, guid.short0, guid.short1, + guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3], + guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]); + } + else + { + vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); + } + } + + return status; +} diff --git a/containers/core/containers_io_helpers.h b/containers/core/containers_io_helpers.h new file mode 100755 index 0000000..1f9b460 --- /dev/null +++ b/containers/core/containers_io_helpers.h @@ -0,0 +1,716 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_IO_HELPERS_H +#define VC_CONTAINERS_IO_HELPERS_H + +/** \file containers_io_helpers.h + * Helper functions and macros which provide functionality which is often used by containers + */ + +#include "containers/core/containers_io.h" +#include "containers/core/containers_utils.h" + +/***************************************************************************** + * Helper inline functions to read integers from an i/o stream + *****************************************************************************/ + +/** Reads an unsigned 8 bits integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint8_t vc_container_io_read_uint8(VC_CONTAINER_IO_T *io) +{ + uint8_t value; + size_t ret = vc_container_io_read(io, &value, 1); + return ret == 1 ? value : 0; +} + +/** Reads a FOURCC from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The FOURCC to read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE VC_CONTAINER_FOURCC_T vc_container_io_read_fourcc(VC_CONTAINER_IO_T *io) +{ + VC_CONTAINER_FOURCC_T value; + size_t ret = vc_container_io_read(io, (int8_t *)&value, 4); + return ret == 4 ? value : 0; +} + +/** Reads an unsigned 16 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_read_be_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_read(io, value, 2); + return ret == 2 ? (value[0] << 8) | value[1] : 0; +} + +/** Reads an unsigned 24 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_be_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_read(io, value, 3); + return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0; +} + +/** Reads an unsigned 32 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_be_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_read(io, value, 4); + return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0; +} + +/** Reads an unsigned 40 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint40(VC_CONTAINER_IO_T *io) +{ + uint8_t value[5]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 5); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = value[4]; + + return ret == 5 ? (((uint64_t)value1) << 8)|value2 : 0; +} + +/** Reads an unsigned 48 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint48(VC_CONTAINER_IO_T *io) +{ + uint8_t value[6]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 6); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 8) | value[5]; + + return ret == 6 ? (((uint64_t)value1) << 16)|value2 : 0; +} + +/** Reads an unsigned 56 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint56(VC_CONTAINER_IO_T *io) +{ + uint8_t value[7]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 7); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 16) | (value[5] << 8) | value[6]; + + return ret == 7 ? (((uint64_t)value1) << 24)|value2 : 0; +} + +/** Reads an unsigned 64 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 8); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7]; + + return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0; +} + +/** Reads an unsigned 16 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_read_le_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_read(io, value, 2); + return ret == 2 ? (value[1] << 8) | value[0] : 0; +} + +/** Reads an unsigned 24 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_le_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_read(io, value, 3); + return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Reads an unsigned 32 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_le_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_read(io, value, 4); + return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Reads an unsigned 40 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint40(VC_CONTAINER_IO_T *io) +{ + uint8_t value[5]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 5); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = value[4]; + + return ret == 5 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/** Reads an unsigned 48 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint48(VC_CONTAINER_IO_T *io) +{ + uint8_t value[6]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 6); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[5] << 8) | value[4]; + + return ret == 6 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/** Reads an unsigned 56 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint56(VC_CONTAINER_IO_T *io) +{ + uint8_t value[7]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 7); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[6] << 16) | (value[5] << 8) | value[4]; + + return ret == 7 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/** Reads an unsigned 64 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 8); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4]; + + return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/***************************************************************************** + * Helper inline functions to peek integers from an i/o stream + *****************************************************************************/ + +/** Peeks an unsigned 8 bits integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint8_t vc_container_io_peek_uint8(VC_CONTAINER_IO_T *io) +{ + uint8_t value; + size_t ret = vc_container_io_peek(io, &value, 1); + return ret == 1 ? value : 0; +} + +/** Peeks an unsigned 16 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_peek_be_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_peek(io, value, 2); + return ret == 2 ? (value[0] << 8) | value[1] : 0; +} + +/** Peeks an unsigned 24 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_be_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_peek(io, value, 3); + return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0; +} + +/** Peeks an unsigned 32 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_be_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_peek(io, value, 4); + return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0; +} + +/** Peeks an unsigned 64 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_peek_be_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_peek(io, value, 8); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7]; + + return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0; +} + +/** Peeks an unsigned 16 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_peek_le_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_peek(io, value, 2); + return ret == 2 ? (value[1] << 8) | value[0] : 0; +} + +/** Peeks an unsigned 24 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_le_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_peek(io, value, 3); + return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Peeks an unsigned 32 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_le_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_peek(io, value, 4); + return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Peeks an unsigned 64 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_peek_le_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_peek(io, value, 8); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4]; + + return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/***************************************************************************** + * Helper inline functions to write integers to an i/o stream + *****************************************************************************/ + +/** Writes an unsigned 8 bits integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_uint8(VC_CONTAINER_IO_T *io, uint8_t value) +{ + size_t ret = vc_container_io_write(io, &value, 1); + return ret == 1 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes a FOURCC to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The FOURCC to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_fourcc(VC_CONTAINER_IO_T *io, VC_CONTAINER_FOURCC_T value) +{ + size_t ret = vc_container_io_write(io, (uint8_t *)&value, 4); + return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 16 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint16(VC_CONTAINER_IO_T *io, uint16_t value) +{ + uint8_t bytes[2] = {(uint8_t)(value >> 8), (uint8_t)value}; + size_t ret = vc_container_io_write(io, bytes, 2); + return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 24 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint24(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[3] = {(uint8_t)(value >> 16), (uint8_t)(value >> 8), (uint8_t)value}; + size_t ret = vc_container_io_write(io, bytes, 3); + return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 32 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint32(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[4] = {value >> 24, value >> 16, value >> 8, value}; + size_t ret = vc_container_io_write(io, bytes, 4); + return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 64 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint64(VC_CONTAINER_IO_T *io, uint64_t value) +{ + uint8_t bytes[8] = + { + (uint8_t)(value >> 56), + (uint8_t)(value >> 48), + (uint8_t)(value >> 40), + (uint8_t)(value >> 32), + (uint8_t)(value >> 24), + (uint8_t)(value >> 16), + (uint8_t)(value >> 8), + (uint8_t) value + }; + size_t ret = vc_container_io_write(io, bytes, 8); + return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 16 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint16(VC_CONTAINER_IO_T *io, uint16_t value) +{ + uint8_t bytes[2] = {(uint8_t)value, (uint8_t)(value >> 8)}; + size_t ret = vc_container_io_write(io, bytes, 2); + return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 24 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint24(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[3] = {value, value >> 8, value >> 16}; + size_t ret = vc_container_io_write(io, bytes, 3); + return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 32 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint32(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[4] = {value, value >> 8, value >> 16, value >> 24}; + size_t ret = vc_container_io_write(io, bytes, 4); + return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 64 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint64(VC_CONTAINER_IO_T *io, uint64_t value) +{ + uint8_t bytes[8] = + { + (uint8_t) value, + (uint8_t)(value >> 8), + (uint8_t)(value >> 16), + (uint8_t)(value >> 24), + (uint8_t)(value >> 32), + (uint8_t)(value >> 40), + (uint8_t)(value >> 48), + (uint8_t)(value >> 56) + }; + size_t ret = vc_container_io_write(io, bytes, 8); + return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/***************************************************************************** + * Helper macros for accessing the i/o stream. These will also call the right + * functions depending on the endianness defined. + *****************************************************************************/ + +/** Macro which returns the current position within the stream */ +#define STREAM_POSITION(ctx) (ctx)->priv->io->offset +/** Macro which returns true if the end of stream has been reached */ +#define STREAM_EOS(ctx) ((ctx)->priv->io->status == VC_CONTAINER_ERROR_EOS) +/** Macro which returns the status of the stream */ +#define STREAM_STATUS(ctx) (ctx)->priv->io->status +/** Macro which returns true if an error other than end of stream has occurred */ +#define STREAM_ERROR(ctx) ((ctx)->priv->io->status && (ctx)->priv->io->status != VC_CONTAINER_ERROR_EOS) +/** Macro which returns true if we can seek into the stream */ +#define STREAM_SEEKABLE(ctx) (!((ctx)->priv->io->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)) + +#define PEEK_BYTES(ctx, buffer, size) vc_container_io_peek((ctx)->priv->io, buffer, (size_t)(size)) +#define READ_BYTES(ctx, buffer, size) vc_container_io_read((ctx)->priv->io, buffer, (size_t)(size)) +#define SKIP_BYTES(ctx, size) vc_container_io_skip((ctx)->priv->io, (size_t)(size)) +#define SEEK(ctx, off) vc_container_io_seek((ctx)->priv->io, (int64_t)(off)) +#define CACHE_BYTES(ctx, size) vc_container_io_cache((ctx)->priv->io, (size_t)(size)) + +#define _SKIP_GUID(ctx) vc_container_io_skip((ctx)->priv->io, 16) +#define _SKIP_U8(ctx) (vc_container_io_skip((ctx)->priv->io, 1) != 1) +#define _SKIP_U16(ctx) (vc_container_io_skip((ctx)->priv->io, 2) != 2) +#define _SKIP_U24(ctx) (vc_container_io_skip((ctx)->priv->io, 3) != 3) +#define _SKIP_U32(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4) +#define _SKIP_U64(ctx) (vc_container_io_skip((ctx)->priv->io, 8) != 8) +#define _SKIP_FOURCC(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4) + +#define _READ_GUID(ctx, buffer) vc_container_io_read((ctx)->priv->io, buffer, 16) +#define _READ_U8(ctx) vc_container_io_read_uint8((ctx)->priv->io) +#define _READ_FOURCC(ctx) vc_container_io_read_fourcc((ctx)->priv->io) +#define PEEK_GUID(ctx, buffer) vc_container_io_peek((ctx)->priv->io, buffer, 16) +#define PEEK_U8(ctx) vc_container_io_peek_uint8((ctx)->priv->io) +#ifdef CONTAINER_IS_BIG_ENDIAN +# define _READ_U16(ctx) vc_container_io_read_be_uint16((ctx)->priv->io) +# define _READ_U24(ctx) vc_container_io_read_be_uint24((ctx)->priv->io) +# define _READ_U32(ctx) vc_container_io_read_be_uint32((ctx)->priv->io) +# define _READ_U40(ctx) vc_container_io_read_be_uint40((ctx)->priv->io) +# define _READ_U48(ctx) vc_container_io_read_be_uint48((ctx)->priv->io) +# define _READ_U56(ctx) vc_container_io_read_be_uint56((ctx)->priv->io) +# define _READ_U64(ctx) vc_container_io_read_be_uint64((ctx)->priv->io) +# define PEEK_U16(ctx) vc_container_io_peek_be_uint16((ctx)->priv->io) +# define PEEK_U24(ctx) vc_container_io_peek_be_uint24((ctx)->priv->io) +# define PEEK_U32(ctx) vc_container_io_peek_be_uint32((ctx)->priv->io) +# define PEEK_U64(ctx) vc_container_io_peek_be_uint64((ctx)->priv->io) +#else +# define _READ_U16(ctx) vc_container_io_read_le_uint16((ctx)->priv->io) +# define _READ_U24(ctx) vc_container_io_read_le_uint24((ctx)->priv->io) +# define _READ_U32(ctx) vc_container_io_read_le_uint32((ctx)->priv->io) +# define _READ_U40(ctx) vc_container_io_read_le_uint40((ctx)->priv->io) +# define _READ_U48(ctx) vc_container_io_read_le_uint48((ctx)->priv->io) +# define _READ_U56(ctx) vc_container_io_read_le_uint56((ctx)->priv->io) +# define _READ_U64(ctx) vc_container_io_read_le_uint64((ctx)->priv->io) +# define PEEK_U16(ctx) vc_container_io_peek_le_uint16((ctx)->priv->io) +# define PEEK_U24(ctx) vc_container_io_peek_le_uint24((ctx)->priv->io) +# define PEEK_U32(ctx) vc_container_io_peek_le_uint32((ctx)->priv->io) +# define PEEK_U64(ctx) vc_container_io_peek_le_uint64((ctx)->priv->io) +#endif + +#define WRITE_BYTES(ctx, buffer, size) vc_container_io_write((ctx)->priv->io, buffer, (size_t)(size)) +#define _WRITE_GUID(ctx, buffer) vc_container_io_write((ctx)->priv->io, buffer, 16) +#define _WRITE_U8(ctx, v) vc_container_io_write_uint8((ctx)->priv->io, v) +#define _WRITE_FOURCC(ctx, v) vc_container_io_write_fourcc((ctx)->priv->io, v) +#ifdef CONTAINER_IS_BIG_ENDIAN +# define _WRITE_U16(ctx, v) vc_container_io_write_be_uint16((ctx)->priv->io, v) +# define _WRITE_U24(ctx, v) vc_container_io_write_be_uint24((ctx)->priv->io, v) +# define _WRITE_U32(ctx, v) vc_container_io_write_be_uint32((ctx)->priv->io, v) +# define _WRITE_U64(ctx, v) vc_container_io_write_be_uint64((ctx)->priv->io, v) +#else +# define _WRITE_U16(ctx, v) vc_container_io_write_le_uint16((ctx)->priv->io, v) +# define _WRITE_U24(ctx, v) vc_container_io_write_le_uint24((ctx)->priv->io, v) +# define _WRITE_U32(ctx, v) vc_container_io_write_le_uint32((ctx)->priv->io, v) +# define _WRITE_U64(ctx, v) vc_container_io_write_le_uint64((ctx)->priv->io, v) +#endif + +#ifndef CONTAINER_HELPER_LOG_INDENT +# define CONTAINER_HELPER_LOG_INDENT(a) 0 +#endif + +#ifdef CONTAINER_IS_BIG_ENDIAN +# define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_BE +# define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_BE +#else +# define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_LE +# define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_LE +#endif + +#ifndef ENABLE_CONTAINERS_LOG_FORMAT +#define SKIP_GUID(ctx,n) _SKIP_GUID(ctx) +#define SKIP_U8(ctx,n) _SKIP_U8(ctx) +#define SKIP_U16(ctx,n) _SKIP_U16(ctx) +#define SKIP_U24(ctx,n) _SKIP_U24(ctx) +#define SKIP_U32(ctx,n) _SKIP_U32(ctx) +#define SKIP_U64(ctx,n) _SKIP_U64(ctx) +#define SKIP_FOURCC(ctx,n) _SKIP_FOURCC(ctx) +#define READ_GUID(ctx,buffer,n) _READ_GUID(ctx,(uint8_t *)buffer) +#define READ_U8(ctx,n) _READ_U8(ctx) +#define READ_U16(ctx,n) _READ_U16(ctx) +#define READ_U24(ctx,n) _READ_U24(ctx) +#define READ_U32(ctx,n) _READ_U32(ctx) +#define READ_U40(ctx,n) _READ_U40(ctx) +#define READ_U48(ctx,n) _READ_U48(ctx) +#define READ_U56(ctx,n) _READ_U56(ctx) +#define READ_U64(ctx,n) _READ_U64(ctx) +#define READ_FOURCC(ctx,n) _READ_FOURCC(ctx) +#define READ_STRING(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz) +#define READ_STRING_UTF16(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz) +#define SKIP_STRING(ctx,sz,n) SKIP_BYTES(ctx,sz) +#define SKIP_STRING_UTF16(ctx,sz,n) SKIP_BYTES(ctx,sz) +#else +#define SKIP_GUID(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U8(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U16(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U24(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U32(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define READ_GUID(ctx,buffer,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U8(ctx,n) (uint8_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U16(ctx,n) (uint16_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U24(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U32(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U40(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 5, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U48(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 6, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U56(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 7, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_STRING_UTF16(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_STRING(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define SKIP_STRING_UTF16(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_STRING(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#endif + +#ifndef ENABLE_CONTAINERS_LOG_FORMAT +#define WRITE_GUID(ctx,buffer,n) _WRITE_GUID(ctx,(const uint8_t *)buffer) +#define WRITE_U8(ctx,v,n) _WRITE_U8(ctx,(uint8_t)(v)) +#define WRITE_FOURCC(ctx,v,n) _WRITE_FOURCC(ctx,(uint32_t)(v)) +#define WRITE_U16(ctx,v,n) _WRITE_U16(ctx,(uint16_t)(v)) +#define WRITE_U24(ctx,v,n) _WRITE_U24(ctx,(uint32_t)(v)) +#define WRITE_U32(ctx,v,n) _WRITE_U32(ctx,(uint32_t)(v)) +#define WRITE_U64(ctx,v,n) _WRITE_U64(ctx,(uint64_t)(v)) +#define WRITE_STRING(ctx,buffer,size,n) WRITE_BYTES(ctx, buffer, size) +#else +#define WRITE_GUID(ctx,buffer,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : 16) +#define WRITE_U8(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_FOURCC(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U16(ctx,v,n) (uint16_t)vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U24(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U32(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U64(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_STRING(ctx,buffer,size,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_STRING, size, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : size) +#endif + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +#define LOG_FORMAT(ctx, ...) do { if((ctx)->priv->io->module) vc_container_helper_format_debug(ctx, CONTAINER_HELPER_LOG_INDENT(ctx), __VA_ARGS__); } while(0) +#else +#define LOG_FORMAT(ctx, ...) do {} while (0) +#endif + +#define LOG_FORMAT_TYPE_UINT_LE 0 +#define LOG_FORMAT_TYPE_UINT_BE 1 +#define LOG_FORMAT_TYPE_STRING 2 +#define LOG_FORMAT_TYPE_STRING_UTF16_LE 3 +#define LOG_FORMAT_TYPE_STRING_UTF16_BE 4 +#define LOG_FORMAT_TYPE_FOURCC 5 +#define LOG_FORMAT_TYPE_GUID 6 +#define LOG_FORMAT_TYPE_HEX 0x100 + +uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent); +uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, + uint8_t *buffer, int indent, int b_skip); +VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, + uint64_t value, const uint8_t *buffer, int indent, int silent); +void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...); + +#endif /* VC_CONTAINERS_IO_HELPERS_H */ +/* End of file */ +/*-----------------------------------------------------------------------------*/ diff --git a/containers/core/containers_list.c b/containers/core/containers_list.c new file mode 100755 index 0000000..333f052 --- /dev/null +++ b/containers/core/containers_list.c @@ -0,0 +1,221 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/core/containers_common.h" +#include "containers/core/containers_list.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Find an entry in the list, or the insertion point. + * Uses binary sub-division to find the search item. If index is not NULL, the + * index of the matching entry, or the point at which to insert if not found, is + * written to that address. + * + * \param list The list to be searched. + * \param entry The entry for which to search. + * \param index Set to index of match, or insertion point if not found. May be NULL. + * \return True if a match was found, false if not. */ +static bool vc_containers_list_find_index(const VC_CONTAINERS_LIST_T *list, + const void *entry, + uint32_t *index) +{ + const char *entries = (const char *)list->entries; + size_t entry_size = list->entry_size; + VC_CONTAINERS_LIST_COMPARATOR_T comparator = list->comparator; + uint32_t start = 0, end = list->size; + uint32_t mid = end >> 1; + bool match = false; + + while (mid < end) + { + int comparison = comparator(entry, entries + mid * entry_size); + + if (comparison < 0) + end = mid; + else if (comparison > 0) + start = mid + 1; + else { + match = true; + break; + } + + mid = (start + end) >> 1; + } + + if (index) *index = mid; + return match; +} + +/****************************************************************************** +Functions exported as part of the API +******************************************************************************/ + +/*****************************************************************************/ +VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, + size_t entry_size, + VC_CONTAINERS_LIST_COMPARATOR_T comparator) +{ + VC_CONTAINERS_LIST_T *list; + + list = (VC_CONTAINERS_LIST_T *)malloc(sizeof(VC_CONTAINERS_LIST_T)); + if (!list) + return NULL; + + /* Ensure non-zero capacity, as that signifies a read-only list */ + if (!capacity) capacity = 1; + + list->entries = malloc(capacity * entry_size); + if (!list->entries) + { + free(list); + return NULL; + } + + list->size = 0; + list->capacity = capacity; + list->entry_size = entry_size; + list->comparator = comparator; + + return list; +} + +/*****************************************************************************/ +void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list) +{ + /* Avoid trying to destroy read-only lists */ + if (list && list->capacity) + { + if (list->entries) + free(list->entries); + free(list); + } +} + +/*****************************************************************************/ +void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list) +{ + /* Avoid trying to reset read-only lists */ + if (list && list->capacity) + list->size = 0; +} + +/*****************************************************************************/ +bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, + void *new_entry, + bool allow_duplicates) +{ + uint32_t insert_idx; + char *insert_ptr; + size_t entry_size; + bool match; + + if (!list || !list->capacity) return false; + + entry_size = list->entry_size; + match = vc_containers_list_find_index(list, new_entry, &insert_idx); + insert_ptr = (char *)list->entries + entry_size * insert_idx; + + if (!match || allow_duplicates) + { + /* Ensure there is space for the new entry */ + if (list->size == list->capacity) + { + void *new_entries = realloc(list->entries, (list->size + 1) * entry_size); + + if (!new_entries) + return false; + list->entries = new_entries; + list->capacity++; + } + + /* Move up anything above the insertion point */ + if (insert_idx < list->size) + memmove(insert_ptr + entry_size, insert_ptr, (list->size - insert_idx) * entry_size); + + list->size++; + } + + /* Copy in the new entry (overwriting the old one if necessary) */ + memcpy(insert_ptr, new_entry, list->entry_size); + + return true; +} + +/*****************************************************************************/ +bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, + void *entry) +{ + uint32_t index; + size_t entry_size; + + if (!vc_containers_list_find_index(list, entry, &index)) + return false; + + entry_size = list->entry_size; + memcpy(entry, (const char *)list->entries + entry_size * index, entry_size); + + return true; +} + +/*****************************************************************************/ +void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list) +{ + uint32_t ii, entry_size; + const uint8_t *entry_ptr; + + vc_container_assert(list); + vc_container_assert(!list->capacity || list->size <= list->capacity); + vc_container_assert(list->entry_size); + vc_container_assert(list->comparator); + vc_container_assert(list->entries); + + /* Check all entries are in sorted order */ + entry_ptr = (const uint8_t *)list->entries; + entry_size = list->entry_size; + for (ii = 1; ii < list->size; ii++) + { + vc_container_assert(list->comparator(entry_ptr, entry_ptr + entry_size) <= 0); + entry_ptr += entry_size; + } +} diff --git a/containers/core/containers_list.h b/containers/core/containers_list.h new file mode 100755 index 0000000..5b08997 --- /dev/null +++ b/containers/core/containers_list.h @@ -0,0 +1,102 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VC_CONTAINERS_LIST_H_ +#define _VC_CONTAINERS_LIST_H_ + +#include "containers/containers.h" + +/** List entry comparison prototype. + * Returns zero if items at a and b match, positive if a is "bigger" than b and + * negative if a is "smaller" than b. */ +typedef int (*VC_CONTAINERS_LIST_COMPARATOR_T)(const void *a, const void *b); + +/** Sorted list type. + * Storage type providing efficient insertion and search via binary sub-division. */ +typedef struct vc_containers_list_tag +{ + uint32_t size; /**< Number of defined entries in list */ + uint32_t capacity; /**< Capacity of list, in entries, or zero for read-only */ + size_t entry_size; /**< Size of one entry, in bytes */ + VC_CONTAINERS_LIST_COMPARATOR_T comparator; /**< Entry comparison function */ + void *entries; /**< Pointer to array of entries */ +} VC_CONTAINERS_LIST_T; + +/** Macro to generate a static, read-only list from an array and comparator */ +#define VC_CONTAINERS_STATIC_LIST(L, A, C) static VC_CONTAINERS_LIST_T L = { countof(A), 0, sizeof(*(A)), (VC_CONTAINERS_LIST_COMPARATOR_T)(C), A } + + +/** Create an empty list. + * The list is created based on the details provided, minimum capacity one entry. + * + * \param The initial capacity in entries. + * \param entry_size The size of each entry, in bytes. + * \param comparator A function for comparing two entries. + * \return The new list or NULL. */ +VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, size_t entry_size, VC_CONTAINERS_LIST_COMPARATOR_T comparator); + +/** Destroy a list. + * Has no effect on a static list. + * + * \param list The list to be destroyed. */ +void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list); + +/** Reset a list to be empty. + * Has no effect on a static list. + * + * \param list The list to be reset. */ +void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list); + +/** Insert an entry into the list. + * + * \param list The list. + * \param new_entry The new entry to be inserted. + * \param allow_duplicates Determines whether to insert or overwrite if there + * is an existing matching entry. + * \return True if the entry has successfully been inserted, false if the list + * needed to be enlarged and the memory allocation failed. */ +bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, void *new_entry, bool allow_duplicates); + +/** Find an entry in the list and fill in the result. + * Searches for an entry in the list using the comparator and if found + * overwrites the one passed in with the one found. + * + * \param list The list to search. + * \param entry An entry with enough defined to find it in the list, filled in + * with the rest if found. + * \return True if found, false if not. */ +bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, void *entry); + +/** Validates a list pointer. + * Fields and contents of a list are checked and asserted to be correct. With a + * large list this may be slow, so it is recommended only to call this in debug + * builds. + * + * \param list The list to be validated. */ +void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list); + +#endif /* _VC_CONTAINERS_LIST_H_ */ diff --git a/containers/core/containers_loader.c b/containers/core/containers_loader.c new file mode 100755 index 0000000..b597301 --- /dev/null +++ b/containers/core/containers_loader.c @@ -0,0 +1,436 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_loader.h" + +#if !defined(ENABLE_CONTAINERS_STANDALONE) + #include "vcos_dlfcn.h" + #define DL_SUFFIX VCOS_SO_EXT + #ifndef DL_PATH_PREFIX + #define DL_PATH_PREFIX "" + #endif +#endif + +/****************************************************************************** +Type definitions. +******************************************************************************/ + +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_READER_OPEN_FUNC_T)(VC_CONTAINER_T *); +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_WRITER_OPEN_FUNC_T)(VC_CONTAINER_T *); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ + +static void reset_context(VC_CONTAINER_T *p_ctx); +static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read); +static void unload_library(void *handle); +static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name); +static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name); +static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name); +static const char* container_for_fileext(const char *fileext); + +/******************************************************************************** + List of supported containers + ********************************************************************************/ + +static const char *readers[] = +{"mp4", "asf", "avi", "mkv", "wav", "flv", "simple", "rawvideo", "mpga", "ps", "rtp", "rtsp", "rcv", "rv9", "qsynth", "binary", 0}; +static const char *writers[] = +{"mp4", "asf", "avi", "binary", "simple", "rawvideo", 0}; +static const char *metadata_readers[] = +{"id3", 0}; + +#if defined(ENABLE_CONTAINERS_STANDALONE) +VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); + +VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); + +static struct +{ + const char *name; + VC_CONTAINER_READER_OPEN_FUNC_T func; +} reader_entry_points[] = +{ + {"asf", &asf_reader_open}, + {"avi", &avi_reader_open}, + {"mpga", &mpga_reader_open}, + {"mkv", &mkv_reader_open}, + {"wav", &wav_reader_open}, + {"mp4", &mp4_reader_open}, + {"flv", &flv_reader_open}, + {"ps", &ps_reader_open}, + {"binary", &binary_reader_open}, + {"rtp", &rtp_reader_open}, + {"rtsp", &rtsp_reader_open}, + {"rcv", &rcv_reader_open}, + {"rv9", &rv9_reader_open}, + {"qsynth", &qsynth_reader_open}, + {"simple", &simple_reader_open}, + {"rawvideo", &rawvideo_reader_open}, + {0, 0} +}; + +static struct +{ + const char *name; + VC_CONTAINER_READER_OPEN_FUNC_T func; +} metadata_reader_entry_points[] = +{ + {"id3", &id3_metadata_reader_open}, + {0, 0} +}; + +static struct +{ + const char *name; + VC_CONTAINER_WRITER_OPEN_FUNC_T func; +} writer_entry_points[] = +{ + {"avi", &avi_writer_open}, + {"mp4", &mp4_writer_open}, + {"binary", &binary_writer_open}, + {"simple", &simple_writer_open}, + {"rawvideo", &rawvideo_writer_open}, + {0, 0} +}; +#endif /* defined(ENABLE_CONTAINERS_STANDALONE) */ + +/** Table describing the mapping between file extensions and container name. + This is only used as optimisation to decide which container to try first. + Entries where the file extension and container have the same name can be omitted. */ +static const struct { + const char *extension; + const char *container; +} extension_container_mapping[] = +{ + { "wma", "asf" }, + { "wmv", "asf" }, + { "mov", "mp4" }, + { "3gp", "mp4" }, + { "mp2", "mpga" }, + { "mp3", "mpga" }, + { "webm", "mkv" }, + { "mid", "qsynth" }, + { "mld", "qsynth" }, + { "mmf", "qsynth" }, + { 0, 0 } +}; + +/******************************************************************************** + Public functions + ********************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext) +{ + const char *name; + void *handle = NULL; + VC_CONTAINER_READER_OPEN_FUNC_T func; + VC_CONTAINER_STATUS_T status; + unsigned int i; + int64_t offset; + + vc_container_assert(p_ctx && !p_ctx->priv->module_handle); + + /* FIXME: the missing part here is code that reads a configuration or + searches the filesystem for container libraries. Instead, we currently + rely on static arrays i.e. 'readers', 'writers', etc. */ + + /* Before trying proper container readers, iterate through metadata + readers to parse tags concatenated to start/end of stream */ + for(i = 0; metadata_readers[i]; i++) + { + if ((func = load_metadata_reader(&handle, metadata_readers[i])) != NULL) + { + status = (*func)(p_ctx); + if(!status && p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); + reset_context(p_ctx); + unload_library(handle); + if(status == VC_CONTAINER_SUCCESS) break; + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + } + + /* Store the current position, in case any containers don't leave the stream + at the start, and the IO layer can cope with the seek */ + offset = p_ctx->priv->io->offset; + + /* Now move to containers, try to find a readers using the file extension to name + mapping first */ + if (fileext && (name = container_for_fileext(fileext)) != NULL && (func = load_reader(&handle, name)) != NULL) + { + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + + /* If there was no suitable mapping, iterate through all readers. */ + for(i = 0; readers[i]; i++) + { + if ((func = load_reader(&handle, readers[i])) != NULL) + { + if(vc_container_io_seek(p_ctx->priv->io, offset) != VC_CONTAINER_SUCCESS) + { + unload_library(handle); + goto error; + } + + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + reset_context(p_ctx); + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + } + + error: + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + success: + p_ctx->priv->module_handle = handle; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext) +{ + const char *name; + void *handle = NULL; + VC_CONTAINER_WRITER_OPEN_FUNC_T func; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; + unsigned int i; + + vc_container_assert(p_ctx && !p_ctx->priv->module_handle); + + /* Do we have a container mapping for this file extension? */ + if ((name = container_for_fileext(fileext)) != NULL && (func = load_writer(&handle, name)) != NULL) + { + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + + /* If there was no suitable mapping, iterate through all writers. */ + for(i = 0; writers[i]; i++) + { + if ((func = load_writer(&handle, writers[i])) != NULL) + { + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + } + + error: + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + success: + p_ctx->priv->module_handle = handle; + return status; +} + +/*****************************************************************************/ +void vc_container_unload(VC_CONTAINER_T *p_ctx) +{ + if (p_ctx->priv->module_handle) + { + unload_library(p_ctx->priv->module_handle); + p_ctx->priv->module_handle = NULL; + } +} + +/****************************************************************************** +Local Functions +******************************************************************************/ +static void reset_context(VC_CONTAINER_T *p_ctx) +{ + vc_container_assert(p_ctx); + + p_ctx->capabilities = 0; + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + p_ctx->drm = NULL; + p_ctx->priv->module = NULL; + p_ctx->priv->pf_close = NULL; + p_ctx->priv->pf_read = NULL; + p_ctx->priv->pf_write = NULL; + p_ctx->priv->pf_seek = NULL; + p_ctx->priv->pf_control = NULL; + p_ctx->priv->tmp_io = NULL; +} + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name) +{ + return load_library(handle, name, NULL, 1); +} + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name) +{ + return load_library(handle, name, NULL, 0); +} + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name) +{ + #define DL_PREFIX_METADATA "metadata_" + return load_library(handle, name, DL_PREFIX_METADATA, 1); +} + +#if !defined(ENABLE_CONTAINERS_STANDALONE) + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) +{ + #define DL_PREFIX_RD "reader_" + #define DL_PREFIX_WR "writer_" + const char *entrypt_read = {"reader_open"}; + const char *entrypt_write = {"writer_open"}; + char *dl_name, *entrypt_name; + void *dl_handle; + VC_CONTAINER_READER_OPEN_FUNC_T func = NULL; + unsigned dl_size, ep_size, name_len = strlen(name) + (ext ? strlen(ext) : 0); + + vc_container_assert(read == 0 || read == 1); + + dl_size = strlen(DL_PATH_PREFIX) + MAX(strlen(DL_PREFIX_RD), strlen(DL_PREFIX_WR)) + name_len + strlen(DL_SUFFIX) + 1; + if ((dl_name = malloc(dl_size)) == NULL) + return NULL; + + ep_size = name_len + 1 + MAX(strlen(entrypt_read), strlen(entrypt_write)) + 1; + if ((entrypt_name = malloc(ep_size)) == NULL) + { + free(dl_name); + return NULL; + } + + snprintf(dl_name, dl_size, "%s%s%s%s%s", DL_PATH_PREFIX, read ? DL_PREFIX_RD : DL_PREFIX_WR, ext ? ext : "", name, DL_SUFFIX); + snprintf(entrypt_name, ep_size, "%s_%s%s", name, ext ? ext : "", read ? entrypt_read : entrypt_write); + + if ( (dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL ) + { + /* Try generic entrypoint name before the mangled, full name */ + func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, read ? entrypt_read : entrypt_write); +#if !defined(__VIDEOCORE__) /* The following would be pointless on MW/VideoCore */ + if (!func) func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); +#endif + /* Only return handle if symbol found */ + if (func) + *handle = dl_handle; + else + vcos_dlclose(dl_handle); + } + + free(entrypt_name); + free(dl_name); + return func; +} + +/*****************************************************************************/ +static void unload_library(void *handle) +{ + vcos_dlclose(handle); +} + +#else /* !defined(ENABLE_CONTAINERS_STANDALONE) */ + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) +{ + int i; + VC_CONTAINER_PARAM_UNUSED(handle); + VC_CONTAINER_PARAM_UNUSED(ext); + + if (read) + { + for (i = 0; reader_entry_points[i].name; i++) + if (!strcasecmp(reader_entry_points[i].name, name)) + return reader_entry_points[i].func; + + for (i = 0; metadata_reader_entry_points[i].name; i++) + if (!strcasecmp(metadata_reader_entry_points[i].name, name)) + return metadata_reader_entry_points[i].func; + } + else + { + for (i = 0; writer_entry_points[i].name; i++) + if (!strcasecmp(writer_entry_points[i].name, name)) + return writer_entry_points[i].func; + } + + return NULL; +} + +/*****************************************************************************/ +static void unload_library(void *handle) +{ + (void)handle; +} + +#endif /* !defined(ENABLE_CONTAINERS_STANDALONE) */ + +/*****************************************************************************/ +static const char* container_for_fileext(const char *fileext) +{ + int i; + + for( i = 0; fileext && extension_container_mapping[i].extension; i++ ) + { + if (!strcasecmp( fileext, extension_container_mapping[i].extension )) + return extension_container_mapping[i].container; + } + + return fileext; +} diff --git a/containers/core/containers_loader.h b/containers/core/containers_loader.h new file mode 100755 index 0000000..946eaf6 --- /dev/null +++ b/containers/core/containers_loader.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_CONTAINERS_LOADER_H +#define VC_CONTAINERS_LOADER_H + +/** Find and attempt to load & open reader, 'fileext' is a hint that can be used + to speed up loading. */ +VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext); + +/** Find and attempt to load & open writer, 'fileext' is a hint used to help in + selecting the appropriate container format. */ +VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext); + +void vc_container_unload(VC_CONTAINER_T *p_ctx); + +#endif /* VC_CONTAINERS_LOADER_H */ diff --git a/containers/core/containers_logging.c b/containers/core/containers_logging.c new file mode 100755 index 0000000..5a739a3 --- /dev/null +++ b/containers/core/containers_logging.c @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include "containers/containers.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_logging.h" + +#ifndef ENABLE_CONTAINERS_STANDALONE +# include "vcos.h" +#endif + +#ifdef __ANDROID__ +#define LOG_TAG "ContainersCore" +#include +#endif + +/* Default verbosity that will be inherited by containers */ +static uint32_t default_verbosity_mask = VC_CONTAINER_LOG_ALL; + +/* By default log everything that's not associated with a container context */ +static uint32_t verbosity_mask = VC_CONTAINER_LOG_ALL; + +void vc_container_log_set_default_verbosity(uint32_t mask) +{ + default_verbosity_mask = mask; +} + +uint32_t vc_container_log_get_default_verbosity(void) +{ + return default_verbosity_mask; +} + +void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask) +{ + if(!ctx) verbosity_mask = mask; + else ctx->priv->verbosity = mask; +} + +void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...) +{ + uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask; + va_list args; + + // Optimise out the call to vc_container_log_vargs etc. when it won't do anything. + if(!(type & verbosity)) return; + + va_start( args, format ); + vc_container_log_vargs(ctx, type, format, args); + va_end( args ); +} + +void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args) +{ + uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask; + + // If the verbosity is such that the type doesn't need logging quit now. + if(!(type & verbosity)) return; + +#ifdef __ANDROID__ + { + // Default to Android's "verbose" level (doesn't usually come out) + android_LogPriority logLevel = ANDROID_LOG_VERBOSE; + + // Where type suggest a higher level is required update logLevel. + // (Usually type contains only 1 bit as set by the LOG_DEBUG, LOG_ERROR or LOG_INFO macros) + if (type & VC_CONTAINER_LOG_ERROR) + logLevel = ANDROID_LOG_ERROR; + else if (type & VC_CONTAINER_LOG_INFO) + logLevel = ANDROID_LOG_INFO; + else if (type & VC_CONTAINER_LOG_DEBUG) + logLevel = ANDROID_LOG_DEBUG; + + // Actually put the message out. + LOG_PRI_VA(logLevel, LOG_TAG, format, args); + } +#else +#ifndef ENABLE_CONTAINERS_STANDALONE + vcos_vlog(format, args); +#else + vprintf(format, args); printf("\n"); + fflush(0); +#endif +#endif +} diff --git a/containers/core/containers_logging.h b/containers/core/containers_logging.h new file mode 100755 index 0000000..a7c2bd9 --- /dev/null +++ b/containers/core/containers_logging.h @@ -0,0 +1,77 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_LOGGING_H +#define VC_CONTAINERS_LOGGING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file containers_logging.h + * Logging API used by container readers and writers + */ + +typedef enum { + VC_CONTAINER_LOG_ERROR = 0x01, + VC_CONTAINER_LOG_INFO = 0x02, + VC_CONTAINER_LOG_DEBUG = 0x04, + VC_CONTAINER_LOG_FORMAT = 0x08, + VC_CONTAINER_LOG_ALL = 0xFF +} VC_CONTAINER_LOG_TYPE_T; + +void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...); +void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args); +void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask); +void vc_container_log_set_default_verbosity(uint32_t mask); +uint32_t vc_container_log_get_default_verbosity(void); + +#define ENABLE_CONTAINER_LOG_ERROR +#define ENABLE_CONTAINER_LOG_INFO + +#ifdef ENABLE_CONTAINER_LOG_DEBUG +# define LOG_DEBUG(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_DEBUG, __VA_ARGS__) +#else +# define LOG_DEBUG(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) +#endif + +#ifdef ENABLE_CONTAINER_LOG_ERROR +# define LOG_ERROR(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_ERROR, __VA_ARGS__) +#else +# define LOG_ERROR(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) +#endif + +#ifdef ENABLE_CONTAINER_LOG_INFO +# define LOG_INFO(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_INFO, __VA_ARGS__) +#else +# define LOG_INFO(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_LOGGING_H */ diff --git a/containers/core/containers_private.h b/containers/core/containers_private.h new file mode 100755 index 0000000..8c379c6 --- /dev/null +++ b/containers/core/containers_private.h @@ -0,0 +1,183 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_PRIVATE_H +#define VC_CONTAINERS_PRIVATE_H + +/** \file containers_private.h + * Private interface for container readers and writers + */ + +#include +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_filters.h" +#include "containers/packetizers.h" +#include "containers/core/containers_uri.h" + +#define URI_MAX_LEN 256 + +/** \defgroup VcContainerModuleApi Container Module API + * Private interface for modules implementing container readers and writers */ +/* @{ */ + +/** Track context private to the container reader / writer instance. This private context is used to + * store data which shouldn't be exported by the public API. */ +typedef struct VC_CONTAINER_TRACK_PRIVATE_T +{ + /** Pointer to the private data of the container module in use */ + struct VC_CONTAINER_TRACK_MODULE_T *module; + + /** Pointer to the allocated buffer for the track extradata */ + uint8_t *extradata; + /** Size of the allocated buffer for the track extradata */ + uint32_t extradata_size; + + /** Pointer to the allocated buffer for the track DRM data*/ + uint8_t *drmdata; + /** Size of the allocated buffer for the track DRM data */ + uint32_t drmdata_size; + + /** Packetizer used by this track */ + VC_PACKETIZER_T *packetizer; + +} VC_CONTAINER_TRACK_PRIVATE_T; + +/** Context private to the container reader / writer instance. This private context is used to + * store data which shouldn't be exported by the public API. */ +typedef struct VC_CONTAINER_PRIVATE_T +{ + /** Pointer to the container i/o instance used to read / write to the container */ + struct VC_CONTAINER_IO_T *io; + /** Pointer to the private data of the container module in use */ + struct VC_CONTAINER_MODULE_T *module; + + /** Reads a data packet from a container reader. + * By default, the reader will read whatever packet comes next in the container and update the + * given \ref VC_CONTAINER_PACKET_T structure with this packet's information. + * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n + * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the + * following packet but not its actual data. The data can be retreived later by issuing another + * read request. + * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the + * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting + * to reading the packet which comes next in the container. + * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case + * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure + * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given. + * A combination of all these flags can be used. + * + * \param context Pointer to the context of the reader to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * This needs to be partially filled before the call (buffer, buffer_size) + * \param flags Flags controlling the read operation + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_read)( VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags ); + + /** Writes a data packet to a container writer. + * + * \param context Pointer to the context of the writer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_write)( struct VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet ); + + /** Seek into a container reader. + * + * \param context Pointer to the context of the reader to use + * \param offset Offset to seek to. Used as an input as well as output value. + * \param mode Seeking mode requested. + * \param flags Flags affecting the seeking operation. + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_seek)( VC_CONTAINER_T *context, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags); + + /** Extensible control function for container readers and writers. + * This function takes a variable number of arguments which will depend on the specific operation. + * + * \param context Pointer to the VC_CONTAINER_T context to use + * \param operation The requested operation + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_control)( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, va_list args ); + + /** Closes a container reader / writer module. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_close)( struct VC_CONTAINER_T *context ); + + /** Pointer to container filter instance used for DRM */ + struct VC_CONTAINER_FILTER_T *drm_filter; + + /** Pointer to the container module code and symbols*/ + void *module_handle; + + /** Maximum size of a stream that is being written. + * This is set by the client using the control mechanism */ + int64_t max_size; + + /** Pointer to the temp i/o instance used to write temporary data */ + struct VC_CONTAINER_IO_T *tmp_io; + + /** Current status of the container (only used for writers to prevent trying to write + * more data if one of the writes failed) */ + VC_CONTAINER_STATUS_T status; + + /** Logging verbosity */ + uint32_t verbosity; + + /** Uniform Resource Identifier */ + struct VC_URI_PARTS_T *uri; + + /** Flag specifying whether one of the tracks is being packetized */ + bool packetizing; + + /** Temporary packet structure used to feed data to the packetizer */ + VC_CONTAINER_PACKET_T packetizer_packet; + + /** Temporary buffer used by the packetizer */ + uint8_t *packetizer_buffer; + +} VC_CONTAINER_PRIVATE_T; + +/* Internal functions */ +VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size ); +void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *track ); +VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size ); +VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int size ); + +/* @} */ + +#endif /* VC_CONTAINERS_PRIVATE_H */ diff --git a/containers/core/containers_time.h b/containers/core/containers_time.h new file mode 100755 index 0000000..e625c34 --- /dev/null +++ b/containers/core/containers_time.h @@ -0,0 +1,103 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_TIME_H +#define VC_CONTAINERS_TIME_H + +/** \file + * Utility functions to help with timestamping of elementary stream frames + */ + +typedef struct VC_CONTAINER_TIME_T +{ + uint32_t samplerate_num; + uint32_t samplerate_den; + uint32_t time_base; + + uint32_t remainder; + + int64_t time; + +} VC_CONTAINER_TIME_T; + +/*****************************************************************************/ +STATIC_INLINE void vc_container_time_init( VC_CONTAINER_TIME_T *time, uint32_t time_base ) +{ + time->samplerate_num = 0; + time->samplerate_den = 0; + time->remainder = 0; + time->time_base = time_base; + time->time = VC_CONTAINER_TIME_UNKNOWN; +} + +/*****************************************************************************/ +STATIC_INLINE int64_t vc_container_time_get( VC_CONTAINER_TIME_T *time ) +{ + if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den) + return VC_CONTAINER_TIME_UNKNOWN; + return time->time + time->remainder * (int64_t)time->time_base * time->samplerate_den / time->samplerate_num; +} + +/*****************************************************************************/ +STATIC_INLINE void vc_container_time_set_samplerate( VC_CONTAINER_TIME_T *time, uint32_t samplerate_num, uint32_t samplerate_den ) +{ + if(time->samplerate_num == samplerate_num && + time->samplerate_den == samplerate_den) + return; + + /* We're changing samplerate, we need to reset our remainder */ + if(time->remainder) + time->time = vc_container_time_get( time ); + time->remainder = 0; + time->samplerate_num = samplerate_num; + time->samplerate_den = samplerate_den; +} + +/*****************************************************************************/ +STATIC_INLINE void vc_container_time_set( VC_CONTAINER_TIME_T *time, int64_t new_time ) +{ + if (new_time == VC_CONTAINER_TIME_UNKNOWN) + return; + time->remainder = 0; + time->time = new_time; +} + +/*****************************************************************************/ +STATIC_INLINE int64_t vc_container_time_add( VC_CONTAINER_TIME_T *time, uint32_t samples ) +{ + uint32_t increment; + + if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den) + return VC_CONTAINER_TIME_UNKNOWN; + + samples += time->remainder; + increment = samples * time->samplerate_den / time->samplerate_num; + time->time += increment * time->time_base; + time->remainder = samples - increment * time->samplerate_num / time->samplerate_den; + return vc_container_time_get(time); +} + +#endif /* VC_CONTAINERS_TIME_H */ diff --git a/containers/core/containers_uri.c b/containers/core/containers_uri.c new file mode 100755 index 0000000..b0d3c59 --- /dev/null +++ b/containers/core/containers_uri.c @@ -0,0 +1,1120 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/core/containers_uri.h" + +/*****************************************************************************/ +/* Internal types and definitions */ +/*****************************************************************************/ + +typedef struct VC_URI_QUERY_T +{ + char *name; + char *value; +} VC_URI_QUERY_T; + +struct VC_URI_PARTS_T +{ + char *scheme; /**< Unescaped scheme */ + char *userinfo; /**< Unescaped userinfo */ + char *host; /**< Unescaped host name/IP address */ + char *port; /**< Unescaped port */ + char *path; /**< Unescaped path */ + char *path_extension; /**< Unescaped path extension */ + char *fragment; /**< Unescaped fragment */ + VC_URI_QUERY_T *queries; /**< Array of queries */ + uint32_t num_queries; /**< Number of queries in array */ +}; + +typedef const uint32_t *RESERVED_CHARS_TABLE_T; + +/** Reserved character table for scheme component + * Controls, space, !"#$%&'()*,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */ +static uint32_t scheme_reserved_chars[8] = { + 0xFFFFFFFF, 0xFC0097FF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for userinfo component + * Controls, space, "#%/<>?@[\]^`{|} and 0x7F and above reserved. */ +static uint32_t userinfo_reserved_chars[8] = { + 0xFFFFFFFF, 0xD000802D, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for host component + * Controls, space, "#%/<>?@\^`{|} and 0x7F and above reserved. */ +static uint32_t host_reserved_chars[8] = { + 0xFFFFFFFF, 0xD000802D, 0x50000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for port component + * Controls, space, !"#$%&'()*+,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */ +static uint32_t port_reserved_chars[8] = { + 0xFFFFFFFF, 0xFC009FFF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for path component + * Controls, space, "#%<>?[\]^`{|} and 0x7F and above reserved. */ +static uint32_t path_reserved_chars[8] = { + 0xFFFFFFFF, 0xD000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for query component + * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */ +static uint32_t query_reserved_chars[8] = { + 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for fragment component + * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */ +static uint32_t fragment_reserved_chars[8] = { + 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +#define URI_RESERVED(C, TABLE) (!!((TABLE)[(unsigned char)(C) >> 5] & (1 << ((C) & 0x1F)))) + +#define SCHEME_DELIMITERS ":/?#" +#define NETWORK_DELIMITERS "@/?#" +#define HOST_PORT_DELIMITERS "/?#" +#define PATH_DELIMITERS "?#" +#define QUERY_DELIMITERS "#" + +/*****************************************************************************/ +/* Internal functions */ +/*****************************************************************************/ + +static char to_hex(int v) +{ + if (v > 9) + return 'A' + v - 10; + return '0' + v; +} + +/*****************************************************************************/ +static uint32_t from_hex(const char *str, uint32_t str_len) +{ + uint32_t val = 0; + + while (str_len--) + { + char c = *str++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else + c = 0; /* Illegal character (not hex) */ + val = (val << 4) + c; + } + + return val; +} + +/*****************************************************************************/ +static uint32_t escaped_length( const char *str, RESERVED_CHARS_TABLE_T reserved ) +{ + uint32_t ii; + uint32_t esclen = 0; + char c; + + for (ii = strlen(str); ii > 0; ii--) + { + c = *str++; + if (URI_RESERVED(c, reserved)) + { + /* Reserved character needs escaping as %xx */ + esclen += 3; + } else { + esclen++; + } + } + + return esclen; +} + +/*****************************************************************************/ +static uint32_t escape_string( const char *str, char *escaped, + RESERVED_CHARS_TABLE_T reserved ) +{ + uint32_t ii; + uint32_t esclen = 0; + + if (!str) + return 0; + + for (ii = strlen(str); ii > 0; ii--) + { + char c = *str++; + + if (URI_RESERVED(c, reserved)) + { + escaped[esclen++] = '%'; + escaped[esclen++] = to_hex((c >> 4) & 0xF); + escaped[esclen++] = to_hex(c & 0xF); + } else { + escaped[esclen++] = c; + } + } + + return esclen; +} + +/*****************************************************************************/ +static uint32_t unescaped_length( const char *str, uint32_t str_len ) +{ + uint32_t ii; + uint32_t unesclen = 0; + + for (ii = 0; ii < str_len; ii++) + { + if (*str++ == '%' && (ii + 2) < str_len) + { + str += 2; /* Should be two hex values next */ + ii += 2; + } + unesclen++; + } + + return unesclen; +} + +/*****************************************************************************/ +static void unescape_string( const char *str, uint32_t str_len, char *unescaped ) +{ + uint32_t ii; + + for (ii = 0; ii < str_len; ii++) + { + char c = *str++; + + if (c == '%' && (ii + 2) < str_len ) + { + c = (char)(from_hex(str, 2) & 0xFF); + str += 2; + ii += 2; + } + *unescaped++ = c; + } + + *unescaped = '\0'; +} + +/*****************************************************************************/ +static char *create_unescaped_string( const char *escstr, uint32_t esclen ) +{ + char *unescstr; + + unescstr = (char *)malloc(unescaped_length(escstr, esclen) + 1); /* Allow for NUL */ + if (unescstr) + unescape_string(escstr, esclen, unescstr); + + return unescstr; +} + +/*****************************************************************************/ +static bool duplicate_string( const char *src, char **p_dst ) +{ + if (*p_dst) + free(*p_dst); + + if (src) + { + size_t str_size = strlen(src) + 1; + + *p_dst = (char *)malloc(str_size); + if (!*p_dst) + return false; + + memcpy(*p_dst, src, str_size); + } else + *p_dst = NULL; + + return true; +} + +/*****************************************************************************/ +static void release_string( char **str ) +{ + if (*str) + { + free(*str); + *str = NULL; + } +} + +/*****************************************************************************/ +static void to_lower_string( char *str ) +{ + char c; + + while ((c = *str) != '\0') + { + if (c >= 'A' && c <= 'Z') + *str = c - 'A' + 'a'; + str++; + } +} + +/*****************************************************************************/ +static const char *vc_uri_find_delimiter(const char *str, const char *delimiters) +{ + const char *ptr = str; + char c; + + while ((c = *ptr) != 0) + { + if (strchr(delimiters, c) != 0) + break; + ptr++; + } + + return ptr; +} + +/*****************************************************************************/ +static void vc_uri_set_path_extension(VC_URI_PARTS_T *p_uri) +{ + char *end; + + if (!p_uri) + return; + + p_uri->path_extension = NULL; + + if (!p_uri->path) + return; + + /* Look for the magic dot */ + for (end = p_uri->path + strlen(p_uri->path); *end != '.'; end--) + if (end == p_uri->path || *end == '/' || *end == '\\') + return; + + p_uri->path_extension = end + 1; +} + +/*****************************************************************************/ +static bool parse_authority( VC_URI_PARTS_T *p_uri, const char *str, + uint32_t str_len, const char *userinfo_end ) +{ + const char *marker = userinfo_end; + const char *str_end = str + str_len; + char c; + + if (marker) + { + p_uri->userinfo = create_unescaped_string(str, marker - str); + if (!p_uri->userinfo) + return false; + str = marker + 1; /* Past '@' character */ + } + + if (*str == '[') /* IPvFuture / IPv6 address */ + { + /* Find end of address marker */ + for (marker = str; marker < str_end; marker++) + { + c = *marker; + if (c == ']') + break; + } + + if (marker < str_end) + marker++; /* Found marker, move to next character */ + } else { + /* Find port value marker*/ + for (marker = str; marker < str_end; marker++) + { + c = *marker; + if (c == ':') + break; + } + } + + /* Always store the host, even if empty, to trigger the "://" form of URI */ + p_uri->host = create_unescaped_string(str, marker - str); + if (!p_uri->host) + return false; + to_lower_string(p_uri->host); /* Host names are case-insensitive */ + + if (*marker == ':') + { + str = marker + 1; + p_uri->port = create_unescaped_string(str, str_end - str); + if (!p_uri->port) + return false; + } + + return true; +} + +/*****************************************************************************/ +static bool store_query( VC_URI_PARTS_T *p_uri, const char *name_start, + const char *equals_ptr, const char *query_end) +{ + uint32_t name_len, value_len; + + if (equals_ptr) + { + name_len = equals_ptr - name_start; + value_len = query_end - equals_ptr - 1; /* Don't include '=' itself */ + } else { + name_len = query_end - name_start; + value_len = 0; + } + + /* Only store something if there is a name */ + if (name_len) + { + char *name, *value = NULL; + VC_URI_QUERY_T *p_query; + + if (equals_ptr) + { + value = create_unescaped_string(equals_ptr + 1, value_len); + if (!value) + return false; + equals_ptr = query_end; + } + + name = create_unescaped_string(name_start, name_len); + if (!name) + { + if (value) + free(value); + return false; + } + + /* Store query data in URI structure */ + p_query = &p_uri->queries[ p_uri->num_queries++ ]; + p_query->name = name; + p_query->value = value; + } + + return true; +} + +/*****************************************************************************/ +static bool parse_query( VC_URI_PARTS_T *p_uri, const char *str, uint32_t str_len ) +{ + uint32_t ii; + uint32_t query_count; + VC_URI_QUERY_T *queries; + const char *name_start = str; + const char *equals_ptr = NULL; + char c; + + if (!str_len) + return true; + + /* Scan for the number of query items, so array can be allocated the right size */ + query_count = 1; /* At least */ + for (ii = 0; ii < str_len; ii++) + { + c = str[ii]; + + if (c == '&' || c ==';') + query_count++; + } + + queries = (VC_URI_QUERY_T *)malloc(query_count * sizeof(VC_URI_QUERY_T)); + if (!queries) + return false; + + p_uri->queries = queries; + + /* Go back and parse the string for each query item and store in array */ + for (ii = 0; ii < str_len; ii++) + { + c = *str; + + /* Take first '=' as break between name and value */ + if (c == '=' && !equals_ptr) + equals_ptr = str; + + /* If at the end of the name or name/value pair */ + if (c == '&' || c ==';') + { + if (!store_query(p_uri, name_start, equals_ptr, str)) + return false; + + equals_ptr = NULL; + name_start = str + 1; + } + + str++; + } + + return store_query(p_uri, name_start, equals_ptr, str); +} + +/*****************************************************************************/ +static uint32_t calculate_uri_length(const VC_URI_PARTS_T *p_uri) +{ + uint32_t length = 0; + uint32_t count; + + /* With no scheme, assume this is a plain path (without escaping) */ + if (!p_uri->scheme) + return p_uri->path ? strlen(p_uri->path) : 0; + + length += escaped_length(p_uri->scheme, scheme_reserved_chars); + length++; /* for the colon */ + + if (p_uri->host) + { + length += escaped_length(p_uri->host, host_reserved_chars) + 2; /* for the double slash */ + if (p_uri->userinfo) + length += escaped_length(p_uri->userinfo, userinfo_reserved_chars) + 1; /* for the '@' */ + if (p_uri->port) + length += escaped_length(p_uri->port, port_reserved_chars) + 1; /* for the ':' */ + } + + if (p_uri->path) + length += escaped_length(p_uri->path, path_reserved_chars); + + count = p_uri->num_queries; + if (count) + { + VC_URI_QUERY_T * queries = p_uri->queries; + + while (count--) + { + /* The name is preceded by either the '?' or the '&' */ + length += escaped_length(queries->name, query_reserved_chars) + 1; + + /* The value is optional, but if present will require an '=' */ + if (queries->value) + length += escaped_length(queries->value, query_reserved_chars) + 1; + queries++; + } + } + + if (p_uri->fragment) + length += escaped_length(p_uri->fragment, fragment_reserved_chars) + 1; /* for the '#' */ + + return length; +} + +/*****************************************************************************/ +static void build_uri(const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size) +{ + uint32_t count; + + /* With no scheme, assume this is a plain path (without escaping) */ + if (!p_uri->scheme) + { + if (p_uri->path) + strncpy(buffer, p_uri->path, buffer_size); + else + buffer[0] = '\0'; + return; + } + + buffer += escape_string(p_uri->scheme, buffer, scheme_reserved_chars); + *buffer++ = ':'; + + if (p_uri->host) + { + *buffer++ = '/'; + *buffer++ = '/'; + if (p_uri->userinfo) + { + buffer += escape_string(p_uri->userinfo, buffer, userinfo_reserved_chars); + *buffer++ = '@'; + } + buffer += escape_string(p_uri->host, buffer, host_reserved_chars); + if (p_uri->port) + { + *buffer++ = ':'; + buffer += escape_string(p_uri->port, buffer, port_reserved_chars); + } + } + + if (p_uri->path) + buffer += escape_string(p_uri->path, buffer, path_reserved_chars); + + count = p_uri->num_queries; + if (count) + { + VC_URI_QUERY_T * queries = p_uri->queries; + + *buffer++ = '?'; + while (count--) + { + buffer += escape_string(queries->name, buffer, query_reserved_chars); + + if (queries->value) + { + *buffer++ = '='; + buffer += escape_string(queries->value, buffer, query_reserved_chars); + } + + /* Add separator if there is another item to add */ + if (count) + *buffer++ = '&'; + + queries++; + } + } + + if (p_uri->fragment) + { + *buffer++ = '#'; + buffer += escape_string(p_uri->fragment, buffer, fragment_reserved_chars); + } + + *buffer = '\0'; +} + +/*****************************************************************************/ +static bool vc_uri_copy_base_path( const VC_URI_PARTS_T *base_uri, + VC_URI_PARTS_T *relative_uri ) +{ + const char *base_path = vc_uri_path(base_uri); + + /* No path set (or empty), copy from base */ + if (!vc_uri_set_path(relative_uri, base_path)) + return false; + + /* If relative path has no queries, copy base queries across */ + if (!vc_uri_num_queries(relative_uri)) + { + uint32_t base_queries = vc_uri_num_queries(base_uri); + const char *name, *value; + uint32_t ii; + + for (ii = 0; ii < base_queries; ii++) + { + vc_uri_query(base_uri, ii, &name, &value); + if (!vc_uri_add_query(relative_uri, name, value)) + return false; + } + } + + return true; +} + +/*****************************************************************************/ +static void vc_uri_remove_single_dot_segments( char *path_str ) +{ + char *slash = path_str - 1; + + while (slash++) + { + if (*slash == '.') + { + switch (slash[1]) + { + case '/': /* Single dot segment, remove it */ + memmove(slash, slash + 2, strlen(slash + 2) + 1); + break; + case '\0': /* Trailing single dot, remove it */ + *slash = '\0'; + break; + default: /* Something else (e.g. ".." or ".foo") */ + ; /* Do nothing */ + } + } + slash = strchr(slash, '/'); + } +} + +/*****************************************************************************/ +static void vc_uri_remove_double_dot_segments( char *path_str ) +{ + char *previous_segment = path_str; + char *slash; + + if (previous_segment[0] == '/') + previous_segment++; + + /* Remove strings of the form "/../" (or "/.." at the end of the path) + * as long as is not itself ".." */ + slash = strchr(previous_segment, '/'); + while (slash) + { + if (previous_segment[0] != '.' || previous_segment[1] != '.' || previous_segment[2] != '/') + { + if (slash[1] == '.' && slash[2] == '.') + { + bool previous_segment_removed = true; + + switch (slash[3]) + { + case '/': /* "/../" inside path, snip it and last segment out */ + memmove(previous_segment, slash + 4, strlen(slash + 4) + 1); + break; + case '\0': /* Trailing "/.." on path, just terminate path at last segment */ + *previous_segment = '\0'; + break; + default: /* Not a simple ".." segment, so skip over it */ + previous_segment_removed = false; + } + + if (previous_segment_removed) + { + /* The segment just removed was the first one in the path (optionally + * prefixed by a slash), so no more can be removed: stop. */ + if (previous_segment < path_str + 2) + break; + + /* Move back to slash before previous segment, or the start of the path */ + slash = previous_segment - 1; + while (--slash >= path_str && *slash != '/') + ; /* Everything done in the while */ + } + } + } + previous_segment = slash + 1; + slash = strchr(previous_segment, '/'); + } +} + +/*****************************************************************************/ +/* API functions */ +/*****************************************************************************/ + +VC_URI_PARTS_T *vc_uri_create( void ) +{ + VC_URI_PARTS_T *p_uri; + + p_uri = (VC_URI_PARTS_T *)malloc(sizeof(VC_URI_PARTS_T)); + if (p_uri) + { + memset(p_uri, 0, sizeof(VC_URI_PARTS_T)); + } + + return p_uri; +} + +/*****************************************************************************/ +void vc_uri_clear( VC_URI_PARTS_T *p_uri ) +{ + if (!p_uri) + return; + + release_string(&p_uri->scheme); + release_string(&p_uri->userinfo); + release_string(&p_uri->host); + release_string(&p_uri->port); + release_string(&p_uri->path); + release_string(&p_uri->fragment); + + if (p_uri->queries) + { + VC_URI_QUERY_T *queries = p_uri->queries; + uint32_t count = p_uri->num_queries; + + while (count--) + { + release_string(&queries[count].name); + release_string(&queries[count].value); + } + + free(queries); + p_uri->queries = NULL; + p_uri->num_queries = 0; + } +} + +/*****************************************************************************/ +void vc_uri_release( VC_URI_PARTS_T *p_uri ) +{ + if (!p_uri) + return; + + vc_uri_clear(p_uri); + + free(p_uri); +} + +/*****************************************************************************/ +bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri ) +{ + const char *marker; + uint32_t len; + + if (!p_uri || !uri) + return false; + + vc_uri_clear(p_uri); + + /* URI = scheme ":" hier_part [ "?" query ] [ "#" fragment ] */ + + /* Find end of scheme, or another separator */ + marker = vc_uri_find_delimiter(uri, SCHEME_DELIMITERS); + + if (*marker == ':') + { + len = (marker - uri); + if (isalpha((int)*uri) && len == 1 && marker[1] == '\\') + { + /* Looks like a bare, absolute DOS/Windows filename with a drive letter */ + /* coverity[double_free] Pointer freed and set to NULL */ + bool ret = duplicate_string(uri, &p_uri->path); + vc_uri_set_path_extension(p_uri); + return ret; + } + + p_uri->scheme = create_unescaped_string(uri, len); + if (!p_uri->scheme) + goto error; + + to_lower_string(p_uri->scheme); /* Schemes should be handled case-insensitively */ + uri = marker + 1; + } + + if (uri[0] == '/' && uri[1] == '/') /* hier-part includes authority */ + { + const char *userinfo_end = NULL; + + /* authority = [ userinfo "@" ] host [ ":" port ] */ + uri += 2; + + marker = vc_uri_find_delimiter(uri, NETWORK_DELIMITERS); + if (*marker == '@') + { + userinfo_end = marker; + marker = vc_uri_find_delimiter(marker + 1, HOST_PORT_DELIMITERS); + } + + if (!parse_authority(p_uri, uri, marker - uri, userinfo_end)) + goto error; + uri = marker; + } + + /* path */ + marker = vc_uri_find_delimiter(uri, PATH_DELIMITERS); + len = marker - uri; + if (len) + { + p_uri->path = create_unescaped_string(uri, len); + vc_uri_set_path_extension(p_uri); + if (!p_uri->path) + goto error; + } + + /* query */ + if (*marker == '?') + { + uri = marker + 1; + marker = vc_uri_find_delimiter(uri, QUERY_DELIMITERS); + if (!parse_query(p_uri, uri, marker - uri)) + goto error; + } + + /* fragment */ + if (*marker == '#') + { + uri = marker + 1; + p_uri->fragment = create_unescaped_string(uri, strlen(uri)); + if (!p_uri->fragment) + goto error; + } + + return true; + +error: + vc_uri_clear(p_uri); + return false; +} + +/*****************************************************************************/ +uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size ) +{ + uint32_t required_length; + + if (!p_uri) + return 0; + + required_length = calculate_uri_length(p_uri); + if (buffer && required_length < buffer_size) /* Allow for NUL */ + build_uri(p_uri, buffer, buffer_size); + + return required_length; +} + +/*****************************************************************************/ +const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->scheme : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->userinfo : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_host( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->host : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_port( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->port : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_path( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->path : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->path_extension : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->fragment : NULL; +} + +/*****************************************************************************/ +uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->num_queries : 0; +} + +/*****************************************************************************/ +void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value ) +{ + const char *name = NULL; + const char *value = NULL; + + if (p_uri) + { + if (index < p_uri->num_queries) + { + name = p_uri->queries[index].name; + value = p_uri->queries[index].value; + } + } + + if (p_name) + *p_name = name; + if (p_value) + *p_value = value; +} + +/*****************************************************************************/ +bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value ) +{ + unsigned int i = p_index ? *p_index : 0; + + if (!p_uri) + return false; + + for (; name && i < p_uri->num_queries; i++) + { + if (!strcmp(name, p_uri->queries[i].name)) + { + if (p_value) + *p_value = p_uri->queries[i].value; + if (p_index) + *p_index = i; + return true; + } + } + + return false; +} + +/*****************************************************************************/ +bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme ) +{ + return p_uri ? duplicate_string(scheme, &p_uri->scheme) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo ) +{ + return p_uri ? duplicate_string(userinfo, &p_uri->userinfo) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host ) +{ + return p_uri ? duplicate_string(host, &p_uri->host) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port ) +{ + return p_uri ? duplicate_string(port, &p_uri->port) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path ) +{ + bool ret = p_uri ? duplicate_string(path, &p_uri->path) : false; + vc_uri_set_path_extension(p_uri); + return ret; +} + +/*****************************************************************************/ +bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment ) +{ + return p_uri ? duplicate_string(fragment, &p_uri->fragment) : false; +} + +/*****************************************************************************/ +bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value ) +{ + VC_URI_QUERY_T *queries; + uint32_t count; + + if (!p_uri || !name) + return false; + + count = p_uri->num_queries; + if (p_uri->queries) + queries = (VC_URI_QUERY_T *)realloc(p_uri->queries, (count + 1) * sizeof(VC_URI_QUERY_T)); + else + queries = (VC_URI_QUERY_T *)malloc(sizeof(VC_URI_QUERY_T)); + + if (!queries) + return false; + + /* Always store the pointer, in case it has changed, and even if we fail to copy name/value */ + p_uri->queries = queries; + queries[count].name = NULL; + queries[count].value = NULL; + + if (duplicate_string(name, &queries[count].name)) + { + if (duplicate_string(value, &queries[count].value)) + { + /* Successful exit path */ + p_uri->num_queries++; + return true; + } + + release_string(&queries[count].name); + } + + return false; +} + +/*****************************************************************************/ +bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ) +{ + bool success = true; + const char *relative_path; + + /* If scheme is already set, the URI is already absolute */ + if (relative_uri->scheme) + return true; + + /* Otherwise, copy the base scheme */ + if (!duplicate_string(base_uri->scheme, &relative_uri->scheme)) + return false; + + /* If any of the network info is set, use the rest of the relative URI as-is */ + if (relative_uri->host || relative_uri->port || relative_uri->userinfo) + return true; + + /* Otherwise, copy the base network info */ + if (!duplicate_string(base_uri->host, &relative_uri->host) || + !duplicate_string(base_uri->port, &relative_uri->port) || + !duplicate_string(base_uri->userinfo, &relative_uri->userinfo)) + return false; + + relative_path = relative_uri->path; + + if (!relative_path || !*relative_path) + { + /* No relative path (could be queries and/or fragment), so take base path */ + success = vc_uri_copy_base_path(base_uri, relative_uri); + } + else if (*relative_path != '/') + { + const char *base_path = base_uri->path; + char *merged_path; + char *slash; + size_t len; + + /* Path is relative, merge in with base path */ + if (!base_path || !*base_path) + { + if (relative_uri->host || relative_uri->port || relative_uri->userinfo) + base_path = "/"; /* Need a separator to split network info from path */ + else + base_path = ""; + } + + len = strlen(base_path) + strlen(relative_path) + 1; + + /* Allocate space for largest possible combined path */ + merged_path = (char *)malloc(len); + if (!merged_path) + return false; + + strncpy(merged_path, base_path, len); + + slash = strrchr(merged_path, '/'); /* Note: reverse search */ + if (*relative_path == ';') + { + char *semi; + + /* Relative path is just parameters, so remove any base parameters in final segment */ + if (!slash) + slash = merged_path; + semi = strchr(slash, ';'); + if (semi) + semi[0] = '\0'; + } else { + /* Remove final segment */ + if (slash) + slash[1] = '\0'; + else + merged_path[0] = '\0'; + } + strncat(merged_path, relative_path, len - strlen(merged_path) - 1); + + vc_uri_remove_single_dot_segments(merged_path); + vc_uri_remove_double_dot_segments(merged_path); + + success = duplicate_string(merged_path, &relative_uri->path); + + free(merged_path); + } + /* Otherwise path is absolute, which can be left as-is */ + + return success; +} diff --git a/containers/core/containers_uri.h b/containers/core/containers_uri.h new file mode 100755 index 0000000..77a06e7 --- /dev/null +++ b/containers/core/containers_uri.h @@ -0,0 +1,241 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_CONTAINERS_URI_H +#define VC_CONTAINERS_URI_H + +/** \file containers_uri.h + * API for parsing and building URI strings as described in RFC3986. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers.h" + +typedef struct VC_URI_PARTS_T VC_URI_PARTS_T; + +/** Create an empty URI structure. + * + * \return The new URI structure. */ +VC_URI_PARTS_T *vc_uri_create( void ); + +/** Destroy a URI structure. + * + * \param p_uri Pointer to a URI parts structure. */ +void vc_uri_release( VC_URI_PARTS_T *p_uri ); + +/** Clear a URI structure. + * Any URI component strings held are released, but the structure itself is not. + * + * \param p_uri Pointer to a URI parts structure. */ +void vc_uri_clear( VC_URI_PARTS_T *p_uri ); + +/** Parses and unescapes a URI into the component parts. + * + * \param p_uri Pointer to a URI parts structure. + * \param uri Pointer to a URI string to be parsed. + * \return True if successful, false if not. */ +bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri ); + +/** Builds the URI component parts into a URI string. + * If buffer is NULL, or buffer_size is too small, nothing is written to the + * buffer but the required string length is still returned. buffer_size must be + * at least one more than the value returned. + * + * \param p_uri Pointer to a URI parts structure. + * \param buffer Pointer to where the URI string is to be built, or NULL. + * \param buffer_size Number of bytes available in the buffer. + * \return The length of the URI string. */ +uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size ); + +/** Retrieves the scheme of the URI. + * The string is valid until either the scheme is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the scheme string. */ +const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the userinfo of the URI. + * The string is valid until either the userinfo is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the userinfo string. */ +const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the host of the URI. + * The string is valid until either the host is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the host string. */ +const char *vc_uri_host( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the port of the URI. + * The string is valid until either the port is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the port string. */ +const char *vc_uri_port( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the path of the URI. + * The string is valid until either the path is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the path string. */ +const char *vc_uri_path( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the extension part of the path of the URI. + * The string is valid until either the path is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the extension string. */ +const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the fragment of the URI. + * The string is valid until either the fragment is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the fragment string. */ +const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri ); + +/** Returns the number of query name/value pairs stored. + * + * \param p_uri The parsed URI. + * \return Number of queries stored. */ +uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves a given query's name and value + * If either p_name or p_value are NULL, that part of the query is not returned, + * otherwise it is set to the address of the string (which may itself be NULL). + * + * \param p_uri The parsed URI. + * \param index Selects the query to get. + * \param p_name Address of a string pointer to receive query name, or NULL. + * \param p_value Address of a string pointer to receive query value, or NULL. */ +void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value ); + +/** Finds a specific query in the array. + * If p_index is NULL, then it is assumed the search should start at index 0, + * otherwise the search will start at the specified index and the index will + * be updated on return to point to the query which has been found. + * If p_value is NULL, that part of the query is not returned, + * otherwise it is set to the address of the value string (which may itself be NULL). + * + * \param p_uri Pointer to a URI parts structure. + * \param p_index Index from which to start the search. May be NULL. + * \param name Unescaped query name. + * \param value Unescaped query value. May be NULL. + * \return True if successful, false if not. */ +bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value ); + +/** Sets the scheme of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param scheme Pointer to the new scheme string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme ); + +/** Sets the userinfo of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param userinfo Pointer to the new userinfo string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo ); + +/** Sets the host of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param host Pointer to the new host string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host ); + +/** Sets the port of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param port Pointer to the new port string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port ); + +/** Sets the path of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param path Pointer to the new path string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path ); + +/** Sets the fragment of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param fragment Pointer to the new fragment string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment ); + +/** Adds an query to the array. + * Note that the queries pointer may change after this function is called. + * May fail due to memory allocation failure or invalid parameters. + * + * \param p_uri Pointer to a URI parts structure. + * \param name Unescaped query name. + * \param value Unescaped query value. May be NULL. + * \return True if successful, false if not. */ +bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value ); + +/** Merge a base URI and a relative URI. + * In general, where the relative URI does not have a given element, the + * corresponding element from the base URI is used. See RFC1808. + * The combined URI is left in relative_uri. If the function is unsuccessful, + * the relative_uri may have been partially modified. + * + * \param base_uri Pointer to the base URI parts structure. + * \param relative_uri Pointer to the relative URI parts structure. + * \return True if successful, false if not. */ +bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ); + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_URI_H */ diff --git a/containers/core/containers_utils.c b/containers/core/containers_utils.c new file mode 100755 index 0000000..477093f --- /dev/null +++ b/containers/core/containers_utils.c @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_utils.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define BITMAPINFOHEADER_SIZE_MAX 40 +#define MAX_EXTENSION_SIZE 4 + +#define VC_CONTAINER_ES_FORMAT_MAGIC ((uint32_t)VC_FOURCC('m','a','g','f')) +#define EXTRADATA_SIZE_DEFAULT 32 +#define EXTRADATA_SIZE_MAX (10*1024) + +/*****************************************************************************/ +typedef struct VC_CONTAINER_ES_FORMAT_PRIVATE_T +{ + VC_CONTAINER_ES_FORMAT_T format; + VC_CONTAINER_ES_SPECIFIC_FORMAT_T type; + + uint32_t magic; + + unsigned int extradata_size; + uint8_t *extradata; + + uint8_t buffer[EXTRADATA_SIZE_DEFAULT]; + +} VC_CONTAINER_ES_FORMAT_PRIVATE_T; + +/*****************************************************************************/ +VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size) +{ + VC_CONTAINER_ES_FORMAT_PRIVATE_T *private; + VC_CONTAINER_STATUS_T status; + + private = malloc(sizeof(*private)); + if(!private) return 0; + memset(private, 0, sizeof(*private)); + + private->magic = VC_CONTAINER_ES_FORMAT_MAGIC; + private->format.type = (void *)&private->type; + private->extradata_size = EXTRADATA_SIZE_DEFAULT; + + status = vc_container_format_extradata_alloc(&private->format, extradata_size); + if(status != VC_CONTAINER_SUCCESS) + { + free(private); + return NULL; + } + + return &private->format; +} + +/*****************************************************************************/ +void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; + vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); + if(private->extradata) free(private->extradata); + free(private); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( + VC_CONTAINER_ES_FORMAT_T *format, unsigned int size) +{ + VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; + vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); + + /* Sanity check the size requested */ + if(size > EXTRADATA_SIZE_MAX) + return VC_CONTAINER_ERROR_CORRUPTED; + + /* Allocate memory if needed */ + if(private->extradata_size < size) + { + if(private->extradata) free(private->extradata); + private->extradata = malloc(size); + if(!private->extradata) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + private->extradata_size = size; + } + + /* Set the fields in the actual format structure */ + if(private->extradata) private->format.extradata = private->extradata; + else private->format.extradata = private->buffer; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, + VC_CONTAINER_ES_FORMAT_T *p_in, + unsigned int extra_buffer_size) +{ + void *type = p_out->type; + uint8_t *extradata = p_out->extradata; + + /* Check we have a sufficient buffer to copy the extra data */ + if(p_in->extradata_size > extra_buffer_size || + (p_in->extradata_size && !p_out->extradata)) + return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; + + *p_out->type = *p_in->type; + *p_out = *p_in; + p_out->type = type; + p_out->extradata = extradata; + if(p_in->extradata_size) + memcpy(p_out->extradata, p_in->extradata, p_in->extradata_size); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +int utf8_from_charset(const char *charset, char *out, unsigned int out_size, + const void *in, unsigned int in_size) +{ + unsigned int i; + const uint16_t *in16 = (const uint16_t *)in; + const uint8_t *in8 = (const uint8_t *)in; + + if(out_size < 1) return 1; + if(!strcmp(charset, "UTF16-LE")) goto utf16le; + if(!strcmp(charset, "UTF8")) goto utf8; + else return 1; + + utf16le: + for(i = 0; i < in_size / 2 && in16[i] && i < out_size - 1; i++) + { + out[i] = in16[i]; + } + out[i] = 0; + return 0; + + utf8: + for(i = 0; i < in_size && in8[i] && i < out_size - 1; i++) + { + out[i] = in8[i]; + } + out[i] = 0; + return 0; +} + +/*****************************************************************************/ +unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size) +{ + uint16_t waveformat = codec_to_waveformat(format->codec); + + if(format->es_type != VC_CONTAINER_ES_TYPE_AUDIO || + waveformat == WAVE_FORMAT_UNKNOWN) return 0; + + if(!buffer) return format->extradata_size + 18; + + if(buffer_size < format->extradata_size + 18) return 0; + + /* Build a waveformatex header */ + buffer[0] = waveformat; + buffer[1] = waveformat >> 8; + buffer[2] = format->type->audio.channels; + buffer[3] = 0; + buffer[4] = (format->type->audio.sample_rate >> 0) & 0xFF; + buffer[5] = (format->type->audio.sample_rate >> 8) & 0xFF; + buffer[6] = (format->type->audio.sample_rate >> 16) & 0xFF; + buffer[7] = (format->type->audio.sample_rate >> 24) & 0xFF; + buffer[8] = (format->bitrate >> 3) & 0xFF; + buffer[9] = (format->bitrate >> 11) & 0xFF; + buffer[10] = (format->bitrate >> 19) & 0xFF; + buffer[11] = (format->bitrate >> 27) & 0xFF; + buffer[12] = (format->type->audio.block_align >> 0) & 0xFF; + buffer[13] = (format->type->audio.block_align >> 8) & 0xFF; + buffer[14] = (format->type->audio.bits_per_sample >> 0) & 0xFF; + buffer[15] = (format->type->audio.bits_per_sample >> 8) & 0xFF; + buffer[16] = (format->extradata_size >> 0) & 0xFF; + buffer[17] = (format->extradata_size >> 8) & 0xFF; + memcpy(buffer+18, format->extradata, format->extradata_size); + return format->extradata_size + 18; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_FOURCC_T fourcc; + uint32_t waveformat_id; + + if(!p || buffer_size < 16) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + waveformat_id = (p[1] << 8) | p[0]; + fourcc = waveformat_to_codec(waveformat_id); + + /* Read the waveformatex header */ + if(extra_offset) *extra_offset = 16; + if(extra_size) *extra_size = 0; + format->type->audio.channels = p[2]; + format->type->audio.sample_rate = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; + format->bitrate = ((p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]) * 8; + format->type->audio.block_align = (p[13] << 8) | p[12]; + format->type->audio.bits_per_sample = (p[15] << 8) | p[14]; + + if(waveformat_id == WAVE_FORMAT_PCM && format->type->audio.bits_per_sample == 8) + fourcc = VC_CONTAINER_CODEC_PCM_UNSIGNED_LE; + + if(buffer_size >= 18) + { + if(extra_size) + { + *extra_size = (p[17] << 8) | p[16]; + if(*extra_size + 18 > buffer_size) *extra_size = buffer_size - 18; + } + if(extra_offset) *extra_offset = 18; + } + + /* Skip the MPEGLAYER3WAVEFORMAT structure */ + if(waveformat_id == WAVE_FORMAT_MPEGLAYER3 && extra_size) + { + if(extra_offset) *extra_offset += *extra_size; + *extra_size = 0; + } + + format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + format->codec = fourcc; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size) +{ + uint32_t fourcc = codec_to_vfw_fourcc(format->codec); + uint32_t size = BITMAPINFOHEADER_SIZE_MAX + format->extradata_size; + + if(format->es_type != VC_CONTAINER_ES_TYPE_VIDEO || + fourcc == VC_CONTAINER_CODEC_UNKNOWN) return 0; + + if(!buffer) return size; + if(buffer_size < size) return 0; + + /* Build a bitmapinfoheader header */ + memset(buffer, 0, BITMAPINFOHEADER_SIZE_MAX); + buffer[0] = (size >> 0) & 0xFF; + buffer[1] = (size >> 8) & 0xFF; + buffer[2] = (size >> 16) & 0xFF; + buffer[3] = (size >> 24) & 0xFF; + buffer[4] = (format->type->video.width >> 0) & 0xFF; + buffer[5] = (format->type->video.width >> 8) & 0xFF; + buffer[6] = (format->type->video.width >> 16) & 0xFF; + buffer[7] = (format->type->video.width >> 24) & 0xFF; + buffer[8] = (format->type->video.height >> 0) & 0xFF; + buffer[9] = (format->type->video.height >> 8) & 0xFF; + buffer[10] = (format->type->video.height >> 16) & 0xFF; + buffer[11] = (format->type->video.height >> 24) & 0xFF; + memcpy(buffer + 16, &fourcc, 4); + memcpy(buffer + BITMAPINFOHEADER_SIZE_MAX, format->extradata, format->extradata_size); + return size; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_FOURCC_T fourcc; + + if(!p || buffer_size < BITMAPINFOHEADER_SIZE_MAX) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + /* size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; */ + format->type->video.width = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; + format->type->video.height = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; + memcpy(&fourcc, p + 16, 4); + + format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + format->codec = vfw_fourcc_to_codec(fourcc); + + /* If no mapping is found from vfw, try a more generic one */ + if (format->codec == fourcc && (fourcc = fourcc_to_codec(fourcc)) != VC_CONTAINER_CODEC_UNKNOWN) + format->codec = fourcc; + + if(extra_offset) *extra_offset = BITMAPINFOHEADER_SIZE_MAX; + if(extra_size) + { + if (buffer_size > BITMAPINFOHEADER_SIZE_MAX) + *extra_size = buffer_size - BITMAPINFOHEADER_SIZE_MAX; + else + *extra_size = 0; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static struct { + VC_CONTAINER_METADATA_KEY_T key; + const char *name; +} meta_key_conv[] = +{ {VC_CONTAINER_METADATA_KEY_TITLE, "title"}, + {VC_CONTAINER_METADATA_KEY_ARTIST, "artist"}, + {VC_CONTAINER_METADATA_KEY_ALBUM, "album"}, + {VC_CONTAINER_METADATA_KEY_DESCRIPTION, "description"}, + {VC_CONTAINER_METADATA_KEY_YEAR, "year"}, + {VC_CONTAINER_METADATA_KEY_GENRE, "genre"}, + {VC_CONTAINER_METADATA_KEY_TRACK, "track"}, + {VC_CONTAINER_METADATA_KEY_LYRICS, "lyrics"}, + {VC_CONTAINER_METADATA_KEY_UNKNOWN, 0} }; + +/*****************************************************************************/ +const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key) +{ + int i; + for(i = 0; meta_key_conv[i].key != VC_CONTAINER_METADATA_KEY_UNKNOWN; i++ ) + if(meta_key_conv[i].key == key) break; + return meta_key_conv[i].name; +} + +/*****************************************************************************/ +int64_t vc_container_maths_gcd(int64_t a, int64_t b) +{ + while(b != 0) + { + int64_t t = b; + b = a % b; + a = t; + } + return a; +} + +/*****************************************************************************/ +void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den) +{ + int64_t div = vc_container_maths_gcd((int64_t)*num, (int64_t)*den); + if(div) + { + *num /= div; + *den /= div; + } +} diff --git a/containers/core/containers_utils.h b/containers/core/containers_utils.h new file mode 100755 index 0000000..22349ef --- /dev/null +++ b/containers/core/containers_utils.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_UTILS_H +#define VC_CONTAINERS_UTILS_H + +#include "containers/containers.h" +#include "containers/containers_codecs.h" +#include "containers/core/containers_waveformat.h" + +/***************************************************************************** + * Type definitions + *****************************************************************************/ + +/** Definition of the Global Unique Identifier type as used by some containers */ +typedef struct GUID_T +{ + uint32_t word0; + uint16_t short0; + uint16_t short1; + uint8_t bytes[8]; + +} GUID_T; + +VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size); +void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *); +VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( + VC_CONTAINER_ES_FORMAT_T *format, unsigned int size); +VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, + VC_CONTAINER_ES_FORMAT_T *p_in, + unsigned int extra_buffer_size ); +int utf8_from_charset(const char *charset, char *out, unsigned int out_size, + const void *in, unsigned int in_size); +const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key); + +unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size); +unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size); +VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format); +VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format); + +/** Find the greatest common denominator of 2 numbers. + * + * @param a first number + * @param b second number + * + * @return greatest common denominator of a and b + */ +int64_t vc_container_maths_gcd(int64_t a, int64_t b); + +/** Reduce a rational number to it's simplest form. + * + * @param num Pointer to the numerator of the rational number to simplify + * @param den Pointer to the denominator of the rational number to simplify + */ +void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den); + +#endif /* VC_CONTAINERS_UTILS_H */ diff --git a/containers/core/containers_waveformat.h b/containers/core/containers_waveformat.h new file mode 100755 index 0000000..ade6b9e --- /dev/null +++ b/containers/core/containers_waveformat.h @@ -0,0 +1,180 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_WAVEFORMAT_H +#define VC_CONTAINERS_WAVEFORMAT_H + +/* WAVE form wFormatTag IDs */ +#define WAVE_FORMAT_UNKNOWN 0x0000 /* Microsoft Corporation */ +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* Microsoft Corporation */ +#define WAVE_FORMAT_VSELP 0x0004 /* Compaq Computer Corp. */ +#define WAVE_FORMAT_IBM_CVSD 0x0005 /* IBM Corporation */ +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +#define WAVE_FORMAT_DTS 0x0008 /* Microsoft Corporation */ +#define WAVE_FORMAT_DRM 0x0009 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO_VOICE 0x000A /* Microsoft Corporation */ +#define WAVE_FORMAT_OKI_ADPCM 0x0010 /* OKI */ +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +#define WAVE_FORMAT_IMA_ADPCM (WAVE_FORMAT_DVI_ADPCM) /* Intel Corporation */ +#define WAVE_FORMAT_MEDIASPACE_ADPCM 0x0012 /* Videologic */ +#define WAVE_FORMAT_SIERRA_ADPCM 0x0013 /* Sierra Semiconductor Corp */ +#define WAVE_FORMAT_G723_ADPCM 0x0014 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_DIGISTD 0x0015 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_DIGIFIX 0x0016 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM 0x0017 /* Dialogic Corporation */ +#define WAVE_FORMAT_MEDIAVISION_ADPCM 0x0018 /* Media Vision, Inc. */ +#define WAVE_FORMAT_CU_CODEC 0x0019 /* Hewlett-Packard Company */ +#define WAVE_FORMAT_YAMAHA_ADPCM 0x0020 /* Yamaha Corporation of America */ +#define WAVE_FORMAT_SONARC 0x0021 /* Speech Compression */ +#define WAVE_FORMAT_DSPGROUP_TRUESPEECH 0x0022 /* DSP Group, Inc */ +#define WAVE_FORMAT_ECHOSC1 0x0023 /* Echo Speech Corporation */ +#define WAVE_FORMAT_AUDIOFILE_AF36 0x0024 /* Virtual Music, Inc. */ +#define WAVE_FORMAT_APTX 0x0025 /* Audio Processing Technology */ +#define WAVE_FORMAT_AUDIOFILE_AF10 0x0026 /* Virtual Music, Inc. */ +#define WAVE_FORMAT_PROSODY_1612 0x0027 /* Aculab plc */ +#define WAVE_FORMAT_LRC 0x0028 /* Merging Technologies S.A. */ +#define WAVE_FORMAT_DOLBY_AC2 0x0030 /* Dolby Laboratories */ +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +#define WAVE_FORMAT_MSNAUDIO 0x0032 /* Microsoft Corporation */ +#define WAVE_FORMAT_ANTEX_ADPCME 0x0033 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_CONTROL_RES_VQLPC 0x0034 /* Control Resources Limited */ +#define WAVE_FORMAT_DIGIREAL 0x0035 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_DIGIADPCM 0x0036 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_CONTROL_RES_CR10 0x0037 /* Control Resources Limited */ +#define WAVE_FORMAT_NMS_VBXADPCM 0x0038 /* Natural MicroSystems */ +#define WAVE_FORMAT_CS_IMAADPCM 0x0039 /* Crystal Semiconductor IMA ADPCM */ +#define WAVE_FORMAT_ECHOSC3 0x003A /* Echo Speech Corporation */ +#define WAVE_FORMAT_ROCKWELL_ADPCM 0x003B /* Rockwell International */ +#define WAVE_FORMAT_ROCKWELL_DIGITALK 0x003C /* Rockwell International */ +#define WAVE_FORMAT_XEBEC 0x003D /* Xebec Multimedia Solutions Limited */ +#define WAVE_FORMAT_G721_ADPCM 0x0040 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_G728_CELP 0x0041 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_MSG723 0x0042 /* Microsoft Corporation */ +#define WAVE_FORMAT_PANASONIC_G726 0x0045 /* Not official Panasonic G.726 codec */ +#define WAVE_FORMAT_MPEG 0x0050 /* Microsoft Corporation */ +#define WAVE_FORMAT_RT24 0x0052 /* InSoft, Inc. */ +#define WAVE_FORMAT_PAC 0x0053 /* InSoft, Inc. */ +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +#define WAVE_FORMAT_LUCENT_G723 0x0059 /* Lucent Technologies */ +#define WAVE_FORMAT_CIRRUS 0x0060 /* Cirrus Logic */ +#define WAVE_FORMAT_ESPCM 0x0061 /* ESS Technology */ +#define WAVE_FORMAT_VOXWARE 0x0062 /* Voxware Inc */ +#define WAVE_FORMAT_CANOPUS_ATRAC 0x0063 /* Canopus, co., Ltd. */ +#define WAVE_FORMAT_G726_ADPCM 0x0064 /* APICOM */ +#define WAVE_FORMAT_G722_ADPCM 0x0065 /* APICOM */ +#define WAVE_FORMAT_DSAT_DISPLAY 0x0067 /* Microsoft Corporation */ +#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED 0x0069 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC8 0x0070 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC10 0x0071 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC16 0x0072 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC20 0x0073 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_RT24 0x0074 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_RT29 0x0075 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_RT29HW 0x0076 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_VR12 0x0077 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_VR18 0x0078 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_TQ40 0x0079 /* Voxware Inc */ +#define WAVE_FORMAT_SOFTSOUND 0x0080 /* Softsound, Ltd. */ +#define WAVE_FORMAT_VOXWARE_TQ60 0x0081 /* Voxware Inc */ +#define WAVE_FORMAT_MSRT24 0x0082 /* Microsoft Corporation */ +#define WAVE_FORMAT_G729A 0x0083 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_MVI_MVI2 0x0084 /* Motion Pixels */ +#define WAVE_FORMAT_DF_G726 0x0085 /* DataFusion Systems (Pty) (Ltd) */ +#define WAVE_FORMAT_DF_GSM610 0x0086 /* DataFusion Systems (Pty) (Ltd) */ +#define WAVE_FORMAT_ISIAUDIO 0x0088 /* Iterated Systems, Inc. */ +#define WAVE_FORMAT_ONLIVE 0x0089 /* OnLive! Technologies, Inc. */ +#define WAVE_FORMAT_SBC24 0x0091 /* Siemens Business Communications Sys */ +#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Sonic Foundry */ +#define WAVE_FORMAT_MEDIASONIC_G723 0x0093 /* MediaSonic */ +#define WAVE_FORMAT_PROSODY_8KBPS 0x0094 /* Aculab plc */ +#define WAVE_FORMAT_ZYXEL_ADPCM 0x0097 /* ZyXEL Communications, Inc. */ +#define WAVE_FORMAT_PHILIPS_LPCBB 0x0098 /* Philips Speech Processing */ +#define WAVE_FORMAT_PACKED 0x0099 /* Studer Professional Audio AG */ +#define WAVE_FORMAT_MALDEN_PHONYTALK 0x00A0 /* Malden Electronics Ltd. */ +#define WAVE_FORMAT_MP4A 0x00FF /* AAC */ +#define WAVE_FORMAT_RHETOREX_ADPCM 0x0100 /* Rhetorex Inc. */ +#define WAVE_FORMAT_IRAT 0x0101 /* BeCubed Software Inc. */ +#define WAVE_FORMAT_VIVO_G723 0x0111 /* Vivo Software */ +#define WAVE_FORMAT_VIVO_SIREN 0x0112 /* Vivo Software */ +#define WAVE_FORMAT_DIGITAL_G723 0x0123 /* Digital Equipment Corporation */ +#define WAVE_FORMAT_SANYO_LD_ADPCM 0x0125 /* Sanyo Electric Co., Ltd. */ +#define WAVE_FORMAT_SIPROLAB_ACEPLNET 0x0130 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP4800 0x0131 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP8V3 0x0132 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729 0x0133 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729A 0x0134 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_KELVIN 0x0135 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_G726ADPCM 0x0140 /* Dictaphone Corporation */ +#define WAVE_FORMAT_QUALCOMM_PUREVOICE 0x0150 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_QUALCOMM_HALFRATE 0x0151 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_TUBGSM 0x0155 /* Ring Zero Systems, Inc. */ +#define WAVE_FORMAT_WMAUDIO1 0x0160 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO2 0x0161 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIOPRO 0x0162 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO_LOSSLESS 0x0163 /* Microsoft Corporation */ +#define WAVE_FORMAT_UNISYS_NAP_ADPCM 0x0170 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ULAW 0x0171 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ALAW 0x0172 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_16K 0x0173 /* Unisys Corp. */ +#define WAVE_FORMAT_CREATIVE_ADPCM 0x0200 /* Creative Labs, Inc */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 0x0202 /* Creative Labs, Inc */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 0x0203 /* Creative Labs, Inc */ +#define WAVE_FORMAT_UHER_ADPCM 0x0210 /* UHER informatic GmbH */ +#define WAVE_FORMAT_QUARTERDECK 0x0220 /* Quarterdeck Corporation */ +#define WAVE_FORMAT_ILINK_VC 0x0230 /* I-link Worldwide */ +#define WAVE_FORMAT_RAW_SPORT 0x0240 /* Aureal Semiconductor */ +#define WAVE_FORMAT_ESST_AC3 0x0241 /* ESS Technology, Inc. */ +#define WAVE_FORMAT_IPI_HSX 0x0250 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_IPI_RPELP 0x0251 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_CS2 0x0260 /* Consistent Software */ +#define WAVE_FORMAT_SONY_SCX 0x0270 /* Sony Corp. */ +#define WAVE_FORMAT_FM_TOWNS_SND 0x0300 /* Fujitsu Corp. */ +#define WAVE_FORMAT_BTV_DIGITAL 0x0400 /* Brooktree Corporation */ +#define WAVE_FORMAT_QDESIGN_MUSIC 0x0450 /* QDesign Corporation */ +#define WAVE_FORMAT_VME_VMPCM 0x0680 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_TPC 0x0681 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_OLIGSM 0x1000 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLIADPCM 0x1001 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLICELP 0x1002 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLISBC 0x1003 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLIOPR 0x1004 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_LH_CODEC 0x1100 /* Lernout & Hauspie */ +#define WAVE_FORMAT_NORRIS 0x1400 /* Norris Communications, Inc. */ +#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS 0x1500 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_DVM 0x2000 /* FAST Multimedia AG */ +#define WAVE_FORMAT_AAC 0x706D /* AAC */ + +#if !defined(WAVE_FORMAT_EXTENSIBLE) +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* Microsoft */ +#endif // !defined(WAVE_FORMAT_EXTENSIBLE) + +#if !defined(WAVE_FORMAT_PCM) +#define WAVE_FORMAT_PCM 0x0001 +#endif // !defined(WAVE_FORMAT_PCM) + +#endif /* VC_CONTAINERS_WAVEFORMAT_H */ diff --git a/containers/core/containers_writer_utils.c b/containers/core/containers_writer_utils.c new file mode 100755 index 0000000..583ad93 --- /dev/null +++ b/containers/core/containers_writer_utils.c @@ -0,0 +1,127 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "vcos.h" + +#include + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T vc_container_writer_extraio_create(VC_CONTAINER_T *context, const char *uri, + VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PARAM_UNUSED(context); + + extraio->io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status ); + extraio->refcount = 0; + extraio->temp = 0; + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + return vc_container_writer_extraio_create(context, "null://", extraio); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + unsigned int length = strlen(context->priv->io->uri) + 5; + char *uri = malloc(length); + if(!uri) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + snprintf(uri, length, "%s.tmp", context->priv->io->uri); + status = vc_container_writer_extraio_create(context, uri, extraio); + free(uri); + extraio->temp = true; + + if(status == VC_CONTAINER_SUCCESS && !context->priv->tmp_io) + context->priv->tmp_io = extraio->io; + + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_STATUS_T status; + char *uri = extraio->temp ? vcos_strdup(extraio->io->uri) : 0; + + while(extraio->refcount) vc_container_writer_extraio_disable(context, extraio); + status = vc_container_io_close( extraio->io ); + + /* coverity[check_return] On failure the worst case is a file or directory is not removed */ + if(uri) remove(uri); + if(uri) free(uri); + + if(context->priv->tmp_io == extraio->io) + context->priv->tmp_io = 0; + + return status; +} + +/*****************************************************************************/ +int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_IO_T *tmp; + + if(!extraio->refcount) + { + vc_container_io_seek(extraio->io, INT64_C(0)); + tmp = context->priv->io; + context->priv->io = extraio->io; + extraio->io = tmp; + } + return extraio->refcount++; +} + +/*****************************************************************************/ +int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_IO_T *tmp; + + if(extraio->refcount) + { + extraio->refcount--; + if(!extraio->refcount) + { + tmp = context->priv->io; + context->priv->io = extraio->io; + extraio->io = tmp; + } + } + return extraio->refcount; +} diff --git a/containers/core/containers_writer_utils.h b/containers/core/containers_writer_utils.h new file mode 100755 index 0000000..e2ccf9e --- /dev/null +++ b/containers/core/containers_writer_utils.h @@ -0,0 +1,96 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_WRITER_UTILS_H +#define VC_CONTAINERS_WRITER_UTILS_H + +/** \file containers_writer_utils.h + * Helper functions and macros for container writers + */ + +#include "containers/containers.h" +#include "containers/containers_codecs.h" +#include "containers/core/containers_io_helpers.h" + +/***************************************************************************** + * Helper inline functions to write format specific structus to an i/o stream + *****************************************************************************/ + +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_waveformatex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_ES_FORMAT_T *format) +{ + /* Write waveformatex structure */ + WRITE_U16(p_ctx, codec_to_waveformat(format->codec), "Codec ID"); + WRITE_U16(p_ctx, format->type->audio.channels, "Number of Channels"); + WRITE_U32(p_ctx, format->type->audio.sample_rate, "Samples per Second"); + WRITE_U32(p_ctx, format->bitrate >> 3, "Average Number of Bytes Per Second"); + WRITE_U16(p_ctx, format->type->audio.block_align, "Block Alignment"); + WRITE_U16(p_ctx, format->type->audio.bits_per_sample, "Bits Per Sample"); + WRITE_U16(p_ctx, format->extradata_size, "Codec Specific Data Size"); + WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); + + return STREAM_STATUS(p_ctx); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_FOURCC_T fourcc; + + /* Write bitmapinfoheader structure */ + WRITE_U32(p_ctx, 40, "Format Data Size"); + WRITE_U32(p_ctx, format->type->video.width, "Image Width"); + WRITE_U32(p_ctx, format->type->video.height, "Image Height"); + WRITE_U16(p_ctx, 0, "Reserved"); + WRITE_U16(p_ctx, 0, "Bits Per Pixel Count"); + fourcc = codec_to_vfw_fourcc(format->codec); + WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ + LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); + WRITE_U32(p_ctx, 0, "Image Size"); + WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Colors Used Count"); + WRITE_U32(p_ctx, 0, "Important Colors Count"); + + WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); + + return STREAM_STATUS(p_ctx); +} + +/* Helper functions to create and use extra i/o */ +typedef struct VC_CONTAINER_WRITER_EXTRAIO_T { + VC_CONTAINER_IO_T *io; + unsigned int refcount; + bool temp; +} VC_CONTAINER_WRITER_EXTRAIO_T; + +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); + +#endif /* VC_CONTAINERS_WRITER_UTILS_H */ diff --git a/containers/core/packetizers.c b/containers/core/packetizers.c new file mode 100755 index 0000000..abfb2d7 --- /dev/null +++ b/containers/core/packetizers.c @@ -0,0 +1,233 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" + +/** List of registered packetizers. */ +static VC_PACKETIZER_REGISTRY_ENTRY_T *registry; + +/*****************************************************************************/ +void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry) +{ + LOG_DEBUG(0, "registering packetizer %s", entry->name); + entry->next = registry; + registry = entry; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T vc_packetizer_load(VC_PACKETIZER_T *p_ctx) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + VC_PACKETIZER_REGISTRY_ENTRY_T *entry; + + /* Try all the packetizers until we find the right one */ + for (entry = registry; entry; entry = entry->next) + { + status = entry->open(p_ctx); + if(status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) + break; + } + + return status; +} + +/*****************************************************************************/ +static void vc_packetizer_unload(VC_PACKETIZER_T *p_ctx) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); +} + +/*****************************************************************************/ +VC_PACKETIZER_T *vc_packetizer_open( VC_CONTAINER_ES_FORMAT_T *in, + VC_CONTAINER_FOURCC_T out_variant, VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_PACKETIZER_T *p_ctx = 0; + + /* Allocate our context before trying out the different packetizers */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + p_ctx->priv = (VC_PACKETIZER_PRIVATE_T *)(p_ctx + 1); + bytestream_init( &p_ctx->priv->stream ); + + p_ctx->in = vc_container_format_create(in->extradata_size); + if(!p_ctx->in) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->out = vc_container_format_create(in->extradata_size); + if(!p_ctx->out) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + vc_container_format_copy( p_ctx->in, in, in->extradata_size ); + p_ctx->in->extradata_size = 0; + vc_container_format_copy( p_ctx->out, p_ctx->in, in->extradata_size ); + p_ctx->in->extradata_size = in->extradata_size; + p_ctx->out->extradata = p_ctx->in->extradata; + p_ctx->out->extradata_size = p_ctx->in->extradata_size; + p_ctx->out->codec_variant = out_variant; + + vc_container_time_init(&p_ctx->priv->time, 1000000); + + status = vc_packetizer_load(p_ctx); + if(status != VC_CONTAINER_SUCCESS) + goto error; + + end: + if(p_status) *p_status = status; + return p_ctx; + + error: + if(p_ctx) vc_packetizer_close(p_ctx); + p_ctx = NULL; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + VC_CONTAINER_BYTESTREAM_T *stream; + VC_CONTAINER_PACKET_T *packet, *next; + + if(!p_ctx) return VC_CONTAINER_SUCCESS; + + stream = &p_ctx->priv->stream; + + if(p_ctx->in) vc_container_format_delete(p_ctx->in); + if(p_ctx->out) vc_container_format_delete(p_ctx->out); + if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); + if(p_ctx->priv->module_handle) vc_packetizer_unload(p_ctx); + + /* Free the bytestream */ + for(packet = stream->first; packet; packet = next) + { + next = packet->next; + if(packet->framework_data) free(packet); + } + + free(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *in) +{ + /* Do some sanity checking on packet ? */ + + in->framework_data = 0; + bytestream_push(&p_ctx->priv->stream, in); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T **in, VC_PACKETIZER_FLAGS_T flags) +{ + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_PACKET_T *packet, *new, **prev; + + /* Release the packets which have been read */ + while((*in = bytestream_pop(stream)) != NULL) + { + if(*in && (*in)->framework_data) + { + free(*in); + continue; + } + + if(*in) + return VC_CONTAINER_SUCCESS; + } + + if(!(flags & VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + /* Look for the 1st non-framework packet */ + for (packet = stream->first, prev = &stream->first; + packet && packet->framework_data; prev = &packet->next, packet = packet->next); + + if (!packet || (packet && packet->framework_data)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + /* We'll currently alloc an internal packet for each packet the client forcefully releases. + * We could probably do something a bit more clever than that though. */ + /* Replace the packet with a newly allocated one */ + new = malloc(sizeof(*packet) + packet->size); + if(!new) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + *new = *packet; + new->framework_data = new; + if(!new->next) + stream->last = &new->next; + if(stream->current == packet) + stream->current = new; + *prev = new; + new->data = (uint8_t *)&new[1]; + memcpy(new->data, packet->data, packet->size); + *in = packet; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, VC_PACKETIZER_FLAGS_T flags) +{ + if(!packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(!packet && (flags & VC_CONTAINER_READ_FLAG_INFO)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(packet && !packet->data && + (!(flags & VC_CONTAINER_READ_FLAG_INFO) && + !(flags & VC_CONTAINER_READ_FLAG_SKIP))) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + /* Always having a packet structure to work with simplifies things */ + if(!packet) + packet = &p_ctx->priv->packet; + + return p_ctx->priv->pf_packetize(p_ctx, packet, flags); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + + bytestream_skip( stream, stream->bytes - stream->current_offset - stream->offset ); + + if (p_ctx->priv->pf_reset) + return p_ctx->priv->pf_reset(p_ctx); + else + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/core/packetizers_private.h b/containers/core/packetizers_private.h new file mode 100755 index 0000000..ad5f8f5 --- /dev/null +++ b/containers/core/packetizers_private.h @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_PACKETIZERS_PRIVATE_H +#define VC_PACKETIZERS_PRIVATE_H + +/** \file + * Private interface for packetizers + */ + +#include +#include "containers/packetizers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_bytestream.h" +#include "containers/core/containers_time.h" + +/** \defgroup VcPacketizerModuleApi Packetizer Module API + * Private interface for modules implementing packetizers */ +/* @{ */ + +/** Context private to the packetizer instance. This private context is used to + * store data which shouldn't be exported by the public API. */ +typedef struct VC_PACKETIZER_PRIVATE_T +{ + /** Pointer to the private data of the packetizer module in use */ + struct VC_PACKETIZER_MODULE_T *module; + + /** Bytestream abstraction layer */ + struct VC_CONTAINER_BYTESTREAM_T stream; + + /** Current stream time */ + VC_CONTAINER_TIME_T time; + + /** Packetize the bytestream. + * + * \param context Pointer to the context of the instance of the packetizer + * \param out Pointer to the output packet structure which needs to be filled + * \param flags Miscellaneous flags controlling the packetizing + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_packetize)( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ); + + /** Reset packetizer state. + * + * \param context Pointer to the context of the instance of the packetizer + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_reset)( VC_PACKETIZER_T *context ); + + /** Closes a packetizer module. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_close)( struct VC_PACKETIZER_T *context ); + + /** Pointer to the packetizer module code and symbols*/ + void *module_handle; + + /** Temporary packet structure used when the caller does not provide one */ + VC_CONTAINER_PACKET_T packet; + +} VC_PACKETIZER_PRIVATE_T; + +/** Structure used by packetizers to register themselves with the core. */ +typedef struct VC_PACKETIZER_REGISTRY_ENTRY_T +{ + struct VC_PACKETIZER_REGISTRY_ENTRY_T *next; /**< To link entries together */ + const char *name; /**< Name of the packetizer */ + VC_CONTAINER_STATUS_T (*open)( VC_PACKETIZER_T * ); /**< Called to open packetizer */ +} VC_PACKETIZER_REGISTRY_ENTRY_T; + +/** Register a packetizer with the core. + * + * \param entry Entry to register with the core + */ +void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry); + +/** Utility macro used to register a packetizer with the core */ +#define VC_PACKETIZER_REGISTER(func, name) \ +VC_CONTAINER_CONSTRUCTOR(func##_register); \ +static VC_PACKETIZER_REGISTRY_ENTRY_T registry_entry = {0, name, func}; \ +void func##_register(void) { vc_packetizer_register(®istry_entry); } + +/* @} */ + +#endif /* VC_PACKETIZERS_PRIVATE_H */ diff --git a/containers/dummy/CMakeLists.txt b/containers/dummy/CMakeLists.txt new file mode 100755 index 0000000..4643438 --- /dev/null +++ b/containers/dummy/CMakeLists.txt @@ -0,0 +1,12 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(writer_dummy ${LIBRARY_TYPE} dummy_writer.c) + +target_link_libraries(writer_dummy containers) + +install(TARGETS writer_dummy DESTINATION ${VMCS_PLUGIN_DIR}) diff --git a/containers/dummy/dummy_writer.c b/containers/dummy/dummy_writer.c new file mode 100755 index 0000000..0430b98 --- /dev/null +++ b/containers/dummy/dummy_writer.c @@ -0,0 +1,137 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track[2]; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T * ); + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T dummy_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T dummy_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(packet); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T dummy_writer_control( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_PARAM_UNUSED(args); + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + if(p_ctx->tracks_num >= 2) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + /* Allocate and initialise track data */ + p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "dummy")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->track; + + p_ctx->capabilities |= VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD; + + p_ctx->priv->pf_close = dummy_writer_close; + p_ctx->priv->pf_write = dummy_writer_write; + p_ctx->priv->pf_control = dummy_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "dummy: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open dummy_writer_open +#endif diff --git a/containers/flash/CMakeLists.txt b/containers/flash/CMakeLists.txt new file mode 100755 index 0000000..bf53935 --- /dev/null +++ b/containers/flash/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_flv ${LIBRARY_TYPE} flv_reader.c) + +target_link_libraries(reader_flv containers) + +install(TARGETS reader_flv DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/flash/flv_reader.c b/containers/flash/flv_reader.c new file mode 100755 index 0000000..6e15e6b --- /dev/null +++ b/containers/flash/flv_reader.c @@ -0,0 +1,1174 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +/* Work-around for MSVC debugger issue */ +#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_FLV_READER_T +#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_FLV_READER_T + +//#define ENABLE_FLV_EXTRA_LOGGING +#define CONTAINER_IS_BIG_ENDIAN +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_index.h" +#include "containers/core/containers_logging.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) 0 + +VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define FLV_TRACKS_MAX 2 + +#define FLV_TAG_TYPE_AUDIO 8 +#define FLV_TAG_TYPE_VIDEO 9 +#define FLV_TAG_TYPE_METADATA 18 +#define FLV_TAG_HEADER_SIZE 15 + +#define FLV_SCRIPT_DATA_TYPE_NUMBER 0 +#define FLV_SCRIPT_DATA_TYPE_BOOL 1 +#define FLV_SCRIPT_DATA_TYPE_STRING 2 +#define FLV_SCRIPT_DATA_TYPE_ECMA 8 +#define FLV_SCRIPT_DATA_TYPE_LONGSTRING 12 + +#define FLV_FLAG_DISCARD 1 +#define FLV_FLAG_KEYFRAME 2 +#define FLV_FLAG_INTERFRAME 4 + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct +{ + VC_CONTAINER_STATUS_T status; + + int64_t tag_position; /* position of the current tag we're reading */ + int64_t data_position; /* position to the start of the data within the tag */ + int data_offset; /* current position inside the tag's data */ + int data_size; /* size of the data from the current tag */ + int tag_prev_size; /* size of the previous tag in the stream */ + int flags; /* flags for the current tag */ + uint32_t timestamp; /* timestamp for the current tag */ + uint32_t track; /* track the current tag belongs to */ + VC_CONTAINER_INDEX_T *index; /* index of key frames */ + +} FLV_READER_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + FLV_READER_STATE_T *state; + FLV_READER_STATE_T track_state; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[FLV_TRACKS_MAX]; + + int64_t data_offset; /*< offset to the first FLV tag in the stream */ + + FLV_READER_STATE_T state; /*< global state of the reader */ + + int audio_track; + int video_track; + + uint32_t meta_videodatarate; + uint32_t meta_audiodatarate; + float meta_framerate; + uint32_t meta_width; + uint32_t meta_height; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +/** Reads an FLV tag header + * + * @param p_ctx pointer to our context + * @param[out] p_prev_size size of the previous tag + * @param[out] p_type type of the tag + * @param[out] p_type size of the tag + * @param[out] p_type timestamp for the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_tag_header(VC_CONTAINER_T *p_ctx, int *p_prev_size, + int *p_type, int *p_size, uint32_t *p_timestamp) +{ + int prev_size, type, size; + uint32_t timestamp; + + prev_size = READ_U32(p_ctx, "PreviousTagSize"); + type = READ_U8(p_ctx, "TagType"); + size = READ_U24(p_ctx, "DataSize"); + timestamp = READ_U24(p_ctx, "Timestamp"); + timestamp |= (READ_U8(p_ctx, "TimestampExtended") << 24); + SKIP_U24(p_ctx, "StreamID"); + + if(p_prev_size) *p_prev_size = prev_size + 4; + if(p_type) *p_type = type; + if(p_size) *p_size = size; + if(p_timestamp) *p_timestamp = timestamp; + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV video data header. + * This contains the codec id and the current frame type. + * + * @param p_ctx pointer to our context + * @param[out] codec video codec + * @param[out] frame_type type of the current frame + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_videodata_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T *codec, int *frame_type) +{ + uint8_t header = READ_U8(p_ctx, "FrameType/CodecID"); + + if(frame_type) + *frame_type = (header >> 4) == 1 ? FLV_FLAG_KEYFRAME : + (header >> 4) == 3 ? FLV_FLAG_INTERFRAME : 0; + + switch(header &0xF) + { + case 2: *codec = VC_CONTAINER_CODEC_SPARK; break; + case 3: *codec = VC_FOURCC('s','c','r','1'); break; /* screen video */ + case 4: *codec = VC_CONTAINER_CODEC_VP6; break; + case 5: *codec = VC_FOURCC('v','p','6','a'); break; /* vp6 alpha */ + case 6: *codec = VC_FOURCC('s','c','r','2'); break; /* screen video 2 */ + case 7: *codec = VC_CONTAINER_CODEC_H264; break; + default: *codec = 0; break; + } + + return STREAM_STATUS(p_ctx); +} + +/** Get the properties of a video frame + * This is only really useful at setup time when trying to detect + * the type of content we are dealing with. + * This will try to get some of the properties of the video stream + * as well as codec configuration data if there is any. + * + * @param p_ctx pointer to our context + * @param track track number this data/tag belongs to + * @param size size of the data we are parsing + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_videodata_properties(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, int size) +{ + VC_CONTAINER_STATUS_T status; + int width = 0, height = 0; + + if(track->format->codec == VC_CONTAINER_CODEC_VP6 || + track->format->codec == VC_FOURCC('v','p','6','a')) + { + /* Just extract the width / height */ + uint8_t data = _READ_U8(p_ctx); + _SKIP_U16(p_ctx); + height = _READ_U8(p_ctx) * 16; + width = _READ_U8(p_ctx) * 16; + width -= data >> 4; + height -= data & 0xf; + } + else if(track->format->codec == VC_CONTAINER_CODEC_H264) + { + uint8_t type = _READ_U8(p_ctx); size--; + if(type || size <= 8) return VC_CONTAINER_ERROR_CORRUPTED; + _SKIP_U24(p_ctx); size-=3; + + track->format->codec_variant = VC_FOURCC('a','v','c','C'); + status = vc_container_track_allocate_extradata(p_ctx, track, size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + } + + track->format->type->video.width = width; + track->format->type->video.height = height; + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV audio data header. + * This contains the codec id, samplerate, number of channels and bitrate. + * + * @param p_ctx pointer to our context + * @param[out] codec audio codec + * @param[out] p_samplerate audio sampling rate + * @param[out] p_channels number of audio channels + * @param[out] p_bps bits per sample + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_audiodata_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T *codec, int *p_samplerate, int *p_channels, int *p_bps) +{ + int samplerate, channels, bps; + uint8_t header = _READ_U8(p_ctx); + + switch((header >> 2) & 0x3) + { + case 0: samplerate = 5512; break; + case 1: samplerate = 11025; break; + case 2: samplerate = 22050; break; + default: + case 3: samplerate = 44100; break; + } + + channels = 1 << (header & 1); + bps = 8 << ((header >> 1) & 1); + + switch(header >> 4) + { + case 0: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED; break; + case 1: *codec = VC_CONTAINER_CODEC_ADPCM_SWF; break; + case 2: *codec = VC_CONTAINER_CODEC_MPGA; break; + case 3: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED_LE; break; + case 4: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 8000; channels = 1; break; + case 5: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 16000; channels = 1; break; + case 6: *codec = VC_CONTAINER_CODEC_NELLYMOSER; channels = 1; break; + case 7: *codec = VC_CONTAINER_CODEC_ALAW; break; + case 8: *codec = VC_CONTAINER_CODEC_MULAW; break; + case 10: *codec = VC_CONTAINER_CODEC_MP4A; samplerate = 44100; channels = 2; break; + case 11: *codec = VC_CONTAINER_CODEC_SPEEX; break; + case 14: *codec = VC_CONTAINER_CODEC_MPGA; samplerate = 8000; break; + default: *codec = 0; break; + } + + if(p_samplerate) *p_samplerate = samplerate; + if(p_channels) *p_channels = channels; + if(p_bps) *p_bps = bps; + + return STREAM_STATUS(p_ctx); +} + +/** Get the properties of an audio frame + * This is only really useful at setup time when trying to detect + * the type of content we are dealing with. + * This will try to get some of the properties of the audio stream + * as well as codec configuration data if there is any. + * + * @param p_ctx pointer to our context + * @param track track number this data/tag belongs to + * @param size size of the data we are parsing + * @param samplerate sampling rate of the audio data + * @param channels number of channels of the audio data + * @param bps bits per sample + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_audiodata_properties(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, int size, int samplerate, int channels, int bps) +{ + static const int aac_freq[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350}; + VC_CONTAINER_STATUS_T status; + + if(track->format->codec == VC_CONTAINER_CODEC_MP4A) + { + uint8_t *p_data, data = _READ_U8(p_ctx); + size--; + if(data || size <= 0) return VC_CONTAINER_ERROR_FAILED; + + /* Read the AudioSpecificConfig */ + status = vc_container_track_allocate_extradata(p_ctx, track, size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + + if(track->format->extradata_size >= 2) + { + p_data = track->format->extradata; + data = ((p_data[0] & 0x7) << 1) | (p_data[1] >> 7); + if(data >= countof(aac_freq)) + return VC_CONTAINER_ERROR_FAILED; + + samplerate = aac_freq[data]; + channels = (p_data[1] >> 3) & 0xf; + if(track->format->extradata_size >= 5 && data == 0xf) + { + samplerate = ((p_data[1] & 0x7f) << 17) | (p_data[2] << 9) | + (p_data[3] << 1) | (p_data[4] >> 7); + channels = (p_data[4] >> 3) & 0xf; + } + } + } + + track->format->type->audio.sample_rate = samplerate; + track->format->type->audio.channels = channels; + track->format->type->audio.bits_per_sample = bps; + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV metadata tag. + * This contains metadata information about the stream. + * All the data we extract from this will be placed directly in the context. + * + * @param p_ctx pointer to our context + * @param size size of the tag + * @return FLV_FILE_NO_ERROR on success + */ +static int flv_read_metadata(VC_CONTAINER_T *p_ctx, int size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; +#define MAX_METADATA_STRING_SIZE 25 + char psz_string[MAX_METADATA_STRING_SIZE+1]; + uint16_t length, num_values; + double f_value; + uint64_t u_value; + uint8_t type; + + /* We're looking for an onMetaData script */ + type = READ_U8(p_ctx, "Type"); size--; + if(type != FLV_SCRIPT_DATA_TYPE_STRING) return VC_CONTAINER_SUCCESS; + length = READ_U16(p_ctx, "StringLength"); size -= 2; + if(!length || length > size || length > MAX_METADATA_STRING_SIZE) return VC_CONTAINER_SUCCESS; + if(READ_BYTES(p_ctx, psz_string, length) != length) return VC_CONTAINER_SUCCESS; + psz_string[length] = 0; size -= length; + if(strcmp(psz_string, "onMetaData")) return VC_CONTAINER_SUCCESS; + if(size < 5) return VC_CONTAINER_SUCCESS; + type = READ_U8(p_ctx, "Type"); size--; + if(type != FLV_SCRIPT_DATA_TYPE_ECMA) return VC_CONTAINER_SUCCESS; + num_values = READ_U32(p_ctx, "ECMAArrayLength"); size -= 4; + + /* We found our script, now extract the metadata values */ + while(num_values-- && size >= 2) + { + uint16_t length = _READ_U16(p_ctx); size -= 2; + if(!length || length >= size || length > MAX_METADATA_STRING_SIZE) break; + if(READ_BYTES(p_ctx, psz_string, length) != length) break; + psz_string[length] = 0; size -= length; + type = _READ_U8(p_ctx); size--; + + switch(type) + { + case FLV_SCRIPT_DATA_TYPE_NUMBER: + /* We only cope with DOUBLE types*/ + if(size < 8) return VC_CONTAINER_SUCCESS; + + u_value = _READ_U64(p_ctx); size -= 8; + /* Convert value into a double */ + { + int64_t value = ((u_value & ((UINT64_C(1)<<52)-1)) + (UINT64_C(1)<<52)) * ((((int64_t)u_value)>>63)|1); + int exp = ((u_value>>52)&0x7FF)-1075 + 16; + if(exp >= 0) value <<= exp; + else value >>= -exp; + f_value = ((double)value) / (1 << 16); + } + + LOG_DEBUG(p_ctx, "metadata (%s=%i.%i)", psz_string, + ((int)(f_value*100))/100, ((int)(f_value*100))%100); + + if(!strcmp(psz_string, "duration")) + p_ctx->duration = (int64_t)(f_value*INT64_C(1000000)); + if(!strcmp(psz_string, "videodatarate")) + module->meta_videodatarate = (uint32_t)f_value; + if(!strcmp(psz_string, "width")) + module->meta_width = (uint32_t)f_value; + if(!strcmp(psz_string, "height")) + module->meta_height = (uint32_t)f_value; + if(!strcmp(psz_string, "framerate")) + module->meta_framerate = f_value; + if(!strcmp(psz_string, "audiodatarate")) + module->meta_audiodatarate = (uint32_t)f_value; + continue; + + /* We skip these */ + case FLV_SCRIPT_DATA_TYPE_BOOL: + if(size < 1) return VC_CONTAINER_SUCCESS; + u_value = _READ_U8(p_ctx); size -= 1; + LOG_DEBUG(p_ctx, "metadata (%s=%i)", psz_string, (int)u_value); + continue; + + case FLV_SCRIPT_DATA_TYPE_STRING: + if(size < 2) return VC_CONTAINER_SUCCESS; + length = _READ_U16(p_ctx); size -= 2; + if(length > size) return VC_CONTAINER_SUCCESS; + SKIP_BYTES(p_ctx, length); size -= length; + LOG_DEBUG(p_ctx, "metadata skipping (%s)", psz_string); + continue; + + /* We can't cope with anything else */ + default: + LOG_DEBUG(p_ctx, "unknown amf type (%s,%i)", psz_string, type); + return VC_CONTAINER_SUCCESS; + } + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV frame header. + * This reads the current tag header and matches the contained frame + * with one of the tracks we have. If no match can be found, the frame is marked + * for discarding. The current read position will be updated to the start + * of the data (i.e. the frame) contained within the FLV tag. + * + * @param p_ctx pointer to our context + * @param[out] p_track track this frame belongs to + * @param[out] p_size size of the frame + * @param[out] p_timestamp timestamp of the frame + * @param[out] p_flags flags associated with the frame + * @param b_extra_check whether to perform extra sanity checking on the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static int flv_read_frame_header(VC_CONTAINER_T *p_ctx, int *p_prev_size, + int *p_track, int *p_size, uint32_t *p_timestamp, int *p_flags, + int b_extra_check) +{ + int64_t position = STREAM_POSITION(p_ctx); + int type, size, flags = 0, frametype = 0; + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; + unsigned int track = p_ctx->tracks_num; + uint32_t codec = 0; + + status = flv_read_tag_header(p_ctx, p_prev_size, &type, &size, p_timestamp); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return status; + if(position == STREAM_POSITION(p_ctx)) return VC_CONTAINER_ERROR_EOS; + if(type == 0) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Sanity checking */ + if(b_extra_check && type != FLV_TAG_TYPE_AUDIO && + type != FLV_TAG_TYPE_VIDEO && type != FLV_TAG_TYPE_METADATA) + return VC_CONTAINER_ERROR_CORRUPTED; + + /* We're only interested in audio / video */ + if((type != FLV_TAG_TYPE_AUDIO && type != FLV_TAG_TYPE_VIDEO) || !size) + { + flags |= FLV_FLAG_DISCARD; + goto end; + } + + if(type == FLV_TAG_TYPE_AUDIO) + { + flv_read_audiodata_header(p_ctx, &codec, 0, 0, 0); + es_type = VC_CONTAINER_ES_TYPE_AUDIO; + } + else if(type == FLV_TAG_TYPE_VIDEO) + { + flv_read_videodata_header(p_ctx, &codec, &frametype); + es_type = VC_CONTAINER_ES_TYPE_VIDEO; + } + size--; + + /* Find which track this belongs to */ + for(track = 0; track < p_ctx->tracks_num; track++) + if(es_type == p_ctx->tracks[track]->format->es_type) break; + if(track == p_ctx->tracks_num) + flags |= FLV_FLAG_DISCARD; + + /* Sanity checking */ + if(b_extra_check && codec != p_ctx->tracks[track]->format->codec) + return VC_CONTAINER_ERROR_CORRUPTED; + + end: + // add to the index if we have one, and we're not discarding this frame. + // also require that we either have no video track or this is a video frame marked as a key frame. + if(p_ctx->priv->module->state.index && !(flags & FLV_FLAG_DISCARD) && + (p_ctx->priv->module->video_track < 0 || (es_type == VC_CONTAINER_ES_TYPE_VIDEO && (frametype & FLV_FLAG_KEYFRAME)))) + vc_container_index_add(p_ctx->priv->module->state.index, (int64_t) (*p_timestamp) * 1000LL, position); + + *p_flags = flags | frametype; + *p_size = size; + *p_track = track; + return VC_CONTAINER_SUCCESS; +} + +/** Validate the data contained within the frame and update the read + * position to the start of the frame data that we want to feed to the codec. + * + * Each codec is packed slightly differently so this function is necessary + * to prepare for reading the actual codec data. + * + * @param p_ctx pointer to our context + * @param track track this frame belongs to + * @param[in] p_size size of the frame + * @param[out] p_size updated size of the frame + * @param[in] p_timestamp timestamp for the frame + * @param[out] p_timestamp updated timestamp for the frame + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_validate_frame_data(VC_CONTAINER_T *p_ctx, + int track, int *p_size, uint32_t *p_timestamp) +{ + int32_t time_offset; + + if(track >= (int)p_ctx->tracks_num) + return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE; + + switch(p_ctx->tracks[track]->format->codec) + { + case VC_CONTAINER_CODEC_VP6: + if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; + _READ_U8(p_ctx); *p_size -= 1; + break; + case VC_CONTAINER_CODEC_MP4A: + if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; + *p_size -= 1; + if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/ + break; + case VC_CONTAINER_CODEC_H264: + if(*p_size < 4) return VC_CONTAINER_ERROR_CORRUPTED; + *p_size -= 1; + if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/ + time_offset = _READ_U24(p_ctx); + time_offset <<= 8; time_offset >>= 8; /* change to signed */ + *p_timestamp += time_offset; + *p_size -= 3; + break; + default: + break; + } + + return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE; +} + +/** Small utility function to update the reading position of a track + */ +static void flv_update_track_position(VC_CONTAINER_T *p_ctx, int track, + int64_t tag_position, int tag_prev_size, int64_t data_position, + int data_size, uint32_t timestamp, int flags) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + track_module->state->tag_position = tag_position; + track_module->state->tag_prev_size = tag_prev_size; + track_module->state->data_position = data_position; + track_module->state->data_size = data_size; + track_module->state->data_offset = 0; + track_module->state->timestamp = timestamp; + track_module->state->flags = flags; + track_module->state->track = track; +} + +/** Utility function to find the next frame of a given track in the stream. + * + * This will basically walk through all the tags in the file until it + * finds a tag/frame which belongs to the given track. + * + * @param p_ctx pointer to our context + * @param track track wanted + * @param[out] p_size size of the frame + * @param[out] p_timestamp timestamp of the frame + * @param[out] p_flags flags associated with the frame + * @param b_keyframe whether we specifically want a keyframe or not + * @param b_extra_check whether to perform extra sanity checking on the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_find_next_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size, + uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check) +{ + int frame_track, prev_size, size, flags; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state; + uint32_t timestamp; + int64_t position; + VC_CONTAINER_PARAM_UNUSED(b_extra_check); + + /* Seek to the next tag in the stream or the current position + * if none of its data has been consumed */ + position = state->tag_position; + if(state->data_offset) + position = state->data_position + state->data_size; + status = SEEK(p_ctx, position); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Look for the next frame we want */ + while (status == VC_CONTAINER_SUCCESS) + { + position = STREAM_POSITION(p_ctx); + status = flv_read_frame_header(p_ctx, &prev_size, &frame_track, + &size, ×tamp, &flags, 0); + if(status != VC_CONTAINER_SUCCESS) break; + + if(flags & FLV_FLAG_DISCARD) goto skip; + if(frame_track != track) goto skip; + + if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + !(flags & FLV_FLAG_KEYFRAME)) goto skip; + + if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS) + goto skip; + + /* We have what we need */ + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, flags); + break; + + skip: + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, 0); + state->data_offset = size; /* consume data */ + + if(SKIP_BYTES(p_ctx, size) != (size_t)size) status = STREAM_STATUS(p_ctx); + } + + if(!status) + { + if(p_size) *p_size = size; + if(p_timestamp) *p_timestamp = timestamp; + if(p_flags) *p_flags = flags; + } + + return status; +} + +/** Utility function to find the previous frame of a given track in the stream. + * + * This will basically walk back through all the tags in the file until it + * finds a tag/frame which belongs to the given track. + * + * @param p_ctx pointer to our context + * @param track track wanted + * @param[out] p_size size of the frame + * @param[out] p_timestamp timestamp of the frame + * @param[out] p_flags flags associated with the frame + * @param b_keyframe whether we specifically want a keyframe or not + * @param b_extra_check whether to perform extra sanity checking on the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_find_previous_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size, + uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state; + int frame_track, prev_size, size, flags; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t timestamp; + int64_t position; + + /* Look for the previous frame we want */ + while (status == VC_CONTAINER_SUCCESS) + { + /* Seek to the previous tag in the stream */ + position = state->tag_position - state->tag_prev_size; + if(position < module->data_offset) position = module->data_offset; + status = SEEK(p_ctx, position); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = flv_read_frame_header(p_ctx, &prev_size, &frame_track, + &size, ×tamp, &flags, 0); + if(status) break; + + if(flags & FLV_FLAG_DISCARD) goto skip; + if(frame_track != track) goto skip; + + if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + !(flags & FLV_FLAG_KEYFRAME)) goto skip; + + if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS) + goto skip; + + /* We have what we need */ + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, flags); + break; + + skip: + if(position <= module->data_offset) + { + /* We're back at the beginning but we still want to return something */ + flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0, + (int64_t)module->data_offset, 0, 0, 0); + return flv_find_next_frame(p_ctx, track, p_size, p_timestamp, p_flags, b_keyframe, b_extra_check); + } + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, 0); + state->data_offset = size; /* consume data */ + } + + if(!status) + { + if(p_size) *p_size = size; + if(p_timestamp) *p_timestamp = timestamp; + if(p_flags) *p_flags = flags; + } + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + + if(module->state.index) + vc_container_index_free(module->state.index); + + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_read_sample_header( VC_CONTAINER_T *p_ctx, + FLV_READER_STATE_T *state) +{ + int track, prev_size, size, flags; + uint32_t timestamp; + int64_t position; + + /* Check if we still have some data left to read from the current frame */ + if(state->data_offset < state->data_size) + return state->status; + + /* Read the next tag header */ + position = STREAM_POSITION(p_ctx); + state->status = flv_read_frame_header(p_ctx, &prev_size, &track, + &size, ×tamp, &flags, 0); + if(state->status != VC_CONTAINER_SUCCESS) + return state->status; + + state->status = flv_validate_frame_data(p_ctx, track, &size, ×tamp); + if(state->status == VC_CONTAINER_ERROR_CONTINUE) + { + /* Skip it */ + state->status = VC_CONTAINER_SUCCESS; + track = p_ctx->tracks_num; + } + if(state->status != VC_CONTAINER_SUCCESS) + return state->status; + + state->tag_position = position; + state->data_position = STREAM_POSITION(p_ctx); + state->data_size = size; + state->data_offset = 0; + state->flags = flags; + state->tag_prev_size = prev_size; + state->timestamp = timestamp; + state->track = track; + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_read_sample_data( VC_CONTAINER_T *p_ctx, + FLV_READER_STATE_T *state, uint8_t *data, unsigned int *data_size ) +{ + unsigned int size = state->data_size - state->data_offset; + + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + if(data_size && *data_size < size) size = *data_size; + + if(!data) size = SKIP_BYTES(p_ctx, size); + else size = READ_BYTES(p_ctx, data, size); + state->data_offset += size; + + if(data_size) *data_size = size; + state->status = STREAM_STATUS(p_ctx); + + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FLV_READER_STATE_T *state = &module->state; + unsigned int data_size; + + /* TODO: select right state */ + + status = flv_read_sample_header(p_ctx, state); + if(status != VC_CONTAINER_SUCCESS) return status; + +#ifdef ENABLE_FLV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "read_sample_header (%i,%i,%i/%i/%i/%i)", state->timestamp, state->flags, + (int)state->tag_position, (int)(state->data_position-state->tag_position), state->data_offset, state->data_size); +#endif + + if(state->track >= p_ctx->tracks_num || !p_ctx->tracks[state->track]->is_enabled) + { + /* Skip packet */ + status = flv_read_sample_data(p_ctx, state, 0, 0); + if(status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ + return flv_read_sample_data(p_ctx, state, 0, 0); + + packet->dts = packet->pts = state->timestamp * (int64_t)1000; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + if(state->flags & FLV_FLAG_KEYFRAME) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if(!state->data_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + packet->track = state->track; + + // The frame size is all the data + packet->frame_size = state->data_size; + + // the size is what's left + packet->size = state->data_size - state->data_offset; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + return flv_read_sample_data(p_ctx, state, 0, 0); + else if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + data_size = packet->buffer_size; + status = flv_read_sample_data(p_ctx, state, packet->data, &data_size); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + packet->size = data_size; + if(state->data_offset != state->data_size) + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_reader_seek(VC_CONTAINER_T *p_ctx, + int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FLV_READER_STATE_T last_state = {0}; + FLV_READER_STATE_T *state; + uint32_t time = (*offset / 1000), timestamp, previous_time; + unsigned int i, track; + int size, past = 0; + int64_t position; + VC_CONTAINER_PARAM_UNUSED(mode); + + /* If we have a video track, then we want to find the keyframe closest to + * the requested time, otherwise we just look for the tag with the + * closest timestamp */ + + /* Select the track on which we'll do our seeking */ + for(i = 0, track = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue; + track = i; + break; + } + if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_CORRUPTED; + state = p_ctx->tracks[track]->priv->module->state; + previous_time = state->timestamp; + + LOG_DEBUG(p_ctx, "seek (%i, prev %i)", time, previous_time); + + if(state->index && vc_container_index_get(state->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, + offset, &position, &past) == VC_CONTAINER_SUCCESS) + { + flv_update_track_position(p_ctx, track, position, 0, position, 0, (uint32_t) (*offset / 1000LL), 0); + } + else + { + if(time < state->timestamp / 2) + flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0, + (int64_t)module->data_offset, 0, 0, 0); + past = 1; + } + + /* If past it clear then we're done, otherwise we need to find our point from here */ + if(past == 0) + { + status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + else + { + if(time > previous_time) + { + while(!status) + { + status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + if(status) break; + + /* Check if we have our frame */ + if(time <= timestamp) break; + + last_state = *state; + state->data_offset = size; /* consume data */ + } + } + else + { + while(!status) + { + status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + if(status) break; + + /* Check if we have our frame */ + if(time >= timestamp) break; + + /* Detect when we've reached the 1st keyframe to avoid an infinite loop */ + if(state->timestamp == last_state.timestamp) break; + + last_state = *state; + state->data_offset = size; /* consume data */ + } + } + } + + if(status != VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + { + LOG_DEBUG(p_ctx, "seek failed (%i)", status); + return status; + } + else if(status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "seek failed (%i), look for previous frame", status); + if(last_state.tag_position) *state = last_state; + else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + + LOG_DEBUG(p_ctx, "seek done (%i)", timestamp); + state->status = VC_CONTAINER_SUCCESS; + last_state.status = VC_CONTAINER_SUCCESS; + + if(past == 1) + { + /* Make adjustment based on seek mode */ + if((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp < time && timestamp < previous_time) + { + if(last_state.tag_position) *state = last_state; + else status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + else if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp > time) + { + if(last_state.tag_position) *state = last_state; + else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + + LOG_DEBUG(p_ctx, "seek adjustment (%i)", timestamp); + } + + if(state->data_position == last_state.data_position) + status = SEEK(p_ctx, state->data_position); + + *offset = timestamp * INT64_C(1000); + + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = 0; + uint8_t buffer[4], type_flags; + unsigned int i, frames, audio_present, video_present; + uint32_t data_offset; + + /* Check the FLV marker */ + if( PEEK_BYTES(p_ctx, buffer, 4) < 4 ) goto error; + if( buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + /* Check FLV version */ + if( buffer[3] > 4 ) + { + LOG_DEBUG(p_ctx, "Version too high: %d", buffer[3]); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + SKIP_BYTES(p_ctx, 4); /* FLV marker and version */ + + /* Find out which tracks should be available. + * FLV can only have up to 1 audio track and 1 video track. */ + type_flags = READ_U8(p_ctx, "TypeFlags"); + audio_present = !!(type_flags & 0x04); + video_present = !!(type_flags & 0x01); + + /* Sanity check DataOffset */ + data_offset = READ_U32(p_ctx, "DataOffset"); + if(data_offset < 9) goto error; + + /* + * We are dealing with an FLV file + */ + + LOG_DEBUG(p_ctx, "using flv reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + module->data_offset = data_offset; + module->audio_track = -1; + module->video_track = -1; + + /* Skip to the start of the actual data */ + SKIP_BYTES(p_ctx, data_offset - 9); + + /* We'll start parsing a few of the FLV tags to find out the + * metadata / audio / video properties. + * The first tag we should see is the metadata one which will give us all the + * properties of the stream. However we do not rely on that being there and we + * actually look at the first audio / video tags as well. */ + for(frames = 0; frames < 20; frames++) + { + VC_CONTAINER_TRACK_T *track; + int64_t offset, skip; + int prev_size, type, size, channels, samplerate, bps; + uint32_t codec, timestamp; + + /* Stop there if we have everything we want */ + if(audio_present == (module->audio_track >= 0) && + video_present == (module->video_track >= 0)) break; + if(module->audio_track >= 0 && module->video_track >= 0) break; + + /* Start reading the next tag */ + if(flv_read_tag_header(p_ctx, &prev_size, &type, &size, ×tamp)) break; + if(!size) continue; + + offset = STREAM_POSITION(p_ctx); /* to keep track of how much data we read */ + + switch(type) + { + case FLV_TAG_TYPE_AUDIO: + if(module->audio_track >= 0) break; /* We already have our audio track */ + flv_read_audiodata_header(p_ctx, &codec, &samplerate, &channels, &bps); + + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + track->format->codec = codec; + flv_read_audiodata_properties(p_ctx, track, size - 1, samplerate, channels, bps); + + module->audio_track = p_ctx->tracks_num++; + track->is_enabled = 1; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + break; + + case FLV_TAG_TYPE_VIDEO: + if(module->video_track >= 0) break; /* We already have our video track */ + flv_read_videodata_header(p_ctx, &codec, 0); + + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + track->format->codec = codec; + + status = flv_read_videodata_properties(p_ctx, track, size - 1); + if(status != VC_CONTAINER_SUCCESS) { vc_container_free_track(p_ctx, track); break; } + + module->video_track = p_ctx->tracks_num++; + track->is_enabled = 1; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + break; + + case FLV_TAG_TYPE_METADATA: + flv_read_metadata(p_ctx, size); + break; + + default: break; + } + + /* Skip any data that's left unparsed from the current tag */ + skip = size - (STREAM_POSITION(p_ctx) - offset); + if(skip < 0) break; + SKIP_BYTES(p_ctx, (size_t)skip); + } + + /* Make sure we found something we can play */ + if(!p_ctx->tracks_num) {LOG_DEBUG(p_ctx, "didn't find any track"); goto error;} + + /* Try and create an index. All times are signed, so adding a base timestamp + * of zero means that we will always seek back to the start of the file, even if + * the actual frame timestamps start at some higher number. */ + if(vc_container_index_create(&module->state.index, 512) == VC_CONTAINER_SUCCESS) + vc_container_index_add(module->state.index, 0LL, (int64_t) data_offset); + + /* Use the metadata we read */ + if(module->audio_track >= 0) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->audio_track]; + track->format->bitrate = module->meta_audiodatarate; + } + if(module->video_track >= 0) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->video_track]; + track->format->bitrate = module->meta_videodatarate; + if(module->meta_framerate) + { + track->format->type->video.frame_rate_num = (uint32_t)(100 * module->meta_framerate); + track->format->type->video.frame_rate_den = 100; + } + + if(module->meta_width && module->meta_width > track->format->type->video.width) + track->format->type->video.width = module->meta_width; + if(module->meta_height && module->meta_height > track->format->type->video.height) + track->format->type->video.height = module->meta_height; + } + + status = SEEK(p_ctx, data_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Some initialisation */ + module->state.tag_position = data_offset; + module->state.data_position = data_offset; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module; + track_module->state = &module->state; + } + + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + p_ctx->priv->pf_close = flv_reader_close; + p_ctx->priv->pf_read = flv_reader_read; + p_ctx->priv->pf_seek = flv_reader_seek; + + return VC_CONTAINER_SUCCESS; + + error: + if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + LOG_DEBUG(p_ctx, "flv: error opening stream"); + if(module) flv_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open flv_reader_open +#endif diff --git a/containers/h264/avc1_packetizer.c b/containers/h264/avc1_packetizer.c new file mode 100755 index 0000000..8082f0b --- /dev/null +++ b/containers/h264/avc1_packetizer.c @@ -0,0 +1,343 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of an ISO 14496-15 to Annexe-B AVC video packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" + +#ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#endif + +/** Arbitrary number which should be sufficiently high so that no sane frame will + * be bigger than that. */ +#define MAX_FRAME_SIZE (1920*1088*2) + +VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_FRAME_WAIT = 0, + STATE_BUFFER_INIT, + STATE_NAL_START, + STATE_NAL_DATA, + } state; + + unsigned int length_size; + + unsigned int frame_size; + unsigned int bytes_read; + unsigned int start_code_bytes_left; + unsigned int nal_bytes_left; + +} VC_PACKETIZER_MODULE_T; + +static const uint8_t h264_start_code[] = {0, 0, 0, 1}; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->state = STATE_FRAME_WAIT; + module->frame_size = 0; + module->bytes_read = 0; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_PACKET_T *packet; + unsigned int offset, size, nal_num; + uint8_t data[4]; + VC_CONTAINER_PARAM_UNUSED(nal_num); + + while(1) switch (module->state) + { + case STATE_FRAME_WAIT: + for (packet = stream->current, size = 0; + packet && !(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END); + packet = packet->next) + size += packet->size; + if (!packet) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ + + size += packet->size; + + /* We now have a complete frame available */ + + module->nal_bytes_left = 0; + module->start_code_bytes_left = 0; + + /* Find out the number of NAL units and size of the frame */ + for (offset = nal_num = 0; offset + module->length_size < size; nal_num++) + { + unsigned int nal_size; + + bytestream_peek_at(stream, offset, data, module->length_size); + offset += module->length_size; + + nal_size = data[0]; + if (module->length_size > 1) + nal_size = (nal_size << 8)|data[1]; + if (module->length_size > 2) + nal_size = (nal_size << 8)|data[2]; + if (module->length_size > 3) + nal_size = (nal_size << 8)|data[3]; + if (offset + nal_size > size) + nal_size = size - offset; + + offset += nal_size; + module->frame_size += nal_size + sizeof(h264_start_code); +#ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE + LOG_DEBUG(0, "nal unit size %u", nal_size); +#endif + } + LOG_DEBUG(0, "frame size: %u(%u/%u), pts: %"PRIi64, module->frame_size, + size, nal_num, stream->current->pts); + + /* fall through to the next state */ + module->state = STATE_BUFFER_INIT; + + case STATE_BUFFER_INIT: + packet = stream->current; + out->size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if (!module->bytes_read) + { + out->pts = packet->pts; + out->dts = packet->dts; + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if (flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if (flags & VC_PACKETIZER_FLAG_SKIP) + { + /* The easiest is to just drop all the packets belonging to the frame */ + while (!(stream->current->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)) + bytestream_skip_packet(stream); + bytestream_skip_packet(stream); + + module->frame_size = 0; + module->bytes_read = 0; + return VC_CONTAINER_SUCCESS; + } + + /* We now know that we'll have to read some data so reset the output size */ + out->size = 0; + + /* Go to the next relevant state */ + module->state = STATE_NAL_START; + if (module->nal_bytes_left || module->bytes_read == module->frame_size) + module->state = STATE_NAL_DATA; + break; + + case STATE_NAL_START: + /* Extract the size of the current NAL */ + bytestream_get(stream, data, module->length_size); + + module->nal_bytes_left = data[0]; + if (module->length_size > 1) + module->nal_bytes_left = (module->nal_bytes_left << 8)|data[1]; + if (module->length_size > 2) + module->nal_bytes_left = (module->nal_bytes_left << 8)|data[2]; + if (module->length_size > 3) + module->nal_bytes_left = (module->nal_bytes_left << 8)|data[3]; + + if (module->bytes_read + module->nal_bytes_left + sizeof(h264_start_code) > + module->frame_size) + { + LOG_ERROR(0, "truncating nal (%u/%u)", module->nal_bytes_left, + module->frame_size - module->bytes_read - sizeof(h264_start_code)); + module->nal_bytes_left = module->frame_size - sizeof(h264_start_code); + } + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE + LOG_DEBUG(0, "nal unit size %u", module->nal_bytes_left); +#endif + + module->start_code_bytes_left = sizeof(h264_start_code); + + /* fall through to the next state */ + module->state = STATE_NAL_DATA; + + case STATE_NAL_DATA: + /* Start by adding the start code */ + if (module->start_code_bytes_left) + { + size = MIN(out->buffer_size - out->size, module->start_code_bytes_left); + memcpy(out->data + out->size, h264_start_code + sizeof(h264_start_code) - + module->start_code_bytes_left, size); + module->start_code_bytes_left -= size; + module->bytes_read += size; + out->size += size; + } + + /* Then append the NAL unit itself */ + if (module->nal_bytes_left) + { + size = MIN(out->buffer_size - out->size, module->nal_bytes_left); + bytestream_get( stream, out->data + out->size, size ); + module->nal_bytes_left -= size; + module->bytes_read += size; + out->size += size; + } + + /* Check whether we're done */ + if (module->bytes_read == module->frame_size) + { + bytestream_skip_packet(stream); + module->state = STATE_FRAME_WAIT; + module->frame_size = 0; + module->bytes_read = 0; + return VC_CONTAINER_SUCCESS; + } + else if (out->buffer_size == out->size) + { + out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + module->state = STATE_BUFFER_INIT; + return VC_CONTAINER_SUCCESS; + } + + /* We're not done, go to the next relevant state */ + module->state = STATE_NAL_START; + break; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_codecconfig( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint8_t *out, *extra = p_ctx->in->extradata + 5; + uint8_t *extra_end = extra + p_ctx->in->extradata_size - 5; + unsigned int i, j, nal_size, out_size = 0; + + if (p_ctx->in->extradata_size <= 5 || + p_ctx->in->extradata[0] != 1 /* configurationVersion */) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + status = vc_container_format_extradata_alloc(p_ctx->out, p_ctx->in->extradata_size); + if (status != VC_CONTAINER_SUCCESS) + return status; + + out = p_ctx->out->extradata; + module->length_size = (*(p_ctx->in->extradata + 4) & 0x3) + 1; + + for (i = 0; i < 2 && extra < extra_end - 1; i++) + { + j = *(extra++) & (!i ? 0x1F : 0xFF); + for (; j > 0 && extra < extra_end - 2; j--) + { + nal_size = (extra[0] << 8) | extra[1]; extra += 2; + if (extra + nal_size > extra_end) + { + extra = extra_end; + break; + } + + out[0] = out[1] = out[2] = 0; out[3] = 1; + memcpy(out + 4, extra, nal_size); + out += nal_size + 4; extra += nal_size; + out_size += nal_size + 4; + } + } + + p_ctx->out->extradata_size = out_size; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + VC_CONTAINER_STATUS_T status; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_H264 && + p_ctx->out->codec != VC_CONTAINER_CODEC_H264) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(p_ctx->in->codec_variant != VC_CONTAINER_VARIANT_H264_AVC1 && + p_ctx->out->codec_variant != VC_CONTAINER_VARIANT_H264_DEFAULT) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(!(p_ctx->in->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + + vc_container_format_copy(p_ctx->out, p_ctx->in, 0); + status = avc1_packetizer_codecconfig(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + free(module); + return status; + } + + p_ctx->out->codec_variant = VC_CONTAINER_VARIANT_H264_DEFAULT; + p_ctx->max_frame_size = MAX_FRAME_SIZE; + p_ctx->priv->pf_close = avc1_packetizer_close; + p_ctx->priv->pf_packetize = avc1_packetizer_packetize; + p_ctx->priv->pf_reset = avc1_packetizer_reset; + LOG_DEBUG(0, "using avc1 video packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(avc1_packetizer_open, "avc1"); diff --git a/containers/io/io_file.c b/containers/io/io_file.c new file mode 100755 index 0000000..ea4b945 --- /dev/null +++ b/containers/io/io_file.c @@ -0,0 +1,154 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" + +typedef struct VC_CONTAINER_IO_MODULE_T +{ + FILE *stream; + +} VC_CONTAINER_IO_MODULE_T; + +VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_file_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + fclose(module->stream); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_file_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret = fread(buffer, 1, size, p_ctx->module->stream); + if(ret != size) + { + /* Sanity check return value. Some platforms (e.g. Android) can return -1 */ + if( ((int)ret) < 0 ) ret = 0; + + if( feof(p_ctx->module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + } + return ret; +} + +/*****************************************************************************/ +static size_t io_file_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + return fwrite(buffer, 1, size, p_ctx->module->stream); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_file_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int ret; + + //FIXME: large file support +#ifdef _VIDEOCORE + extern int fseek64(FILE *fp, int64_t offset, int whence); + ret = fseek64(p_ctx->module->stream, offset, SEEK_SET); +#else + if (offset > (int64_t)UINT_MAX) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return VC_CONTAINER_ERROR_EOS; + } + ret = fseek(p_ctx->module->stream, (long)offset, SEEK_SET); +#endif + if(ret) + { + if( feof(p_ctx->module->stream) ) status = VC_CONTAINER_ERROR_EOS; + else status = VC_CONTAINER_ERROR_FAILED; + } + + p_ctx->status = status; + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb"; + const char *uri = p_ctx->uri; + FILE *stream = 0; + VC_CONTAINER_PARAM_UNUSED(unused); + + if(vc_uri_path(p_ctx->uri_parts)) + uri = vc_uri_path(p_ctx->uri_parts); + + stream = fopen(uri, psz_mode); + if(!stream) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + /* Turn off buffering. The container layer will provide its own cache */ + setvbuf(stream, NULL, _IONBF, 0); + + module = malloc( sizeof(*module) ); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + + p_ctx->module = module; + module->stream = stream; + p_ctx->pf_close = io_file_close; + p_ctx->pf_read = io_file_read; + p_ctx->pf_write = io_file_write; + p_ctx->pf_seek = io_file_seek; + + if(mode == VC_CONTAINER_IO_MODE_WRITE) + { + p_ctx->max_size = (1UL<<31)-1; /* For now limit to 2GB */ + } + else + { + //FIXME: large file support, platform-specific file size + fseek(p_ctx->module->stream, 0, SEEK_END); + p_ctx->size = ftell(p_ctx->module->stream); + fseek(p_ctx->module->stream, 0, SEEK_SET); + } + + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; + return VC_CONTAINER_SUCCESS; + + error: + if(stream) fclose(stream); + return status; +} diff --git a/containers/io/io_http.c b/containers/io/io_http.c new file mode 100755 index 0000000..48d8661 --- /dev/null +++ b/containers/io/io_http.c @@ -0,0 +1,898 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_list.h" +#include "containers/core/containers_utils.h" +#include "containers/net/net_sockets.h" + +/* Set to 1 if you want to log all HTTP requests */ +#define ENABLE_HTTP_EXTRA_LOGGING 0 + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define IO_HTTP_DEFAULT_PORT "80" + +/** Space for sending requests and receiving responses */ +#define COMMS_BUFFER_SIZE 4000 + +/** Largest allowed HTTP URI. Must be substantially smaller than COMMS_BUFFER_SIZE + * to allow for the headers that may be sent. */ +#define HTTP_URI_LENGTH_MAX 1024 + +/** Initial capacity of header list */ +#define HEADER_LIST_INITIAL_CAPACITY 16 + +/** Format of the first line of an HTTP request */ +#define HTTP_REQUEST_LINE_FORMAT "%s %s HTTP/1.1\r\nHost: %s\r\n" + +/** Format of a range request */ +#define HTTP_RANGE_REQUEST "Range: bytes=%"PRId64"-%"PRId64"\r\n" + +/** Format string for common headers used with all request methods. + * Note: includes double new line to terminate headers */ +#define TRAILING_HEADERS_FORMAT "User-Agent: Broadcom/1.0\r\n\r\n" + +/** \name HTTP methods, used as the first item in the request line + * @{ */ +#define GET_METHOD "GET" +#define HEAD_METHOD "HEAD" +/* @} */ + +/** \name Names of headers used by the code + * @{ */ +#define CONTENT_LENGTH_NAME "Content-Length" +#define CONTENT_BASE_NAME "Content-Base" +#define CONTENT_LOCATION_NAME "Content-Location" +#define ACCEPT_RANGES_NAME "Accept-Ranges" +#define CONNECTION_NAME "Connection" +/* @} */ + +/** Supported HTTP major version number */ +#define HTTP_MAJOR_VERSION 1 +/** Supported HTTP minor version number */ +#define HTTP_MINOR_VERSION 1 + +/** Lowest successful status code value */ +#define HTTP_STATUS_OK 200 +#define HTTP_STATUS_PARTIAL_CONTENT 206 + +typedef struct http_header_tag { + const char *name; + char *value; +} HTTP_HEADER_T; + + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_IO_MODULE_T +{ + VC_CONTAINER_NET_T *sock; + VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */ + + bool persistent; + int64_t cur_offset; + bool reconnecting; + + /* Buffer used for sending and receiving HTTP messages */ + char comms_buffer[COMMS_BUFFER_SIZE]; +} VC_CONTAINER_IO_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second); +static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx); + +VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Trim whitespace from the end and start of the string + * + * \param str String to be trimmed + * \return Trimmed string + */ +static char *io_http_trim(char *str) +{ + char *s = str + strlen(str); + + /* Search backwards for first non-whitespace */ + while (--s >= str &&(*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) + ; /* Everything done in the while */ + s[1] = '\0'; + + /* Now move start of string forwards to first non-whitespace */ + s = str; + while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') + s++; + + return s; +} + +/**************************************************************************//** + * Header comparison function. + * Compare two header structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second) +{ + return strcasecmp(first->name, second->name); +} + +/**************************************************************************//** + * Check a response status line to see if the response is usable or not. + * Reasons for invalidity include: + * - Incorrectly formatted + * - Unsupported version + * - Status code is not in the 2xx range + * + * @param status_line The response status line. + * @return The resulting status of the function. + */ +static bool io_http_successful_response_status(const char *status_line) +{ + unsigned int major_version, minor_version, status_code; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(status_line, "HTTP/%u.%u %u", &major_version, &minor_version, &status_code) != 3) + { + LOG_ERROR(NULL, "HTTP: Invalid response status line:\n%s", status_line); + return false; + } + + if (major_version != HTTP_MAJOR_VERSION || minor_version != HTTP_MINOR_VERSION) + { + LOG_ERROR(NULL, "HTTP: Unexpected response HTTP version: %u.%u", major_version, minor_version); + return false; + } + + if (status_code != HTTP_STATUS_OK && status_code != HTTP_STATUS_PARTIAL_CONTENT) + { + LOG_ERROR(NULL, "HTTP: Response status unsuccessful:\n%s", status_line); + return false; + } + + return true; +} + +/**************************************************************************//** + * Get the content length header from the response headers as an unsigned + * 64-bit integer. + * If the content length header is not found or badly formatted, zero is + * returned. + * + * @param header_list The response headers. + * @return The content length. + */ +static uint64_t io_http_get_content_length(VC_CONTAINERS_LIST_T *header_list) +{ + uint64_t content_length = 0; + HTTP_HEADER_T header; + + header.name = CONTENT_LENGTH_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + /* coverity[secure_coding] String is null-terminated */ + sscanf(header.value, "%"PRIu64, &content_length); + + return content_length; +} + +/**************************************************************************//** + * Get the accept ranges header from the response headers and verify that + * the server accepts byte ranges.. + * If the accept ranges header is not found false is returned. + * + * @param header_list The response headers. + * @return The resulting status of the function. + */ +static bool io_http_check_accept_range(VC_CONTAINERS_LIST_T *header_list) +{ + HTTP_HEADER_T header; + + header.name = ACCEPT_RANGES_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + { + /* coverity[secure_coding] String is null-terminated */ + if (!strcasecmp(header.value, "bytes")) + return true; + } + + return false; +} + +/**************************************************************************//** + * Check whether the server supports persistent connections. + * + * @param header_list The response headers. + * @return The resulting status of the function. + */ +static bool io_http_check_persistent_connection(VC_CONTAINERS_LIST_T *header_list) +{ + HTTP_HEADER_T header; + + header.name = CONNECTION_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + { + /* coverity[secure_coding] String is null-terminated */ + if (!strcasecmp(header.value, "close")) + return false; + } + + return true; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status) +{ + switch (net_status) + { + case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS; + case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED; + case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE; + default: return VC_CONTAINER_ERROR_FAILED; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_open_socket(VC_CONTAINER_IO_T *ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = ctx->module; + VC_CONTAINER_STATUS_T status; + const char *host, *port; + + /* Treat empty host or port strings as not defined */ + port = vc_uri_port(ctx->uri_parts); + if (port && !*port) + port = NULL; + + /* Require the port to be defined */ + if (!port) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + + host = vc_uri_host(ctx->uri_parts); + if (host && !*host) + host = NULL; + + if (!host) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + + module->sock = vc_container_net_open(host, port, VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL); + if (!module->sock) + { + status = VC_CONTAINER_ERROR_URI_NOT_FOUND; + goto error; + } + + return VC_CONTAINER_SUCCESS; + +error: + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_close_socket(VC_CONTAINER_IO_MODULE_T *module) +{ + if (module->sock) + { + vc_container_net_close(module->sock); + module->sock = NULL; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_http_read_from_net(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret; + vc_container_net_status_t net_status; + + ret = vc_container_net_read(p_ctx->module->sock, buffer, size); + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + + return ret; +} + +/**************************************************************************//** + * Reads an HTTP response and parses it into headers and content. + * The headers and content remain stored in the comms buffer, but referenced + * by the module's header list. Content uses a special header name that cannot + * occur in the real headers. + * + * @param p_ctx The HTTP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_read_response(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + char *next_read = module->comms_buffer; + size_t space_available = sizeof(module->comms_buffer) - 1; /* Allow for a NUL */ + char *ptr = next_read; + bool end_response = false; + HTTP_HEADER_T header; + const char endstr[] = "\r\n\r\n"; + int endcount = sizeof(endstr) - 1; + int endchk = 0; + + vc_containers_list_reset(module->header_list); + + /* Response status line doesn't need to be stored, just checked */ + header.name = NULL; + header.value = next_read; + + /* + * We need to read just a byte at a time to make sure that we just read the HTTP response and + * no more. For example, if a GET operation was requested the file being fetched will also + * be waiting to be read on the socket. + */ + + while (space_available) + { + if (io_http_read_from_net(p_ctx, next_read, 1) != 1) + break; + + next_read++; + space_available--; + + if (next_read[-1] == endstr[endchk]) + { + if (++endchk == endcount) + break; + } + else + endchk = 0; + } + if (!space_available) + { + LOG_ERROR(NULL, "comms buffer too small for complete HTTP message (%d)", + sizeof(module->comms_buffer)); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + *next_read = '\0'; + + if (endchk == endcount) + { + if (ENABLE_HTTP_EXTRA_LOGGING) + LOG_DEBUG(NULL, "READ FROM SERVER: %d bytes\n%s\n-----------------------------------------", + sizeof(module->comms_buffer) - 1 - space_available, module->comms_buffer); + + while (!end_response && ptr < next_read) + { + switch (*ptr) + { + case ':': + if (header.value) + { + /* Just another character in the value */ + ptr++; + } else { + /* End of name, expect value next */ + *ptr++ = '\0'; + header.value = ptr; + } + break; + + case '\n': + if (header.value) + { + /* End of line while parsing the value part of the header, add name/value pair to list */ + *ptr++ = '\0'; + header.value = io_http_trim(header.value); + if (header.name) + { + if (!vc_containers_list_insert(module->header_list, &header, false)) + { + LOG_ERROR(NULL, "HTTP: Failed to add <%s> header to list", header.name); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } else { + /* Check response status line */ + if (!io_http_successful_response_status(header.value)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + /* Ready for next header */ + header.name = ptr; + header.value = NULL; + } else { + /* End of line while parsing the name of a header */ + *ptr++ = '\0'; + if (*header.name && *header.name != '\r') + { + /* A non-empty name is invalid, so fail */ + LOG_ERROR(NULL, "HTTP: Invalid name in header - no colon:\n%s", header.name); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* An empty name signifies the end of the HTTP response */ + end_response = true; + } + break; + + default: + /* Just another character in either the name or the value */ + ptr++; + } + } + } + + if (!space_available && !end_response) + { + /* Ran out of buffer space */ + LOG_ERROR(NULL, "HTTP: Response header section too big"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return p_ctx->status; +} + +/**************************************************************************//** + * Send a GET request to the HTTP server. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_send_get_request(VC_CONTAINER_IO_T *p_ctx, size_t size) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer); + int64_t end_offset; + + ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, GET_METHOD, + vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts)); + + end_offset = module->cur_offset + size - 1; + if (end_offset >= p_ctx->size) + end_offset = p_ctx->size - 1; + + if (ptr < end) + ptr += snprintf(ptr, end - ptr, HTTP_RANGE_REQUEST, module->cur_offset, end_offset); + + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT); + + if (ptr >= end) + { + LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr), + sizeof(module->comms_buffer)); + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + if (ENABLE_HTTP_EXTRA_LOGGING) + LOG_DEBUG(NULL, "Sending server read request:\n%s\n---------------------\n", module->comms_buffer); + return io_http_send(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + + /* + * No seeking past the end of the file. + */ + + if (offset < 0 || offset > p_ctx->size) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return VC_CONTAINER_ERROR_EOS; + } + + module->cur_offset = offset; + p_ctx->status = VC_CONTAINER_SUCCESS; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_close(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + + if (!module) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + io_http_close_socket(module); + if (module->header_list) + vc_containers_list_destroy(module->header_list); + + free(module); + p_ctx->module = NULL; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_http_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + size_t content_length; + size_t bytes_read; + size_t ret = 0; + char *ptr = buffer; + + /* + * Are we at the end of the file? + */ + + if (module->cur_offset >= p_ctx->size) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return 0; + } + + if (!module->persistent) + { + status = io_http_open_socket(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(NULL, "Error opening socket for GET request"); + return status; + } + } + + /* Send GET request and get response */ + status = io_http_send_get_request(p_ctx, size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(NULL, "Error sending GET request"); + goto error; + } + + status = io_http_read_response(p_ctx); + if (status == VC_CONTAINER_ERROR_EOS && !module->reconnecting) + { + LOG_DEBUG(NULL, "reconnecting"); + io_http_close_socket(module); + status = io_http_open_socket(p_ctx); + if (status == VC_CONTAINER_SUCCESS) + { + module->reconnecting = true; + status = io_http_read(p_ctx, buffer, size); + module->reconnecting = false; + return status; + } + } + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(NULL, "Error reading GET response"); + goto error; + } + + /* + * How much data is the server offering us? + */ + + content_length = (size_t)io_http_get_content_length(module->header_list); + if (content_length > size) + { + LOG_ERROR(NULL, "received too much data (%i/%i)", + (int)content_length, (int)size); + status = VC_CONTAINER_ERROR_CORRUPTED; + goto error; + } + + bytes_read = 0; + while (bytes_read < content_length && p_ctx->status == VC_CONTAINER_SUCCESS) + { + ret = io_http_read_from_net(p_ctx, ptr, content_length - bytes_read); + if (p_ctx->status == VC_CONTAINER_SUCCESS) + { + bytes_read += ret; + ptr += ret; + } + } + + if (p_ctx->status == VC_CONTAINER_SUCCESS) + { + module->cur_offset += bytes_read; + ret = bytes_read; + } + + if (!module->persistent) + io_http_close_socket(module); + + return ret; + +error: + if (!module->persistent) + io_http_close_socket(module); + + return status; +} + +/*****************************************************************************/ +static size_t io_http_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size); + vc_container_net_status_t net_status; + + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_control(struct VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, + va_list args) +{ + vc_container_net_status_t net_status; + VC_CONTAINER_STATUS_T status; + + switch (operation) + { + case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args); + break; + case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args); + break; + default: + net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + status = translate_net_status_to_container_status(net_status); + p_ctx->status = status; + + return status; +} + +/**************************************************************************//** + * Send out the data in the comms buffer. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + size_t to_write; + size_t written; + const char *buffer = module->comms_buffer; + + to_write = strlen(buffer); + + while (to_write) + { + written = io_http_write(p_ctx, buffer, to_write); + if (p_ctx->status != VC_CONTAINER_SUCCESS) + break; + + to_write -= written; + buffer += written; + } + + return p_ctx->status; +} + +/**************************************************************************//** + * Send a HEAD request to the HTTP server. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_send_head_request(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer); + + ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, HEAD_METHOD, + vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts)); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT); + + if (ptr >= end) + { + LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr), + sizeof(module->comms_buffer)); + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + return io_http_send(p_ctx); +} + +static VC_CONTAINER_STATUS_T io_http_head(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + uint64_t content_length; + + /* Send HEAD request and get response */ + status = io_http_send_head_request(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + status = io_http_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + + /* + * Save the content length since that's our file size. + */ + + content_length = io_http_get_content_length(module->header_list); + if (content_length) + { + p_ctx->size = content_length; + LOG_DEBUG(NULL, "File size is %"PRId64, p_ctx->size); + } + + /* + * Now make sure that the server supports byte range requests. + */ + + if (!io_http_check_accept_range(module->header_list)) + { + LOG_ERROR(NULL, "Server doesn't support byte range requests"); + return VC_CONTAINER_ERROR_FAILED; + } + + /* + * Does it support persistent connections? + */ + + if (io_http_check_persistent_connection(module->header_list)) + { + module->persistent = true; + } + else + { + LOG_DEBUG(NULL, "Server does not support persistent connections"); + io_http_close_socket(module); + } + + module->cur_offset = 0; + + return status; +} + +/***************************************************************************** +Functions exported as part of the I/O Module API + *****************************************************************************/ + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + VC_CONTAINER_PARAM_UNUSED(unused); + + /* Check the URI to see if we're dealing with an http stream */ + if (!vc_uri_scheme(p_ctx->uri_parts) || + strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "http")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * Some basic error checking. + */ + + if (mode == VC_CONTAINER_IO_MODE_WRITE) + { + status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + goto error; + } + + if (strlen(p_ctx->uri) > HTTP_URI_LENGTH_MAX) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + + module = calloc(1, sizeof(*module)); + if (!module) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + p_ctx->module = module; + + /* header_list will contain pointers into the response_buffer, so take care in re-use */ + module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(HTTP_HEADER_T), + (VC_CONTAINERS_LIST_COMPARATOR_T)io_http_header_comparator); + if (!module->header_list) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + + /* + * Make sure that we have a port number. + */ + + if (vc_uri_port(p_ctx->uri_parts) == NULL) + vc_uri_set_port(p_ctx->uri_parts, IO_HTTP_DEFAULT_PORT); + + status = io_http_open_socket(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + /* + * Whoo hoo! Our socket is open. Now let's send a HEAD request. + */ + + status = io_http_head(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + p_ctx->pf_close = io_http_close; + p_ctx->pf_read = io_http_read; + p_ctx->pf_write = NULL; + p_ctx->pf_control = io_http_control; + p_ctx->pf_seek = io_http_seek; + + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; + p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_SEEK_SLOW; + + return VC_CONTAINER_SUCCESS; + +error: + io_http_close(p_ctx); + return status; +} diff --git a/containers/io/io_net.c b/containers/io/io_net.c new file mode 100755 index 0000000..a1912e5 --- /dev/null +++ b/containers/io/io_net.c @@ -0,0 +1,379 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" +#include "containers/net/net_sockets.h" + +/* Uncomment this macro definition to capture data read and written through this interface */ +/* #define IO_NET_CAPTURE_PACKETS */ + +#ifdef IO_NET_CAPTURE_PACKETS +#include + +#ifdef ENABLE_CONTAINERS_STANDALONE +#ifdef _MSC_VER +#define IO_NET_CAPTURE_PREFIX "C:\\" +#else /* !_MSC_VER */ +#define IO_NET_CAPTURE_PREFIX "~/" +#endif +#else /* !ENABLE_CONTAINERS_STANDALONE */ +#define IO_NET_CAPTURE_PREFIX "/mfs/sd/" +#endif + +#define IO_NET_CAPTURE_READ_FILE "capture_read_%s_%s%c.pkt" +#define IO_NET_CAPTURE_WRITE_FILE "capture_write_%s_%s%c.pkt" +#define IO_NET_CAPTURE_READ_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_READ_FILE +#define IO_NET_CAPTURE_WRITE_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_WRITE_FILE + +#define CAPTURE_FILENAME_BUFFER_SIZE 300 + +#define CAPTURE_BUFFER_SIZE 65536 + +/** Native byte order word */ +#define NATIVE_BYTE_ORDER 0x50415753 +#endif + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_IO_MODULE_T +{ + VC_CONTAINER_NET_T *sock; +#ifdef IO_NET_CAPTURE_PACKETS + FILE *read_capture_file; + FILE *write_capture_file; +#endif +} VC_CONTAINER_IO_MODULE_T; + +/** List of recognised network URI schemes (TCP or UDP). + * Note: always use lower case for the scheme name. */ +static struct +{ + const char *scheme; + bool is_udp; +} recognised_schemes[] = { + { "rtp:", true }, + { "rtsp:", false }, +}; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +#ifdef IO_NET_CAPTURE_PACKETS +/*****************************************************************************/ +static FILE *io_net_open_capture_file(const char *host_str, + const char *port_str, + bool is_udp, + VC_CONTAINER_IO_MODE_T mode) +{ + char filename[CAPTURE_FILENAME_BUFFER_SIZE]; + const char *format; + FILE *stream = NULL; + uint32_t byte_order = NATIVE_BYTE_ORDER; + + switch (mode) + { + case VC_CONTAINER_IO_MODE_WRITE: + format = IO_NET_CAPTURE_WRITE_FORMAT; + break; + case VC_CONTAINER_IO_MODE_READ: + format = IO_NET_CAPTURE_READ_FORMAT; + break; + default: + /* Invalid mode */ + return NULL; + } + + if (!host_str) + host_str = ""; + if (!port_str) + port_str = ""; + + /* Check filename will fit in buffer */ + if (strlen(format) + strlen(host_str) + strlen(port_str) - 4 > CAPTURE_FILENAME_BUFFER_SIZE) + return NULL; + + /* Create the file */ + sprintf(filename, format, host_str, port_str, is_udp ? 'u' : 't'); + stream = fopen(filename, "wb"); + if (!stream) + return NULL; + + /* Buffer plenty of data at a time, if possible */ + setvbuf(stream, NULL, _IOFBF, CAPTURE_BUFFER_SIZE); + + /* Start file with a byte order marker */ + if (fwrite(&byte_order, 1, sizeof(byte_order), stream) != sizeof(byte_order)) + { + /* Failed to write even just the byte order mark - abort */ + fclose(stream); + stream = NULL; + remove(filename); + } + + return stream; +} + +/*****************************************************************************/ +static void io_net_capture_write_packet( FILE *stream, + const char *buffer, + uint32_t buffer_size ) +{ + if (stream && buffer && buffer_size) + { + fwrite(&buffer_size, 1, sizeof(buffer_size), stream); + fwrite(buffer, 1, buffer_size, stream); + } +} +#endif + +/*****************************************************************************/ +static bool io_net_recognise_scheme(const char *uri, bool *is_udp) +{ + size_t ii; + const char *scheme; + + if (!uri) + return false; + + for (ii = 0; ii < countof(recognised_schemes); ii++) + { + scheme = recognised_schemes[ii].scheme; + if (strncmp(scheme, uri, strlen(scheme)) == 0) + { + *is_udp = recognised_schemes[ii].is_udp; + return true; + } + } + + return false; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status) +{ + switch (net_status) + { + case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS; + case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED; + case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE; + default: return VC_CONTAINER_ERROR_FAILED; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_net_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + + if (!module) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + if (module->sock) + vc_container_net_close(module->sock); +#ifdef IO_NET_CAPTURE_PACKETS + if (module->read_capture_file) + fclose(module->read_capture_file); + if (module->write_capture_file) + fclose(module->write_capture_file); +#endif + free(module); + p_ctx->module = NULL; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_net_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret = vc_container_net_read(p_ctx->module->sock, buffer, size); + vc_container_net_status_t net_status; + + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + +#ifdef IO_NET_CAPTURE_PACKETS + if (p_ctx->status == VC_CONTAINER_SUCCESS) + io_net_capture_write_packet(p_ctx->module->read_capture_file, (const char *)buffer, ret); +#endif + + return ret; +} + +/*****************************************************************************/ +static size_t io_net_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size); + vc_container_net_status_t net_status; + + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + +#ifdef IO_NET_CAPTURE_PACKETS + if (p_ctx->status == VC_CONTAINER_SUCCESS) + io_net_capture_write_packet(p_ctx->module->write_capture_file, (const char *)buffer, ret); +#endif + + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_net_control(struct VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, + va_list args) +{ + vc_container_net_status_t net_status; + VC_CONTAINER_STATUS_T status; + + switch (operation) + { + case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args); + break; + case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args); + break; + default: + net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + status = translate_net_status_to_container_status(net_status); + p_ctx->status = status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_net_open_socket(VC_CONTAINER_IO_T *ctx, + VC_CONTAINER_IO_MODE_T mode, bool is_udp) +{ + VC_CONTAINER_IO_MODULE_T *module = ctx->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + const char *host, *port; + + /* Treat empty host or port strings as not defined */ + port = vc_uri_port(ctx->uri_parts); + if (port && !*port) + port = NULL; + + /* Require the port to be defined */ + if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } + + host = vc_uri_host(ctx->uri_parts); + if (host && !*host) + host = NULL; + + if (!host) + { + /* TCP servers cannot be handled by this interface and UDP senders need a target */ + if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + } + + module->sock = vc_container_net_open(host, port, is_udp ? 0 : VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL); + if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + +#ifdef IO_NET_CAPTURE_PACKETS + if (!is_udp || mode == VC_CONTAINER_IO_MODE_READ) + module->read_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_READ); + if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE) + module->write_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_WRITE); +#endif + +error: + return status; +} + +/***************************************************************************** +Functions exported as part of the I/O Module API + *****************************************************************************/ + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + bool is_udp; + VC_CONTAINER_PARAM_UNUSED(unused); + + if (!io_net_recognise_scheme(p_ctx->uri, &is_udp)) + { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + module = (VC_CONTAINER_IO_MODULE_T *)malloc( sizeof(*module) ); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->module = module; + + status = io_net_open_socket(p_ctx, mode, is_udp); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + p_ctx->pf_close = io_net_close; + p_ctx->pf_read = io_net_read; + p_ctx->pf_write = io_net_write; + p_ctx->pf_control = io_net_control; + + /* Disable caching, as this will block waiting for enough data to fill the cache or an error */ + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK; + + return VC_CONTAINER_SUCCESS; + +error: + io_net_close(p_ctx); + return status; +} diff --git a/containers/io/io_null.c b/containers/io/io_null.c new file mode 100755 index 0000000..cd83bb8 --- /dev/null +++ b/containers/io/io_null.c @@ -0,0 +1,91 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" + +VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_null_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_null_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + return size; +} + +/*****************************************************************************/ +static size_t io_null_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + return size; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_null_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(offset); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_PARAM_UNUSED(unused); + VC_CONTAINER_PARAM_UNUSED(mode); + + /* Check the URI */ + if (!vc_uri_scheme(p_ctx->uri_parts) || + (strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null") && + strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null"))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->pf_close = io_null_close; + p_ctx->pf_read = io_null_read; + p_ctx->pf_write = io_null_write; + p_ctx->pf_seek = io_null_seek; + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/io/io_pktfile.c b/containers/io/io_pktfile.c new file mode 100755 index 0000000..7f4defb --- /dev/null +++ b/containers/io/io_pktfile.c @@ -0,0 +1,261 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" + +/** Native byte order word */ +#define NATIVE_BYTE_ORDER 0x50415753 +/** Reverse of native byte order - need to swap bytes around */ +#define SWAP_BYTE_ORDER 0x53574150 + +typedef struct VC_CONTAINER_IO_MODULE_T +{ + FILE *stream; + bool is_native_order; +} VC_CONTAINER_IO_MODULE_T; + +/** List of recognised schemes. + * Note: always use lower case for the scheme name. */ +static const char * recognised_schemes[] = { + "rtp", "rtppkt", "rtsp", "rtsppkt", "pktfile", +}; + +VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/*****************************************************************************/ +static bool recognise_scheme(const char *scheme) +{ + size_t ii; + + if (!scheme) + return false; + + for (ii = 0; ii < countof(recognised_schemes); ii++) + { + if (strcmp(recognised_schemes[ii], scheme) == 0) + return true; + } + + return false; +} + +/*****************************************************************************/ +static uint32_t swap_byte_order( uint32_t value ) +{ + /* Reverse the order of the bytes in the word */ + return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24)); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_pktfile_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + fclose(module->stream); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_pktfile_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + uint32_t length = 0; + size_t ret; + + ret = fread(&length, 1, sizeof(length), module->stream); + if (ret != sizeof(length)) + { + if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + return 0; + } + + if (!module->is_native_order) + length = swap_byte_order(length); + + if (length > 1<<20) + { + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + return 0; + } + + if (size > length) + size = length; + ret = fread(buffer, 1, size, module->stream); + if(ret != size) + { + if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + } + else if (length > size) + { + /* Not enough space to read all the packet, so skip to the next one. */ + length -= size; + vc_container_assert((long)length > 0); + fseek(module->stream, (long)length, SEEK_CUR); + } + + return ret; +} + +/*****************************************************************************/ +static size_t io_pktfile_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + uint32_t size_word; + size_t ret; + + if (size >= 0xFFFFFFFFUL) + size_word = 0xFFFFFFFFUL; + else + size_word = (uint32_t)size; + + ret = fwrite(&size_word, 1, sizeof(size_word), p_ctx->module->stream); + if (ret != sizeof(size_word)) + { + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + return 0; + } + + ret = fwrite(buffer, 1, size_word, p_ctx->module->stream); + if (ret != size_word) + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + if (fflush(p_ctx->module->stream) != 0) + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + + return ret; +} + +/*****************************************************************************/ +static FILE *open_file(VC_CONTAINER_IO_T *ctx, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_STATUS_T *p_status) +{ + const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb"; + FILE *stream = 0; + const char *port, *path; + + /* Treat empty port or path strings as not defined */ + port = vc_uri_port(ctx->uri_parts); + if (port && !*port) + port = NULL; + + path = vc_uri_path(ctx->uri_parts); + if (path && !*path) + path = NULL; + + /* Require the port to be undefined and the path to be defined */ + if (port || !path) { *p_status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } + + if (!recognise_scheme(vc_uri_scheme(ctx->uri_parts))) + { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + stream = fopen(path, psz_mode); + if(!stream) { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + *p_status = VC_CONTAINER_SUCCESS; + return stream; + +error: + return NULL; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T write_byte_order(FILE *stream) +{ + /* Simple byte order header word */ + uint32_t value = NATIVE_BYTE_ORDER; + + if (fwrite(&value, 1, sizeof(value), stream) != sizeof(value)) + return VC_CONTAINER_ERROR_OUT_OF_SPACE; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T read_byte_order(FILE *stream, bool *is_native) +{ + uint32_t value; + + if (fread(&value, 1, sizeof(value), stream) != sizeof(value)) + return VC_CONTAINER_ERROR_EOS; + + switch (value) + { + case NATIVE_BYTE_ORDER: *is_native = true; break; + case SWAP_BYTE_ORDER: *is_native = false; break; + default: return VC_CONTAINER_ERROR_CORRUPTED; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + FILE *stream = 0; + bool is_native_order = true; + VC_CONTAINER_PARAM_UNUSED(unused); + + stream = open_file(p_ctx, mode, &status); + if (status != VC_CONTAINER_SUCCESS) goto error; + + if (mode == VC_CONTAINER_IO_MODE_WRITE) + status = write_byte_order(stream); + else + status = read_byte_order(stream, &is_native_order); + if (status != VC_CONTAINER_SUCCESS) goto error; + + module = malloc( sizeof(*module) ); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + + p_ctx->module = module; + module->stream = stream; + module->is_native_order = is_native_order; + p_ctx->pf_close = io_pktfile_close; + p_ctx->pf_read = io_pktfile_read; + p_ctx->pf_write = io_pktfile_write; + + /* Do not allow caching by I/O core, as this will merge packets in the cache. */ + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK; + return VC_CONTAINER_SUCCESS; + +error: + if(stream) fclose(stream); + return status; +} diff --git a/containers/metadata/id3/CMakeLists.txt b/containers/metadata/id3/CMakeLists.txt new file mode 100755 index 0000000..30178f8 --- /dev/null +++ b/containers/metadata/id3/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_metadata_id3 ${LIBRARY_TYPE} id3_metadata_reader.c) + +target_link_libraries(reader_metadata_id3 containers) + +install(TARGETS reader_metadata_id3 DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/metadata/id3/id3_metadata_reader.c b/containers/metadata/id3/id3_metadata_reader.c new file mode 100755 index 0000000..549273b --- /dev/null +++ b/containers/metadata/id3/id3_metadata_reader.c @@ -0,0 +1,450 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "id3_metadata_strings.h" + +/****************************************************************************** +Defines +******************************************************************************/ +#define ID3_SYNC_SAFE(x) ((((x >> 24) & 0x7f) << 21) | (((x >> 16) & 0x7f) << 14) | \ + (((x >> 8) & 0x7f) << 7) | (((x >> 0) & 0x7f) << 0)) + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_metadata_append( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, + unsigned int size ) +{ + VC_CONTAINER_METADATA_T *meta, **p_meta; + unsigned int i; + + for (i = 0; i != p_ctx->meta_num; ++i) + { + if (key == p_ctx->meta[i]->key) break; + } + + /* Avoid duplicate entries for now */ + if (i < p_ctx->meta_num) return NULL; + + /* Sanity check size, truncate if necessary */ + size = MIN(size, 512); + + /* Allocate a new metadata entry */ + if((meta = malloc(sizeof(VC_CONTAINER_METADATA_T) + size)) == NULL) + return NULL; + + /* We need to grow the array holding the metadata entries somehow, ideally, + we'd like to use a linked structure of some sort but realloc is probably + okay in this case */ + if((p_meta = realloc(p_ctx->meta, sizeof(VC_CONTAINER_METADATA_T *) * (p_ctx->meta_num + 1))) == NULL) + { + free(meta); + return NULL; + } + + p_ctx->meta = p_meta; + memset(meta, 0, sizeof(VC_CONTAINER_METADATA_T) + size); + p_ctx->meta[p_ctx->meta_num] = meta; + meta->key = key; + meta->value = (char *)&meta[1]; + meta->size = size; + p_ctx->meta_num++; + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_read_metadata_entry( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, unsigned int len ) +{ + VC_CONTAINER_METADATA_T *meta; + + if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL) + { + unsigned int size = meta->size - 1; + READ_BYTES(p_ctx, meta->value, size); + + if (len > size) + { + LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); + SKIP_BYTES(p_ctx, len - size); + } + } + else + { + SKIP_BYTES(p_ctx, len); + } + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_read_metadata_entry_ex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, unsigned int len, const char *encoding ) +{ + VC_CONTAINER_METADATA_T *meta; + + if ((meta = id3_metadata_append(p_ctx, key, encoding ? len + 2 : len + 1)) != NULL) + { + unsigned int size; + + if (encoding) + { + size = meta->size - 2; + READ_STRING_UTF16(p_ctx, meta->value, size, "ID3v2 data"); + } + else + { + size = meta->size - 1; + READ_STRING(p_ctx, meta->value, size, "ID3v2 data"); + } + + if (len > size) + { + LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); + SKIP_BYTES(p_ctx, len - size); + } + } + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_add_metadata_entry( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, const char *value ) +{ + VC_CONTAINER_METADATA_T *meta; + unsigned int len = strlen(value); + + if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL) + { + unsigned int size = meta->size - 1; + + if (len > size) + { + LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); + } + + strncpy(meta->value, value, size); + } + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_read_id3v2_frame( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T frame_id, uint32_t frame_size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_METADATA_KEY_T key; + VC_CONTAINER_METADATA_T *meta = NULL; + uint8_t encoding; + const char *charset = NULL; + + if(frame_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; + + switch (frame_id) + { + case VC_FOURCC('T','A','L','B'): key = VC_CONTAINER_METADATA_KEY_ALBUM; break; + case VC_FOURCC('T','I','T','2'): key = VC_CONTAINER_METADATA_KEY_TITLE; break; + case VC_FOURCC('T','R','C','K'): key = VC_CONTAINER_METADATA_KEY_TRACK; break; + case VC_FOURCC('T','P','E','1'): key = VC_CONTAINER_METADATA_KEY_ARTIST; break; + case VC_FOURCC('T','C','O','N'): key = VC_CONTAINER_METADATA_KEY_GENRE; break; + default: key = VC_CONTAINER_METADATA_KEY_UNKNOWN; break; + } + + if (key == VC_CONTAINER_METADATA_KEY_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + encoding = READ_U8(p_ctx, "ID3v2 text encoding byte"); + frame_size -= 1; + + switch(encoding) + { + case 0: /* ISO-8859-1 */ + case 3: /* UTF-8 */ + break; + case 1: /* UTF-16 with BOM */ + if(frame_size < 2) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U16(p_ctx, "ID3v2 text encoding BOM"); /* FIXME: Check BOM, 0xFFFE vs 0xFEFFF */ + frame_size -= 2; + charset = "UTF16-LE"; + break; + case 2: /* UTF-16BE */ + charset = "UTF16-BE"; + break; + default: + LOG_DEBUG(p_ctx, "skipping frame, text encoding %x not supported", encoding); + SKIP_BYTES(p_ctx, frame_size); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + if ((meta = id3_read_metadata_entry_ex(p_ctx, key, frame_size, charset)) != NULL) + { + if (charset) + { + utf8_from_charset(charset, meta->value, meta->size, meta->value, meta->size); + } + + meta->encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; /* Okay for ISO-8859-1 as well? */ + + status = VC_CONTAINER_SUCCESS; + } + else + { + SKIP_BYTES(p_ctx, frame_size); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_read_id3v2_tag( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint8_t maj_version, flags; + uint32_t tag_size, size = 0; + uint8_t peek_buf[10]; + + SKIP_STRING(p_ctx, 3, "ID3v2 identifier"); + maj_version = READ_U8(p_ctx, "ID3v2 version (major)"); + SKIP_U8(p_ctx, "ID3v2 version (minor)"); + flags = READ_U8(p_ctx, "ID3v2 flags"); + tag_size = READ_U32(p_ctx, "ID3v2 syncsafe tag size"); + tag_size = ID3_SYNC_SAFE(tag_size); + LOG_DEBUG(p_ctx, "ID3v2 tag size: %d", tag_size); + + /* Check that we support this major version */ + if (!(maj_version == 4 || maj_version == 3 || maj_version == 2)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* We can't currently handle unsynchronisation */ + if ((flags >> 7) & 1) + { + LOG_DEBUG(p_ctx, "skipping unsynchronised tag, not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* FIXME: check for version 2.2 and extract iTunes gapless playback information */ + if (maj_version == 2) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if ((flags >> 6) & 1) + { + /* Skip extended header, we don't support it */ + uint32_t ext_hdr_size; + LOG_DEBUG(p_ctx, "skipping ID3v2 extended header, not supported"); + ext_hdr_size = READ_U32(p_ctx, "ID3v2 syncsafe extended header size"); + ext_hdr_size = ID3_SYNC_SAFE(ext_hdr_size); + LOG_DEBUG(p_ctx, "ID3v2 extended header size: %d", ext_hdr_size); + SKIP_BYTES(p_ctx, MIN(tag_size, ext_hdr_size)); + size += ext_hdr_size; + } + + while (PEEK_BYTES(p_ctx, peek_buf, 10) == 10 && size < tag_size) + { + VC_CONTAINER_FOURCC_T frame_id; + uint32_t frame_size; + uint8_t format_flags; + + frame_id = READ_FOURCC(p_ctx, "Frame ID"); + frame_size = READ_U32(p_ctx, "Frame Size"); + + if (maj_version >= 4) + { + frame_size = ID3_SYNC_SAFE(frame_size); + LOG_DEBUG(p_ctx, "ID3v2 actual frame size: %d", frame_size); + } + + SKIP_U8(p_ctx, "ID3v2 status message flags"); + format_flags = READ_U8(p_ctx, "ID3v2 format description flags"); + + size += 10; + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS || !frame_id) + break; + + /* Early exit if we detect an invalid tag size */ + if (size + frame_size > tag_size) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + break; + } + + /* We can't currently handle unsynchronised frames */ + if ((format_flags >> 1) & 1) + { + LOG_DEBUG(p_ctx, "skipping unsynchronised frame, not supported"); + SKIP_BYTES(p_ctx, frame_size); + continue; + } + + if ((status = id3_read_id3v2_frame(p_ctx, frame_id, frame_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "skipping unsupported frame"); + SKIP_BYTES(p_ctx, frame_size); + } + + size += frame_size; + } + + /* Try to skip to end of tag in case we bailed out early */ + if (size < tag_size) SKIP_BYTES(p_ctx, tag_size - size); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_read_id3v1_tag( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint8_t track, genre; + char track_num[4] = {0}; + + SKIP_STRING(p_ctx, 3, "ID3v1 identifier"); + /* ID3v1 title */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TITLE, 30); + /* ID3v1 artist */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ARTIST, 30); + /* ID3v1 album */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ALBUM, 30); + /* ID3v1 year */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_YEAR, 4); + SKIP_STRING(p_ctx, 28, "ID3v1 comment"); + if (READ_U8(p_ctx, "ID3v1 zero-byte") == 0) + { + track = READ_U8(p_ctx, "ID3v1 track"); + snprintf(track_num, sizeof(track_num) - 1, "%02d", track); + id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TRACK, track_num); + } + else + { + SKIP_BYTES(p_ctx, 1); + } + genre = READ_U8(p_ctx, "ID3v1 genre"); + if (genre < countof(id3_genres)) + { + id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_GENRE, id3_genres[genre]); + } + + status = STREAM_STATUS(p_ctx); + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_metadata_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint8_t peek_buf[10]; + int64_t data_offset; + + if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Initial ID3v2 tag(s), variable size */ + while ((peek_buf[0] == 'I') && (peek_buf[1] == 'D') && (peek_buf[2] == '3')) + { + if ((status = id3_read_id3v2_tag(p_ctx)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "error reading ID3v2 tag (%i)", status); + } + + if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) break; + } + + data_offset = STREAM_POSITION(p_ctx); + + /* ID3v1 tag, 128 bytes at the end of a file */ + if (p_ctx->priv->io->size >= INT64_C(128) && STREAM_SEEKABLE(p_ctx)) + { + SEEK(p_ctx, p_ctx->priv->io->size - INT64_C(128)); + if (PEEK_BYTES(p_ctx, peek_buf, 3) != 3) goto end; + + if ((peek_buf[0] == 'T') && (peek_buf[1] == 'A') && (peek_buf[2] == 'G')) + { + if ((status = id3_read_id3v1_tag(p_ctx)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "error reading ID3v1 tag (%i)", status); + } + } + } + +end: + /* Restore position to start of data */ + if (STREAM_POSITION(p_ctx) != data_offset) + SEEK(p_ctx, data_offset); + + p_ctx->priv->pf_close = id3_metadata_reader_close; + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + return VC_CONTAINER_SUCCESS; + +error: + LOG_DEBUG(p_ctx, "error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open id3_metadata_reader_open +#endif diff --git a/containers/metadata/id3/id3_metadata_strings.h b/containers/metadata/id3/id3_metadata_strings.h new file mode 100755 index 0000000..1c953d9 --- /dev/null +++ b/containers/metadata/id3/id3_metadata_strings.h @@ -0,0 +1,179 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* ID3 genre byte translation table */ +static const char* id3_genres[] = +{ + "Blues", + "Classic Rock", + "Country", + "Dance", + "Disco", + "Funk", + "Grunge", + "Hip-Hop", + "Jazz", + "Metal", + "New Age", + "Oldies", + "Other", + "Pop", + "R&B", + "Rap", + "Reggae", + "Rock", + "Techno", + "Industrial", + "Alternative", + "Ska", + "Death Metal", + "Pranks", + "Soundtrack", + "Euro-Techno", + "Ambient", + "Trip-Hop", + "Vocal", + "Jazz+Funk", + "Fusion", + "Trance", + "Classical", + "Instrumental", + "Acid", + "House", + "Game", + "Sound Clip", + "Gospel", + "Noise", + "Alternative Rock", + "Bass", + "Soul", + "Punk", + "Space", + "Meditative", + "Instrumental Pop", + "Instrumental Rock", + "Ethnic", + "Gothic", + "Darkwave", + "Techno-Industrial", + "Electronic", + "Pop-Folk", + "Eurodance", + "Dream", + "Southern Rock", + "Comedy", + "Cult", + "Gangsta", + "Top 40", + "Christian Rap", + "Pop/Funk", + "Jungle", + "Native American", + "Cabaret", + "New Wave", + "Psychadelic", + "Rave", + "Showtunes", + "Trailer", + "Lo-Fi", + "Tribal", + "Acid Punk", + "Acid Jazz", + "Polka", + "Retro", + "Musical", + "Rock & Roll", + "Hard Rock", + "Folk", + "Folk-Rock", + "National Folk", + "Swing", + "Fast Fusion", + "Bebob", + "Latin", + "Revival", + "Celtic", + "Bluegrass", + "Avantgarde", + "Gothic Rock", + "Progressive Rock", + "Psychedelic Rock", + "Symphonic Rock", + "Slow Rock", + "Big Band", + "Chorus", + "Easy Listening", + "Acoustic", + "Humour", + "Speech", + "Chanson", + "Opera", + "Chamber Music", + "Sonata", + "Symphony", + "Booty Bass", + "Primus", + "Porn Groove", + "Satire", + "Slow Jam", + "Club", + "Tango", + "Samba", + "Folklore", + "Ballad", + "Power Ballad", + "Rhythmic Soul", + "Freestyle", + "Duet", + "Punk Rock", + "Drum Solo", + "A capella", + "Euro-House", + "Dance Hall", + "Goa", + "Drum & Bass", + "Club-House", + "Hardcore", + "Terror", + "Indie", + "BritPop", + "Negerpunk", + "Polsk Punk", + "Beat", + "Christian Gangsta Rap", + "Heavy Metal", + "Black Metal", + "Crossover", + "Contemporary Christian", + "Christian Rock", + "Merengue", + "Salsa", + "Thrash Metal", + "Anime", + "JPop", + "SynthPop" +}; diff --git a/containers/mkv/CMakeLists.txt b/containers/mkv/CMakeLists.txt new file mode 100755 index 0000000..0c31a30 --- /dev/null +++ b/containers/mkv/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_mkv ${LIBRARY_TYPE} matroska_reader.c) + +target_link_libraries(reader_mkv containers) + +install(TARGETS reader_mkv DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mkv/matroska_reader.c b/containers/mkv/matroska_reader.c new file mode 100755 index 0000000..8625d0c --- /dev/null +++ b/containers/mkv/matroska_reader.c @@ -0,0 +1,2323 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +//#define ENABLE_MKV_EXTRA_LOGGING +#define CONTAINER_IS_BIG_ENDIAN +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->element_level +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MKV_TRACKS_MAX 16 +#define MKV_CODECID_MAX 32 +#define MKV_MAX_LACING_NUM 64 + +#define MKV_MAX_ENCODINGS 1 +#define MKV_MAX_ENCODING_DATA 256 + +#define MKV_MAX_ELEMENT_LEVEL 8 +#define MKV_MAX_CONSECUTIVE_UNKNOWN_ELEMENTS 5 +#define MKV_MAX_ELEMENT_SIZE (1<<29) /* Does not apply to the data element */ +#define MKV_MAX_STRING_SIZE 256 +#define MKV_ELEMENT_MIN_HEADER_SIZE 2 + +#define MKV_MAX_READER_STATE_LEVEL 4 + +#define MKV_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) +#define MKV_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) +#define MKV_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) +#define MKV_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) +#define MKV_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) +#define MKV_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) +#define MKV_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) +#define MKV_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) +#define MKV_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) +#define MKV_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) +#define MKV_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) +#define MKV_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) + +#define CHECK_POINT(a) do { \ + /*if(size < 0 && size != INT64_C(-1)) return VC_CONTAINER_ERROR_CORRUPTED;*/ \ + if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); } while(0) + +static uint32_t mkv_io_read_id(VC_CONTAINER_IO_T *io, int64_t *size) +{ + uint32_t value, mask; + + value = vc_container_io_read_uint8(io); (*size)--; + for(mask = 0x80; mask; mask <<= 7) + { + if(value & mask) return value; + value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; + } + return 0; +} + +static int64_t mkv_io_read_uint(VC_CONTAINER_IO_T *io, int64_t *size) +{ + uint64_t value, mask; + + value = vc_container_io_read_uint8(io); (*size)--; + if(value == 0xFF) return -1; + + for(mask = 0x80; mask; mask <<= 7) + { + if(value & mask) return value & ~mask; + value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; + } + return 0; +} + +static int64_t mkv_io_read_sint(VC_CONTAINER_IO_T *io, int64_t *size) +{ + int64_t value, count = io->offset; + value = mkv_io_read_uint(io, size); + count = io->offset - count; + + switch(count) + { + case 1: value -= 0x3F; break; + case 2: value -= 0x1FFF; break; + case 3: value -= 0xFFFFF; break; + case 4: value -= 0x7FFFFFF; break; + default: break; + } + return value; +} + +#define MKV_READ_ID(ctx, n) mkv_io_read_id((ctx)->priv->io, &size) +#define MKV_READ_UINT(ctx, n) mkv_io_read_uint((ctx)->priv->io, &size) +#define MKV_READ_SINT(ctx, n) mkv_io_read_sint((ctx)->priv->io, &size) + +/****************************************************************************** +Type definitions. +******************************************************************************/ + +typedef enum +{ + MKV_ELEMENT_ID_UNKNOWN = 0, + + /* EBML Basics */ + MKV_ELEMENT_ID_EBML = 0x1A45DFA3, + MKV_ELEMENT_ID_EBML_VERSION = 0x4286, + MKV_ELEMENT_ID_EBML_READ_VERSION = 0x42F7, + MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH = 0x42F2, + MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH = 0x42F3, + MKV_ELEMENT_ID_DOCTYPE = 0x4282, + MKV_ELEMENT_ID_DOCTYPE_VERSION = 0x4287, + MKV_ELEMENT_ID_DOCTYPE_READ_VERSION = 0x4285, + + /* Global Elements */ + MKV_ELEMENT_ID_CRC32 = 0xBF, + MKV_ELEMENT_ID_VOID = 0xEC, + + /* Segment */ + MKV_ELEMENT_ID_SEGMENT = 0x18538067, + + /* Meta Seek Information */ + MKV_ELEMENT_ID_SEEK_HEAD = 0x114D9B74, + MKV_ELEMENT_ID_SEEK = 0x4DBB, + MKV_ELEMENT_ID_SEEK_ID = 0x53AB, + MKV_ELEMENT_ID_SEEK_POSITION = 0x53AC, + + /* Segment Information */ + MKV_ELEMENT_ID_INFO = 0x1549A966, + MKV_ELEMENT_ID_SEGMENT_UID = 0x73A4, + MKV_ELEMENT_ID_SEGMENT_FILENAME = 0x7384, + MKV_ELEMENT_ID_PREV_UID = 0x3CB923, + MKV_ELEMENT_ID_PREV_FILENAME = 0x3C83AB, + MKV_ELEMENT_ID_NEXT_UID = 0x3EB923, + MKV_ELEMENT_ID_NEXT_FILENAME = 0x3E83BB, + MKV_ELEMENT_ID_SEGMENT_FAMILY = 0x4444, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE = 0x6924, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID = 0x69FC, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC = 0x69BF, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID = 0x69A5, + MKV_ELEMENT_ID_TIMECODE_SCALE = 0x2AD7B1, + MKV_ELEMENT_ID_DURATION = 0x4489, + MKV_ELEMENT_ID_DATE_UTC = 0x4461, + MKV_ELEMENT_ID_TITLE = 0x7BA9, + MKV_ELEMENT_ID_MUXING_APP = 0x4D80, + MKV_ELEMENT_ID_WRITING_APP = 0x5741, + + /* Cluster */ + MKV_ELEMENT_ID_CLUSTER = 0x1F43B675, + MKV_ELEMENT_ID_TIMECODE = 0xE7, + MKV_ELEMENT_ID_SILENT_TRACKS = 0x5854, + MKV_ELEMENT_ID_SILENT_TRACK_NUMBER = 0x58D7, + MKV_ELEMENT_ID_POSITION = 0xA7, + MKV_ELEMENT_ID_PREV_SIZE = 0xAB, + MKV_ELEMENT_ID_BLOCKGROUP = 0xA0, + MKV_ELEMENT_ID_BLOCK = 0xA1, + MKV_ELEMENT_ID_BLOCK_ADDITIONS = 0x75A1, + MKV_ELEMENT_ID_BLOCK_MORE = 0xA6, + MKV_ELEMENT_ID_BLOCK_ADD_ID = 0xEE, + MKV_ELEMENT_ID_BLOCK_ADDITIONAL = 0xA5, + MKV_ELEMENT_ID_BLOCK_DURATION = 0x9B, + MKV_ELEMENT_ID_REFERENCE_PRIORITY = 0xFA, + MKV_ELEMENT_ID_REFERENCE_BLOCK = 0xFB, + MKV_ELEMENT_ID_CODEC_STATE = 0xA4, + MKV_ELEMENT_ID_SLICES = 0x8E, + MKV_ELEMENT_ID_TIME_SLICE = 0xE8, + MKV_ELEMENT_ID_LACE_NUMBER = 0xCC, + MKV_ELEMENT_ID_SIMPLE_BLOCK = 0xA3, + + /* Track */ + MKV_ELEMENT_ID_TRACKS = 0x1654AE6B, + MKV_ELEMENT_ID_TRACK_ENTRY = 0xAE, + MKV_ELEMENT_ID_TRACK_NUMBER = 0xD7, + MKV_ELEMENT_ID_TRACK_UID = 0x73C5, + MKV_ELEMENT_ID_TRACK_TYPE = 0x83, + MKV_ELEMENT_ID_FLAG_ENABLED = 0xB9, + MKV_ELEMENT_ID_FLAG_DEFAULT = 0x88, + MKV_ELEMENT_ID_FLAG_FORCED = 0x55AA, + MKV_ELEMENT_ID_FLAG_LACING = 0x9C, + MKV_ELEMENT_ID_MIN_CACHE = 0x6DE7, + MKV_ELEMENT_ID_MAX_CACHE = 0x6DF8, + MKV_ELEMENT_ID_DEFAULT_DURATION = 0x23E383, + MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE = 0x23314F, + MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID = 0x55EE, + MKV_ELEMENT_ID_NAME = 0x536E, + MKV_ELEMENT_ID_LANGUAGE = 0x22B59C, + MKV_ELEMENT_ID_TRACK_CODEC_ID = 0x86, + MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE = 0x63A2, + MKV_ELEMENT_ID_TRACK_CODEC_NAME = 0x258688, + MKV_ELEMENT_ID_ATTACHMENT_LINK = 0x7446, + MKV_ELEMENT_ID_CODEC_DECODE_ALL = 0xAA, + MKV_ELEMENT_ID_TRACK_OVERLAY = 0x6FAB, + MKV_ELEMENT_ID_TRACK_TRANSLATE = 0x6624, + MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID = 0x66FC, + MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC = 0x66BF, + MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID = 0x66A5, + + /* Video */ + MKV_ELEMENT_ID_VIDEO = 0xE0, + MKV_ELEMENT_ID_FLAG_INTERLACED = 0x9A, + MKV_ELEMENT_ID_STEREO_MODE = 0x53B8, + MKV_ELEMENT_ID_PIXEL_WIDTH = 0xB0, + MKV_ELEMENT_ID_PIXEL_HEIGHT = 0xBA, + MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM = 0x54AA, + MKV_ELEMENT_ID_PIXEL_CROP_TOP = 0x54BB, + MKV_ELEMENT_ID_PIXEL_CROP_LEFT = 0x54CC, + MKV_ELEMENT_ID_PIXEL_CROP_RIGHT = 0x54DD, + MKV_ELEMENT_ID_DISPLAY_WIDTH = 0x54B0, + MKV_ELEMENT_ID_DISPLAY_HEIGHT = 0x54BA, + MKV_ELEMENT_ID_DISPLAY_UNIT = 0x54B2, + MKV_ELEMENT_ID_ASPECT_RATIO_TYPE = 0x54B3, + MKV_ELEMENT_ID_COLOUR_SPACE = 0x2EB524, + MKV_ELEMENT_ID_FRAME_RATE = 0x2383E3, + + /* Audio */ + MKV_ELEMENT_ID_AUDIO = 0xE1, + MKV_ELEMENT_ID_SAMPLING_FREQUENCY = 0xB5, + MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY = 0x78B5, + MKV_ELEMENT_ID_CHANNELS = 0x9F, + MKV_ELEMENT_ID_BIT_DEPTH = 0x6264, + + /* Content Encoding */ + MKV_ELEMENT_ID_CONTENT_ENCODINGS = 0x6D80, + MKV_ELEMENT_ID_CONTENT_ENCODING = 0x6240, + MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER = 0x5031, + MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE = 0x5032, + MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE = 0x5033, + MKV_ELEMENT_ID_CONTENT_COMPRESSION = 0x5034, + MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO = 0x4254, + MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS = 0x4255, + MKV_ELEMENT_ID_CONTENT_ENCRYPTION = 0x5035, + MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO = 0x47E1, + MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2, + MKV_ELEMENT_ID_CONTENT_SIGNATURE = 0x47E3, + MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID = 0x47E4, + MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO = 0x47E5, + MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO = 0x47E6, + + /* Cueing Data */ + MKV_ELEMENT_ID_CUES = 0x1C53BB6B, + MKV_ELEMENT_ID_CUE_POINT = 0xBB, + MKV_ELEMENT_ID_CUE_TIME = 0xB3, + MKV_ELEMENT_ID_CUE_TRACK_POSITIONS = 0xB7, + MKV_ELEMENT_ID_CUE_TRACK = 0xF7, + MKV_ELEMENT_ID_CUE_CLUSTER_POSITION = 0xF1, + MKV_ELEMENT_ID_CUE_BLOCK_NUMBER = 0x5378, + + /* Attachments */ + MKV_ELEMENT_ID_ATTACHMENTS = 0x1941A469, + + /* Chapters */ + MKV_ELEMENT_ID_CHAPTERS = 0x1043A770, + + /* Tagging */ + MKV_ELEMENT_ID_TAGS = 0x1254C367, + MKV_ELEMENT_ID_TAG = 0x7373, + MKV_ELEMENT_ID_TAG_TARGETS = 0x63C0, + MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE = 0x68CA, + MKV_ELEMENT_ID_TAG_TARGET_TYPE = 0x63CA, + MKV_ELEMENT_ID_TAG_TRACK_UID = 0x63C5, + MKV_ELEMENT_ID_TAG_EDITION_UID = 0x63C9, + MKV_ELEMENT_ID_TAG_CHAPTER_UID = 0x63C4, + MKV_ELEMENT_ID_TAG_ATTACHMENT_UID = 0x63C6, + MKV_ELEMENT_ID_TAG_SIMPLE_TAG = 0x67C8, + MKV_ELEMENT_ID_TAG_NAME = 0x45A3, + MKV_ELEMENT_ID_TAG_LANGUAGE = 0x447A, + MKV_ELEMENT_ID_TAG_DEFAULT = 0x4484, + MKV_ELEMENT_ID_TAG_STRING = 0x4487, + MKV_ELEMENT_ID_TAG_BINARY = 0x4485, + + MKV_ELEMENT_ID_INVALID = 0xFFFFFFFF +} MKV_ELEMENT_ID_T; + +/** Context for our reader + */ + +typedef struct +{ + unsigned int track; + unsigned int flags; + int64_t pts; + int64_t cluster_timecode; + int64_t prev_cluster_size; /* Size of the previous cluster if available */ + int64_t frame_duration; + + int level; + struct { + int64_t offset; + int64_t data_start; + int64_t data_offset; + int64_t size; + MKV_ELEMENT_ID_T id; + } levels[MKV_MAX_READER_STATE_LEVEL]; + + bool eos; + bool corrupted; + bool seen_ref_block; + + uint32_t lacing_num_frames; + uint32_t lacing_size; + uint16_t lacing_sizes[MKV_MAX_LACING_NUM]; + uint32_t lacing_current_size; + + /* For header stripping compression */ + uint32_t header_size; + uint8_t *header_data; + uint32_t header_size_backup; +} MKV_READER_STATE_T; + +typedef struct +{ + const MKV_ELEMENT_ID_T id; + const MKV_ELEMENT_ID_T parent_id; + const char *psz_name; + VC_CONTAINER_STATUS_T (*pf_func)(VC_CONTAINER_T *, MKV_ELEMENT_ID_T, int64_t); + +} MKV_ELEMENT_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + MKV_READER_STATE_T *state; + MKV_READER_STATE_T track_state; + + /* Information extracted from the track entry */ + uint32_t number; + uint32_t type; + int64_t timecode_scale; + uint32_t duration; + int64_t frame_duration; + char codecid[MKV_CODECID_MAX]; + + union { + /* video specific */ + struct { + unsigned int interlaced:1; + unsigned int stereo_mode:2; + uint32_t pixel_width; + uint32_t pixel_height; + uint32_t pixel_crop_bottom; + uint32_t pixel_crop_top; + uint32_t pixel_crop_left; + uint32_t pixel_crop_right; + uint32_t display_width; + uint32_t display_height; + uint32_t display_unit; + uint32_t aspect_ratio_type; + float frame_rate; + } video; + + /* audio specific */ + struct { + uint32_t sampling_frequency; + uint32_t output_sampling_frequency; + uint32_t channels; + uint32_t bit_depth; + } audio; + } es_type; + + /* content encoding (i.e. lossless compression and encryption) */ + unsigned int encodings_num; + struct { + enum { + MKV_CONTENT_ENCODING_COMPRESSION_ZLIB, + MKV_CONTENT_ENCODING_COMPRESSION_HEADER, + MKV_CONTENT_ENCODING_ENCRYPTION, + MKV_CONTENT_ENCODING_UNKNOWN + } type; + unsigned int data_size; + uint8_t *data; + } encodings[MKV_MAX_ENCODINGS]; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + MKV_ELEMENT_T *elements_list; + int element_level; + MKV_ELEMENT_ID_T parent_id; + + uint64_t element_offset; /**< Offset to the start of the current element */ + + uint64_t segment_offset; /**< Offset to the start of the data packets */ + int64_t segment_size; + + int tracks_num; + VC_CONTAINER_TRACK_T *tracks[MKV_TRACKS_MAX]; + + MKV_READER_STATE_T state; + int64_t timecode_scale; + float duration; + + uint64_t cluster_offset; /**< Offset to the first cluster */ + uint64_t cues_offset; /**< Offset to the start of the seeking cues */ + uint64_t tags_offset; /**< Offset to the start of the tags */ + + /* + * Variables only used during parsing of the header + */ + + VC_CONTAINER_TRACK_T *parsing; /**< Current track being parsed */ + bool is_doctype_valid; + + MKV_ELEMENT_ID_T seekhead_elem_id; + int64_t seekhead_elem_offset; + + /* Cues */ + unsigned int cue_track; + int64_t cue_timecode; + uint64_t cue_cluster_offset; + unsigned int cue_block; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T mkv_read_element( VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T parent_id ); +static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_data_uint( VC_CONTAINER_T *p_ctx, int64_t size, uint64_t *value ); +static VC_CONTAINER_STATUS_T mkv_read_element_data_float( VC_CONTAINER_T *p_ctx, int64_t size, double *value ); +static VC_CONTAINER_STATUS_T mkv_read_element_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); + +static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); + +/****************************************************************************** +List of element IDs and their associated processing functions +******************************************************************************/ +MKV_ELEMENT_T mkv_elements_list[] = +{ + /* EBML Basics */ + {MKV_ELEMENT_ID_EBML, MKV_ELEMENT_ID_UNKNOWN, "EBML", mkv_read_element_ebml}, + {MKV_ELEMENT_ID_EBML_VERSION, MKV_ELEMENT_ID_EBML, "EBMLVersion", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_EBML_READ_VERSION, MKV_ELEMENT_ID_EBML, "EBMLReadVersion", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxIDLength", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxSizeLength", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_DOCTYPE, MKV_ELEMENT_ID_EBML, "DocType", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_DOCTYPE_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeVersion", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_DOCTYPE_READ_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeReadVersion", mkv_read_subelements_ebml}, + + /* Global Elements */ + {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, + {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, + + /* Segment */ + {MKV_ELEMENT_ID_SEGMENT, MKV_ELEMENT_ID_UNKNOWN, "Segment", mkv_read_element_segment}, + + /* Meta Seek Information */ + {MKV_ELEMENT_ID_SEEK_HEAD, MKV_ELEMENT_ID_SEGMENT, "SeekHead", mkv_read_elements}, + {MKV_ELEMENT_ID_SEEK, MKV_ELEMENT_ID_SEEK_HEAD, "Seek", mkv_read_subelements_seek_head}, + {MKV_ELEMENT_ID_SEEK_ID, MKV_ELEMENT_ID_SEEK, "SeekID", mkv_read_subelements_seek_head}, + {MKV_ELEMENT_ID_SEEK_POSITION, MKV_ELEMENT_ID_SEEK, "SeekPosition", mkv_read_subelements_seek_head}, + + /* Segment Information */ + {MKV_ELEMENT_ID_INFO, MKV_ELEMENT_ID_SEGMENT, "Info", mkv_read_elements}, + {MKV_ELEMENT_ID_SEGMENT_UID, MKV_ELEMENT_ID_INFO, "SegmentUID", 0}, + {MKV_ELEMENT_ID_SEGMENT_FILENAME, MKV_ELEMENT_ID_INFO, "SegmentFilename", 0}, + {MKV_ELEMENT_ID_PREV_UID, MKV_ELEMENT_ID_INFO, "PrevUID", 0}, + {MKV_ELEMENT_ID_PREV_FILENAME, MKV_ELEMENT_ID_INFO, "PrevFilename", 0}, + {MKV_ELEMENT_ID_NEXT_UID, MKV_ELEMENT_ID_INFO, "NextUID", 0}, + {MKV_ELEMENT_ID_NEXT_FILENAME, MKV_ELEMENT_ID_INFO, "NextFilename", 0}, + {MKV_ELEMENT_ID_SEGMENT_FAMILY, MKV_ELEMENT_ID_INFO, "SegmentFamily", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE, MKV_ELEMENT_ID_INFO, "ChapterTranslate", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_INFO, "ChapterTranslateEditionUID", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC, MKV_ELEMENT_ID_INFO, "ChapterTranslateCodec", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID, MKV_ELEMENT_ID_INFO, "ChapterTranslateID", 0}, + {MKV_ELEMENT_ID_TIMECODE_SCALE, MKV_ELEMENT_ID_INFO, "TimecodeScale", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_DURATION, MKV_ELEMENT_ID_INFO, "Duration", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_DATE_UTC, MKV_ELEMENT_ID_INFO, "DateUTC", 0}, + {MKV_ELEMENT_ID_TITLE, MKV_ELEMENT_ID_INFO, "Title", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_MUXING_APP, MKV_ELEMENT_ID_INFO, "MuxingApp", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_WRITING_APP, MKV_ELEMENT_ID_INFO, "WritingApp", mkv_read_subelements_info}, + + /* Cluster */ + {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, + {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", 0}, + {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, + {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, + {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, + {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, + {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, + {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, + {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, + {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, + {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", 0}, + {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, + {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, + {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, + {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, + {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, + {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, + {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, + + /* Track */ + {MKV_ELEMENT_ID_TRACKS, MKV_ELEMENT_ID_SEGMENT, "Tracks", mkv_read_elements}, + {MKV_ELEMENT_ID_TRACK_ENTRY, MKV_ELEMENT_ID_TRACKS, "TrackEntry", mkv_read_element_track_entry}, + {MKV_ELEMENT_ID_TRACK_NUMBER, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackNumber", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_UID, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackUID", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_TYPE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackType", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_ENABLED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagEnabled", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_DEFAULT, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagDefault", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_FORCED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagForced", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_LACING, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagLacing", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_MIN_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MinCache", 0}, + {MKV_ELEMENT_ID_MAX_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxCache", 0}, + {MKV_ELEMENT_ID_DEFAULT_DURATION, MKV_ELEMENT_ID_TRACK_ENTRY, "DefaultDuration", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTimecodeScale", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxBlockAdditionID", 0}, + {MKV_ELEMENT_ID_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "Name", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_LANGUAGE, MKV_ELEMENT_ID_TRACK_ENTRY, "Language", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_CODEC_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecID", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecPrivate", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_CODEC_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecName", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_ATTACHMENT_LINK, MKV_ELEMENT_ID_TRACK_ENTRY, "AttachmentLink", 0}, + {MKV_ELEMENT_ID_CODEC_DECODE_ALL, MKV_ELEMENT_ID_TRACK_ENTRY, "DecodeAll", 0}, + {MKV_ELEMENT_ID_TRACK_OVERLAY, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackOverlay", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTranslate", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateEditionUID", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateCodec", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateTrackID", 0}, + + /* Video */ + {MKV_ELEMENT_ID_VIDEO, MKV_ELEMENT_ID_TRACK_ENTRY, "Video", mkv_read_elements}, + {MKV_ELEMENT_ID_FLAG_INTERLACED, MKV_ELEMENT_ID_VIDEO, "FlagInterlaced", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_STEREO_MODE, MKV_ELEMENT_ID_VIDEO, "StereoMode", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_WIDTH, MKV_ELEMENT_ID_VIDEO, "PixelWidth", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_HEIGHT, MKV_ELEMENT_ID_VIDEO, "PixelHeight", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM, MKV_ELEMENT_ID_VIDEO, "PixelCropBottom", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_TOP, MKV_ELEMENT_ID_VIDEO, "PixelCropTop", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_LEFT, MKV_ELEMENT_ID_VIDEO, "PixelCropLeft", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_RIGHT, MKV_ELEMENT_ID_VIDEO, "PixelCropRight", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_DISPLAY_WIDTH, MKV_ELEMENT_ID_VIDEO, "DisplayWidth", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_DISPLAY_HEIGHT, MKV_ELEMENT_ID_VIDEO, "DisplayHeight", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_DISPLAY_UNIT, MKV_ELEMENT_ID_VIDEO, "DisplayUnit", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_ASPECT_RATIO_TYPE, MKV_ELEMENT_ID_VIDEO, "AspectRatioType", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_COLOUR_SPACE, MKV_ELEMENT_ID_VIDEO, "ColourSpace", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_FRAME_RATE, MKV_ELEMENT_ID_VIDEO, "FrameRate", mkv_read_subelements_video}, + + /* Audio */ + {MKV_ELEMENT_ID_AUDIO, MKV_ELEMENT_ID_TRACK_ENTRY, "Audio", mkv_read_elements}, + {MKV_ELEMENT_ID_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "SamplingFrequency", mkv_read_subelements_audio}, + {MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "OutputSamplingFrequency", mkv_read_subelements_audio}, + {MKV_ELEMENT_ID_CHANNELS, MKV_ELEMENT_ID_AUDIO, "Channels", mkv_read_subelements_audio}, + {MKV_ELEMENT_ID_BIT_DEPTH, MKV_ELEMENT_ID_AUDIO, "BitDepth", mkv_read_subelements_audio}, + + /* Content Encoding */ + {MKV_ELEMENT_ID_CONTENT_ENCODINGS, MKV_ELEMENT_ID_TRACK_ENTRY, "ContentEncodings", mkv_read_elements}, + {MKV_ELEMENT_ID_CONTENT_ENCODING, MKV_ELEMENT_ID_CONTENT_ENCODINGS, "ContentEncoding", mkv_read_element_encoding}, + {MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingOrder", mkv_read_subelements_encoding}, + {MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingScope", mkv_read_subelements_encoding}, + {MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingType", mkv_read_subelements_encoding}, + {MKV_ELEMENT_ID_CONTENT_COMPRESSION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentCompression", mkv_read_elements}, + {MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompAlgo", mkv_read_subelements_compression}, + {MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompSettings", mkv_read_subelements_compression}, + {MKV_ELEMENT_ID_CONTENT_ENCRYPTION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncryption", mkv_read_elements}, + {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncAlgo", 0}, + {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncKeyID", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSignature", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigKeyID", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigAlgo", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigHashAlgo", 0}, + + /* Cueing data */ + {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", mkv_read_element_cues}, + {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", 0}, + {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", 0}, + {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", 0}, + {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", 0}, + + /* Attachments */ + {MKV_ELEMENT_ID_ATTACHMENTS, MKV_ELEMENT_ID_SEGMENT, "Attachments", 0}, + + /* Chapters */ + {MKV_ELEMENT_ID_CHAPTERS, MKV_ELEMENT_ID_SEGMENT, "Chapters", 0}, + + /* Tagging */ + {MKV_ELEMENT_ID_TAGS, MKV_ELEMENT_ID_SEGMENT, "Tags", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG, MKV_ELEMENT_ID_TAGS, "Tag", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG_TARGETS, MKV_ELEMENT_ID_TAG, "Tag Targets", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type Value", 0}, + {MKV_ELEMENT_ID_TAG_TARGET_TYPE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type", 0}, + {MKV_ELEMENT_ID_TAG_TRACK_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Track UID", 0}, + {MKV_ELEMENT_ID_TAG_EDITION_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Edition UID", 0}, + {MKV_ELEMENT_ID_TAG_CHAPTER_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Chapter UID", 0}, + {MKV_ELEMENT_ID_TAG_ATTACHMENT_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Attachment UID", 0}, + {MKV_ELEMENT_ID_TAG_SIMPLE_TAG, MKV_ELEMENT_ID_TAG, "Simple Tag", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG_NAME, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Name", 0}, + {MKV_ELEMENT_ID_TAG_LANGUAGE, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Language", 0}, + {MKV_ELEMENT_ID_TAG_DEFAULT, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Default", 0}, + {MKV_ELEMENT_ID_TAG_STRING, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag String", 0}, + {MKV_ELEMENT_ID_TAG_BINARY, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Binary", 0}, + + {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} +}; + +MKV_ELEMENT_T mkv_cluster_elements_list[] = +{ + /* Cluster */ + {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, + {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", mkv_read_subelements_cluster}, + {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, + {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, + {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, + {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, + {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, + {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, + {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, + {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, + {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", mkv_read_subelements_cluster}, + {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, + {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, + {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, + {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, + {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, + {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, + {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, + + /* Global Elements */ + {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, + {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, + + {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} +}; + +MKV_ELEMENT_T mkv_cue_elements_list[] = +{ + /* Cueing data */ + {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", 0}, + {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", mkv_read_subelements_cue_point}, + {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", mkv_read_subelements_cue_point}, + {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", mkv_read_subelements_cue_point}, + {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", mkv_read_subelements_cue_point}, + + /* Global Elements */ + {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, + {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, + + {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} +}; + +/****************************************************************************** +List of codec mapping +******************************************************************************/ +static const struct { + VC_CONTAINER_FOURCC_T fourcc; + const char *codecid; + VC_CONTAINER_FOURCC_T variant; +} codecid_to_fourcc_table[] = +{ + /* Video */ + {VC_CONTAINER_CODEC_MP1V, "V_MPEG1", 0}, + {VC_CONTAINER_CODEC_MP2V, "V_MPEG2", 0}, + {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/ASP", 0}, + {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/SP", 0}, + {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/AP", 0}, + {VC_CONTAINER_CODEC_DIV3, "V_MPEG4/MS/V3", 0}, + {VC_CONTAINER_CODEC_H264, "V_MPEG4/ISO/AVC", VC_CONTAINER_VARIANT_H264_AVC1}, + {VC_CONTAINER_CODEC_MJPEG, "V_MJPEG", 0}, + {VC_CONTAINER_CODEC_RV10, "V_REAL/RV10", 0}, + {VC_CONTAINER_CODEC_RV20, "V_REAL/RV20", 0}, + {VC_CONTAINER_CODEC_RV30, "V_REAL/RV30", 0}, + {VC_CONTAINER_CODEC_RV40, "V_REAL/RV40", 0}, + {VC_CONTAINER_CODEC_THEORA, "V_THEORA", 0}, + {VC_CONTAINER_CODEC_DIRAC, "V_DIRAC", 0}, + {VC_CONTAINER_CODEC_VP8, "V_VP8", 0}, + + /* Audio */ + {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L3", VC_CONTAINER_VARIANT_MPGA_L3}, + {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L2", VC_CONTAINER_VARIANT_MPGA_L2}, + {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L1", VC_CONTAINER_VARIANT_MPGA_L1}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/MAIN", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/SSR", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC/SBR", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/MAIN", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/SSR", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC/SBR", 0}, + {VC_CONTAINER_CODEC_AC3, "A_AC3", 0}, + {VC_CONTAINER_CODEC_EAC3, "A_EAC3", 0}, + {VC_CONTAINER_CODEC_DTS, "A_DTS", 0}, + {VC_CONTAINER_CODEC_MLP, "A_MLP", 0}, + {0, "A_TRUEHD", 0}, + {VC_CONTAINER_CODEC_VORBIS, "A_VORBIS", 0}, + {VC_CONTAINER_CODEC_FLAC, "A_FLAC", 0}, + {VC_CONTAINER_CODEC_PCM_SIGNED_LE, "A_PCM/INT/LIT", 0}, + {VC_CONTAINER_CODEC_PCM_SIGNED_BE, "A_PCM/INT/BIG", 0}, + {VC_CONTAINER_CODEC_PCM_FLOAT_LE, "A_PCM/FLOAT/IEEE", 0}, + {0, "A_REAL/xyzt", 0}, + {0, "A_REAL/14_4", 0}, + + /* Text */ + {VC_CONTAINER_CODEC_TEXT, "S_TEXT/ASCII", 0}, + {VC_CONTAINER_CODEC_TEXT, "S_TEXT/UTF8", 0}, + {VC_CONTAINER_CODEC_SSA, "S_TEXT/ASS", 0}, + {VC_CONTAINER_CODEC_SSA, "S_TEXT/SSA", 0}, + {VC_CONTAINER_CODEC_SSA, "S_ASS", 0}, + {VC_CONTAINER_CODEC_SSA, "S_SSA", 0}, + {VC_CONTAINER_CODEC_USF, "S_TEXT/USF", 0}, + {VC_CONTAINER_CODEC_VOBSUB, "S_VOBSUB", 0}, + + {0, 0} +}; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_FOURCC_T mkv_codecid_to_fourcc(const char *codecid, + VC_CONTAINER_FOURCC_T *variant) +{ + unsigned int i; + for(i = 0; codecid_to_fourcc_table[i].codecid; i++) + if(!strcmp(codecid_to_fourcc_table[i].codecid, codecid)) break; + if (variant) *variant = codecid_to_fourcc_table[i].variant; + return codecid_to_fourcc_table[i].fourcc; +} + +#if 0 +/** Find the track associated with an MKV track number */ +static VC_CONTAINER_TRACK_T *mkv_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int mkv_track_num) +{ + VC_CONTAINER_TRACK_T *p_track = 0; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->module->number == mkv_track_num) break; + + if(i < p_ctx->tracks_num) /* We found it */ + p_track = p_ctx->tracks[i]; + + return p_track; +} +#endif + +/** Base function used to read an MKV/EBML element header. + * This will read the element header do lots of sanity checking and return the element id + * and the size of the data contained in the element */ +static VC_CONTAINER_STATUS_T mkv_read_element_header(VC_CONTAINER_T *p_ctx, int64_t size, + MKV_ELEMENT_ID_T *id, int64_t *element_size, MKV_ELEMENT_ID_T parent_id, + MKV_ELEMENT_T **elem) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + MKV_ELEMENT_T *element; + + module->element_offset = STREAM_POSITION(p_ctx); + + *id = MKV_READ_ID(p_ctx, "Element ID"); + CHECK_POINT(p_ctx); + if(!*id) + { + LOG_DEBUG(p_ctx, "invalid element id %i", *id); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(elem) element = *elem; + else element = mkv_elements_list; + + /* Find out which Element we are dealing with */ + while(element->id && *id != element->id) element++; + + *element_size = MKV_READ_UINT(p_ctx, "Element Size"); + CHECK_POINT(p_ctx); + LOG_FORMAT(p_ctx, "- Element %s (ID 0x%x), Size: %"PRIi64", Offset: %"PRIi64, + element->psz_name, *id, *element_size, module->element_offset); + + /* Sanity check the element size */ + if(*element_size + 1 < 0 /* Shouldn't ever get that big */ || + /* Only the segment / cluster elements can really be massive */ + (*id != MKV_ELEMENT_ID_SEGMENT && *id != MKV_ELEMENT_ID_CLUSTER && + *element_size > MKV_MAX_ELEMENT_SIZE)) + { + LOG_DEBUG(p_ctx, "element %s has an invalid size (%"PRIi64")", + element->psz_name, *element_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + if(size >= 0 && *element_size > size) + { + LOG_DEBUG(p_ctx, "element %s is bigger than it should (%"PRIi64" > %"PRIi64")", + element->psz_name, *element_size, size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Sanity check that the element has the right parent */ + if(element->id && element->parent_id != MKV_ELEMENT_ID_INVALID && + parent_id != MKV_ELEMENT_ID_INVALID && parent_id != element->parent_id) + { + LOG_FORMAT(p_ctx, "Ignoring mis-placed element %s (ID 0x%x)", element->psz_name, *id); + while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; + } + + /* Sanity check that the element isn't too deeply nested */ + if(module->element_level >= MKV_MAX_ELEMENT_LEVEL) + { + LOG_DEBUG(p_ctx, "element %s is too deep. skipping", element->psz_name); + while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; + } + + if(elem) *elem = element; + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T mkv_read_element_data(VC_CONTAINER_T *p_ctx, + MKV_ELEMENT_T *element, int64_t element_size, int64_t size) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset; + + offset = STREAM_POSITION(p_ctx); + if (size < 0) size = element_size; + if (size < 0) size = INT64_C(1) << 62; + + /* Call the element specific parsing function */ + if(element->pf_func) + status = element->pf_func(p_ctx, element->id, element_size < 0 ? size : element_size); + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "element %s appears to be corrupted (%i)", element->psz_name, status); + + if(element_size < 0) return STREAM_STATUS(p_ctx); /* Unknown size */ + + /* Skip the rest of the element */ + element_size -= (STREAM_POSITION(p_ctx) - offset); + if(element_size < 0) /* Check for overruns */ + { + /* Things have gone really bad here and we ended up reading past the end of the + * element. We could maybe try to be clever and recover by seeking back to the end + * of the element. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of element %s", + -element_size, element->psz_name); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(element_size) + LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in element %s", element_size, element->psz_name); + + if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); + else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); + + return STREAM_STATUS(p_ctx); +} + +/** Base function used to read an MKV/EBML element. + * This will read the element header do lots of sanity checking and pass on the rest + * of the reading to the element specific reading function */ +static VC_CONTAINER_STATUS_T mkv_read_element(VC_CONTAINER_T *p_ctx, + int64_t size, MKV_ELEMENT_ID_T parent_id) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + MKV_ELEMENT_T *element = p_ctx->priv->module->elements_list; + int64_t element_size; + MKV_ELEMENT_ID_T id; + + status = mkv_read_element_header(p_ctx, size, &id, &element_size, parent_id, &element); + if(status != VC_CONTAINER_SUCCESS) return status; + return mkv_read_element_data(p_ctx, element, element_size, size); +} + +/** Reads an unsigned integer element */ +static VC_CONTAINER_STATUS_T mkv_read_element_data_uint(VC_CONTAINER_T *p_ctx, + int64_t size, uint64_t *value) +{ + switch(size) + { + case 1: *value = READ_U8(p_ctx, "u8-integer"); break; + case 2: *value = READ_U16(p_ctx, "u16-integer"); break; + case 3: *value = READ_U24(p_ctx, "u24-integer"); break; + case 4: *value = READ_U32(p_ctx, "u32-integer"); break; + case 5: *value = READ_U40(p_ctx, "u40-integer"); break; + case 6: *value = READ_U48(p_ctx, "u48-integer"); break; + case 7: *value = READ_U56(p_ctx, "u56-integer"); break; + case 8: *value = READ_U64(p_ctx, "u64-integer"); break; + default: return VC_CONTAINER_ERROR_CORRUPTED; + } + return STREAM_STATUS(p_ctx); +} + +/** Reads a float element */ +static VC_CONTAINER_STATUS_T mkv_read_element_data_float(VC_CONTAINER_T *p_ctx, + int64_t size, double *value) +{ + union { + uint32_t u32; + uint64_t u64; + float f; + double d; + } u; + + switch(size) + { + case 4: u.u32 = READ_U32(p_ctx, "f32-float"); *value = u.f; break; + case 8: u.u64 = READ_U64(p_ctx, "f64-float"); *value = u.d; break; + default: return VC_CONTAINER_ERROR_CORRUPTED; + } + LOG_FORMAT(p_ctx, "float: %f", *value); + return STREAM_STATUS(p_ctx); +} + +/** Reads an MKV EBML element */ +static VC_CONTAINER_STATUS_T mkv_read_element_ebml(VC_CONTAINER_T *p_ctx, + MKV_ELEMENT_ID_T id, int64_t size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + + /* Read contained elements */ + module->element_level++; + while(status == VC_CONTAINER_SUCCESS && size >= MKV_ELEMENT_MIN_HEADER_SIZE) + { + offset = STREAM_POSITION(p_ctx); + status = mkv_read_element(p_ctx, size, id); + size -= (STREAM_POSITION(p_ctx) - offset); + } + module->element_level--; + return status; +} + +/** Reads the MKV EBML sub-element */ +static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint64_t value; + + /* Deal with DocType first since it's a special case */ + if(id == MKV_ELEMENT_ID_DOCTYPE) + { + char doctype[] = "matroska doctype"; + + /* Check we've got the right doctype string for matroska */ + if(size <= 0) goto unknown_doctype; + if(size > (int)sizeof(doctype)) goto unknown_doctype; + if((int)READ_STRING(p_ctx, doctype, size, "string") != size) return STREAM_STATUS(p_ctx); + if((size != sizeof("matroska")-1 || strncmp(doctype, "matroska", (int)size)) && + (size != sizeof("webm")-1 || strncmp(doctype, "webm", (int)size))) + goto unknown_doctype; + + module->is_doctype_valid = true; + return VC_CONTAINER_SUCCESS; + + unknown_doctype: + LOG_DEBUG(p_ctx, "invalid doctype"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_EBML_VERSION: + case MKV_ELEMENT_ID_EBML_READ_VERSION: + if(value != 1) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + break; + case MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH: + if(value > 4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + break; + case MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH: + if(value > 8) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + break; + case MKV_ELEMENT_ID_DOCTYPE_VERSION: + case MKV_ELEMENT_ID_DOCTYPE_READ_VERSION: + default: break; + } + + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + bool unknown_size = size < 0; + + /* Read contained elements */ + module->element_level++; + while(status == VC_CONTAINER_SUCCESS && + (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) + { + offset = STREAM_POSITION(p_ctx); + status = mkv_read_element(p_ctx, size, id); + if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); + } + module->element_level--; + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + bool unknown_size = size < 0; + + /* Read contained elements */ + /* Initialise state used by reader */ + module->state.level = 0; + module->state.levels[0].offset = STREAM_POSITION(p_ctx); + module->state.levels[0].size = size; + module->state.levels[0].id = MKV_ELEMENT_ID_SEGMENT; + module->state.levels[0].data_start = 0; + module->state.levels[0].data_offset = 0; + module->timecode_scale = 1000000; + module->duration = 0.0; + module->segment_offset = STREAM_POSITION(p_ctx); + module->segment_size = size; + + /* Read contained elements until we have all the information we need to start + * playing the stream */ + module->element_level++; + while(status == VC_CONTAINER_SUCCESS && + (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) + { + MKV_ELEMENT_T *child = mkv_elements_list; + MKV_ELEMENT_ID_T child_id; + int64_t child_size; + + offset = STREAM_POSITION(p_ctx); + + status = mkv_read_element_header(p_ctx, size, &child_id, &child_size, id, &child); + if(status != VC_CONTAINER_SUCCESS) break; + + if(child_id == MKV_ELEMENT_ID_CLUSTER) + { + /* We found the start of the data */ + module->cluster_offset = module->element_offset; + module->state.level = 1; + module->state.levels[1].offset = STREAM_POSITION(p_ctx); + module->state.levels[1].size = child_size; + module->state.levels[1].id = MKV_ELEMENT_ID_CLUSTER; + module->state.levels[1].data_start = 0; + module->state.levels[1].data_offset = 0; + break; + } + + status = mkv_read_element_data(p_ctx, child, child_size, size); + if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); + } + + module->element_level--; + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_FOURCC_T fourcc = 0, variant = 0; + unsigned int i, extra_size = 0, extra_offset = 0, is_wf = 0, is_bmih = 0; + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= MKV_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + module->parsing = track; + track_module = track->priv->module; + track->is_enabled = true; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + track_module->timecode_scale = 1.0; + track_module->es_type.video.frame_rate = 0; + + status = mkv_read_elements( p_ctx, id, size ); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Sanity check the data we got from the track entry */ + if(!track_module->number || !track_module->type) + { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; } + + /* Check the encodings for the track are supported */ + if(track_module->encodings_num > MKV_MAX_ENCODINGS) + { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } + for(i = 0; i < track_module->encodings_num; i++) + { + if(track_module->encodings[i].type != MKV_CONTENT_ENCODING_COMPRESSION_HEADER || + !track_module->encodings[i].data_size) + { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } + } + + /* Find out the track type */ + if(track_module->type == 0x1) + es_type = VC_CONTAINER_ES_TYPE_VIDEO; + else if(track_module->type == 0x2) + es_type = VC_CONTAINER_ES_TYPE_AUDIO; + else if(track_module->type == 0x11) + es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + + if(es_type == VC_CONTAINER_ES_TYPE_UNKNOWN) + { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } + + if(!strcmp(track_module->codecid, "V_MS/VFW/FOURCC")) + { + if(vc_container_bitmapinfoheader_to_es_format(track->format->extradata, + track->format->extradata_size, &extra_offset, &extra_size, + track->format) == VC_CONTAINER_SUCCESS) + { + fourcc = track->format->codec; + is_bmih = 1; + } + track->format->extradata += extra_offset; + track->format->extradata_size = extra_size; + } + else if(!strcmp(track_module->codecid, "A_MS/ACM")) + { + if(vc_container_waveformatex_to_es_format(track->format->extradata, + track->format->extradata_size, &extra_offset, &extra_size, + track->format) == VC_CONTAINER_SUCCESS) + { + fourcc = track->format->codec; + is_wf = 1; + } + track->format->extradata += extra_offset; + track->format->extradata_size = extra_size; + } + else if((!strncmp(track_module->codecid, "A_AAC/MPEG2/", sizeof("A_AAC/MPEG2/")-1) || + !strncmp(track_module->codecid, "A_AAC/MPEG4/", sizeof("A_AAC/MPEG4/")-1)) && + !track->format->extradata_size) + { + static const unsigned int sample_rates[16] = + {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; + unsigned int samplerate, samplerate_idx, profile, sbr = 0; + uint8_t *extra; + + fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); + + /* Create extra data */ + if( !strcmp( &track_module->codecid[12], "MAIN" ) ) profile = 0; + else if( !strcmp( &track_module->codecid[12], "LC" ) ) profile = 1; + else if( !strcmp( &track_module->codecid[12], "SSR" ) ) profile = 2; + else if( !strcmp( &track_module->codecid[12], "LC/SBR" ) ) { profile = 1; sbr = 1; } + else profile = 3; + + samplerate = track_module->es_type.audio.sampling_frequency; + for( samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++ ) + if( sample_rates[samplerate_idx] == samplerate ) break; + + status = vc_container_track_allocate_extradata(p_ctx, track, sbr ? 5 : 2); + if(status != VC_CONTAINER_SUCCESS) goto error; + track->format->extradata_size = sbr ? 5 : 2; + extra = track->format->extradata; + + extra[0] = ((profile + 1) << 3) | ((samplerate_idx & 0xe) >> 1); + extra[1] = ((samplerate_idx & 0x1) << 7) | (track_module->es_type.audio.channels << 3); + + if(sbr) + { + unsigned int sync_extension_type = 0x2B7; + samplerate = track_module->es_type.audio.output_sampling_frequency; + for(samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++) + if(sample_rates[samplerate_idx] == samplerate) break; + extra[2] = (sync_extension_type >> 3) & 0xFF; + extra[3] = ((sync_extension_type & 0x7) << 5) | 5; + extra[4] = (1 << 7) | (samplerate_idx << 3); + } + } + else fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); + + if(!fourcc) + { + LOG_DEBUG(p_ctx, "codec id %s is not supported", track_module->codecid); + } + + LOG_DEBUG(p_ctx, "found track %4.4s", (char *)&fourcc); + track->format->codec = fourcc; + track->format->codec_variant = variant; + track->format->es_type = es_type; + + switch(es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: + if(!is_bmih) + { + track->format->type->video.width = track_module->es_type.video.pixel_width; + track->format->type->video.height = track_module->es_type.video.pixel_height; + } + track->format->type->video.visible_width = track->format->type->video.width; + track->format->type->video.visible_height = track->format->type->video.height; + if(track_module->es_type.video.pixel_crop_left < track->format->type->video.visible_width && + track_module->es_type.video.pixel_crop_top < track->format->type->video.visible_height) + { + track->format->type->video.x_offset = track_module->es_type.video.pixel_crop_left; + track->format->type->video.y_offset = track_module->es_type.video.pixel_crop_right; + track->format->type->video.visible_width -= track->format->type->video.x_offset; + track->format->type->video.visible_height -= track->format->type->video.y_offset; + } + if(track_module->es_type.video.pixel_crop_right < track->format->type->video.visible_width && + track_module->es_type.video.pixel_crop_bottom < track->format->type->video.visible_height) + { + track->format->type->video.visible_width -= track_module->es_type.video.pixel_crop_right; + track->format->type->video.visible_height -= track_module->es_type.video.pixel_crop_bottom; + } + if(track_module->es_type.video.frame_rate) + { + track->format->type->video.frame_rate_den = 100; + track->format->type->video.frame_rate_num = 100 * track_module->es_type.video.frame_rate; + } + if(track_module->es_type.video.display_width && track_module->es_type.video.display_height) + { + track->format->type->video.par_num = track_module->es_type.video.display_width * + track->format->type->video.visible_height; + track->format->type->video.par_den = track_module->es_type.video.display_height * + track->format->type->video.visible_width; + vc_container_maths_rational_simplify(&track->format->type->video.par_num, + &track->format->type->video.par_den); + } + break; + case VC_CONTAINER_ES_TYPE_AUDIO: + if(is_wf) break; + track->format->type->audio.sample_rate = track_module->es_type.audio.sampling_frequency; + if(track_module->es_type.audio.output_sampling_frequency) + track->format->type->audio.sample_rate = track_module->es_type.audio.output_sampling_frequency; + track->format->type->audio.channels = track_module->es_type.audio.channels; + track->format->type->audio.bits_per_sample = track_module->es_type.audio.bit_depth; + break; + default: + case VC_CONTAINER_ES_TYPE_SUBPICTURE: + track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; + if(!strcmp(track_module->codecid, "S_TEXT/ASCII")) + track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UNKNOWN; + } + + track->is_enabled = true; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + for(i = 0; i < MKV_MAX_ENCODINGS; i++) free(track_module->encodings[i].data); + vc_container_free_track(p_ctx, track); + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = module->parsing; + VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; + uint64_t value; + + /* Deal with string elements */ + if( id == MKV_ELEMENT_ID_NAME || + id == MKV_ELEMENT_ID_LANGUAGE || + id == MKV_ELEMENT_ID_TRACK_CODEC_ID || + id == MKV_ELEMENT_ID_TRACK_CODEC_NAME ) + { + char stringbuf[MKV_MAX_STRING_SIZE+1]; + + if(size > MKV_MAX_STRING_SIZE) + { + LOG_DEBUG(p_ctx, "string truncated (%i>%i)", (int)size, MKV_MAX_STRING_SIZE); + size = MKV_MAX_STRING_SIZE; + } + if(READ_BYTES(p_ctx, stringbuf, size) != (size_t)size) + { + //XXX do sane thing + return STREAM_STATUS(p_ctx); + } + stringbuf[size] = 0; /* null terminate */ + + LOG_FORMAT(p_ctx, "%s", stringbuf); + + if(id == MKV_ELEMENT_ID_TRACK_CODEC_ID) + strncpy(track_module->codecid, stringbuf, MKV_CODECID_MAX-1); + + return VC_CONTAINER_SUCCESS; + } + + /* Deal with codec private data */ + if( id == MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE ) + { + status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + return STREAM_STATUS(p_ctx); + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_TRACK_NUMBER: + track_module->number = value; break; + case MKV_ELEMENT_ID_TRACK_TYPE: + track_module->type = value; break; + case MKV_ELEMENT_ID_DEFAULT_DURATION: + track_module->frame_duration = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + uint64_t value; + + /* Deal with floating point values first */ + if(id == MKV_ELEMENT_ID_FRAME_RATE) + { + double fvalue; + status = mkv_read_element_data_float(p_ctx, size, &fvalue); + if(status != VC_CONTAINER_SUCCESS) return status; + track_module->es_type.video.frame_rate = fvalue; + return status; + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_PIXEL_WIDTH: track_module->es_type.video.pixel_width = value; break; + case MKV_ELEMENT_ID_PIXEL_HEIGHT: track_module->es_type.video.pixel_height = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM: track_module->es_type.video.pixel_crop_bottom = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_TOP: track_module->es_type.video.pixel_crop_top = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_LEFT: track_module->es_type.video.pixel_crop_left = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_RIGHT: track_module->es_type.video.pixel_crop_right = value; break; + case MKV_ELEMENT_ID_DISPLAY_WIDTH: track_module->es_type.video.display_width = value; break; + case MKV_ELEMENT_ID_DISPLAY_HEIGHT: track_module->es_type.video.display_height = value; break; + case MKV_ELEMENT_ID_DISPLAY_UNIT: track_module->es_type.video.display_unit = value; break; + case MKV_ELEMENT_ID_ASPECT_RATIO_TYPE: track_module->es_type.video.aspect_ratio_type = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + uint64_t value; + + /* Deal with floating point values first */ + if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY || + id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) + { + double fvalue; + status = mkv_read_element_data_float(p_ctx, size, &fvalue); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY) + track_module->es_type.audio.sampling_frequency = fvalue; + + if(id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) + track_module->es_type.audio.output_sampling_frequency = fvalue; + + return status; + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_CHANNELS: track_module->es_type.audio.channels = value; break; + case MKV_ELEMENT_ID_BIT_DEPTH: track_module->es_type.audio.bit_depth = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + VC_CONTAINER_STATUS_T status; + status = mkv_read_elements(p_ctx, id, size); + track_module->encodings_num++; + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + + /* These are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(track_module->encodings_num >= MKV_MAX_ENCODINGS) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + switch(id) + { + case MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE: + if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; + if(value == 1) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_ENCRYPTION; + else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; + break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + uint8_t *data; + + if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO) + { + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; + if(value == 3) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_HEADER; + else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; + return status; + } + + if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS) + { + if(track_module->encodings[track_module->encodings_num].type == MKV_CONTENT_ENCODING_COMPRESSION_HEADER) + { + if(size > MKV_MAX_ENCODING_DATA) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + data = malloc((int)size); + if(!data) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track_module->encodings[track_module->encodings_num].data = data; + track_module->encodings[track_module->encodings_num].data_size = READ_BYTES(p_ctx, data, size); + if(track_module->encodings[track_module->encodings_num].data_size != size) + track_module->encodings[track_module->encodings_num].data_size = 0; + return STREAM_STATUS(p_ctx); + } + else + { + SKIP_BYTES(p_ctx, size); + } + return STREAM_STATUS(p_ctx); + } + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t value; + double fvalue; + + switch(id) + { + case MKV_ELEMENT_ID_TIMECODE_SCALE: + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) break; + module->timecode_scale = value; + break; + case MKV_ELEMENT_ID_DURATION: + status = mkv_read_element_data_float(p_ctx, size, &fvalue); + if(status != VC_CONTAINER_SUCCESS) break; + module->duration = fvalue; + break; + case MKV_ELEMENT_ID_TITLE: + case MKV_ELEMENT_ID_MUXING_APP: + case MKV_ELEMENT_ID_WRITING_APP: + SKIP_STRING(p_ctx, size, "string"); + break; + + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t value; + + if(id == MKV_ELEMENT_ID_SEEK) + { + module->seekhead_elem_id = MKV_ELEMENT_ID_UNKNOWN; + module->seekhead_elem_offset = -1; + status = mkv_read_elements(p_ctx, id, size); + if(status == VC_CONTAINER_SUCCESS && !module->cues_offset && + module->seekhead_elem_id == MKV_ELEMENT_ID_CUES && module->seekhead_elem_offset) + module->cues_offset = module->seekhead_elem_offset; + if(status == VC_CONTAINER_SUCCESS && !module->tags_offset && + module->seekhead_elem_id == MKV_ELEMENT_ID_TAGS && module->seekhead_elem_offset) + module->tags_offset = module->seekhead_elem_offset; + return status; + } + + if(id == MKV_ELEMENT_ID_SEEK_ID) + { + MKV_ELEMENT_T *element = mkv_elements_list; + id = MKV_READ_ID(p_ctx, "Element ID"); + module->seekhead_elem_id = id; + + /* Find out which Element we are dealing with */ + while(element->id && id != element->id) element++; + LOG_FORMAT(p_ctx, "element: %s (ID 0x%x)", element->psz_name, id); + } + else if(id == MKV_ELEMENT_ID_SEEK_POSITION) + { + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + LOG_FORMAT(p_ctx, "offset: %"PRIi64, value + module->segment_offset); + module->seekhead_elem_offset = value + module->segment_offset; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(id); + VC_CONTAINER_PARAM_UNUSED(size); + module->cues_offset = module->element_offset; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + + /* All the values are unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_CUE_TIME: + module->cue_timecode = value; break; + case MKV_ELEMENT_ID_CUE_TRACK: + module->cue_track = value; break; + case MKV_ELEMENT_ID_CUE_CLUSTER_POSITION: + module->cue_cluster_offset = value; break; + case MKV_ELEMENT_ID_CUE_BLOCK_NUMBER: + module->cue_block = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + //XXX + case MKV_ELEMENT_ID_TIMECODE: module->state.cluster_timecode = value; break; + case MKV_ELEMENT_ID_BLOCK_DURATION: module->state.frame_duration = value; break; + default: break; + } + + return status; +} + +/*******************************/ + +static VC_CONTAINER_STATUS_T mkv_skip_element(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state) +{ + /* Skip any trailing data from the current element */ + int64_t skip = state->levels[state->level].offset + + state->levels[state->level].size - STREAM_POSITION(p_ctx); + + if(skip < 0) /* Check for overruns */ + { + /* Things have gone really bad here and we ended up reading past the end of the + * element. We could maybe try to be clever and recover by seeking back to the end + * of the element. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of the element", -skip); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(skip) + LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread at the end of element", skip); + + state->level--; + + if (skip >= MKV_MAX_ELEMENT_SIZE) + return SEEK(p_ctx, STREAM_POSITION(p_ctx) + skip); + SKIP_BYTES(p_ctx, skip); + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T mkv_find_next_element(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state, MKV_ELEMENT_ID_T element_id) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t element_size, element_offset; + MKV_ELEMENT_ID_T id; + + /* Skip all elements until we find the next requested element */ + do + { + MKV_ELEMENT_T *element = mkv_cluster_elements_list; + + /* Check whether we've reach the end of the parent element */ + if(STREAM_POSITION(p_ctx) >= state->levels[state->level].offset + + state->levels[state->level].size) + return VC_CONTAINER_ERROR_NOT_FOUND; + + status = mkv_read_element_header(p_ctx, INT64_C(1) << 30, &id, + &element_size, state->levels[state->level].id, &element); + element_offset = STREAM_POSITION(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + if(id == element_id) break; + if(element_id == MKV_ELEMENT_ID_BLOCKGROUP && id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; + + if(element_id == MKV_ELEMENT_ID_BLOCK && id == MKV_ELEMENT_ID_REFERENCE_BLOCK) + state->seen_ref_block = 1; + + /* Check whether we've reached the end of the parent element */ + if(STREAM_POSITION(p_ctx) + element_size >= state->levels[state->level].offset + + state->levels[state->level].size) + return VC_CONTAINER_ERROR_NOT_FOUND; + + status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(1) << 30); +#if 0 + if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); + else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); +#endif + } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + return STREAM_STATUS(p_ctx); + + state->level++; + state->levels[state->level].offset = element_offset; + state->levels[state->level].size = element_size; + state->levels[state->level].id = id; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_find_next_segment(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t element_size, element_offset; + MKV_ELEMENT_ID_T id; + + /* Skip all elements until we find the next segment */ + do + { + MKV_ELEMENT_T *element = mkv_cluster_elements_list; + + status = mkv_read_element_header(p_ctx, INT64_C(-1), &id, + &element_size, MKV_ELEMENT_ID_INVALID, &element); + + element_offset = STREAM_POSITION(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + if(id == MKV_ELEMENT_ID_SEGMENT) break; + + status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(-1)); + } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + return STREAM_STATUS(p_ctx); + + state->level++; + state->levels[state->level].offset = element_offset; + state->levels[state->level].size = element_size; + state->levels[state->level].id = id; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_find_next_block(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + + do + { + if(state->level < 0) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find segment"); +#endif + status = mkv_find_next_segment(p_ctx, state); + if(status == VC_CONTAINER_SUCCESS) + { + LOG_ERROR(p_ctx, "multi-segment files not supported"); + status = VC_CONTAINER_ERROR_EOS; + break; + } + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || + state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) + { + status = mkv_skip_element(p_ctx, state); + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCKGROUP) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find block"); +#endif + state->seen_ref_block = 0; + state->frame_duration = 0; + status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCK); + if(status == VC_CONTAINER_SUCCESS) break; + + if(status == VC_CONTAINER_ERROR_NOT_FOUND) + status = mkv_skip_element(p_ctx, state); + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_CLUSTER) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find blockgroup or simpleblock"); +#endif + state->frame_duration = 0; + status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCKGROUP); + if(status == VC_CONTAINER_SUCCESS && + state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; + if(status == VC_CONTAINER_ERROR_NOT_FOUND) + status = mkv_skip_element(p_ctx, state); + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_SEGMENT) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find cluster"); +#endif + status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_CLUSTER); + if(status == VC_CONTAINER_ERROR_NOT_FOUND) + status = mkv_skip_element(p_ctx, state); + } + + } while(status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; +} + +static VC_CONTAINER_STATUS_T mkv_read_next_frame_header(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state, uint32_t *pi_track, uint32_t *pi_length) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = 0; + unsigned int i, track, flags, is_first_lace = 0; + int64_t size, pts; + + if ((state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || + state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) && + state->levels[state->level].data_start + state->levels[state->level].data_offset < + state->levels[state->level].size) + goto end; + + status = mkv_find_next_block(p_ctx, state); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* We have a block */ + size = state->levels[state->level].size; + track = MKV_READ_UINT(p_ctx, "Track Number"); LOG_FORMAT(p_ctx, "Track Number: %u", track); + pts = (int16_t)MKV_READ_U16(p_ctx, "Timecode"); + flags = MKV_READ_U8(p_ctx, "Flags"); + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK) flags &= 0x0F; + + //TODO improve sanity checking + /* Sanity checking */ + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); + + for (i = 0; i < p_ctx->tracks_num; i++) + if (p_ctx->tracks[i]->priv->module->number == track) + { track_module = p_ctx->tracks[i]->priv->module; break; } + + /* Finding out if we have a keyframe when dealing with an MKV_ELEMENT_ID_BLOCK is tricky */ + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK && + i < p_ctx->tracks_num && p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + /* The absence of a ReferenceBlock element tells us if we are a keyframe or not. + * The problem we have is that this element can appear before as well as after the block element. + * To avoid seeking to look for this element, we do some guess work. */ + if(!state->seen_ref_block && state->level && + state->levels[state->level].offset + state->levels[state->level].size >= + state->levels[state->level-1].offset + state->levels[state->level-1].size) + flags |= 0x80; + } + + /* Take care of the lacing */ + state->lacing_num_frames = 0; + if(i < p_ctx->tracks_num && (flags & 0x06)) + { + unsigned int i, value = 0; + int32_t fs = 0; + + state->lacing_num_frames = MKV_READ_U8(p_ctx, "Lacing Head"); + state->lacing_size = 0; + switch((flags & 0x06)>>1) + { + case 1: /* Xiph lacing */ + for(i = 0; i < state->lacing_num_frames; i++, fs = 0) + { + do { + value = vc_container_io_read_uint8(p_ctx->priv->io); + fs += value; size--; + } while(value == 255); + LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); + if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; + state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; + } + break; + case 3: /* EBML lacing */ + for(i = 0; i < state->lacing_num_frames; i++) + { + if(!i) fs = MKV_READ_UINT(p_ctx, "Frame Size"); + else fs += MKV_READ_SINT(p_ctx, "Frame Size"); + LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); + if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; + state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; + } + break; + default: /* Fixed-size lacing */ + state->lacing_size = size / (state->lacing_num_frames+1); + break; + } + + /* There is a max number of laced frames we can support but after we can still give back + * all the other frames in 1 packet */ + if(state->lacing_num_frames > MKV_MAX_LACING_NUM) + state->lacing_num_frames = MKV_MAX_LACING_NUM; + + /* Sanity check the size of the frames */ + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); + state->lacing_current_size = state->lacing_size; + if(!state->lacing_size) + { + int64_t frames_size = 0; + for(i = state->lacing_num_frames; i > 0; i--) + { + if(frames_size + state->lacing_sizes[i-1] > size) /* return error ? */ + state->lacing_sizes[i-1] = size - frames_size; + frames_size += state->lacing_sizes[i-1]; + } + } + state->lacing_current_size = 0; + state->lacing_num_frames++; /* Will be decremented further down */ + is_first_lace = 1; + } + + state->track = i; + state->pts = (state->cluster_timecode + pts) * module->timecode_scale; + if(track_module) state->pts *= track_module->timecode_scale; + state->pts /= 1000; + state->flags = flags; + + state->frame_duration = state->frame_duration * module->timecode_scale / 1000; + if(state->lacing_num_frames) state->frame_duration /= state->lacing_num_frames; + if(!state->frame_duration && track_module) + state->frame_duration = track_module->frame_duration / 1000; + + state->levels[state->level].data_start = STREAM_POSITION(p_ctx) - + state->levels[state->level].offset; + state->levels[state->level].data_offset = 0; + + /* Deal with header stripping compression */ + state->header_size = 0; + if(track_module && track_module->encodings_num) + { + state->header_size = track_module->encodings[0].data_size; + state->header_data = track_module->encodings[0].data; + } + state->header_size_backup = state->header_size; + + end: + *pi_length = state->levels[state->level].size - state->levels[state->level].data_start - + state->levels[state->level].data_offset + state->header_size; + *pi_track = state->track; + + /* Special case for lacing */ + if(state->lacing_num_frames && + state->levels[state->level].data_offset >= state->lacing_current_size) + { + /* We need to switch to the next lace */ + if(--state->lacing_num_frames) + { + unsigned int lace_size = state->lacing_size; + if(!state->lacing_size) lace_size = state->lacing_sizes[state->lacing_num_frames-1]; + state->lacing_current_size = lace_size; + } + state->levels[state->level].data_start += state->levels[state->level].data_offset; + state->levels[state->level].data_offset = 0; + if(!is_first_lace && state->frame_duration) + state->pts += state->frame_duration; + else if(!is_first_lace) + state->pts = VC_CONTAINER_TIME_UNKNOWN; + + /* Deal with header stripping compression */ + state->header_data -= (state->header_size_backup - state->header_size); + state->header_size = state->header_size_backup; + } + if(state->lacing_num_frames) + *pi_length = state->lacing_current_size - state->levels[state->level].data_offset + state->header_size; + + return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; +} + +static VC_CONTAINER_STATUS_T mkv_read_frame_data(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state, uint8_t *p_data, uint32_t *pi_length) +{ + uint64_t size; + uint32_t header_size; + + size = state->levels[state->level].size - state->levels[state->level].data_start - + state->levels[state->level].data_offset; + + /* Special case for lacing */ + if(state->lacing_num_frames) + { + size = state->lacing_current_size - state->levels[state->level].data_offset; + + if(!p_data) + { + size = SKIP_BYTES(p_ctx, size); + state->levels[state->level].data_offset += size; + return STREAM_STATUS(p_ctx); + } + } + + size += state->header_size; + + if(!p_data) return mkv_skip_element(p_ctx, state); + if(size > *pi_length) size = *pi_length; + + header_size = state->header_size; + if(header_size) + { + if(header_size > size) header_size = size; + memcpy(p_data, state->header_data, header_size); + state->header_size -= header_size; + state->header_data += header_size; + size -= header_size; + } + + size = READ_BYTES(p_ctx, p_data + header_size, size); + state->levels[state->level].data_offset += size; + *pi_length = size + header_size; + + return STREAM_STATUS(p_ctx); +} + +/***************************************************************************** + Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T mkv_reader_read(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *p_track = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t buffer_size = 0, track = 0, data_size; + MKV_READER_STATE_T *state = &module->state; + + /* If a specific track has been selected, we need to use the track packet state */ + if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + p_track = p_ctx->tracks[p_packet->track]; + state = p_track->priv->module->state; + } + + /**/ + if(state->eos) return VC_CONTAINER_ERROR_EOS; + if(state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Look at the next frame header */ + status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); + if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; + if(status == VC_CONTAINER_ERROR_CORRUPTED) state->corrupted = true; + if(status != VC_CONTAINER_SUCCESS) return status; + + if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) + { + /* Skip frame */ + status = mkv_read_frame_data(p_ctx, state, 0, &data_size); + if (status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ + return mkv_read_frame_data(p_ctx, state, 0, &data_size); + + p_packet->dts = p_packet->pts = state->pts; + p_packet->flags = 0; + if(state->flags & 0x80) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if(!state->levels[state->level].data_offset) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + p_packet->size = data_size; + p_packet->track = track; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + return mkv_read_frame_data(p_ctx, state, 0, &data_size ); + else if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + /* Read the frame data */ + buffer_size = p_packet->buffer_size; + status = mkv_read_frame_data(p_ctx, state, p_packet->data, &buffer_size); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + p_packet->size = buffer_size; + if(buffer_size != data_size) + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mkv_reader_seek(VC_CONTAINER_T *p_ctx, + int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + MKV_READER_STATE_T *state = &module->state; + uint64_t offset = 0, prev_offset = 0, position = STREAM_POSITION(p_ctx); + int64_t time_offset = 0, prev_time_offset = 0; + unsigned int i, video_track; + MKV_ELEMENT_T *element = mkv_cue_elements_list; + int64_t size, element_size; + MKV_ELEMENT_ID_T id; + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + /* Find out if we have a video track */ + for(video_track = 0; video_track < p_ctx->tracks_num; video_track++) + if(p_ctx->tracks[video_track]->is_enabled && + p_ctx->tracks[video_track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; + + if(!*p_offset) goto end; /* Nothing much to do */ + if(!module->cues_offset) {status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error;} + + /* We need to do a search in the cue list */ + status = SEEK(p_ctx, module->cues_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* First read the header of the cues element */ + status = mkv_read_element_header(p_ctx, INT64_C(-1) /* TODO */, &id, &element_size, + MKV_ELEMENT_ID_SEGMENT, &element); + if(status != VC_CONTAINER_SUCCESS || id != MKV_ELEMENT_ID_CUES) goto error; + size = element_size; + + module->elements_list = mkv_cue_elements_list; + do + { + MKV_ELEMENT_T *element = mkv_cue_elements_list; + int64_t element_offset = STREAM_POSITION(p_ctx); + + /* Exit condition for when we've scanned the whole cues list */ + if(size <= 0) + { + if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + break; /* Just use the last valid entry in that case */ + status = VC_CONTAINER_ERROR_EOS; + goto error; + } + + status = mkv_read_element_header(p_ctx, size, &id, &element_size, + MKV_ELEMENT_ID_CUES, &element); + size -= STREAM_POSITION(p_ctx) - element_offset; + if(status == VC_CONTAINER_SUCCESS && element->id != MKV_ELEMENT_ID_UNKNOWN) + status = mkv_read_element_data(p_ctx, element, element_size, size); + + if(status != VC_CONTAINER_SUCCESS || element->id == MKV_ELEMENT_ID_UNKNOWN) + { + if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + break; /* Just use the last valid entry in that case */ + goto error; + } + + size -= element_size; + if(id != MKV_ELEMENT_ID_CUE_POINT) continue; + + /* Ignore cue points which don't belong to the track we want */ + if(video_track != p_ctx->tracks_num && + p_ctx->tracks[video_track]->priv->module->number != module->cue_track) continue; + + time_offset = module->cue_timecode * module->timecode_scale / 1000; + offset = module->cue_cluster_offset; + LOG_DEBUG(p_ctx, "INDEX: %"PRIi64, time_offset); + if( time_offset > *p_offset ) + { + if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + { + time_offset = prev_time_offset; + offset = prev_offset; + } + break; + } + prev_time_offset = time_offset; + prev_offset = offset; + } while( 1 ); + module->elements_list = mkv_elements_list; + *p_offset = time_offset; + + end: + /* Try seeking to the requested position */ + status = SEEK(p_ctx, module->segment_offset + offset); + if(status != VC_CONTAINER_SUCCESS && status != VC_CONTAINER_ERROR_EOS) goto error; + + /* Reinitialise the state */ + memset(state, 0, sizeof(*state)); + state->levels[0].offset = module->segment_offset; + state->levels[0].size = module->segment_size; + state->levels[0].id = MKV_ELEMENT_ID_SEGMENT; + if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; + p_track->priv->module->state = state; + } + + /* If we have a video track, we skip frames until the next keyframe */ + for(i = 0; video_track != p_ctx->tracks_num && i < 200 /* limit search */; ) + { + uint32_t track, data_size; + status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); + if(status != VC_CONTAINER_SUCCESS) break; //FIXME + if(track == video_track) i++; + if(track == video_track && (state->flags & 0x80) && + state->pts >= time_offset) break; + + /* Skip frame */ + status = mkv_read_frame_data(p_ctx, state, 0, &data_size); + } + + return VC_CONTAINER_SUCCESS; + + error: + /* Reset everything as it was before the seek */ + SEEK(p_ctx, position); + if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FAILED; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mkv_reader_close(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i, j; + + for(i = 0; i < p_ctx->tracks_num; i++) + { + for(j = 0; j < MKV_MAX_ENCODINGS; j++) + free(p_ctx->tracks[i]->priv->module->encodings[j].data); + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + } + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mkv_reader_open(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint8_t buffer[4]; + + // Can start with ASCII strings ???? + + /* Check for an EBML element */ + if(PEEK_BYTES(p_ctx, buffer, 4) < 4 || + buffer[0] != 0x1A || buffer[1] != 0x45 || buffer[2] != 0xDF || buffer[3] != 0xA3) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We are dealing with an MKV file + */ + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) {status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error;} + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + module->elements_list = mkv_elements_list; + + /* Read and sanity check the EBML header */ + status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); + if(status != VC_CONTAINER_SUCCESS) goto error; + if(!module->is_doctype_valid) {status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; goto error;} + + /* Read the other elements until we find the start of the data */ + do + { + status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); + if(status != VC_CONTAINER_SUCCESS) break; + + if(module->cluster_offset) break; + } while(1); + + /* Bail out if we didn't find a track */ + if(!p_ctx->tracks_num) + { + status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; + goto error; + } + + /* + * We now have all the information we really need to start playing the stream + */ + + p_ctx->priv->pf_close = mkv_reader_close; + p_ctx->priv->pf_read = mkv_reader_read; + p_ctx->priv->pf_seek = mkv_reader_seek; + p_ctx->duration = module->duration / 1000 * module->timecode_scale; + + /* Check if we're done */ + if(!STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_SUCCESS; + + if(module->cues_offset && (int64_t)module->cues_offset < p_ctx->size) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + if(module->tags_offset) + { + status = SEEK(p_ctx, module->tags_offset); + if(status == VC_CONTAINER_SUCCESS) + status = mkv_read_element(p_ctx, INT64_C(-1) /*FIXME*/, MKV_ELEMENT_ID_SEGMENT); + } + + /* Seek back to the start of the data */ + return SEEK(p_ctx, module->state.levels[1].offset); + + error: + LOG_DEBUG(p_ctx, "mkv: error opening stream (%i)", status); + if(module) mkv_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open mkv_reader_open +#endif diff --git a/containers/mp4/CMakeLists.txt b/containers/mp4/CMakeLists.txt new file mode 100755 index 0000000..87892c2 --- /dev/null +++ b/containers/mp4/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_mp4 ${LIBRARY_TYPE} mp4_reader.c) + +target_link_libraries(reader_mp4 containers) + +install(TARGETS reader_mp4 DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_mp4 ${LIBRARY_TYPE} mp4_writer.c) + +target_link_libraries(writer_mp4 containers) + +install(TARGETS writer_mp4 DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mp4/mp4_common.h b/containers/mp4/mp4_common.h new file mode 100755 index 0000000..8718535 --- /dev/null +++ b/containers/mp4/mp4_common.h @@ -0,0 +1,128 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MP4_COMMON_H +#define MP4_COMMON_H + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef enum { + MP4_BOX_TYPE_UNKNOWN = 0, + MP4_BOX_TYPE_ROOT = VC_FOURCC('r','o','o','t'), + MP4_BOX_TYPE_FTYP = VC_FOURCC('f','t','y','p'), + MP4_BOX_TYPE_MDAT = VC_FOURCC('m','d','a','t'), + MP4_BOX_TYPE_MOOV = VC_FOURCC('m','o','o','v'), + MP4_BOX_TYPE_MVHD = VC_FOURCC('m','v','h','d'), + MP4_BOX_TYPE_TRAK = VC_FOURCC('t','r','a','k'), + MP4_BOX_TYPE_TKHD = VC_FOURCC('t','k','h','d'), + MP4_BOX_TYPE_MDIA = VC_FOURCC('m','d','i','a'), + MP4_BOX_TYPE_MDHD = VC_FOURCC('m','d','h','d'), + MP4_BOX_TYPE_HDLR = VC_FOURCC('h','d','l','r'), + MP4_BOX_TYPE_MINF = VC_FOURCC('m','i','n','f'), + MP4_BOX_TYPE_VMHD = VC_FOURCC('v','m','h','d'), + MP4_BOX_TYPE_SMHD = VC_FOURCC('s','m','h','d'), + MP4_BOX_TYPE_DINF = VC_FOURCC('d','i','n','f'), + MP4_BOX_TYPE_DREF = VC_FOURCC('d','r','e','f'), + MP4_BOX_TYPE_STBL = VC_FOURCC('s','t','b','l'), + MP4_BOX_TYPE_STSD = VC_FOURCC('s','t','s','d'), + MP4_BOX_TYPE_STTS = VC_FOURCC('s','t','t','s'), + MP4_BOX_TYPE_CTTS = VC_FOURCC('c','t','t','s'), + MP4_BOX_TYPE_STSC = VC_FOURCC('s','t','s','c'), + MP4_BOX_TYPE_STSZ = VC_FOURCC('s','t','s','z'), + MP4_BOX_TYPE_STCO = VC_FOURCC('s','t','c','o'), + MP4_BOX_TYPE_CO64 = VC_FOURCC('c','o','6','4'), + MP4_BOX_TYPE_STSS = VC_FOURCC('s','t','s','s'), + MP4_BOX_TYPE_VIDE = VC_FOURCC('v','i','d','e'), + MP4_BOX_TYPE_SOUN = VC_FOURCC('s','o','u','n'), + MP4_BOX_TYPE_TEXT = VC_FOURCC('t','e','x','t'), + MP4_BOX_TYPE_FREE = VC_FOURCC('f','r','e','e'), + MP4_BOX_TYPE_SKIP = VC_FOURCC('s','k','i','p'), + MP4_BOX_TYPE_WIDE = VC_FOURCC('w','i','d','e'), + MP4_BOX_TYPE_PNOT = VC_FOURCC('p','m','o','t'), + MP4_BOX_TYPE_PICT = VC_FOURCC('P','I','C','T'), + MP4_BOX_TYPE_UDTA = VC_FOURCC('u','d','t','a'), + MP4_BOX_TYPE_UUID = VC_FOURCC('u','u','i','d'), + MP4_BOX_TYPE_ESDS = VC_FOURCC('e','s','d','s'), + MP4_BOX_TYPE_AVCC = VC_FOURCC('a','v','c','C'), + MP4_BOX_TYPE_D263 = VC_FOURCC('d','2','6','3'), + MP4_BOX_TYPE_DAMR = VC_FOURCC('d','a','m','r'), + MP4_BOX_TYPE_DAWP = VC_FOURCC('d','a','w','p'), + MP4_BOX_TYPE_DEVC = VC_FOURCC('d','e','v','c'), + MP4_BOX_TYPE_WAVE = VC_FOURCC('w','a','v','e'), + MP4_BOX_TYPE_ZERO = 0 +} MP4_BOX_TYPE_T; + +typedef enum { + MP4_BRAND_ISOM = VC_FOURCC('i','s','o','m'), + MP4_BRAND_MP42 = VC_FOURCC('m','p','4','2'), + MP4_BRAND_3GP4 = VC_FOURCC('3','g','p','4'), + MP4_BRAND_3GP5 = VC_FOURCC('3','g','p','5'), + MP4_BRAND_3GP6 = VC_FOURCC('3','g','p','6'), + MP4_BRAND_SKM2 = VC_FOURCC('s','k','m','2'), + MP4_BRAND_SKM3 = VC_FOURCC('s','k','m','3'), + MP4_BRAND_QT = VC_FOURCC('q','t',' ',' '), + MP4_BRAND_NUM +} MP4_BRAND_T; + +typedef enum +{ + MP4_SAMPLE_TABLE_STTS = 0, /* decoding time to sample */ + MP4_SAMPLE_TABLE_STSZ = 1, /* sample size */ + MP4_SAMPLE_TABLE_STSC = 2, /* sample to chunk */ + MP4_SAMPLE_TABLE_STCO = 3, /* sample to chunk-offset */ + MP4_SAMPLE_TABLE_STSS = 4, /* sync sample */ + MP4_SAMPLE_TABLE_CO64 = 5, /* sample to chunk-offset */ + MP4_SAMPLE_TABLE_CTTS = 6, /* composite time to sample */ + MP4_SAMPLE_TABLE_NUM +} MP4_SAMPLE_TABLE_T; + +/* Values for object_type_indication (mp4_decoder_config_descriptor) + * see ISO/IEC 14496-1:2001(E) section 8.6.6.2 table 8 p. 30 + * see ISO/IEC 14496-15:2003 (draft) section 4.2.2 table 3 p. 11 + * see SKT Spec 8.2.3 p. 107 + * see 3GPP2 Spec v1.0 p. 22 */ +#define MP4_MPEG4_VISUAL_OBJECT_TYPE 0x20 /* visual ISO/IEC 14496-2 */ +#define MP4_MPEG4_H264_OBJECT_TYPE 0x21 /* visual ISO/IEC 14496-10 */ +#define MP4_MPEG4_H264_PS_OBJECT_TYPE 0x22 /* visual ISO/IEC 14496-10 (used for parameter ES) */ +#define MP4_MPEG4_AAC_LC_OBJECT_TYPE 0x40 /* audio ISO/IEC 14496-3 */ +#define MP4_MPEG2_SP_OBJECT_TYPE 0x60 /* visual ISO/IEC 13818-2 Simple Profile */ +#define MP4_MPEG2_MP_OBJECT_TYPE 0x61 /* visual ISO/IEC 13818-2 Main Profile */ +#define MP4_MPEG2_SNR_OBJECT_TYPE 0x62 /* visual ISO/IEC 13818-2 SNR Profile */ +#define MP4_MPEG2_AAC_LC_OBJECT_TYPE 0x67 /* audio ISO/IEC 13818-7 LowComplexity Profile */ +#define MP4_MP3_OBJECT_TYPE 0x69 /* audio ISO/IEC 13818-3 */ +#define MP4_MPEG1_VISUAL_OBJECT_TYPE 0x6A /* visual ISO/IEC 11172-2 */ +#define MP4_MPEG1_AUDIO_OBJECT_TYPE 0x6B /* audio ISO/IEC 11172-3 */ +#define MP4_JPEG_OBJECT_TYPE 0x6C /* visual ISO/IEC 10918-1 */ +#define MP4_SKT_EVRC_2V1_OBJECT_TYPE 0x82 /* SKT spec V2.1 for EVRC */ +#define MP4_KTF_EVRC_OBJECT_TYPE 0xC2 /* KTF spec V1.2 for EVRC */ +#define MP4_KTF_AMR_OBJECT_TYPE 0xC4 /* KTF spec V1.2 for AMR */ +#define MP4_KTF_MP3_OBJECT_TYPE 0xC5 /* KTF spec V1.2 for MP3 */ +#define MP4_SKT_TEXT_OBJECT_TYPE 0xD0 /* SKT spec V2.2 for Text */ +#define MP4_SKT_EVRC_OBJECT_TYPE 0xD1 /* SKT spec V2.2 for EVRC */ +#define MP4_3GPP2_QCELP_OBJECT_TYPE 0xE1 /* 3GPP2 spec V1.0 for QCELP13K */ + +#endif /* MP4_COMMON_H */ diff --git a/containers/mp4/mp4_reader.c b/containers/mp4/mp4_reader.c new file mode 100755 index 0000000..08341f5 --- /dev/null +++ b/containers/mp4/mp4_reader.c @@ -0,0 +1,1879 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +/* Work-around for MSVC debugger issue */ +#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_MP4_READER_T +#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_MP4_READER_T + +#define CONTAINER_IS_BIG_ENDIAN +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/mp4/mp4_common.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level + +VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +TODO: +- aspect ratio +- itunes gapless +- edit list +- subpicture track +******************************************************************************/ + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MP4_TRACKS_MAX 16 + +#define MP4_BOX_MIN_HEADER_SIZE 8 +#define MP4_MAX_BOX_SIZE (1<<29) /* Does not apply to the mdat box */ +#define MP4_MAX_BOX_LEVEL 10 + +#define MP4_MAX_SAMPLES_BATCH_SIZE (16*1024) + +#define MP4_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) +#define MP4_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) +#define MP4_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) +#define MP4_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) +#define MP4_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) +#define MP4_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) +#define MP4_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) +#define MP4_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) +#define MP4_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) +#define MP4_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) +#define MP4_READ_FOURCC(ctx,n) (size -= 4, READ_FOURCC(ctx,n)) +#define MP4_SKIP_FOURCC(ctx,n) (size -= 4, SKIP_FOURCC(ctx,n)) +#define MP4_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) +#define MP4_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) +#define MP4_SKIP_STRING(ctx,sz,n) (size -= sz, SKIP_STRING(ctx,sz,n)) + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct +{ + VC_CONTAINER_STATUS_T status; + + int64_t duration; + int64_t pts; + int64_t dts; + + uint32_t sample; + int64_t offset; + unsigned int sample_offset; + unsigned int sample_size; + + uint32_t sample_duration; + uint32_t sample_duration_count; + int32_t sample_composition_offset; + uint32_t sample_composition_count; + + uint32_t next_sync_sample; + bool keyframe; + + uint32_t samples_per_chunk; + uint32_t chunks; + uint32_t samples_in_chunk; + + struct { + uint32_t entry; + } sample_table[MP4_SAMPLE_TABLE_NUM]; + +} MP4_READER_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + MP4_READER_STATE_T state; + + int64_t timescale; + uint8_t object_type_indication; + + uint32_t sample_size; + struct { + int64_t offset; + uint32_t entries; + uint32_t entry_size; + } sample_table[MP4_SAMPLE_TABLE_NUM]; + + uint32_t samples_batch_size; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int64_t box_offset; + int box_level; + + MP4_BRAND_T brand; + + int64_t timescale; + + VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX]; + unsigned int current_track; + + bool found_moov; + int64_t data_offset; + int64_t data_size; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T parent_type ); +static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T type ); +static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size ); + +static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size ); + +static struct { + const MP4_BOX_TYPE_T type; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t ); + const MP4_BOX_TYPE_T parent_type; +} mp4_box_list[] = +{ + {MP4_BOX_TYPE_FTYP, mp4_read_box_ftyp, MP4_BOX_TYPE_ROOT}, + {MP4_BOX_TYPE_MDAT, 0, MP4_BOX_TYPE_ROOT}, + {MP4_BOX_TYPE_MOOV, mp4_read_box_moov, MP4_BOX_TYPE_ROOT}, + {MP4_BOX_TYPE_MVHD, mp4_read_box_mvhd, MP4_BOX_TYPE_MOOV}, + {MP4_BOX_TYPE_TRAK, mp4_read_box_trak, MP4_BOX_TYPE_MOOV}, + {MP4_BOX_TYPE_TKHD, mp4_read_box_tkhd, MP4_BOX_TYPE_TRAK}, + {MP4_BOX_TYPE_MDIA, mp4_read_box_mdia, MP4_BOX_TYPE_TRAK}, + {MP4_BOX_TYPE_MDHD, mp4_read_box_mdhd, MP4_BOX_TYPE_MDIA}, + {MP4_BOX_TYPE_HDLR, mp4_read_box_hdlr, MP4_BOX_TYPE_MDIA}, + {MP4_BOX_TYPE_MINF, mp4_read_box_minf, MP4_BOX_TYPE_MDIA}, + {MP4_BOX_TYPE_VMHD, mp4_read_box_vmhd, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_SMHD, mp4_read_box_smhd, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_DINF, mp4_read_box_dinf, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_DREF, mp4_read_box_dref, MP4_BOX_TYPE_DINF}, + {MP4_BOX_TYPE_STBL, mp4_read_box_stbl, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_STSD, mp4_read_box_stsd, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STTS, mp4_read_box_stts, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_CTTS, mp4_read_box_ctts, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STSC, mp4_read_box_stsc, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STSZ, mp4_read_box_stsz, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STCO, mp4_read_box_stco, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_CO64, mp4_read_box_co64, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STSS, mp4_read_box_stss, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_VIDE, mp4_read_box_vide, MP4_BOX_TYPE_STSD}, + {MP4_BOX_TYPE_SOUN, mp4_read_box_soun, MP4_BOX_TYPE_STSD}, + {MP4_BOX_TYPE_TEXT, mp4_read_box_text, MP4_BOX_TYPE_STSD}, + + /* Codec specific boxes */ + {MP4_BOX_TYPE_AVCC, mp4_read_box_vide_avcC, MP4_BOX_TYPE_VIDE}, + {MP4_BOX_TYPE_D263, mp4_read_box_vide_d263, MP4_BOX_TYPE_VIDE}, + {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_VIDE}, + {MP4_BOX_TYPE_DAMR, mp4_read_box_soun_damr, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_DAWP, mp4_read_box_soun_dawp, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_DEVC, mp4_read_box_soun_devc, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_WAVE, mp4_read_box_soun_wave, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_SOUN}, + + {MP4_BOX_TYPE_UNKNOWN, 0, MP4_BOX_TYPE_UNKNOWN} +}; + +static struct { + const VC_CONTAINER_FOURCC_T type; + const VC_CONTAINER_FOURCC_T codec; + bool batch; +} mp4_codec_mapping[] = +{ + {VC_FOURCC('a','v','c','1'), VC_CONTAINER_CODEC_H264, 0}, + {VC_FOURCC('m','p','4','v'), VC_CONTAINER_CODEC_MP4V, 0}, + {VC_FOURCC('s','2','6','3'), VC_CONTAINER_CODEC_H263, 0}, + {VC_FOURCC('m','p','e','g'), VC_CONTAINER_CODEC_MP2V, 0}, + {VC_FOURCC('m','j','p','a'), VC_CONTAINER_CODEC_MJPEGA, 0}, + {VC_FOURCC('m','j','p','b'), VC_CONTAINER_CODEC_MJPEGB, 0}, + + {VC_FOURCC('j','p','e','g'), VC_CONTAINER_CODEC_JPEG, 0}, + + {VC_FOURCC('m','p','4','a'), VC_CONTAINER_CODEC_MP4A, 0}, + {VC_FOURCC('s','a','m','r'), VC_CONTAINER_CODEC_AMRNB, 0}, + {VC_FOURCC('s','a','w','b'), VC_CONTAINER_CODEC_AMRWB, 0}, + {VC_FOURCC('s','a','w','p'), VC_CONTAINER_CODEC_AMRWBP, 0}, + {VC_FOURCC('a','c','-','3'), VC_CONTAINER_CODEC_AC3, 0}, + {VC_FOURCC('e','c','-','3'), VC_CONTAINER_CODEC_EAC3, 0}, + {VC_FOURCC('s','e','v','c'), VC_CONTAINER_CODEC_EVRC, 0}, + {VC_FOURCC('e','v','r','c'), VC_CONTAINER_CODEC_EVRC, 0}, + {VC_FOURCC('s','q','c','p'), VC_CONTAINER_CODEC_QCELP, 0}, + {VC_FOURCC('a','l','a','w'), VC_CONTAINER_CODEC_ALAW, 1}, + {VC_FOURCC('u','l','a','w'), VC_CONTAINER_CODEC_MULAW, 1}, + {VC_FOURCC('t','w','o','s'), VC_CONTAINER_CODEC_PCM_SIGNED_BE, 1}, + {VC_FOURCC('s','o','w','t'), VC_CONTAINER_CODEC_PCM_SIGNED_LE, 1}, + + {0, 0}, +}; +static VC_CONTAINER_FOURCC_T mp4_box_type_to_codec(VC_CONTAINER_FOURCC_T type) +{ + int i; + for(i = 0; mp4_codec_mapping[i].type; i++ ) + if(mp4_codec_mapping[i].type == type) break; + return mp4_codec_mapping[i].codec; +} + +static bool codec_needs_batch_mode(VC_CONTAINER_FOURCC_T codec) +{ + int i; + for(i = 0; mp4_codec_mapping[i].codec; i++ ) + if(mp4_codec_mapping[i].codec == codec) break; + return mp4_codec_mapping[i].batch; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_header( VC_CONTAINER_T *p_ctx, int64_t size, + MP4_BOX_TYPE_T *box_type, int64_t *box_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t offset = STREAM_POSITION(p_ctx); + + module->box_offset = offset; + + *box_size = _READ_U32(p_ctx); + *box_type = _READ_FOURCC(p_ctx); + if(!*box_type) return VC_CONTAINER_ERROR_CORRUPTED; + + if(*box_size == 1) *box_size = _READ_U64(p_ctx); + LOG_FORMAT(p_ctx, "- Box %4.4s, Size: %"PRIi64", Offset: %"PRIi64, + (const char *)box_type, *box_size, offset); + + /* Sanity check the box size */ + if(*box_size < 0 /* Shouldn't ever get that big */ || + /* Only the mdat box can really be massive */ + (*box_type != MP4_BOX_TYPE_MDAT && *box_size > MP4_MAX_BOX_SIZE)) + { + LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")", + (const char *)box_type, *box_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + +#if 0 + /* It is valid for a box to have a zero size (i.e unknown) if it is the last one */ + if(*box_size == 0 && size >= 0) *box_size = size; + else if(*box_size == 0) *box_size = INT64_C(-1); +#else + if(*box_size <= 0) + { + LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")", + (const char *)box_type, *box_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } +#endif + + /* Sanity check box size against parent */ + if(size >= 0 && *box_size > size) + { + LOG_DEBUG(p_ctx, "box %4.4s is bigger than it should (%"PRIi64" > %"PRIi64")", + (const char *)box_type, *box_size, size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + *box_size -= (STREAM_POSITION(p_ctx) - offset); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_data( VC_CONTAINER_T *p_ctx, + MP4_BOX_TYPE_T box_type, int64_t box_size, MP4_BOX_TYPE_T parent_type ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t offset = STREAM_POSITION(p_ctx); + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + unsigned int i; + + /* Check if the box is a recognised one */ + for(i = 0; mp4_box_list[i].type; i++) + if(mp4_box_list[i].type == box_type && + mp4_box_list[i].parent_type == parent_type) break; + if(mp4_box_list[i].type == MP4_BOX_TYPE_UNKNOWN) + for(i = 0; mp4_box_list[i].type; i++) + if(mp4_box_list[i].type == box_type) break; + + /* Sanity check that the box has the right parent */ + if(mp4_box_list[i].type != MP4_BOX_TYPE_UNKNOWN && + mp4_box_list[i].parent_type != MP4_BOX_TYPE_UNKNOWN && + parent_type != MP4_BOX_TYPE_UNKNOWN && parent_type != mp4_box_list[i].parent_type) + { + LOG_FORMAT(p_ctx, "Ignoring mis-placed box %4.4s", (const char *)&box_type); + goto skip; + } + + /* Sanity check that the element isn't too deeply nested */ + if(module->box_level >= 2 * MP4_MAX_BOX_LEVEL) + { + LOG_DEBUG(p_ctx, "box %4.4s is too deep. skipping", (const char *)&box_type); + goto skip; + } + + module->box_level++; + + /* Call the box specific parsing function */ + if(mp4_box_list[i].pf_func) + status = mp4_box_list[i].pf_func(p_ctx, box_size); + + module->box_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted (%i)", (const char *)&box_type, status); + + skip: + /* Skip the rest of the box */ + box_size -= (STREAM_POSITION(p_ctx) - offset); + if(box_size < 0) /* Check for overruns */ + { + /* Things have gone really bad here and we ended up reading past the end of the + * box. We could maybe try to be clever and recover by seeking back to the end + * of the box. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of box %4.4s", + -box_size, (const char *)&box_type); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(box_size) + LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in box %4.4s", + box_size, (const char *)&box_type ); + + if(box_size < MP4_MAX_BOX_SIZE) box_size = SKIP_BYTES(p_ctx, box_size); + else SEEK(p_ctx, STREAM_POSITION(p_ctx) + box_size); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, + MP4_BOX_TYPE_T parent_type ) +{ + VC_CONTAINER_STATUS_T status; + MP4_BOX_TYPE_T box_type; + int64_t box_size; + + status = mp4_read_box_header( p_ctx, size, &box_type, &box_size ); + if(status != VC_CONTAINER_SUCCESS) return status; + return mp4_read_box_data( p_ctx, box_type, box_size, parent_type ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, + MP4_BOX_TYPE_T type) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + bool unknown_size = size < 0; + + /* Read contained boxes */ + module->box_level++; + while(status == VC_CONTAINER_SUCCESS && + (unknown_size || size >= MP4_BOX_MIN_HEADER_SIZE)) + { + offset = STREAM_POSITION(p_ctx); + status = mp4_read_box(p_ctx, size, type); + if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); + } + module->box_level--; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + module->brand = MP4_READ_FOURCC(p_ctx, "major_brand"); + MP4_SKIP_U32(p_ctx, "minor_version"); + while(size >= 4) MP4_SKIP_FOURCC(p_ctx, "compatible_brands"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MOOV); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t version, i; + int64_t duration; + + version = MP4_READ_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + if(version) + { + MP4_SKIP_U64(p_ctx, "creation_time"); + MP4_SKIP_U64(p_ctx, "modification_time"); + module->timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U64(p_ctx, "duration"); + } + else + { + MP4_SKIP_U32(p_ctx, "creation_time"); + MP4_SKIP_U32(p_ctx, "modification_time"); + module->timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U32(p_ctx, "duration"); + } + + if(module->timescale) + p_ctx->duration = duration * 1000000 / module->timescale; + + MP4_SKIP_U32(p_ctx, "rate"); + MP4_SKIP_U16(p_ctx, "volume"); + MP4_SKIP_U16(p_ctx, "reserved"); + for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved"); + for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix"); + for(i = 0; i < 6; i++) MP4_SKIP_U32(p_ctx, "pre_defined"); + MP4_SKIP_U32(p_ctx, "next_track_ID"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track; + + /* We have a new track. Allocate and initialise our track context */ + if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8; + + status = mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_TRAK); + + /* TODO: Sanity check track */ + + track->is_enabled = true; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + module->current_track++; + p_ctx->tracks_num++; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t i, version; + int64_t duration; + + version = MP4_READ_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + if(version) + { + MP4_SKIP_U64(p_ctx, "creation_time"); + MP4_SKIP_U64(p_ctx, "modification_time"); + MP4_SKIP_U32(p_ctx, "track_ID"); + MP4_SKIP_U32(p_ctx, "reserved"); + duration = MP4_READ_U64(p_ctx, "duration"); + } + else + { + MP4_SKIP_U32(p_ctx, "creation_time"); + MP4_SKIP_U32(p_ctx, "modification_time"); + MP4_SKIP_U32(p_ctx, "track_ID"); + MP4_SKIP_U32(p_ctx, "reserved"); + duration = MP4_READ_U32(p_ctx, "duration"); + } + + if(module->timescale) + duration = duration * 1000000 / module->timescale; + + for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "layer"); + MP4_SKIP_U16(p_ctx, "alternate_group"); + MP4_SKIP_U16(p_ctx, "volume"); + MP4_SKIP_U16(p_ctx, "reserved"); + for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix"); + + MP4_SKIP_U32(p_ctx, "width"); + MP4_SKIP_U32(p_ctx, "height"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MDIA); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + uint32_t version, timescale; + int64_t duration; + + version = MP4_READ_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + if(version) + { + MP4_SKIP_U64(p_ctx, "creation_time"); + MP4_SKIP_U64(p_ctx, "modification_time"); + timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U64(p_ctx, "duration"); + } + else + { + MP4_SKIP_U32(p_ctx, "creation_time"); + MP4_SKIP_U32(p_ctx, "modification_time"); + timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U32(p_ctx, "duration"); + } + + if(timescale) duration = duration * 1000000 / timescale; + track_module->timescale = timescale; + + MP4_SKIP_U16(p_ctx, "language"); /* ISO-639-2/T language code */ + MP4_SKIP_U16(p_ctx, "pre_defined"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + uint32_t i, fourcc, string_size; + VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; + + if(size <= 24) return VC_CONTAINER_ERROR_CORRUPTED; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U32(p_ctx, "pre-defined"); + + fourcc = MP4_READ_FOURCC(p_ctx, "handler_type"); + if(fourcc == MP4_BOX_TYPE_VIDE) es_type = VC_CONTAINER_ES_TYPE_VIDEO; + if(fourcc == MP4_BOX_TYPE_SOUN) es_type = VC_CONTAINER_ES_TYPE_AUDIO; + if(fourcc == MP4_BOX_TYPE_TEXT) es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + track->format->es_type = es_type; + + for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "reserved"); + + string_size = size; + if(module->brand == MP4_BRAND_QT) + string_size = MP4_READ_U8(p_ctx, "string_size"); + + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + if(string_size > size) string_size = size; + + MP4_SKIP_STRING(p_ctx, string_size, "name"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MINF); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U16(p_ctx, "graphicsmode"); + MP4_SKIP_U16(p_ctx, "opcolor"); + MP4_SKIP_U16(p_ctx, "opcolor"); + MP4_SKIP_U16(p_ctx, "opcolor"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U16(p_ctx, "balance"); + MP4_SKIP_U16(p_ctx, "reserved"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_DINF); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U32(p_ctx, "entry_count"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_STBL); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status; + MP4_BOX_TYPE_T box_type; + int64_t box_size; + uint32_t count; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + count = MP4_READ_U32(p_ctx, "entry_count"); + if(!count) return VC_CONTAINER_ERROR_CORRUPTED; + + status = mp4_read_box_header( p_ctx, size, &box_type, &box_size ); + if(status != VC_CONTAINER_SUCCESS) return status; + + track->format->codec = mp4_box_type_to_codec(box_type); + if(!track->format->codec) track->format->codec = box_type; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) box_type = MP4_BOX_TYPE_VIDE; + if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) box_type = MP4_BOX_TYPE_SOUN; + if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) box_type = MP4_BOX_TYPE_TEXT; + status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_STSD ); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Special treatment for MPEG4 */ + if(track->format->codec == VC_CONTAINER_CODEC_MP4A) + { + switch (track->priv->module->object_type_indication) + { + case MP4_MPEG4_AAC_LC_OBJECT_TYPE: + case MP4_MPEG2_AAC_LC_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP4A; break; + case MP4_MP3_OBJECT_TYPE: + case MP4_MPEG1_AUDIO_OBJECT_TYPE: + case MP4_KTF_MP3_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MPGA; break; + case MP4_SKT_EVRC_2V1_OBJECT_TYPE: + case MP4_SKT_EVRC_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_EVRC; break; + case MP4_3GPP2_QCELP_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_QCELP; break; + default: + track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break; + } + } + else if(track->format->codec == VC_CONTAINER_CODEC_MP4V) + { + switch (track->priv->module->object_type_indication) + { + case MP4_MPEG4_VISUAL_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP4V; break; + case MP4_JPEG_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_JPEG; break; + case MP4_MPEG2_SP_OBJECT_TYPE: + case MP4_MPEG2_SNR_OBJECT_TYPE: + case MP4_MPEG2_AAC_LC_OBJECT_TYPE: + case MP4_MPEG2_MP_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP2V; break; + case MP4_MPEG1_VISUAL_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP1V; break; + default: + track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break; + } + } + + /* For some codecs we process the samples in batches to be more efficient */ + if(codec_needs_batch_mode(track->format->codec)) + track->priv->module->samples_batch_size = MP4_MAX_SAMPLES_BATCH_SIZE; + + /* Fix-up some of the data */ + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_ALAW: + case VC_CONTAINER_CODEC_MULAW: + track->format->type->audio.bits_per_sample = 8; + track->priv->module->sample_size = track->format->type->audio.channels; + break; + case VC_CONTAINER_CODEC_PCM_SIGNED_LE: + case VC_CONTAINER_CODEC_PCM_SIGNED_BE: + track->priv->module->sample_size = (track->format->type->audio.bits_per_sample + 7) / + 8 * track->format->type->audio.channels; + break; + case VC_CONTAINER_CODEC_MP4A: + /* samplerate / channels is sometimes invalid so sanity check it using the codec config data */ + if(track->format->extradata_size >= 2) + { + static unsigned int rate[] = + { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350 }; + unsigned int samplerate = 0, channels = 0; + uint8_t *p = track->format->extradata; + uint32_t index = (p[0] & 7) << 1 | (p[1] >> 7); + if(index == 15 && track->format->extradata_size >= 5) + { + samplerate = (p[1] & 0x7f) << 17 | (p[2] << 9) | (p[3] << 1) | (p[4] >> 7); + channels = (p[4] >> 3) & 15; + } + else if(index < 13) + { + samplerate = rate[index]; + channels = (p[1] >> 3) & 15;; + } + + if(samplerate && samplerate != track->format->type->audio.sample_rate && + 2 * samplerate != track->format->type->audio.sample_rate) + track->format->type->audio.sample_rate = samplerate; + if(channels && channels != track->format->type->audio.channels && + 2 * channels != track->format->type->audio.channels) + track->format->type->audio.channels = channels; + } + break; + default: break; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_cache_table( VC_CONTAINER_T *p_ctx, MP4_SAMPLE_TABLE_T table, + uint32_t entries, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + uint32_t available_entries, entries_size; + + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + + track_module->sample_table[table].offset = STREAM_POSITION(p_ctx); + track_module->sample_table[table].entries = entries; + + available_entries = size / track_module->sample_table[table].entry_size; + if(available_entries < entries) + { + LOG_DEBUG(p_ctx, "table has less entries than advertised (%i/%i)", available_entries, entries); + entries = available_entries; + } + + entries_size = entries * track_module->sample_table[table].entry_size; + size = vc_container_io_cache(p_ctx->priv->io, entries_size ); + if(size != entries_size) + { + available_entries = size / track_module->sample_table[table].entry_size; + LOG_DEBUG(p_ctx, "cached less table entries than advertised (%i/%i)", available_entries, entries); + track_module->sample_table[table].entries = available_entries; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STTS, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CTTS, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSC, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + track_module->sample_size = READ_U32(p_ctx, "sample_size"); + if(track_module->sample_size) return STREAM_STATUS(p_ctx); + + entries = MP4_READ_U32(p_ctx, "sample_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSZ, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STCO, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CO64, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSS, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_esds_descriptor_header(VC_CONTAINER_T *p_ctx, int64_t *size, + uint32_t *descriptor_length, uint8_t *descriptor_type) +{ + uint32_t byte, length = 0; + + if(*size <= 0) return VC_CONTAINER_ERROR_CORRUPTED; + + *descriptor_type = _READ_U8(p_ctx); + (*size)--; + + /* Read descriptor size */ + while(*size) + { + byte = _READ_U8(p_ctx); + (*size)--; + length = (length << 7) | (byte&0x7F); + if(!(byte & 0x80)) break; + } + + if(*size <= 0 || length > *size) + { + LOG_FORMAT(p_ctx, "esds descriptor is corrupted"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + *descriptor_length = length; + LOG_FORMAT(p_ctx, "esds descriptor %x, size %i", *descriptor_type, *descriptor_length); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status; + uint32_t descriptor_length; + uint8_t descriptor_type; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(descriptor_type == 0x3) /* ES descriptor */ + { + uint8_t flags; + + MP4_SKIP_U16(p_ctx, "es_id"); + flags = MP4_READ_U8(p_ctx, "flags"); + + if(flags & 0x80) /* Stream dependence */ + MP4_SKIP_U16(p_ctx, "depend_on_es_id"); + + if(flags & 0x40) /* URL */ + { + uint32_t url_size = MP4_READ_U8(p_ctx, "url_size"); + MP4_SKIP_STRING(p_ctx, url_size, "url"); + } + + if(flags & 0x20) /* OCR_stream*/ + MP4_SKIP_U16(p_ctx, "OCR_es_id"); + + status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + if(descriptor_type == 0x4) /* Decoder Config descriptor */ + { + track->priv->module->object_type_indication = MP4_READ_U8(p_ctx, "object_type_indication"); + MP4_SKIP_U8(p_ctx, "stream_type"); + MP4_SKIP_U24(p_ctx, "buffer_size_db"); + MP4_SKIP_U32(p_ctx, "max_bitrate"); + track->format->bitrate = MP4_READ_U32(p_ctx, "avg_bitrate"); + + if(size <= 0 || descriptor_length <= 13) return STREAM_STATUS(p_ctx); + + status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); + if(status != VC_CONTAINER_SUCCESS) return status; + if(descriptor_type == 0x05 && descriptor_length) + { + status = vc_container_track_allocate_extradata(p_ctx, track, descriptor_length); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = MP4_READ_BYTES(p_ctx, track->format->extradata, descriptor_length); + } + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i; + + for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "data_reference_index"); + + MP4_SKIP_U16(p_ctx, "pre_defined"); + MP4_SKIP_U16(p_ctx, "reserved"); + for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "pre_defined"); + track->format->type->video.width = MP4_READ_U16(p_ctx, "width"); + track->format->type->video.height = MP4_READ_U16(p_ctx, "height"); + MP4_SKIP_U32(p_ctx, "horizresolution"); /* dpi */ + MP4_SKIP_U32(p_ctx, "vertresolution"); /* dpi */ + MP4_SKIP_U32(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "frame_count"); + MP4_SKIP_BYTES(p_ctx, 32); + MP4_SKIP_U16(p_ctx, "depth"); + MP4_SKIP_U16(p_ctx, "pre_defined"); + + if(size > 0) + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_VIDE ); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status; + + if(track->format->codec != VC_CONTAINER_CODEC_H264 || size <= 0) + return VC_CONTAINER_ERROR_CORRUPTED; + + track->format->codec_variant = VC_FOURCC('a','v','c','C'); + + status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U8(p_ctx, "level"); + MP4_SKIP_U8(p_ctx, "profile"); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i, version = 0; + + for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "data_reference_index"); + + version = MP4_READ_U16(p_ctx, "version"); + MP4_SKIP_U16(p_ctx, "revision_level"); + MP4_SKIP_U32(p_ctx, "vendor"); + + track->format->type->audio.channels = MP4_READ_U16(p_ctx, "channelcount"); + track->format->type->audio.bits_per_sample = MP4_READ_U16(p_ctx, "samplesize"); + MP4_SKIP_U16(p_ctx, "pre_defined"); + MP4_SKIP_U16(p_ctx, "reserved"); + track->format->type->audio.sample_rate = MP4_READ_U16(p_ctx, "samplerate"); + MP4_SKIP_U16(p_ctx, "samplerate_fp_low"); + + if(version == 1) + { + MP4_SKIP_U32(p_ctx, "samples_per_packet"); + MP4_SKIP_U32(p_ctx, "bytes_per_packet"); + MP4_SKIP_U32(p_ctx, "bytes_per_frame"); + MP4_SKIP_U32(p_ctx, "bytes_per_sample"); + } + + if(size > 0) + return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_SOUN ); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U8(p_ctx, "mode_set"); + MP4_SKIP_U8(p_ctx, "mode_change_period"); + MP4_SKIP_U8(p_ctx, "frame_per_second"); + + track->format->type->audio.channels = 1; + if(track->format->codec == VC_CONTAINER_CODEC_AMRNB) + track->format->type->audio.sample_rate = 8000; + else if(track->format->codec == VC_CONTAINER_CODEC_AMRWB) + track->format->type->audio.sample_rate = 16000; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + + track->format->type->audio.channels = 2; + track->format->type->audio.sample_rate = 16000; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U8(p_ctx, "samples_per_frame"); + + track->format->type->audio.channels = 1; + track->format->type->audio.sample_rate = 8000; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_SOUN); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(module); + + /* TODO */if(1) return VC_CONTAINER_ERROR_FAILED; + + if(size > 0) + return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_TEXT ); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +#ifdef ENABLE_MP4_READER_LOG_STATE +static void mp4_log_state( VC_CONTAINER_T *p_ctx, MP4_READER_STATE_T *state ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + LOG_DEBUG(p_ctx, "state:"); + LOG_DEBUG(p_ctx, "duration: %i, pts %i, dts %i", (int)state->duration, + (int)state->pts, (int)state->dts); + LOG_DEBUG(p_ctx, "sample: %i, offset %i, sample_offset %i, sample_size %i", + state->sample, (int)state->offset, state->sample_offset, + state->sample_size); + LOG_DEBUG(p_ctx, "sample_duration: %i, count %i", + state->sample_duration, state->sample_duration_count); + LOG_DEBUG(p_ctx, "sample_composition_offset: %i, count %i", + state->sample_composition_offset, state->sample_composition_count); + LOG_DEBUG(p_ctx, "next_sync_sample: %i, keyframe %i", + state->next_sync_sample, state->keyframe); + LOG_DEBUG(p_ctx, "samples_per_chunk: %i, chunks %i, samples_in_chunk %i", + state->samples_per_chunk, state->chunks, state->samples_in_chunk); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STTS %i", state->sample_table[MP4_SAMPLE_TABLE_STTS].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSZ %i", state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSC %i", state->sample_table[MP4_SAMPLE_TABLE_STSC].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STCO %i", state->sample_table[MP4_SAMPLE_TABLE_STCO].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CO64 %i", state->sample_table[MP4_SAMPLE_TABLE_CO64].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CTTS %i", state->sample_table[MP4_SAMPLE_TABLE_CTTS].entry); +} +#endif /* ENABLE_MP4_READER_LOG_STATE */ + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_seek_sample_table( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state, + MP4_SAMPLE_TABLE_T table) +{ + int64_t seek_offset; + + /* Seek to the next entry in the table */ + if(state->sample_table[table].entry >= track_module->sample_table[table].entries) + return VC_CONTAINER_ERROR_EOS; + + seek_offset = track_module->sample_table[table].offset + + track_module->sample_table[table].entry_size * state->sample_table[table].entry; + + return SEEK(p_ctx, seek_offset); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_sample_table( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state, + MP4_SAMPLE_TABLE_T table, unsigned int seek) +{ + uint32_t value; + + if(table == MP4_SAMPLE_TABLE_STSZ && track_module->sample_size) + { + state->sample_size = track_module->sample_size; + return state->status; + } + + /* CO64 support */ + if(table == MP4_SAMPLE_TABLE_STCO && + track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries) + table = MP4_SAMPLE_TABLE_CO64; + + /* Seek to the next entry in the table */ + if(seek) + { + state->status = mp4_seek_sample_table( p_ctx, track_module, state, table ); + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + } + + switch(table) + { + case MP4_SAMPLE_TABLE_STSZ: + state->sample_size = _READ_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + break; + + case MP4_SAMPLE_TABLE_STTS: + state->sample_duration_count = _READ_U32(p_ctx); + state->sample_duration = _READ_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + if(!state->sample_duration_count) state->status = VC_CONTAINER_ERROR_CORRUPTED; + break; + + case MP4_SAMPLE_TABLE_CTTS: + state->sample_composition_count = _READ_U32(p_ctx); + state->sample_composition_offset = _READ_U32(p_ctx); /* Converted to signed */ + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + if(!state->sample_composition_count) state->status = VC_CONTAINER_ERROR_CORRUPTED; + break; + + case MP4_SAMPLE_TABLE_STSC: + state->chunks = _READ_U32(p_ctx); + state->samples_per_chunk = _READ_U32(p_ctx); + _SKIP_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + + if(state->sample_table[table].entry + 1 < + track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries) value = _READ_U32(p_ctx); + else value = -1; + + if(!state->chunks || !state->samples_per_chunk || state->chunks >= value ) + {state->status = VC_CONTAINER_ERROR_CORRUPTED; break;} + state->chunks = value - state->chunks; + state->samples_in_chunk = state->samples_per_chunk; + break; + + case MP4_SAMPLE_TABLE_STCO: + case MP4_SAMPLE_TABLE_CO64: + state->offset = table == MP4_SAMPLE_TABLE_STCO ? _READ_U32(p_ctx) : _READ_U64(p_ctx); + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + if(!state->offset) state->status = VC_CONTAINER_ERROR_CORRUPTED; + state->samples_in_chunk = state->samples_per_chunk; + break; + + case MP4_SAMPLE_TABLE_STSS: + state->next_sync_sample = _READ_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + break; + + default: break; + } + + state->sample_table[table].entry++; + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_sample_header( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + if(state->sample_offset < state->sample_size) + return state->status; /* We still have data left from the current sample */ + + /* Switch to the next sample */ + state->offset += state->sample_size; + state->sample_offset = 0; + state->sample_size = 0; + state->sample++; + + if(!state->samples_in_chunk) + { + /* We're switching to the next chunk */ + if(!state->chunks) + { + /* Seek to the next entry in the STSC */ + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + } + + /* Get the offset of the new chunk */ + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + state->chunks--; + } + state->samples_in_chunk--; + + /* Get the new sample size */ + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + /* Get the timestamp */ + if(track_module->timescale) + state->pts = state->dts = state->duration * 1000000 / track_module->timescale; + if(!state->sample_duration_count) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + } + state->sample_duration_count--; + + /* Get the composition time */ + if(track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries) + { + if(!state->sample_composition_count) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + } + if(track_module->timescale) + state->pts = (state->duration + state->sample_composition_offset) * 1000000 / track_module->timescale; + state->sample_composition_count--; + } + state->duration += state->sample_duration; + + /* Get the keyframe flag */ + if(state->sample_table[MP4_SAMPLE_TABLE_STSS].entry < + track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries && + !state->next_sync_sample) + { + mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, 1 ); + state->status = VC_CONTAINER_SUCCESS; /* This isn't a critical error */ + } + + state->keyframe = + track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries && + state->sample == state->next_sync_sample; + if(state->keyframe) + state->next_sync_sample = 0; + + /* Try to batch several samples together if requested. We'll always stop at the chunk boundary */ + if(track_module->samples_batch_size) + { + uint32_t size = state->sample_size; + while(state->samples_in_chunk && size < track_module->samples_batch_size) + { + if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 )) break; + + if(!state->sample_duration_count) + if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 )) break; + + state->sample_duration_count--; + state->duration += state->sample_duration; + + size += state->sample_size; + state->samples_in_chunk--; + state->sample++; + } + state->sample_size = size; + } + +#ifdef ENABLE_MP4_READER_LOG_STATE + mp4_log_state(p_ctx, state); +#endif + + error: + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_sample_data( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state, uint8_t *data, unsigned int *data_size ) +{ + VC_CONTAINER_STATUS_T status; + unsigned int size = state->sample_size - state->sample_offset; + + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + if(data_size && *data_size < size) size = *data_size; + + if(data) + { + state->status = SEEK(p_ctx, state->offset + state->sample_offset); + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + size = READ_BYTES(p_ctx, data, size); + } + state->sample_offset += size; + + if(data_size) *data_size = size; + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + status = state->status; + + /* Switch to the start of the next sample */ + if(state->sample_offset >= state->sample_size) + mp4_read_sample_header(p_ctx, track, state); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status; + MP4_READER_STATE_T *state; + uint32_t i, track; + unsigned int data_size; + uint8_t *data = 0; + int64_t offset; + + /* Select the track to read from. If no specific track is requested by the caller, this + * will be the track to which the next bit of data in the mdat belongs to */ + if(!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)) + { + for(i = 0, track = 0, offset = -1; i < p_ctx->tracks_num; i++) + { + track_module = p_ctx->tracks[i]->priv->module; + + /* Ignore tracks which have no more readable data */ + if(track_module->state.status != VC_CONTAINER_SUCCESS) continue; + + if(offset >= 0 && track_module->state.offset >= offset) continue; + offset = track_module->state.offset; + track = i; + } + } + else track = packet->track; + + if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + track_module = p_ctx->tracks[track]->priv->module; + state = &track_module->state; + + status = mp4_read_sample_header(p_ctx, track, state); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(!packet) /* Skip packet */ + return mp4_read_sample_data(p_ctx, track, state, 0, 0); + + packet->dts = state->dts; + packet->pts = state->pts; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + if(state->keyframe) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if(!state->sample_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + packet->track = track; + packet->frame_size = state->sample_size; + packet->size = state->sample_size - state->sample_offset; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + return mp4_read_sample_data(p_ctx, track, state, 0, 0); + else if((flags & VC_CONTAINER_READ_FLAG_INFO) || !packet->data) + return VC_CONTAINER_SUCCESS; + + data = packet->data; + data_size = packet->buffer_size; + + status = mp4_read_sample_data(p_ctx, track, state, data, &data_size); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + packet->size = data_size; + if(state->sample_offset) //? + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return status; +} + +/*****************************************************************************/ +static uint32_t mp4_find_sample( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state, int64_t seek_time, VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t sample = 0, sample_duration_count; + int64_t sample_duration, seek_time_up = seek_time + 1; + unsigned int i; + VC_CONTAINER_PARAM_UNUSED(state); + + seek_time = seek_time * track_module->timescale / 1000000; + /* We also need to check against the time rounded up to account for + * rounding errors in the timestamp (because of the timescale conversion) */ + seek_time_up = seek_time_up * track_module->timescale / 1000000; + + status = SEEK(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].offset); + if(status != VC_CONTAINER_SUCCESS) goto end; + + /* Find the sample which corresponds to the requested time */ + for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++) + { + sample_duration_count = _READ_U32(p_ctx); + sample_duration = _READ_U32(p_ctx); + status = STREAM_STATUS(p_ctx); + if(status != VC_CONTAINER_SUCCESS) break; + + if(sample_duration_count * sample_duration <= seek_time) + { + seek_time -= sample_duration_count * sample_duration; + seek_time_up -= sample_duration_count * sample_duration; + sample += sample_duration_count; + continue; + } + if(!sample_duration) break; + + seek_time /= sample_duration; + seek_time_up /= sample_duration; + sample += MAX(seek_time, seek_time_up); + break; + } + + end: + if(p_status) *p_status = status; + return sample; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_seek_track( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state, uint32_t sample ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + uint32_t chunk = 0, samples; + unsigned int i; + + memset(state, 0, sizeof(*state)); + + /* Find the right chunk */ + for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->chunks * state->samples_per_chunk <= samples) + { + samples -= state->chunks * state->samples_per_chunk; + chunk += state->chunks; + continue; + } + + while(samples >= state->samples_per_chunk) + { + samples -= state->samples_per_chunk; + state->chunks--; + chunk++; + } + + state->chunks--; + break; + } + + /* Get the offset of the selected chunk */ + state->sample_table[MP4_SAMPLE_TABLE_STCO].entry = chunk; + state->sample_table[MP4_SAMPLE_TABLE_CO64].entry = chunk; + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + /* Find the sample offset within the chunk */ + state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry = sample - samples; + for(i = 0; i < samples; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + state->offset += state->sample_size; + state->samples_in_chunk--; + } + + /* Get the timestamp */ + for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->sample_duration_count <= samples) + { + samples -= state->sample_duration_count; + state->duration += state->sample_duration * state->sample_duration_count; + continue; + } + + state->sample_duration_count -= samples; + state->duration += samples * state->sample_duration; + break; + } + + /* Find the right place in the sample composition table */ + for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->sample_composition_count <= samples) + { + samples -= state->sample_composition_count; + continue; + } + + state->sample_composition_count -= samples; + break; + } + + /* Find the right place in the synchronisation table */ + for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->next_sync_sample >= sample + 1) break; + } + + state->sample = sample; + state->sample_size = 0; + mp4_read_sample_header(p_ctx, track, state); + + error: + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_reader_seek(VC_CONTAINER_T *p_ctx, + int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status; + uint32_t i, track, sample, prev_sample, next_sample; + int64_t seek_time = *offset; + VC_CONTAINER_PARAM_UNUSED(module); + VC_CONTAINER_PARAM_UNUSED(mode); + + /* Reset the states */ + for(i = 0; i < p_ctx->tracks_num; i++) + memset(&p_ctx->tracks[i]->priv->module->state, 0, sizeof(p_ctx->tracks[i]->priv->module->state)); + + /* Deal with the easy case first */ + if(!*offset) + { + /* Initialise tracks */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + /* FIXME: we should check we've got at least one success */ + mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state); + } + return VC_CONTAINER_SUCCESS; + } + + /* Find the first enabled video track */ + for(track = 0; track < p_ctx->tracks_num; track++) + if(p_ctx->tracks[track]->is_enabled && + p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; + if(track == p_ctx->tracks_num) goto seek_time_found; /* No video track found */ + track_module = p_ctx->tracks[track]->priv->module; + + /* Find the sample number for the requested time */ + sample = mp4_find_sample( p_ctx, track, &track_module->state, seek_time, &status ); + if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; + + /* Find the closest sync sample */ + status = mp4_seek_sample_table( p_ctx, track_module, &track_module->state, MP4_SAMPLE_TABLE_STSS ); + if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; + for(i = 0, prev_sample = 0, next_sample = 0; + i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++) + { + next_sample = _READ_U32(p_ctx) - 1; + if(next_sample > sample) + { + sample = (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) ? next_sample : prev_sample; + break; + } + prev_sample = next_sample; + } + + /* Do the seek on this track and use its timestamp as the new seek point */ + status = mp4_seek_track(p_ctx, track, &track_module->state, sample); + if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; + seek_time = track_module->state.pts; + + seek_time_found: + + for(i = 0; i < p_ctx->tracks_num; i++) + { + uint32_t sample; + track_module = p_ctx->tracks[i]->priv->module; + if(track_module->state.offset) continue; + sample = mp4_find_sample( p_ctx, i, &track_module->state, seek_time, &status ); + if(status != VC_CONTAINER_SUCCESS) return status; //FIXME + + status = mp4_seek_track(p_ctx, i, &track_module->state, sample); + } + + *offset = seek_time; + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + VC_CONTAINER_MODULE_T *module = 0; + unsigned int i; + uint8_t h[8]; + + /* Check for a known box type to see if we're dealing with mp4 */ + if( PEEK_BYTES(p_ctx, h, 8) != 8 ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + switch(VC_FOURCC(h[4],h[5],h[6],h[7])) + { + case MP4_BOX_TYPE_FTYP: + case MP4_BOX_TYPE_MDAT: + case MP4_BOX_TYPE_MOOV: + case MP4_BOX_TYPE_FREE: + case MP4_BOX_TYPE_SKIP: + case MP4_BOX_TYPE_WIDE: + case MP4_BOX_TYPE_PNOT: + case MP4_BOX_TYPE_PICT: + case MP4_BOX_TYPE_UDTA: + case MP4_BOX_TYPE_UUID: + break; + default: + /* Couldn't recognize the box type. This doesn't look like an mp4. */ + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* + * We are dealing with an MP4 file + */ + + LOG_DEBUG(p_ctx, "using mp4 reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS) + { + MP4_BOX_TYPE_T box_type; + int64_t box_size; + + status = mp4_read_box_header( p_ctx, INT64_C(-1), &box_type, &box_size ); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(box_type == MP4_BOX_TYPE_MDAT) + { + module->data_offset = STREAM_POSITION(p_ctx); + module->data_size = box_size; + if(module->found_moov) break; /* We've got everything we want */ + } + else if(box_type == MP4_BOX_TYPE_MOOV) + module->found_moov = true; + + status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_ROOT ); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(module->found_moov && module->data_offset) break; /* We've got everything we want */ + } + + /* Initialise tracks */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + /* FIXME: we should check we've got at least one success */ + status = mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state); + } + + status = SEEK(p_ctx, module->data_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + + p_ctx->priv->pf_close = mp4_reader_close; + p_ctx->priv->pf_read = mp4_reader_read; + p_ctx->priv->pf_seek = mp4_reader_seek; + + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "mp4: error opening stream"); + if(module) mp4_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open mp4_reader_open +#endif diff --git a/containers/mp4/mp4_writer.c b/containers/mp4/mp4_writer.c new file mode 100755 index 0000000..5b33e01 --- /dev/null +++ b/containers/mp4/mp4_writer.c @@ -0,0 +1,1441 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/mp4/mp4_common.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level + +VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MP4_TRACKS_MAX 16 +#define MP4_TIMESCALE 1000 + +#define MP4_64BITS_TIME 0 /* 0 to disable / 1 to enable */ + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + uint32_t fourcc; + uint32_t samples; + uint32_t chunks; + + int64_t offset; + int64_t timestamp; + int64_t delta_timestamp; + int64_t samples_in_chunk; + int64_t samples_in_prev_chunk; + struct { + uint32_t entries; + uint32_t entry_size; + } sample_table[MP4_SAMPLE_TABLE_NUM]; + + int64_t first_pts; + int64_t last_pts; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int box_level; + MP4_BRAND_T brand; + + VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX]; + bool tracks_add_done; + + VC_CONTAINER_WRITER_EXTRAIO_T null; + + unsigned int current_track; + + unsigned moov_size; + int64_t mdat_offset; + int64_t data_offset; + + uint32_t samples; + VC_CONTAINER_WRITER_EXTRAIO_T temp; + VC_CONTAINER_PACKET_T sample; + int64_t sample_offset; + int64_t prev_sample_dts; + + int64_t duration; + /**/ + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type, uint32_t fourcc ); +static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type ); +static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx ); + +static struct { + const MP4_BOX_TYPE_T type; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); +} mp4_box_list[] = +{ + {MP4_BOX_TYPE_FTYP, mp4_write_box_ftyp}, + {MP4_BOX_TYPE_MOOV, mp4_write_box_moov}, + {MP4_BOX_TYPE_MVHD, mp4_write_box_mvhd}, + {MP4_BOX_TYPE_TRAK, mp4_write_box_trak}, + {MP4_BOX_TYPE_TKHD, mp4_write_box_tkhd}, + {MP4_BOX_TYPE_MDIA, mp4_write_box_mdia}, + {MP4_BOX_TYPE_MDHD, mp4_write_box_mdhd}, + {MP4_BOX_TYPE_HDLR, mp4_write_box_hdlr}, + {MP4_BOX_TYPE_MINF, mp4_write_box_minf}, + {MP4_BOX_TYPE_VMHD, mp4_write_box_vmhd}, + {MP4_BOX_TYPE_SMHD, mp4_write_box_smhd}, + {MP4_BOX_TYPE_DINF, mp4_write_box_dinf}, + {MP4_BOX_TYPE_DREF, mp4_write_box_dref}, + {MP4_BOX_TYPE_STBL, mp4_write_box_stbl}, + {MP4_BOX_TYPE_STSD, mp4_write_box_stsd}, + {MP4_BOX_TYPE_STTS, mp4_write_box_stts}, + {MP4_BOX_TYPE_CTTS, mp4_write_box_ctts}, + {MP4_BOX_TYPE_STSC, mp4_write_box_stsc}, + {MP4_BOX_TYPE_STSZ, mp4_write_box_stsz}, + {MP4_BOX_TYPE_STCO, mp4_write_box_stco}, + {MP4_BOX_TYPE_CO64, mp4_write_box_co64}, + {MP4_BOX_TYPE_STSS, mp4_write_box_stss}, + {MP4_BOX_TYPE_VIDE, mp4_write_box_vide}, + {MP4_BOX_TYPE_SOUN, mp4_write_box_soun}, + {MP4_BOX_TYPE_ESDS, mp4_write_box_esds}, + {MP4_BOX_TYPE_UNKNOWN, 0} +}; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type, uint32_t fourcc ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t box_size = 0; + unsigned int i; + + /* Find out which object we want to write */ + for( i = 0; mp4_box_list[i].type && mp4_box_list[i].type != type; i++ ); + + /* Check we found the requested type */ + if(!mp4_box_list[i].type) + { + vc_container_assert(0); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* We need to find out the size of the object we're going to write it. */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) + { + status = mp4_write_box_extended( p_ctx, type, fourcc ); + box_size = STREAM_POSITION(p_ctx); + } + vc_container_writer_extraio_disable(p_ctx, &module->null); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Write the object header */ + LOG_FORMAT(p_ctx, "- Box %4.4s, size: %"PRIi64, (const char *)&fourcc, box_size); + _WRITE_U32(p_ctx, (uint32_t)box_size); + _WRITE_FOURCC(p_ctx, fourcc); + + module->box_level++; + + /* Call the object specific writing function */ + status = mp4_box_list[i].pf_func(p_ctx); + + module->box_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted", (char *)mp4_box_list[i].type); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type ) +{ + return mp4_write_box_extended( p_ctx, type, type ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_FOURCC(p_ctx, module->brand, "major_brand"); + WRITE_U32(p_ctx, 512, "minor_version"); + if(module->brand == MP4_BRAND_QT) + { + WRITE_FOURCC(p_ctx, MP4_BRAND_QT, "compatible_brands"); + return STREAM_STATUS(p_ctx); + } + + if(module->brand == MP4_BRAND_SKM2) + WRITE_FOURCC(p_ctx, MP4_BRAND_SKM2, "compatible_brands"); + WRITE_FOURCC(p_ctx, MP4_BRAND_ISOM, "compatible_brands"); + WRITE_FOURCC(p_ctx, MP4_BRAND_MP42, "compatible_brands"); + WRITE_FOURCC(p_ctx, MP4_BRAND_3GP4, "compatible_brands"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MVHD); + if(status != VC_CONTAINER_SUCCESS) return status; + + for(i = 0; i < p_ctx->tracks_num; i++) + { + module->current_track = i; + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TRAK); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx ) +{ + static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 }; + unsigned int version = MP4_64BITS_TIME; + unsigned int i; + + WRITE_U8(p_ctx, version, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + /**/ + p_ctx->duration = 0; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; + int64_t track_duration = track_module->last_pts - track_module->first_pts; + if(track_duration > p_ctx->duration) + p_ctx->duration = track_duration; + } + + if(version) + { + WRITE_U64(p_ctx, 0, "creation_time"); + WRITE_U64(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + else + { + WRITE_U32(p_ctx, 0, "creation_time"); + WRITE_U32(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + + WRITE_U32(p_ctx, 0x10000, "rate"); /* 1.0 */ + WRITE_U16(p_ctx, 0x100, "volume"); /* full volume */ + WRITE_U16(p_ctx, 0, "reserved"); + for(i = 0; i < 2; i++) + WRITE_U32(p_ctx, 0, "reserved"); + for(i = 0; i < 9; i++) /* unity matrix */ + WRITE_U32(p_ctx, matrix[i], "matrix"); + for(i = 0; i < 6; i++) + WRITE_U32(p_ctx, 0, "pre_defined"); + WRITE_U32(p_ctx, p_ctx->tracks_num + 1, "next_track_ID"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TKHD); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDIA); + if(status != VC_CONTAINER_SUCCESS) return status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx ) +{ + static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 }; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int version = MP4_64BITS_TIME; + uint32_t i, width = 0, height = 0; + + WRITE_U8(p_ctx, version, "version"); + WRITE_U24(p_ctx, 0x7, "flags"); /* track enabled */ + + if(version) + { + WRITE_U64(p_ctx, 0, "creation_time"); + WRITE_U64(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, module->current_track + 1, "track_ID"); + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + else + { + WRITE_U32(p_ctx, 0, "creation_time"); + WRITE_U32(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, module->current_track + 1, "track_ID"); + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + + for(i = 0; i < 2; i++) + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 0, "layer"); + WRITE_U16(p_ctx, 0, "alternate_group"); + WRITE_U16(p_ctx, track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO ? 0x100 : 0, "volume"); + WRITE_U16(p_ctx, 0, "reserved"); + for(i = 0; i < 9; i++) /* unity matrix */ + WRITE_U32(p_ctx, matrix[i], "matrix"); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + width = track->format->type->video.width << 16; + height = track->format->type->video.height << 16; + if(track->format->type->video.par_num && track->format->type->video.par_den) + width = width * (uint64_t)track->format->type->video.par_num / + track->format->type->video.par_den; + } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + { + /* FIXME */ + } + + WRITE_U32(p_ctx, width, "width"); + WRITE_U32(p_ctx, height, "height"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDHD); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_HDLR); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MINF); + if(status != VC_CONTAINER_SUCCESS) return status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx ) +{ + unsigned int version = MP4_64BITS_TIME; + + WRITE_U8(p_ctx, version, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + // FIXME: take a better timescale ?? + if(version) + { + WRITE_U64(p_ctx, 0, "creation_time"); + WRITE_U64(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + else + { + WRITE_U32(p_ctx, 0, "creation_time"); + WRITE_U32(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + + WRITE_U16(p_ctx, 0x55c4, "language"); /* ISO-639-2/T language code */ + WRITE_U16(p_ctx, 0, "pre_defined"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + uint32_t i, handler_size, fourcc = 0; + const char *handler_name; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) fourcc = VC_FOURCC('v','i','d','e'); + if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) fourcc = VC_FOURCC('s','o','u','n'); + if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) fourcc = VC_FOURCC('t','e','x','t'); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + if(module->brand == MP4_BRAND_QT) + WRITE_FOURCC(p_ctx, VC_FOURCC('m','h','l','r'), "component_type"); + else + WRITE_U32(p_ctx, 0, "pre-defined"); + + WRITE_FOURCC(p_ctx, fourcc, "handler_type"); + for(i = 0; i < 3; i++) + WRITE_U32(p_ctx, 0, "reserved"); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { handler_name = "Video Media Handler"; handler_size = sizeof("Video Media Handler"); } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { handler_name = "Audio Media Handler"; handler_size = sizeof("Audio Media Handler"); } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + { handler_name = "Text Media Handler"; handler_size = sizeof("Text Media Handler"); } + else { handler_name = ""; handler_size = sizeof(""); } + + if(module->brand == MP4_BRAND_QT) + { handler_size--; WRITE_U8(p_ctx, handler_size, "string_size"); } + + WRITE_STRING(p_ctx, handler_name, handler_size, "name"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_VMHD); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_SMHD); +#if 0 + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + /*FIXME */; +#endif + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DINF); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STBL); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 1, "flags"); + + WRITE_U16(p_ctx, 0, "graphicsmode"); + WRITE_U16(p_ctx, 0, "opcolor"); + WRITE_U16(p_ctx, 0, "opcolor"); + WRITE_U16(p_ctx, 0, "opcolor"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U16(p_ctx, 0, "balance"); + WRITE_U16(p_ctx, 0, "reserved"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DREF); + if(status != VC_CONTAINER_SUCCESS) return status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, 1, "entry_count"); + + /* Add a URL box */ + WRITE_U32(p_ctx, 12, "box_size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('u','r','l',' '), "box_type"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0x1, "flags"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSD); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STTS); + if(status != VC_CONTAINER_SUCCESS) return status; + + if( 0 && track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CTTS); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSC); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSZ); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(1) + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STCO); + else + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CO64); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSS); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, 1, "entry_count"); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_VIDE, track->priv->module->fourcc); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_SOUN, track->priv->module->fourcc); +#if 0 + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + /*FIXME*/; +#endif + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_write_sample_to_temp( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int32_t dts_diff = packet->dts - module->prev_sample_dts; + uint8_t keyframe = (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? 0x80 : 0; + + vc_container_io_write_be_uint32(module->temp.io, packet->size); + vc_container_io_write_be_uint32(module->temp.io, dts_diff); + vc_container_io_write_be_uint24(module->temp.io, (uint32_t)(packet->pts - packet->dts)); + vc_container_io_write_uint8(module->temp.io, packet->track | keyframe); + module->prev_sample_dts = packet->dts; + return module->temp.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_read_sample_from_temp( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + packet->size = vc_container_io_read_be_uint32(module->temp.io); + packet->dts += (int32_t)vc_container_io_read_be_uint32(module->temp.io); + packet->pts = packet->dts + vc_container_io_read_be_uint24(module->temp.io); + packet->track = vc_container_io_read_uint8(module->temp.io); + packet->flags = (packet->track & 0x80) ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0; + packet->track &= 0x7F; + return module->temp.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + unsigned int entries = 0; + int64_t last_dts = 0, delta; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries * 8); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + sample.dts = 0; + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + delta = sample.dts * MP4_TIMESCALE / 1000000 - last_dts; + if(delta < 0) delta = 0; + WRITE_U32(p_ctx, 1, "sample_count"); + WRITE_U32(p_ctx, delta, "sample_delta"); + entries++; + last_dts += delta; + + skip: + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries, "entry_count"); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + int64_t offset = 0, track_offset = -1; + unsigned int entries = 0, chunks = 0, first_chunk = 0, samples_in_chunk = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries * 12); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + /* Is it a new chunk ? */ + if(track_offset != offset) + { + chunks++; + if(samples_in_chunk) + { + WRITE_U32(p_ctx, first_chunk, "first_chunk"); + WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk"); + WRITE_U32(p_ctx, 1, "sample_description_index"); + entries++; + } + first_chunk = chunks; + samples_in_chunk = 0; + } + track_offset = offset + sample.size; + samples_in_chunk++; + + skip: + offset += sample.size; + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + + if(samples_in_chunk) + { + WRITE_U32(p_ctx, first_chunk, "first_chunk"); + WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk"); + WRITE_U32(p_ctx, 1, "sample_description_index"); + entries++; + } + + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + unsigned int entries = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, 0, "sample_size"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries, "sample_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries * 4); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + WRITE_U32(p_ctx, sample.size, "entry_size"); + entries++; + + skip: + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + int64_t offset = module->data_offset, track_offset = -1; + unsigned int entries = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries * 4); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + /* Is it a new chunk ? */ + if(track_offset != offset) + { + WRITE_U32(p_ctx, offset, "chunk_offset"); + entries++; + } + track_offset = offset + sample.size; + + skip: + offset += sample.size; + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries * 8); + return STREAM_STATUS(p_ctx); + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + unsigned int entries = 0, samples = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries * 4); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + samples++; + if(sample.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) + { + WRITE_U32(p_ctx, samples, "sample_number"); + entries++; + } + + skip: + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vide_avcC( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + WRITE_U32(p_ctx, track->format->extradata_size + 8, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','c','C'), "type"); + WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vide_d263( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 7, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','2','6','3'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U8(p_ctx, 10, "level"); + WRITE_U8(p_ctx, 0, "profile"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i; + + for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 1, "data_reference_index"); + + WRITE_U16(p_ctx, 0, "pre_defined"); + WRITE_U16(p_ctx, 0, "reserved"); + for(i = 0; i < 3; i++) WRITE_U32(p_ctx, 0, "pre_defined"); + WRITE_U16(p_ctx, track->format->type->video.width, "width"); + WRITE_U16(p_ctx, track->format->type->video.height, "height"); + WRITE_U32(p_ctx, 0x480000, "horizresolution"); /* 72 dpi */ + WRITE_U32(p_ctx, 0x480000, "vertresolution"); /* 72 dpi */ + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 1, "frame_count"); + for(i = 0; i < 32; i++) _WRITE_U8(p_ctx, 0); + WRITE_U16(p_ctx, 0x18, "depth"); + WRITE_U16(p_ctx, -1, "pre_defined"); + + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_H264: return mp4_write_box_vide_avcC(p_ctx); + case VC_CONTAINER_CODEC_H263: return mp4_write_box_vide_d263(p_ctx); + case VC_CONTAINER_CODEC_MP4V: return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS); + default: break; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun_damr( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 8, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','m','r'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U8(p_ctx, 0x80, "mode_set"); + WRITE_U8(p_ctx, 0, "mode_change_period"); + WRITE_U8(p_ctx, 1, "frame_per_second"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun_dawp( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 5, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','w','p'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun_devc( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 6, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','e','v','c'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U8(p_ctx, 1, "samples_per_frame"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i, version = 0; + + for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 1, "data_reference_index"); + + if(module->brand == MP4_BRAND_QT) + { + if(track->format->codec == VC_CONTAINER_CODEC_MP4A) version = 1; + WRITE_U16(p_ctx, version, "version"); + WRITE_U16(p_ctx, 0, "revision_level"); + WRITE_U32(p_ctx, 0, "vendor"); + } + else + { + for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved"); + } + + WRITE_U16(p_ctx, track->format->type->audio.channels, "channelcount"); + WRITE_U16(p_ctx, 0, "samplesize"); + WRITE_U16(p_ctx, 0, "pre_defined"); + WRITE_U16(p_ctx, 0, "reserved"); + WRITE_U32(p_ctx, track->format->type->audio.sample_rate << 16, "samplerate"); + + if(module->brand == MP4_BRAND_QT && version == 1) /* FIXME */ + { + WRITE_U32(p_ctx, 1024, "samples_per_packet"); + WRITE_U32(p_ctx, 1536, "bytes_per_packet"); + WRITE_U32(p_ctx, 2, "bytes_per_frame"); + WRITE_U32(p_ctx, 2, "bytes_per_sample"); + } + + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_AMRNB: + case VC_CONTAINER_CODEC_AMRWB: + return mp4_write_box_soun_damr(p_ctx); + case VC_CONTAINER_CODEC_AMRWBP: + return mp4_write_box_soun_dawp(p_ctx); + case VC_CONTAINER_CODEC_EVRC: + return mp4_write_box_soun_devc(p_ctx); + case VC_CONTAINER_CODEC_MP4A: + case VC_CONTAINER_CODEC_MPGA: + return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS); + default: break; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int decoder_specific_size = 0, decoder_config_size, sl_size; + unsigned int stream_type, object_type; + +#define MP4_GET_DESCRIPTOR_SIZE(size) \ + ((size) < 0x0080) ? 2 + (size) : ((size) < 0x4000) ? 3 + (size) : 4 + (size) +#define MP4_WRITE_DESCRIPTOR_HEADER(type, size) \ + LOG_FORMAT(p_ctx, "descriptor %x, size %i", type, size); _WRITE_U8(p_ctx, type); \ + if((size) >= 0x4000) _WRITE_U8(p_ctx, (((size) >> 14) & 0x7F) | 0x80); \ + if((size) >= 0x80 ) _WRITE_U8(p_ctx, (((size) >> 7 ) & 0x7F) | 0x80); \ + _WRITE_U8(p_ctx, (size) & 0x7F) + + /* We only support small size descriptors */ + if(track->format->extradata_size > 0x200000 - 100) + return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: stream_type = 0x4; break; + case VC_CONTAINER_ES_TYPE_AUDIO: stream_type = 0x5; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: stream_type = 0x20; break; + default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + } + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_MP4V: object_type = 0x20; break; + case VC_CONTAINER_CODEC_MP1V: object_type = 0x6B; break; + case VC_CONTAINER_CODEC_MP2V: object_type = 0x60; break; + case VC_CONTAINER_CODEC_JPEG: object_type = 0x6C; break; + case VC_CONTAINER_CODEC_MP4A: object_type = 0x40; break; + case VC_CONTAINER_CODEC_MPGA: + object_type = track->format->type->audio.sample_rate < 32000 ? 0x69 : 0x6B; break; + default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + } + + decoder_specific_size = MP4_GET_DESCRIPTOR_SIZE(track->format->extradata_size); + decoder_config_size = MP4_GET_DESCRIPTOR_SIZE(13 + decoder_specific_size); + sl_size = MP4_GET_DESCRIPTOR_SIZE(1); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + /* Write the ES descriptor */ + MP4_WRITE_DESCRIPTOR_HEADER(0x3, 3 + decoder_config_size + sl_size); + WRITE_U16(p_ctx, module->current_track + 1, "es_id"); + WRITE_U8(p_ctx, 0x1f, "flags"); /* stream_priority = 0x1f */ + + /* Write the Decoder Config descriptor */ + MP4_WRITE_DESCRIPTOR_HEADER(0x4, 13 + decoder_specific_size); + WRITE_U8(p_ctx, object_type, "object_type_indication"); + WRITE_U8(p_ctx, (stream_type << 2) | 1, "stream_type"); + WRITE_U24(p_ctx, 8000, "buffer_size_db"); + WRITE_U32(p_ctx, track->format->bitrate, "max_bitrate"); + WRITE_U32(p_ctx, track->format->bitrate, "avg_bitrate"); + if(track->format->extradata_size) + { + MP4_WRITE_DESCRIPTOR_HEADER(0x5, track->format->extradata_size); + WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size); + } + + /* Write the SL descriptor */ + MP4_WRITE_DESCRIPTOR_HEADER(0x6, 1); + WRITE_U8(p_ctx, 0x2, "flags"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + int64_t mdat_size; + + mdat_size = STREAM_POSITION(p_ctx) - module->mdat_offset; + + /* Write the moov box */ + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV); + + /* Finalise the mdat box */ + SEEK(p_ctx, module->mdat_offset); + WRITE_U32(p_ctx, (uint32_t)mdat_size, "mdat size" ); + + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + + vc_container_writer_extraio_delete(p_ctx, &module->temp); + vc_container_writer_extraio_delete(p_ctx, &module->null); + free(module); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_T *track; + uint32_t type = 0; + + if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Check we support this format */ + switch(format->codec) + { + case VC_CONTAINER_CODEC_AMRNB: type = VC_FOURCC('s','a','m','r'); break; + case VC_CONTAINER_CODEC_AMRWB: type = VC_FOURCC('s','a','w','b'); break; + case VC_CONTAINER_CODEC_AMRWBP: type = VC_FOURCC('s','a','w','p'); break; + case VC_CONTAINER_CODEC_EVRC: type = VC_FOURCC('s','e','v','c'); break; + case VC_CONTAINER_CODEC_MP4A: type = VC_FOURCC('m','p','4','a'); break; + case VC_CONTAINER_CODEC_MPGA: type = VC_FOURCC('m','p','4','a'); break; + + case VC_CONTAINER_CODEC_MP4V: type = VC_FOURCC('m','p','4','v'); break; + case VC_CONTAINER_CODEC_JPEG: type = VC_FOURCC('m','p','4','v'); break; + case VC_CONTAINER_CODEC_H263: type = VC_FOURCC('s','2','6','3'); break; + case VC_CONTAINER_CODEC_H264: + if(format->codec_variant == VC_FOURCC('a','v','c','C')) type = VC_FOURCC('a','v','c','1'); break; + case VC_CONTAINER_CODEC_MJPEG: type = VC_FOURCC('j','p','e','g'); break; + case VC_CONTAINER_CODEC_MJPEGA: type = VC_FOURCC('m','j','p','a'); break; + case VC_CONTAINER_CODEC_MJPEGB: type = VC_FOURCC('m','j','p','b'); break; + case VC_CONTAINER_CODEC_MP1V: type = VC_FOURCC('m','p','e','g'); break; + case VC_CONTAINER_CODEC_MP2V: type = VC_FOURCC('m','p','e','g'); break; + + default: type = 0; break; + } + + if(!type) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status) goto error; + } + + vc_container_format_copy(track->format, format, format->extradata_size); + track->priv->module->fourcc = type; + track->priv->module->offset = -1; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + vc_container_free_track(p_ctx, track); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_add_track_done( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + if(module->tracks_add_done) return status; + + /* We need to find out the size of the object we're going to write it. */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) + { + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV); + module->moov_size = STREAM_POSITION(p_ctx); + p_ctx->size = module->moov_size; + } + vc_container_writer_extraio_disable(p_ctx, &module->null); + + if(status == VC_CONTAINER_SUCCESS) module->tracks_add_done = true; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + { + VC_CONTAINER_ES_FORMAT_T *p_format = + (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + if(module->tracks_add_done) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + return mp4_writer_add_track(p_ctx, p_format); + } + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return mp4_writer_add_track_done(p_ctx); + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_add_sample( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[packet->track]; + VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; + + track_module->last_pts = packet->pts; + if(!track_module->samples) track_module->first_pts = packet->pts; + + track_module->samples++; + track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries++; /* sample size */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size; + track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries++; /* time to sample */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size; + + #if 0 + delta_ts = packet->dts - track_module->timestamp; + track_module->timestamp = packet->dts; + if(!track_module->samples) track_module->delta_ts = + if() +#endif + + /* Is it a new chunk ? */ + if(module->sample_offset != track_module->offset) + { + track_module->chunks++; + track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries++; /* chunk offset */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size; + track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries++; /* sample to chunk */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size; + } + track_module->offset = module->sample_offset + packet->size; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME)) + { + track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries++; /* sync sample */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PACKET_T *sample = &module->sample; + VC_CONTAINER_STATUS_T status; + + if(!module->tracks_add_done) + { + status = mp4_writer_add_track_done(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) + ++module->samples; /* Switching to a new sample */ + + if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) + { + module->sample_offset = STREAM_POSITION(p_ctx); + sample->size = packet->size; + sample->pts = packet->pts; + sample->dts = packet->pts; + sample->track = packet->track; + sample->flags = packet->flags; + } + else + { + sample->size += packet->size; + sample->flags |= packet->flags; + } + + if(WRITE_BYTES(p_ctx, packet->data, packet->size) != packet->size) + return STREAM_STATUS(p_ctx); // TODO do something + p_ctx->size += packet->size; + + // + if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) + { + status = mp4_writer_write_sample_to_temp(p_ctx, sample); + status = mp4_writer_add_sample(p_ctx, sample); + } + + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + MP4_BRAND_T brand; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "3gp") && strcasecmp(extension, "skm") && + strcasecmp(extension, "mov") && strcasecmp(extension, "mp4") && + strcasecmp(extension, "m4v") && strcasecmp(extension, "m4a")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + /* Find out which brand we're going write */ + if(!strcasecmp(extension, "3gp")) brand = MP4_BRAND_3GP5; + else if(!strcasecmp(extension, "skm")) brand = MP4_BRAND_SKM2; + else if(!strcasecmp(extension, "mov")) brand = MP4_BRAND_QT; + else brand = MP4_BRAND_ISOM; + module->brand = brand; + + /* Create a null i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_null(p_ctx, &module->null); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Create a temporary i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp); + if(status != VC_CONTAINER_SUCCESS) goto error; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_FTYP); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Start the mdat box */ + module->mdat_offset = STREAM_POSITION(p_ctx); + WRITE_U32(p_ctx, 0, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('m','d','a','t'), "type"); + module->data_offset = STREAM_POSITION(p_ctx); + + p_ctx->priv->pf_close = mp4_writer_close; + p_ctx->priv->pf_write = mp4_writer_write; + p_ctx->priv->pf_control = mp4_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "mp4: error opening stream"); + if(module) + { + if(module->null.io) vc_container_writer_extraio_delete(p_ctx, &module->null); + free(module); + } + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open mp4_writer_open +#endif diff --git a/containers/mpeg/CMakeLists.txt b/containers/mpeg/CMakeLists.txt new file mode 100755 index 0000000..5afe585 --- /dev/null +++ b/containers/mpeg/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_ps ${LIBRARY_TYPE} ps_reader.c) + +target_link_libraries(reader_ps containers) + +install(TARGETS reader_ps DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mpeg/ps_reader.c b/containers/mpeg/ps_reader.c new file mode 100755 index 0000000..1394300 --- /dev/null +++ b/containers/mpeg/ps_reader.c @@ -0,0 +1,1268 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#include "containers/core/containers_bits.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (2*(a)->priv->module->level) + +/****************************************************************************** +Defines. +******************************************************************************/ +#define PS_TRACKS_MAX 2 +#define PS_EXTRADATA_MAX 256 + +#define PS_SYNC_FAIL_MAX 65536 /** Maximum number of byte-wise sync attempts, + should be enough to stride at least one + PES packet (length encoded using 16 bits). */ + +/** Maximum number of pack/packet start codes scanned when searching for tracks + at open time or when resyncing. */ +#define PS_PACK_SCAN_MAX 128 + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + /** Coding and elementary stream id of the track */ + uint32_t stream_id; + + /** Sub-stream id (for private_stream_1 only) */ + uint32_t substream_id; + + /** PES packet payload offset (for private_stream_1) */ + unsigned int payload_offset; + + uint8_t extradata[PS_EXTRADATA_MAX]; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + /** Logging indentation level */ + uint32_t level; + + /** Track data */ + int tracks_num; + VC_CONTAINER_TRACK_T *tracks[PS_TRACKS_MAX]; + + /** State flag denoting whether or not we are searching + for tracks (at open time) */ + bool searching_tracks; + + /** Size of program stream data (if known) */ + uint64_t data_size; + + /** Offset to the first pack or PES packet start code we've seen */ + uint64_t data_offset; + + /** The first system_clock_reference value we've seen, in (27MHz ticks) */ + int64_t scr_offset; + + /** Most recent system_clock_reference value we've seen, in (27MHz ticks) */ + int64_t scr; + + /** Global offset we add to PES timestamps to make them zero based and + to work around discontinuity in the system_clock_reference */ + int64_t scr_bias; + + /** Most recent program stream mux rate (in units of 50 bytes/second). */ + uint32_t mux_rate; + + /** Offset to the most recent pack start code we've seen */ + uint64_t pack_offset; + + /** Program stream mux rate is often incorrect or fixed to 25200 (10.08 + Mbit/s) which yields inaccurate duration estimate for most files. We + maintain a moving average data rate (in units of bytes/second) based + on the system_clock_reference to give better estimates. */ + int64_t data_rate; + + /** Offset to the most recent PES packet start code prefix we've seen */ + unsigned int packet_data_size; + unsigned int packet_data_left; + int64_t packet_pts; + int64_t packet_dts; + int packet_track; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Find the track associated with a PS stream_id */ +static VC_CONTAINER_TRACK_T *ps_find_track( VC_CONTAINER_T *ctx, uint32_t stream_id, + uint32_t substream_id, bool b_create ) +{ + VC_CONTAINER_TRACK_T *track = 0; + unsigned int i; + + for(i = 0; i < ctx->tracks_num; i++) + if(ctx->tracks[i]->priv->module->stream_id == stream_id && + ctx->tracks[i]->priv->module->substream_id == substream_id) break; + + if(i < ctx->tracks_num) /* We found it */ + track = ctx->tracks[i]; + + if(!track && b_create && i < PS_TRACKS_MAX) + { + /* Allocate and initialise a new track */ + ctx->tracks[i] = track = + vc_container_allocate_track(ctx, sizeof(*ctx->tracks[0]->priv->module)); + if(track) + { + track->priv->module->stream_id = stream_id; + track->priv->module->substream_id = substream_id; + ctx->tracks_num++; + } + } + + if(!track && b_create) + LOG_DEBUG(ctx, "could not create track for stream id: %i", stream_id); + + return track; +} + +/*****************************************************************************/ +STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_start_code( VC_CONTAINER_T *ctx, uint8_t *buffer ) +{ + unsigned int i; + + /* Scan for a pack or PES packet start code prefix */ + for (i = 0; i < PS_SYNC_FAIL_MAX; ++i) + { + if(PEEK_BYTES(ctx, buffer, 4) < 4) + return VC_CONTAINER_ERROR_EOS; + + if(buffer[0] == 0x0 && buffer[1] == 0x0 && buffer[2] == 0x1 && buffer[3] >= 0xB9) + break; + + if (SKIP_BYTES(ctx, 1) != 1) + return VC_CONTAINER_ERROR_EOS; + } + + if(i == PS_SYNC_FAIL_MAX) /* We didn't find a valid pack or PES packet */ + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (buffer[3] == 0xB9) /* MPEG_program_end_code */ + return VC_CONTAINER_ERROR_EOS; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_system_header( VC_CONTAINER_T *ctx ) +{ + uint8_t header[8]; + uint32_t length; + VC_CONTAINER_BITS_T bits; + + if(_READ_U32(ctx) != 0x1BB) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "system_header"); + ctx->priv->module->level++; + + length = READ_U16(ctx, "header_length"); + if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; + + BITS_INIT(ctx, &bits, header, 6); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 22, "rate_bound"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 6, "audio_bound"); + BITS_SKIP(ctx, &bits, 1, "fixed_flag"); + BITS_SKIP(ctx, &bits, 1, "CSPS_flag"); + BITS_SKIP(ctx, &bits, 1, "system_audio_lock_flag"); + BITS_SKIP(ctx, &bits, 1, "system_video_lock_flag"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 5, "video_bound"); + BITS_SKIP(ctx, &bits, 1, "packet_rate_restriction_flag"); + BITS_SKIP(ctx, &bits, 7, "reserved_bits"); + length -= 6; + + while(length >= 3 && (PEEK_U8(ctx) & 0x80)) + { + SKIP_U8(ctx, "stream_id"); + SKIP_BYTES(ctx, 2); + length -= 3; + } + SKIP_BYTES(ctx, length); + + ctx->priv->module->level--; + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pack_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + uint8_t header[10]; + int64_t scr, scr_base, scr_ext = INT64_C(0); + uint64_t pack_offset = STREAM_POSITION(ctx); + uint32_t mux_rate, stuffing; + VC_CONTAINER_BITS_T bits; + VC_CONTAINER_STATUS_T status; + + if(_READ_U32(ctx) != 0x1BA) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "pack_header"); + + module->level++; + + if (PEEK_U8(ctx) & 0x40) /* program stream */ + { + if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 10); + if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); + LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_ext = BITS_READ_U32(ctx, &bits, 9, "system_clock_reference_extension"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 5, "reserved"); + stuffing = BITS_READ_U32(ctx, &bits, 3, "pack_stuffing_length"); + SKIP_BYTES(ctx, stuffing); + } + else /* system stream */ + { + if(READ_BYTES(ctx, header, 8) != 8) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 8); + if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); + LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + } + + if ((status = STREAM_STATUS(ctx)) != VC_CONTAINER_SUCCESS) return status; + + module->level--; + + /* Set or update system_clock_reference, adjust bias if necessary */ + scr = scr_base * INT64_C(300) + scr_ext; + + if (module->scr_offset == VC_CONTAINER_TIME_UNKNOWN) + module->scr_offset = scr; + + if (module->scr == VC_CONTAINER_TIME_UNKNOWN) + module->scr_bias = -scr; + else if (scr < module->scr) + module->scr_bias = module->scr - scr; + + if (module->scr != VC_CONTAINER_TIME_UNKNOWN) + { + /* system_clock_reference is not necessarily continuous across the entire stream */ + if (scr > module->scr) + { + int64_t data_rate; + data_rate = INT64_C(27000000) * (pack_offset - module->pack_offset) / (scr - module->scr); + + if (module->data_rate) + { + /* Simple moving average over data rate seen so far */ + module->data_rate = (module->data_rate * 31 + data_rate) >> 5; + } + else + { + module->data_rate = mux_rate * 50; + } + } + + module->pack_offset = pack_offset; + } + + module->scr = scr; + module->mux_rate = mux_rate; + + /* Check for a system header */ + if(PEEK_U32(ctx) == 0x1BB) + return ps_read_system_header(ctx); + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static void ps_get_stream_coding( VC_CONTAINER_T *ctx, unsigned int stream_id, + VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec, + VC_CONTAINER_FOURCC_T *p_variant) +{ + VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; + VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN; + VC_CONTAINER_FOURCC_T variant = 0; + + VC_CONTAINER_PARAM_UNUSED(ctx); + + if (stream_id == 0xE2) /* FIXME: why is this stream number reserved for H264? */ + { + type = VC_CONTAINER_ES_TYPE_VIDEO; + codec = VC_CONTAINER_CODEC_H264; + } + else if ((stream_id & 0xF0) == 0xE0) + { + type = VC_CONTAINER_ES_TYPE_VIDEO; + codec = VC_CONTAINER_CODEC_MP2V; + } + else if ((stream_id & 0xE0) == 0xC0) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_MPGA; + variant = VC_CONTAINER_VARIANT_MPGA_L2; + } + + /* FIXME: PRIVATE_EVOB_PES_PACKET with stream_id 0xFD ? */ + + *p_type = type; + *p_codec = codec; + *p_variant = variant; +} + +/*****************************************************************************/ +static int64_t ps_pes_time_to_us( VC_CONTAINER_T *ctx, int64_t time ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + + if (time == VC_CONTAINER_TIME_UNKNOWN) + return VC_CONTAINER_TIME_UNKNOWN; + + /* Need to wait for system_clock_reference first */ + if (module->scr_bias == VC_CONTAINER_TIME_UNKNOWN) + return VC_CONTAINER_TIME_UNKNOWN; + + /* Can't have valid bias without known system_clock_reference */ + vc_container_assert(module->scr != VC_CONTAINER_TIME_UNKNOWN); + + /* 90kHz (PES) clock --> (zero based) 27MHz system clock --> microseconds */ + return (INT64_C(300) * time + module->scr_bias) / INT64_C(27); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_time( VC_CONTAINER_T *ctx, + uint32_t *p_length, unsigned int pts_dts, int64_t *p_pts, int64_t *p_dts ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint8_t header[10]; + uint32_t length = *p_length; + VC_CONTAINER_BITS_T bits; + int64_t pts, dts; + + if (p_pts) *p_pts = VC_CONTAINER_TIME_UNKNOWN; + if (p_dts) *p_dts = VC_CONTAINER_TIME_UNKNOWN; + + if (pts_dts == 0x2) + { + /* PTS only */ + LOG_FORMAT(ctx, "PTS"); + ctx->priv->module->level++; + if(length < 5) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 5) != 5) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 5); + + if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; + pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "PTS %"PRId64, pts); + if (p_pts) *p_pts = pts; + length -= 5; + ctx->priv->module->level--; + } + else if (pts_dts == 0x3) + { + /* PTS & DTS */ + LOG_FORMAT(ctx, "PTS DTS"); + ctx->priv->module->level++; + if(length < 10) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 10); + + /* PTS */ + if(BITS_READ_U32(ctx, &bits, 4, "'0011' marker bits") != 0x3) return VC_CONTAINER_ERROR_CORRUPTED; + pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + + /* DTS */ + if(BITS_READ_U32(ctx, &bits, 4, "'0001' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + dts = BITS_READ_U32(ctx, &bits, 3, "DTS [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "PTS %"PRId64, pts); + LOG_FORMAT(ctx, "DTS %"PRId64, dts); + if (p_pts) *p_pts = pts; + if (p_dts) *p_dts = dts; + length -= 10; + ctx->priv->module->level--; + } + else + { + status = VC_CONTAINER_ERROR_NOT_FOUND; + } + + *p_length = *p_length - length; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_extension( VC_CONTAINER_T *ctx, + uint32_t *p_length ) +{ + unsigned int pes_private_data, pack_header, packet_seq_counter, pstd_buffer, extension2; + uint8_t header[2]; + uint32_t length = *p_length; + VC_CONTAINER_BITS_T bits; + unsigned int i; + + LOG_FORMAT(ctx, "PES_extension"); + ctx->priv->module->level++; + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 1); + + pes_private_data = BITS_READ_U32(ctx, &bits, 1, "PES_private_data_flag"); + pack_header = BITS_READ_U32(ctx, &bits, 1, "pack_header_field_flag"); + packet_seq_counter = BITS_READ_U32(ctx, &bits, 1, "program_packet_sequence_counter_flag"); + pstd_buffer = BITS_READ_U32(ctx, &bits, 1, "P-STD_buffer_flag"); + BITS_SKIP(ctx, &bits, 3, "3 reserved_bits"); + extension2 = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag_2"); + length -= 1; + + if (pes_private_data) + { + if(length < 16) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_BYTES(ctx, 16); /* PES_private_data */ + length -= 16; + } + + if (pack_header) + { + unsigned int pack_field_len; + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + pack_field_len = READ_U8(ctx, "pack_field_length"); + length -= 1; + if(length < pack_field_len) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_BYTES(ctx, pack_field_len); /* pack_header */ + length -= pack_field_len; + } + + if (packet_seq_counter) + { + if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 2); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 7, "program_packet_sequence_counter"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 1, "MPEG1_MPEG2_identifier"); + BITS_SKIP(ctx, &bits, 6, "original_stuff_length"); + length -= 2; + } + + if (pstd_buffer) + { + if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 2); + + if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 1, "P-STD_buffer_scale"); + BITS_SKIP(ctx, &bits, 13, "P-STD_buffer_size"); + length -= 2; + } + + if (extension2) + { + uint8_t ext_field_len; + + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, &ext_field_len, 1) != 1) return VC_CONTAINER_ERROR_EOS; + length -= 1; + + if((ext_field_len & 0x80) != 0x80) return VC_CONTAINER_ERROR_CORRUPTED; /* marker_bit */ + ext_field_len &= ~0x80; + LOG_FORMAT(ctx, "PES_extension_field_length %d", ext_field_len); + + for (i = 0; i < ext_field_len; i++) + { + SKIP_U8(ctx, "reserved"); + length--; + } + } + + ctx->priv->module->level--; + + *p_length = *p_length - length; /* Number of bytes read from stream */ + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_packet_header( VC_CONTAINER_T *ctx, + uint32_t *p_length, int64_t *p_pts, int64_t *p_dts ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_BITS_T bits; + uint32_t size, length = *p_length; + unsigned int pts_dts; + uint8_t header[10]; + + if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED; + + if ((PEEK_U8(ctx) & 0xC0) == 0x80) /* program stream */ + { + unsigned int escr, es_rate, dsm_trick_mode, additional_copy_info, pes_crc, pes_extension; + unsigned int header_length; + + if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 3); + + if (BITS_READ_U32(ctx, &bits, 2, "'10' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 2, "PES_scrambling_control"); + BITS_SKIP(ctx, &bits, 1, "PES_priority"); + BITS_SKIP(ctx, &bits, 1, "data_alignment_indicator"); + BITS_SKIP(ctx, &bits, 1, "copyright"); + BITS_SKIP(ctx, &bits, 1, "original_or_copy"); + pts_dts = BITS_READ_U32(ctx, &bits, 2, "PTS_DTS_flags"); + escr = BITS_READ_U32(ctx, &bits, 1, "ESCR_flag"); + es_rate = BITS_READ_U32(ctx, &bits, 1, "ES_rate_flag"); + dsm_trick_mode = BITS_READ_U32(ctx, &bits, 1, "DSM_trick_mode_flag"); + additional_copy_info = BITS_READ_U32(ctx, &bits, 1, "additional_copy_info_flag"); + pes_crc = BITS_READ_U32(ctx, &bits, 1, "PES_CRC_flag"); + pes_extension = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag"); + header_length = BITS_READ_U32(ctx, &bits, 8, "PES_header_data_length"); + length -= 3; + + size = length; + status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts); + if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) return status; + length -= size; + header_length -= size; + + if (escr) + { + /* Elementary stream clock reference */ + int64_t escr; + + ctx->priv->module->level++; + if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 6); + + BITS_SKIP(ctx, &bits, 2, "reserved_bits"); + escr = BITS_READ_U32(ctx, &bits, 3, "ESCR_base [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_READ_U32(ctx, &bits, 9, "ESCR_extension"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + + LOG_FORMAT(ctx, "ESCR_base %"PRId64, escr); + length -= 6; + header_length -= 6; + ctx->priv->module->level--; + } + + if (es_rate) + { + /* Elementary stream rate */ + if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 3); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_READ_U32(ctx, &bits, 22, "ES_rate"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + length -= 3; + header_length -= 3; + } + + if (dsm_trick_mode) + { + unsigned int trick_mode; + + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 1); + + trick_mode = BITS_READ_U32(ctx, &bits, 3, "trick_mode_control"); + if (trick_mode == 0x0 /* fast_forward */) + { + BITS_SKIP(ctx, &bits, 2, "field_id"); + BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh"); + BITS_SKIP(ctx, &bits, 2, "frequency_truncation"); + } + else if (trick_mode == 0x1 /* slow_motion */) + { + BITS_SKIP(ctx, &bits, 5, "rep_cntrl"); + } + else if (trick_mode == 0x2 /* freeze_frame */) + { + BITS_SKIP(ctx, &bits, 2, "field_id"); + BITS_SKIP(ctx, &bits, 3, "reserved_bits"); + } + else if (trick_mode == 0x3 /* fast_reverse */) + { + BITS_SKIP(ctx, &bits, 2, "field_id"); + BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh"); + BITS_SKIP(ctx, &bits, 2, "frequency_truncation"); + } + else if (trick_mode == 0x4 /* slow_reverse */) + BITS_SKIP(ctx, &bits, 5, "rep_cntrl"); + else + BITS_SKIP(ctx, &bits, 5, "5 reserved_bits"); + + length -= 1; + header_length -= 1; + } + + if (additional_copy_info) + { + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 1); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 7, "additional_copy_info"); + + length -= 1; + header_length -= 1; + } + + if (pes_crc) + { + SKIP_U16(ctx, "previous_PES_packet_CRC"); + length -= 2; + header_length -= 2; + } + + if (pes_extension) + { + size = length; + if ((status = ps_read_pes_extension(ctx, &size)) != VC_CONTAINER_SUCCESS) return status; + length -= size; + header_length -= size; + } + + if (header_length <= length) + { + SKIP_BYTES(ctx, header_length); /* header stuffing */ + length -= header_length; + } + } + else /* MPEG 1 PES header */ + { + if(length < 12) return VC_CONTAINER_ERROR_CORRUPTED; + + while (PEEK_U8(ctx) == 0xFF && length > 0) + { + SKIP_U8(ctx, "stuffing"); + length--; + } + + if (length == 0) return VC_CONTAINER_ERROR_CORRUPTED; + + if ((PEEK_U8(ctx) & 0xC0) == 0x40) + { + if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U8(ctx, "???"); + SKIP_U8(ctx, "???"); + length -= 2; + } + + pts_dts = (PEEK_U8(ctx) & 0x30) >> 4; + size = length; + status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts); + if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) + return status; + length -= size; + + if (status == VC_CONTAINER_ERROR_NOT_FOUND) + { + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U8(ctx, "???"); + length -= 1; + } + } + + *p_length = length; + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_private_stream_1_coding( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec, + uint32_t *substream_id, uint32_t *p_length ) +{ + VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; + VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN; + uint32_t length; + uint8_t id = 0; + + length = *p_length; + + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, &id, 1) != 1) return VC_CONTAINER_ERROR_EOS; + length -= 1; + + LOG_FORMAT(ctx, "private_stream_1 byte: 0x%x (%u)", id, id); + + if (id >= 0x20 && id <= 0x3f) + { + type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + codec = VC_CONTAINER_CODEC_UNKNOWN; + } + else if ((id >= 0x80 && id <= 0x87) || (id >= 0xC0 && id <= 0xCF)) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_AC3; + } + else if ((id >= 0x88 && id <= 0x8F) || (id >= 0x98 && id <= 0x9F)) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_DTS; + } + else if (id >= 0xA0 && id <= 0xBF) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_PCM_SIGNED; + } + else + { + LOG_FORMAT(ctx, "Unknown private_stream_1 byte: 0x%x (%u)", id, id); + } + + *substream_id = id; + *p_type = type; + *p_codec = codec; + *p_length = length; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_private_stream_1_format( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_FORMAT_T *format, uint32_t *length ) +{ + uint8_t header[8]; + VC_CONTAINER_BITS_T bits; + + if (format->codec == VC_CONTAINER_CODEC_PCM_SIGNED) + { + static const unsigned fs_tab[4] = { 48000, 96000, 44100, 32000 }; + static const unsigned bps_tab[] = {16, 20, 24, 0}; + + unsigned fs, bps, nchan; + + if(*length < 6) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 6); + + BITS_SKIP(ctx, &bits, 8, "???"); + BITS_SKIP(ctx, &bits, 8, "???"); + BITS_SKIP(ctx, &bits, 8, "???"); + BITS_SKIP(ctx, &bits, 1, "emphasis"); + BITS_SKIP(ctx, &bits, 1, "mute"); + BITS_SKIP(ctx, &bits, 1, "reserved"); + BITS_SKIP(ctx, &bits, 5, "frame number"); + bps = BITS_READ_U32(ctx, &bits, 2, "quant"); + fs = BITS_READ_U32(ctx, &bits, 2, "freq"); + BITS_SKIP(ctx, &bits, 1, "reserved"); + nchan = BITS_READ_U32(ctx, &bits, 3, "channels"); + *length -= 6; + + format->type->audio.sample_rate = fs_tab[fs]; + format->type->audio.bits_per_sample = bps_tab[bps]; + format->type->audio.channels = nchan + 1; + format->type->audio.block_align = + (format->type->audio.channels * format->type->audio.bits_per_sample + 7 ) / 8; + } + else + { + if(*length < 3) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U8(ctx, "num of frames"); + SKIP_U8(ctx, "start pos hi"); + SKIP_U8(ctx, "start pos lo"); + *length -= 3; + } + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_packet( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint8_t header[10]; + VC_CONTAINER_BITS_T bits; + uint32_t length, stream_id, substream_id = 0; + VC_CONTAINER_ES_TYPE_T type; + VC_CONTAINER_FOURCC_T codec, variant = 0; + VC_CONTAINER_TRACK_T *track; + int64_t pts, dts; + + if(_READ_U24(ctx) != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; + LOG_FORMAT(ctx, "pes_packet_header"); + module->level++; + + BITS_INIT(ctx, &bits, header, 3); + stream_id = BITS_READ_U32(ctx, &bits, 8, "stream_id"); + length = BITS_READ_U32(ctx, &bits, 16, "PES_packet_length"); + + if (stream_id < 0xBC) return VC_CONTAINER_ERROR_CORRUPTED; + + if (stream_id == 0xBC /* program_stream_map */ || stream_id == 0xBE /* padding_stream */ || + stream_id == 0xBF /* private_stream_2 */ || stream_id == 0xF0 /* ECM */ || + stream_id == 0xF1 /* EMM */ || stream_id == 0xFF /* program_stream_directory */ || + stream_id == 0xF2 /* DSMCC_stream */ || stream_id == 0xF8 /* ITU-T Rec. H.222.1 type E */) + goto skip; + + /* Parse PES packet header */ + if ((status = ps_read_pes_packet_header(ctx, &length, &pts, &dts)) != VC_CONTAINER_SUCCESS) + return status; + + /* For private_stream_1, encoding format is stored in the payload */ + if (stream_id == 0xBD) + { + status = ps_read_private_stream_1_coding(ctx, &type, &codec, &substream_id, &length); + if (status) return status; + } + else + ps_get_stream_coding(ctx, stream_id, &type, &codec, &variant); + + /* Check that we know what to do with this track */ + if(type == VC_CONTAINER_ES_TYPE_UNKNOWN || codec == VC_CONTAINER_CODEC_UNKNOWN) + goto skip; + + track = ps_find_track(ctx, stream_id, substream_id, module->searching_tracks); + if(!track) goto skip; + + if (module->searching_tracks) + { + track->is_enabled = true; + track->format->es_type = type; + track->format->codec = codec; + track->format->codec_variant = variant; + + /* For private_stream_1, we need to parse payload further to get elementary stream + format */ + if (stream_id == 0xBD) + { + uint32_t current_length = length; + status = ps_read_private_stream_1_format(ctx, track->format, &length); + if (status) return status; + track->priv->module->payload_offset = current_length - length; + } + + goto skip; + } + else + { + unsigned i; + SKIP_BYTES(ctx, track->priv->module->payload_offset); + length -= track->priv->module->payload_offset; + + /* Find track index */ + for(i = 0; i < ctx->tracks_num; i++) + if(ctx->tracks[i] == track) break; + vc_container_assert(i < ctx->tracks_num); + + module->packet_track = i; + module->packet_data_size = length; + module->packet_pts = pts; + module->packet_dts = dts; + } + +end: + module->level--; + return STREAM_STATUS(ctx); + +skip: + SKIP_BYTES(ctx, length); /* remaining PES_packet_data */ + goto end; +} + +/*****************************************************************************/ +STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_pes_packet( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + uint8_t buffer[4]; + unsigned int i; + + module->packet_data_size = 0; + + for (i = 0; i != PS_PACK_SCAN_MAX; ++i) + { + if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) + break; + + if (buffer[3] == 0xBA && ((status = ps_read_pack_header(ctx)) != VC_CONTAINER_SUCCESS)) + continue; /* pack start code but parsing failed, goto resync */ + + if ((status = ps_read_pes_packet(ctx)) == VC_CONTAINER_SUCCESS) + break; + } + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_reader_read( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + + vc_container_assert(!module->searching_tracks); + + while(!module->packet_data_left) + { + if(ps_find_pes_packet(ctx) != VC_CONTAINER_SUCCESS) + { + status = VC_CONTAINER_ERROR_EOS; + goto error; + } + + module->packet_data_left = module->packet_data_size; + } + + p_packet->track = module->packet_track; + p_packet->size = module->packet_data_left; + p_packet->flags = 0; + p_packet->pts = ps_pes_time_to_us(ctx, module->packet_pts); + p_packet->dts = ps_pes_time_to_us(ctx, module->packet_dts); + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + SKIP_BYTES(ctx, module->packet_data_left); + module->packet_data_left = 0; + return VC_CONTAINER_SUCCESS; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + p_packet->size = MIN(p_packet->buffer_size, module->packet_data_left); + p_packet->size = READ_BYTES(ctx, p_packet->data, p_packet->size); + module->packet_data_left -= p_packet->size; + + /* Temporary work-around for lpcm audio */ + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[module->packet_track]; + if (track->format->codec == VC_CONTAINER_CODEC_PCM_SIGNED) + { + unsigned i; + uint16_t *ptr = (uint16_t *)p_packet->data; + + for (i = 0; i < p_packet->size / 2; i ++) + { + uint32_t v = *ptr; + *ptr++ = v >> 8 | ( (v & 0xFF) << 8 ); + } + } + } + + if (module->packet_data_left) + module->packet_pts = module->packet_dts = VC_CONTAINER_TIME_UNKNOWN; + + return STREAM_STATUS(ctx); + +error: + if (status == VC_CONTAINER_ERROR_EOS) + { + /* Reset time reference and calculation state */ + ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN; + ctx->priv->module->scr_bias = -module->scr_offset; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_reader_seek( VC_CONTAINER_T *ctx, + int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t seekpos, position; + int64_t scr; + + VC_CONTAINER_PARAM_UNUSED(flags); + + if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(ctx)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + position = STREAM_POSITION(ctx); + scr = module->scr; + + if (*p_offset == INT64_C(0)) + seekpos = module->data_offset; + else + { + if (!ctx->duration) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* The following is an estimate that might be quite inaccurate */ + seekpos = module->data_offset + (*p_offset * module->data_size) / ctx->duration; + } + + SEEK(ctx, seekpos); + module->scr = module->scr_offset; + status = ps_find_pes_packet(ctx); + if (status && status != VC_CONTAINER_ERROR_EOS) + goto error; + + module->packet_data_left = module->packet_data_size; + + if (module->packet_pts != VC_CONTAINER_TIME_UNKNOWN) + *p_offset = ps_pes_time_to_us(ctx, module->packet_pts); + else if (module->data_size) + *p_offset = (STREAM_POSITION(ctx) - module->data_offset) * ctx->duration / module->data_size; + + return STREAM_STATUS(ctx); + +error: + module->scr = scr; + SEEK(ctx, position); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_reader_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int i; + + for(i = 0; i < ctx->tracks_num; i++) + vc_container_free_track(ctx, ctx->tracks[i]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T *ctx ) +{ + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + uint8_t buffer[4]; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Since MPEG is difficult to auto-detect, we use the extension as + part of the autodetection */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "ps") && strcasecmp(extension, "vob") && + strcasecmp(extension, "mpg") && strcasecmp(extension, "mp2") && + strcasecmp(extension, "mp3") && strcasecmp(extension, "mpeg")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* We didn't find a valid pack or PES packet */ + + LOG_DEBUG(ctx, "using ps reader"); + + /* We are probably dealing with a PS file */ + LOG_FORMAT(ctx, "MPEG PS reader, found start code: 0x%02x%02x%02x%02x", + buffer[0], buffer[1], buffer[2], buffer[3]); + + /* Need to allocate context before searching for streams */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = module->tracks; + + /* Store offset so we can get back to what we consider the first pack or + packet */ + module->data_offset = STREAM_POSITION(ctx); + + /* Search for tracks, reset time reference and calculation state first */ + ctx->priv->module->scr_offset = ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN; + ctx->priv->module->searching_tracks = true; + + for (i = 0; i != PS_PACK_SCAN_MAX; ++i) + { + if (buffer[3] == 0xBA && (ps_read_pack_header(ctx) != VC_CONTAINER_SUCCESS)) + goto resync; + + if (ps_read_pes_packet(ctx) == VC_CONTAINER_SUCCESS) + continue; + +resync: + LOG_DEBUG(ctx, "Lost sync, scanning for start code"); + if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_CORRUPTED; + LOG_DEBUG(ctx, "MPEG PS reader, found start code: 0x%"PRIx64" (%"PRId64"): 0x%02x%02x%02x%02x", + STREAM_POSITION(ctx), STREAM_POSITION(ctx), buffer[0], buffer[1], buffer[2], buffer[3]); + } + + /* Seek back to the start of data */ + SEEK(ctx, module->data_offset); + + /* Bail out if we didn't find any tracks */ + if(!ctx->tracks_num) + { + status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; + goto error; + } + + /* Set data size (necessary for seeking) */ + module->data_size = MAX(ctx->priv->io->size - module->data_offset, INT64_C(0)); + + /* Estimate data rate (necessary for seeking) */ + if(STREAM_SEEKABLE(ctx)) + { + /* Estimate data rate by jumping in the stream */ + #define PS_PACK_SEARCH_MAX 64 + uint64_t position = module->data_offset; + for (i = 0; i != PS_PACK_SEARCH_MAX; ++i) + { + position += (module->data_size / (PS_PACK_SEARCH_MAX + 1)); + SEEK(ctx, position); + + for(;;) + { + if(ps_find_start_code(ctx, buffer) != VC_CONTAINER_SUCCESS) + break; + + if (buffer[3] == 0xBA) + { + if (ps_read_pack_header(ctx) == VC_CONTAINER_SUCCESS) + break; + } + else + { + /* Skip PES packet */ + unsigned length; + SKIP_U32(ctx, "PES packet startcode"); + length = READ_U16(ctx, "PES packet length"); + SKIP_BYTES(ctx, length); + } + } + } + + ctx->duration = (INT64_C(1000000) * module->data_size) / (module->data_rate); + + if (module->scr > module->scr_offset) + { + int64_t delta = (module->scr - module->scr_offset) / INT64_C(27); + + if (delta > ctx->duration) + ctx->duration = delta; + } + + /* Seek back to the start of data */ + SEEK(ctx, module->data_offset); + } + else + { + /* For most files, program_mux_rate is not reliable at all */ + ctx->duration = (INT64_C(100000) * module->data_size) / (INT64_C(5) * module->mux_rate); + } + + /* Reset time reference and calculation state, we're now ready to read data */ + module->scr = VC_CONTAINER_TIME_UNKNOWN; + module->scr_bias = VC_CONTAINER_TIME_UNKNOWN; + + ctx->priv->module->searching_tracks = false; + + if(STREAM_SEEKABLE(ctx)) ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + ctx->priv->pf_close = ps_reader_close; + ctx->priv->pf_read = ps_reader_read; + ctx->priv->pf_seek = ps_reader_seek; + + return STREAM_STATUS(ctx); + + error: + LOG_DEBUG(ctx, "ps: error opening stream (%i)", status); + if(module) ps_reader_close(ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open ps_reader_open +#endif diff --git a/containers/mpga/CMakeLists.txt b/containers/mpga/CMakeLists.txt new file mode 100755 index 0000000..79cf39a --- /dev/null +++ b/containers/mpga/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_mpga ${LIBRARY_TYPE} mpga_reader.c) + +target_link_libraries(reader_mpga containers) + +install(TARGETS reader_mpga DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mpga/mpga_common.h b/containers/mpga/mpga_common.h new file mode 100755 index 0000000..b8ba8e5 --- /dev/null +++ b/containers/mpga/mpga_common.h @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define MPGA_HEADER_SIZE 6 + +#define MPGA_MODE_STEREO 0 +#define MPGA_MODE_JSTEREO 1 +#define MPGA_MODE_DUAL 2 +#define MPGA_MODE_MONO 3 + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_read_header( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset ) +{ + static const uint16_t mpga_bitrate[2][3][15] = + {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, /* MPEG1, Layer 1 */ + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, /* MPEG1, Layer 2 */ + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},/* MPEG1, Layer 3 */ + {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* MPEG2 and MPEG2.5, Layer 1 */ + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* MPEG2 and MPEG2.5, Layer 2 */ + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}} /* MPEG2 and MPEG2.5, Layer 3 */}; + static const uint16_t mpga_sample_rate[] = {44100, 48000, 32000}; + static const uint16_t mpga_frame_size[] = {384, 1152, 576}; + + unsigned int version, layer, br_id, sr_id, emphasis; + unsigned int bitrate, sample_rate, padding, mode; + + /* Check frame sync, 11 bits as we want to allow for MPEG2.5 */ + if (frame_header[0] != 0xff || (frame_header[1] & 0xe0) != 0xe0) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + version = 4 - ((frame_header[1] >> 3) & 3); + layer = 4 - ((frame_header[1] >> 1) & 3 ); + br_id = (frame_header[2] >> 4) & 0xf; + sr_id = (frame_header[2] >> 2) & 3; + padding = (frame_header[2] >> 1) & 1; + mode = (frame_header[3] >> 6) & 3; + emphasis = (frame_header[3]) & 3; + + /* Check for invalid values */ + if (version == 3 || layer == 4 || br_id == 15 || sr_id == 3 || emphasis == 2) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (version == 4) version = 3; + + bitrate = mpga_bitrate[version == 1 ? 0 : 1][layer-1][br_id]; + bitrate *= 1000; + + sample_rate = mpga_sample_rate[sr_id]; + sample_rate >>= (version - 1); + + if (p_version) *p_version = version; + if (p_layer) *p_layer = layer; + if (p_sample_rate) *p_sample_rate = sample_rate; + if (p_channels) *p_channels = mode == MPGA_MODE_MONO ? 1 : 2; + if (p_frame_bitrate) *p_frame_bitrate = bitrate; + if (p_offset) *p_offset = 0; + + if (p_frame_size_samples) + { + *p_frame_size_samples = mpga_frame_size[layer - 1]; + if (version == 1 && layer == 3) *p_frame_size_samples <<= 1; + } + + if (!p_frame_size) + return VC_CONTAINER_SUCCESS; + + if (!bitrate) + *p_frame_size = 0; + else if (layer == 1) + *p_frame_size = (padding + bitrate * 12 / sample_rate) * 4; + else if (layer == 2) + *p_frame_size = padding + bitrate * 144 / sample_rate; + else + *p_frame_size = padding + bitrate * (version == 1 ? 144 : 72) / sample_rate; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T adts_read_header( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset ) +{ + static const unsigned int adts_sample_rate[16] = + {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; + unsigned int profile, sr_id, bitrate, sample_rate, frame_size, channels, crc; + unsigned int frame_size_samples = 1024; + + /* Check frame sync (12 bits) */ + if (frame_header[0] != 0xff || (frame_header[1] & 0xf0) != 0xf0) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* Layer must be 0 */ + if ((frame_header[1] >> 1) & 3) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + crc = !(frame_header[1] & 0x1); + profile = (frame_header[2] >> 6) + 1; /* MPEG-4 Audio Object Type */ + + sr_id = (frame_header[2] >> 2) & 0xf; + sample_rate = adts_sample_rate[sr_id]; + channels = ((frame_header[2] & 0x1) << 2) | ((frame_header[3] >> 6) & 0x3); + frame_size = ((frame_header[3] & 0x03) << 11) | (frame_header[4] << 3) | (frame_header[5] >> 5); + + if (!sample_rate || !channels || !frame_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + bitrate = frame_size * 8 * sample_rate / frame_size_samples; + + if (p_version) *p_version = profile; + if (p_layer) *p_layer = 0; + if (p_sample_rate) *p_sample_rate = sample_rate; + if (p_channels) *p_channels = channels; + if (p_frame_bitrate) *p_frame_bitrate = bitrate; + if (p_frame_size) *p_frame_size = frame_size; + if (p_frame_size_samples) *p_frame_size_samples = frame_size_samples; + if (p_offset) *p_offset = crc ? 9 : 7; + + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/mpga/mpga_packetizer.c b/containers/mpga/mpga_packetizer.c new file mode 100755 index 0000000..1d7b7bf --- /dev/null +++ b/containers/mpga/mpga_packetizer.c @@ -0,0 +1,288 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of an MPEG1/2/2.5 audio Layer I/II/III and AAC ADTS packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_time.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" +#include "mpga_common.h" + +#define MAX_FRAME_SIZE 2881 /* MPEG 2.5 Layer II, 8000 Hz, 160 kbps */ + +VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_SYNC = 0, + STATE_SYNC_LOST, + STATE_SYNC_NEXT, + STATE_SYNC_DONE, + STATE_HEADER, + STATE_DATA, + } state; + + VC_CONTAINER_STATUS_T (*pf_read_header)( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset); + + uint32_t frame_size; + unsigned int frame_bitrate; + unsigned int version; + unsigned int layer; + unsigned int sample_rate; + unsigned int channels; + unsigned int frame_size_samples; + unsigned int offset; + + unsigned int lost_sync; + + unsigned int stream_version; + unsigned int stream_layer; + + uint32_t bytes_read; + +} VC_PACKETIZER_MODULE_T; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->lost_sync = 0; + module->state = STATE_SYNC; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; + uint8_t header[MPGA_HEADER_SIZE]; + VC_CONTAINER_STATUS_T status; + unsigned int version, layer; + int64_t pts, dts; + + while(1) switch (module->state) + { + case STATE_SYNC_LOST: + bytestream_skip_byte( stream ); + if( !module->lost_sync++ ) + LOG_DEBUG(0, "lost sync"); + module->state = STATE_SYNC; + + case STATE_SYNC: + while( bytestream_peek( stream, header, 2 ) == VC_CONTAINER_SUCCESS ) + { + /* 11 bits sync work (0xffe) */ + if( header[0] == 0xff && (header[1] & 0xe0) == 0xe0 ) + { + module->state = STATE_HEADER; + break; + } + bytestream_skip_byte( stream ); + module->lost_sync++; + } + if( module->state != STATE_HEADER ) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ + + case STATE_HEADER: + if( bytestream_peek( stream, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS ) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + status = mpga_read_header( header, + &module->frame_size, &module->frame_bitrate, &module->version, + &module->layer, &module->sample_rate, &module->channels, + &module->frame_size_samples, &module->offset ); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(0, "invalid header"); + module->state = STATE_SYNC_LOST; + break; + } + + /* Version and layer are not allowed to change mid-stream */ + if ((module->stream_version && module->stream_version != module->version) || + (module->stream_layer && module->stream_layer != module->layer)) + { + LOG_ERROR(0, "invalid header"); + module->state = STATE_SYNC_LOST; + break; + } + /* We currently do not support free format streams */ + if (!module->frame_size) + { + LOG_ERROR(0, "free format not supported"); + module->state = STATE_SYNC_LOST; + break; + } + module->state = STATE_SYNC_NEXT; + /* fall through to the next state */ + + case STATE_SYNC_NEXT: + /* To avoid being caught by emulated start codes, we also look at where the next frame is supposed to be */ + if( bytestream_peek_at( stream, module->frame_size, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS ) + { + /* If we know there won't be anymore data then we can just assume + * we've got the frame we're looking for */ + if (flags & VC_PACKETIZER_FLAG_FLUSH) + { + module->state = STATE_SYNC_DONE; + break; + } + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + } + + status = mpga_read_header( header, 0, 0, &version, &layer, 0, 0, 0, 0 ); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(0, "invalid next header"); + module->state = STATE_SYNC_LOST; + break; + } + + /* Version and layer are not allowed to change mid-stream */ + if (module->version != version || module->layer != layer) + { + LOG_ERROR(0, "invalid header"); + module->state = STATE_SYNC_LOST; + break; + } + + module->state = STATE_SYNC_DONE; + /* fall through to the next state */ + + case STATE_SYNC_DONE: + if( module->lost_sync ) + LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync); + module->lost_sync = 0; + + bytestream_skip( stream, module->offset ); + module->stream_version = module->version; + module->stream_layer = module->layer; + + vc_container_time_set_samplerate(time, module->sample_rate, 1); + bytestream_get_timestamps(stream, &pts, &dts, true); + + vc_container_time_set(time, pts); + + module->bytes_read = 0; + module->state = STATE_DATA; + /* fall through to the next state */ + + case STATE_DATA: + if( bytestream_size( stream ) < module->frame_size) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + out->size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if(!module->bytes_read) + { + out->pts = out->dts = vc_container_time_get(time); + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if(flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if(flags & VC_PACKETIZER_FLAG_SKIP) + { + bytestream_skip( stream, out->size ); + } + else + { + out->size = MIN(out->size, out->buffer_size); + bytestream_get( stream, out->data, out->size ); + } + module->bytes_read += out->size; + + if(module->bytes_read == module->frame_size) + { + vc_container_time_add(time, module->frame_size_samples); + module->state = STATE_HEADER; + } + return VC_CONTAINER_SUCCESS; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_MPGA && + p_ctx->in->codec != VC_CONTAINER_CODEC_MP4A) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + + if(p_ctx->in->codec == VC_CONTAINER_CODEC_MPGA) + module->pf_read_header = mpga_read_header; + else + module->pf_read_header = adts_read_header; + + vc_container_format_copy( p_ctx->out, p_ctx->in, 0); + p_ctx->max_frame_size = MAX_FRAME_SIZE; + p_ctx->priv->pf_close = mpga_packetizer_close; + p_ctx->priv->pf_packetize = mpga_packetizer_packetize; + p_ctx->priv->pf_reset = mpga_packetizer_reset; + LOG_DEBUG(0, "using mpeg audio packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(mpga_packetizer_open, "mpga"); diff --git a/containers/mpga/mpga_reader.c b/containers/mpga/mpga_reader.c new file mode 100755 index 0000000..da5df83 --- /dev/null +++ b/containers/mpga/mpga_reader.c @@ -0,0 +1,618 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "mpga_common.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ +#define MPGA_XING_HAS_FRAMES 0x00000001 +#define MPGA_XING_HAS_BYTES 0x00000002 +#define MPGA_XING_HAS_TOC 0x00000004 +#define MPGA_XING_HAS_QUALITY 0x00000008 + +#define MPGA_MAX_BAD_FRAMES 4096 /*< Maximum number of failed byte-wise syncs, + should be at least 2881+4 to cover the largest + frame size (MPEG2.5 Layer 2, 160kbit/s 8kHz) + + next frame header */ + +static const unsigned int mpga_sample_rate_adts[16] = +{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; + +static const GUID_T asf_guid_header = +{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint64_t data_offset; + uint64_t data_size; + uint64_t num_frames; /**< Total number of frames (if known) */ + unsigned int frame_size_samples; /**< Frame size in samples */ + unsigned int bitrate; /**< Bitrate (might change on a per-frame basis if VBR) */ + unsigned int sample_rate; + unsigned int channels; + + /* MPEG audio header information */ + unsigned int version; /**< 1 for MPEG1, 2 for MPEG2, etc. */ + unsigned int layer; + + /* VBR header information */ + uint8_t xing_toc[100]; + int xing_toc_valid; + + /* Per-frame state (updated upon a read or a seek) */ + unsigned int frame_size; + unsigned int frame_data_left; + uint64_t frame_index; + int64_t frame_offset; + int64_t frame_time_pos; /**< pts of current frame */ + unsigned int frame_bitrate; /**< bitrate of current frame */ + + VC_CONTAINER_STATUS_T (*pf_parse_header)( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset); + + uint8_t extradata[2]; /**< codec extra data for aac */ + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static uint32_t PEEK_BYTES_AT( VC_CONTAINER_T *p_ctx, int64_t offset, uint8_t *buffer, int size ) +{ + int ret; + int64_t current_position = STREAM_POSITION(p_ctx); + SEEK(p_ctx, current_position + offset); + ret = PEEK_BYTES(p_ctx, buffer, size); + SEEK(p_ctx, current_position); + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_check_frame_header( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_MODULE_T *module, uint8_t frame_header[MPGA_HEADER_SIZE] ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return module->pf_parse_header(frame_header, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_sync( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint8_t frame_header[MPGA_HEADER_SIZE]; + uint32_t frame_size; + unsigned int frame_bitrate, version, layer, sample_rate, channels; + unsigned int frame_size_samples, offset; + int sync_count = 0; + + /* If we can't see a full frame header, we treat this as EOS although it + could be a bad stream as well, the caller should distinct between + these two cases */ + if (PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE) + return VC_CONTAINER_ERROR_EOS; + + while (sync_count++ < MPGA_MAX_BAD_FRAMES) + { + status = module->pf_parse_header(frame_header, &frame_size, &frame_bitrate, + &version, &layer, &sample_rate, &channels, + &frame_size_samples, &offset); + if (status == VC_CONTAINER_SUCCESS && + frame_size /* We do not support free format streams */) + { + LOG_DEBUG(p_ctx, "MPEGv%d, layer %d, %d bps, %d Hz", + version, layer, frame_bitrate, sample_rate); + if (PEEK_BYTES_AT(p_ctx, (int64_t)frame_size, frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE || + mpga_check_frame_header(p_ctx, module, frame_header) == VC_CONTAINER_SUCCESS) + break; + + /* If we've reached an ID3 tag then the frame is valid as well */ + if((frame_header[0] == 'I' && frame_header[1] == 'D' && frame_header[2] == '3') || + (frame_header[0] == 'T' && frame_header[1] == 'A' && frame_header[2] == 'G')) + break; + } + else if (status == VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "free format not supported"); + } + + if (SKIP_BYTES(p_ctx, 1) != 1 || PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE) + return VC_CONTAINER_ERROR_EOS; + } + + if(sync_count > MPGA_MAX_BAD_FRAMES) /* We didn't find a valid frame */ + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (module->version) + { + /* FIXME: we don't currently care whether or not the number of channels changes mid-stream */ + if (version != module->version || layer != module->layer) + { + LOG_DEBUG(p_ctx, "version or layer not allowed to change mid-stream"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + } + else + { + module->version = version; + module->layer = layer; + module->sample_rate = sample_rate; + module->channels = channels; + module->frame_size_samples = frame_size_samples; + } + + if(offset) SKIP_BYTES(p_ctx, offset); + module->frame_data_left = module->frame_size = frame_size - offset; + module->frame_bitrate = frame_bitrate; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static int64_t mpga_calculate_frame_time( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t time; + time = INT64_C(1000000) * module->frame_index * + module->frame_size_samples / module->sample_rate; + return time; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_read_vbr_headers( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + uint32_t peek_buf[1]; + int64_t offset, start = STREAM_POSITION(p_ctx); + + /* Look for XING header (immediately after layer 3 side information) */ + offset = (module->version == 1) ? ((module->channels == 1) ? INT64_C(21) : INT64_C(36)) : + ((module->channels == 1) ? INT64_C(13) : INT64_C(21)); + + if (PEEK_BYTES_AT(p_ctx, offset, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would be way too small */ + + if (peek_buf[0] == VC_FOURCC('X','i','n','g') || peek_buf[0] == VC_FOURCC('I','n','f','o')) + { + uint32_t flags = 0, num_frames = 0, data_size = 0; + + /* If the first frame has a XING header then we know it's a valid (but empty) audio + frame so we safely parse the header whilst skipping to the next frame */ + SKIP_BYTES(p_ctx, offset); /* FIXME: we don't care about layer 3 side information? */ + + SKIP_FOURCC(p_ctx, "XING"); + flags = READ_U32(p_ctx, "XING flags"); + + if (flags & MPGA_XING_HAS_FRAMES) + num_frames = READ_U32(p_ctx, "XING frames"); + + if (flags & MPGA_XING_HAS_BYTES) + data_size = READ_U32(p_ctx, "XING bytes"); + + if (flags & MPGA_XING_HAS_TOC) + { + READ_BYTES(p_ctx, module->xing_toc, sizeof(module->xing_toc)); + /* TOC is useful only if we know the number of frames */ + if (num_frames) module->xing_toc_valid = 1; + /* Ensure time zero points to first frame even if TOC is broken */ + module->xing_toc[0] = 0; + } + + if (flags & MPGA_XING_HAS_QUALITY) + SKIP_U32(p_ctx, "XING quality"); + + module->data_size = data_size; + module->num_frames = num_frames; + + if (module->num_frames && module->data_size) + { + /* We can calculate average bitrate */ + module->bitrate = + module->data_size * module->sample_rate * 8 / (module->num_frames * module->frame_size_samples); + } + + p_ctx->duration = (module->num_frames * module->frame_size_samples * 1000000LL) / module->sample_rate; + + /* Look for additional LAME header (follows XING) */ + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would still be way too small */ + + if (peek_buf[0] == VC_FOURCC('L','A','M','E')) + { + uint32_t encoder_delay; + + SKIP_FOURCC(p_ctx, "LAME"); + SKIP_STRING(p_ctx, 5, "LAME encoder version"); + SKIP_U8(p_ctx, "LAME tag revision/VBR method"); + SKIP_U8(p_ctx, "LAME LP filter value"); + SKIP_U32(p_ctx, "LAME peak signal amplitude"); + SKIP_U16(p_ctx, "LAME radio replay gain"); + SKIP_U16(p_ctx, "LAME audiophile replay gain"); + SKIP_U8(p_ctx, "LAME encoder flags"); + SKIP_U8(p_ctx, "LAME ABR/minimal bitrate"); + encoder_delay = READ_U24(p_ctx, "LAME encoder delay/padding"); + SKIP_U8(p_ctx, "LAME misc"); + SKIP_U8(p_ctx, "LAME MP3 gain"); + SKIP_U16(p_ctx, "LAME presets and surround info"); + SKIP_U32(p_ctx, "LAME music length"); + SKIP_U16(p_ctx, "LAME music CRC"); + SKIP_U16(p_ctx, "LAME tag CRC"); + track->format->type->audio.gap_delay = (encoder_delay >> 12) + module->frame_size_samples; + track->format->type->audio.gap_padding = encoder_delay & 0xfff; + } + + SEEK(p_ctx, start); + status = VC_CONTAINER_SUCCESS; + } + + /* FIXME: if not success, try to read 'VBRI' header */ + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (module->frame_data_left == 0) + { + status = mpga_sync(p_ctx); + if (status != VC_CONTAINER_SUCCESS) goto error; + } + + if (module->bitrate) + { + /* Simple moving average over bitrate values seen so far */ + module->bitrate = (module->bitrate * 31 + module->frame_bitrate) >> 5; + } + else + { + module->bitrate = module->frame_bitrate; + } + + /* Check if we can skip the frame straight-away */ + if (!track->is_enabled || + ((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO))) + { + /* Just skip the frame */ + SKIP_BYTES(p_ctx, module->frame_size); + module->frame_data_left = 0; + if(!track->is_enabled) + status = VC_CONTAINER_ERROR_CONTINUE; + goto end; + } + + /* Fill in packet information */ + p_packet->flags = p_packet->track = 0; + if (module->frame_data_left == module->frame_size) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME; + else + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + p_packet->size = module->frame_data_left; + + p_packet->pts = module->frame_time_pos; + p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; + + if ((flags & VC_CONTAINER_READ_FLAG_SKIP)) + { + SKIP_BYTES(p_ctx, module->frame_size); + module->frame_data_left = 0; + goto end; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + p_packet->size = MIN(p_packet->buffer_size, module->frame_data_left); + p_packet->size = READ_BYTES(p_ctx, p_packet->data, p_packet->size); + module->frame_data_left -= p_packet->size; + + end: + if (module->frame_data_left == 0) + { + module->frame_index++; + module->frame_offset += module->frame_size; + module->frame_time_pos = mpga_calculate_frame_time(p_ctx); + +#if 0 /* FIXME: is this useful e.g. progressive download? */ + module->num_frames = MAX(module->num_frames, module->frame_index); + module->data_size = MAX(module->data_size, module->frame_offset); + p_ctx->duration = MAX(p_ctx->duration, mpga_calculate_frame_time(p_ctx)); +#endif + } + + return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; + +error: + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t seekpos, position = STREAM_POSITION(p_ctx); + VC_CONTAINER_PARAM_UNUSED(flags); + + if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if (*p_offset != INT64_C(0)) + { + if (!p_ctx->duration) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if (module->xing_toc_valid) + { + int64_t ppm; + int percent, lower, upper, delta; + + ppm = (*p_offset * module->sample_rate) / (module->num_frames * module->frame_size_samples); + ppm = MIN(ppm, INT64_C(999999)); + + percent = ppm / 10000; + delta = ppm % 10000; + + lower = module->xing_toc[percent]; + upper = percent < 99 ? module->xing_toc[percent + 1] : 256; + + seekpos = module->data_offset + + (((module->data_size * lower) + (module->data_size * (upper - lower) * delta) / 10000) >> 8); + } + else + { + /* The following will be accurate for CBR only */ + seekpos = module->data_offset + (*p_offset * module->data_size) / p_ctx->duration; + } + } + else + { + seekpos = module->data_offset; + } + + SEEK(p_ctx, seekpos); + status = mpga_sync(p_ctx); + if (status && status != VC_CONTAINER_ERROR_EOS) + goto error; + + module->frame_index = (*p_offset * module->num_frames + (p_ctx->duration >> 1)) / p_ctx->duration; + module->frame_offset = STREAM_POSITION(p_ctx) - module->data_offset; + + *p_offset = module->frame_time_pos = mpga_calculate_frame_time(p_ctx); + + return STREAM_STATUS(p_ctx); + +error: + SEEK(p_ctx, position); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + if (p_ctx->tracks_num != 0) + vc_container_free_track(p_ctx, p_ctx->tracks[0]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + free(module); + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + unsigned int i; + GUID_T guid; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Since mpeg audio is difficult to auto-detect, we use the extension as + part of the autodetection */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "mp3") && strcasecmp(extension, "mp2") && + strcasecmp(extension, "aac") && strcasecmp(extension, "adts")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Check we're not in fact dealing with an ASF file */ + if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) == sizeof(guid) && + !memcmp(&guid, &asf_guid_header, sizeof(guid))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(p_ctx, "using mpga reader"); + + /* Allocate our context */ + if ((module = malloc(sizeof(*module))) == NULL) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = &module->track; + + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + p_ctx->tracks_num = 1; + + module->pf_parse_header = mpga_read_header; + if(!strcasecmp(extension, "aac") || !strcasecmp(extension, "adts")) + module->pf_parse_header = adts_read_header; + + if ((status = mpga_sync(p_ctx)) != VC_CONTAINER_SUCCESS) + { + /* An error here probably means it's not an mpga file at all */ + if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + goto error; + } + + /* If we got this far, we're probably dealing with an mpeg audio file */ + track = p_ctx->tracks[0]; + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + track->format->codec = VC_CONTAINER_CODEC_MPGA; + if(module->pf_parse_header == adts_read_header) + { + uint8_t *extra = track->format->extradata = module->extradata; + unsigned int sr_id; + for( sr_id = 0; sr_id < 13; sr_id++ ) + if( mpga_sample_rate_adts[sr_id] == module->sample_rate ) break; + extra[0] = (module->version << 3) | ((sr_id & 0xe) >> 1); + extra[1] = ((sr_id & 0x1) << 7) | (module->channels << 3); + track->format->extradata_size = 2; + track->format->codec = VC_CONTAINER_CODEC_MP4A; + } + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + track->is_enabled = true; + track->format->type->audio.channels = module->channels; + track->format->type->audio.sample_rate = module->sample_rate; + track->format->type->audio.bits_per_sample = 0; + track->format->type->audio.block_align = 1; + + module->data_offset = STREAM_POSITION(p_ctx); + + /* Look for VBR headers within the first frame */ + status = mpga_read_vbr_headers(p_ctx); + if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) goto error; + + /* If we couldn't get this information from VBR headers, try to determine + file size, bitrate, number of frames and duration */ + if (!module->data_size) + module->data_size = MAX(p_ctx->priv->io->size - module->data_offset, INT64_C(0)); + + if (!module->bitrate) + { + if (STREAM_SEEKABLE(p_ctx)) + { + /* Scan past a few hundred frames (audio will often have + silence in the beginning so we need to see more than + just a few frames) and estimate bitrate */ + for (i = 0; i < 256; ++i) + if (mpga_reader_read(p_ctx, NULL, VC_CONTAINER_READ_FLAG_SKIP)) break; + /* Seek back to start of data */ + SEEK(p_ctx, module->data_offset); + module->frame_index = 0; + module->frame_offset = INT64_C(0); + module->frame_time_pos = mpga_calculate_frame_time(p_ctx); + } + else + { + /* Bitrate will be correct for CBR only */ + module->bitrate = module->frame_bitrate; + } + } + + track->format->bitrate = module->bitrate; + + if (!module->num_frames) + { + module->num_frames = (module->data_size * module->sample_rate * 8LL) / + (module->bitrate * module->frame_size_samples); + } + + if (!p_ctx->duration && module->bitrate) + { + p_ctx->duration = (INT64_C(8000000) * module->data_size) / module->bitrate; + } + + p_ctx->priv->pf_close = mpga_reader_close; + p_ctx->priv->pf_read = mpga_reader_read; + p_ctx->priv->pf_seek = mpga_reader_seek; + + if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + +error: + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "error opening stream (%i)", status); + if (p_ctx->tracks_num != 0) + vc_container_free_track(p_ctx, p_ctx->tracks[0]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) free(module); + p_ctx->priv->module = NULL; + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open mpga_reader_open +#endif diff --git a/containers/mpgv/mpgv_packetizer.c b/containers/mpgv/mpgv_packetizer.c new file mode 100755 index 0000000..79ccf78 --- /dev/null +++ b/containers/mpgv/mpgv_packetizer.c @@ -0,0 +1,431 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of an MPEG1/2 video packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_time.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" + +/** Arbitrary number which should be sufficiently high so that no sane frame will + * be bigger than that. */ +#define MAX_FRAME_SIZE (1920*1088*2) + +static uint8_t mpgv_startcode[3] = {0x0, 0x0, 0x1}; + +#define PICTURE_CODING_TYPE_I 0x1 +#define PICTURE_CODING_TYPE_P 0x2 +#define PICTURE_CODING_TYPE_B 0x3 + +VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_SYNC = 0, + STATE_SYNC_NEXT, + STATE_FRAME_DONE, + STATE_UNIT_HEADER, + STATE_UNIT_SEQUENCE, + STATE_UNIT_GROUP, + STATE_UNIT_PICTURE, + STATE_UNIT_SLICE, + STATE_UNIT_OTHER, + STATE_DATA, + } state; + + size_t frame_size; + size_t unit_offset; + + unsigned int seen_sequence_header; + unsigned int seen_picture_header; + unsigned int seen_slice; + unsigned int lost_sync; + + unsigned int picture_type; + unsigned int picture_temporal_ref; + int64_t pts; + int64_t dts; + + uint32_t bytes_read; + + unsigned int width, height; + unsigned int frame_rate_num, frame_rate_den; + unsigned int aspect_ratio_num, aspect_ratio_den; + bool low_delay; + +} VC_PACKETIZER_MODULE_T; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->lost_sync = 0; + module->state = STATE_SYNC; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_read_sequence_header(VC_CONTAINER_BYTESTREAM_T *stream, + size_t offset, unsigned int *width, unsigned int *height, + unsigned int *frame_rate_num, unsigned int *frame_rate_den, + unsigned int *aspect_ratio_num, unsigned int *aspect_ratio_den) +{ + static const int frame_rate[16][2] = + { {0, 0}, {24000, 1001}, {24, 1}, {25, 1}, {30000, 1001}, {30, 1}, {50, 1}, + {60000, 1001}, {60, 1}, + /* Unofficial values */ + {15, 1001}, /* From Xing */ + {5, 1001}, {10, 1001}, {12, 1001}, {15, 1001} /* From libmpeg3 */ }; + static const int aspect_ratio[16][2] = + { {0, 0}, {1, 1}, {4, 3}, {16, 9}, {221, 100} }; + + VC_CONTAINER_STATUS_T status; + unsigned int w, h, fr, ar; + int64_t ar_num, ar_den, div; + uint8_t header[8]; + + status = bytestream_peek_at( stream, offset, header, sizeof(header)); + if(status != VC_CONTAINER_SUCCESS) + return status; + + w = (header[4] << 4) | (header[5] >> 4); + h = ((header[5]&0x0f) << 8) | header[6]; + ar = header[7] >> 4; + fr = header[7]&0x0f; + if (!w || !h || !ar || !fr) + return VC_CONTAINER_ERROR_CORRUPTED; + + *width = w; + *height = h; + *frame_rate_num = frame_rate[fr][0]; + *frame_rate_den = frame_rate[fr][1]; + ar_num = (int64_t)aspect_ratio[ar][0] * h; + ar_den = (int64_t)aspect_ratio[ar][1] * w; + div = vc_container_maths_gcd(ar_num, ar_den); + if (div) + { + ar_num /= div; + ar_den /= div; + } + *aspect_ratio_num = ar_num; + *aspect_ratio_den = ar_den; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_read_picture_header(VC_CONTAINER_BYTESTREAM_T *stream, + size_t offset, unsigned int *type, unsigned int *temporal_ref) +{ + VC_CONTAINER_STATUS_T status; + uint8_t h[2]; + + status = bytestream_peek_at(stream, offset + sizeof(mpgv_startcode) + 1, h, sizeof(h)); + if(status != VC_CONTAINER_SUCCESS) + return status; + + *temporal_ref = (h[0] << 2) | (h[1] >> 6); + *type = (h[1] >> 3) & 0x7; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_update_format( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + + LOG_DEBUG(0, "mpgv format: width %i, height %i, rate %i/%i, ar %i/%i", + module->width, module->height, module->frame_rate_num, module->frame_rate_den, + module->aspect_ratio_num, module->aspect_ratio_den); + + p_ctx->out->type->video.width = p_ctx->out->type->video.visible_width = module->width; + p_ctx->out->type->video.height = p_ctx->out->type->video.visible_height = module->height; + p_ctx->out->type->video.par_num = module->aspect_ratio_num; + p_ctx->out->type->video.par_den = module->aspect_ratio_den; + p_ctx->out->type->video.frame_rate_num = module->frame_rate_num; + p_ctx->out->type->video.frame_rate_den = module->frame_rate_den; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; + VC_CONTAINER_STATUS_T status; + uint8_t header[4]; + size_t offset; + + while(1) switch (module->state) + { + case STATE_SYNC: + offset = 0; + status = bytestream_find_startcode( stream, &offset, + mpgv_startcode, sizeof(mpgv_startcode) ); + + if(offset && !module->lost_sync) + LOG_DEBUG(0, "lost sync"); + + bytestream_skip(stream, offset); + module->lost_sync += offset; + + if(status != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ + + if(module->lost_sync) + LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync); + module->lost_sync = 0; + module->state = STATE_UNIT_HEADER; + module->frame_size = 0; + module->unit_offset = 0; + /* fall through to the next state */ + + case STATE_UNIT_HEADER: + status = bytestream_peek_at( stream, module->unit_offset, header, sizeof(header)); + if(status != VC_CONTAINER_SUCCESS) + { + if (!(flags & VC_PACKETIZER_FLAG_FLUSH) || + !module->seen_picture_header || !module->seen_slice) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + module->state = STATE_FRAME_DONE; + break; + } + +#if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) + LOG_DEBUG(0, "found unit (%x)", header[3]); +#endif + + /* Detect start of new frame */ + if(module->seen_picture_header && module->seen_slice && + (header[3] == 0x00 /* A picture header */ || + (header[3] > 0xAF && header[3] != 0xB7) /* Not a slice or sequence end */)) + { + module->state = STATE_FRAME_DONE; + break; + } + + module->frame_size += sizeof(mpgv_startcode); + module->state = STATE_SYNC_NEXT; + /* fall through to the next state */ + + case STATE_SYNC_NEXT: + status = bytestream_find_startcode( stream, &module->frame_size, + mpgv_startcode, sizeof(mpgv_startcode) ); + + /* Sanity check the size of frames. This makes sure we don't endlessly accumulate data + * to make up a new frame. */ + if(module->frame_size > p_ctx->max_frame_size) + { + LOG_ERROR(0, "frame too big (%i/%i), dropping", module->frame_size, p_ctx->max_frame_size); + bytestream_skip(stream, module->frame_size); + module->state = STATE_SYNC; + break; + } + if(status != VC_CONTAINER_SUCCESS) + { + if (!(flags & VC_PACKETIZER_FLAG_FLUSH) || + !module->seen_picture_header || !module->seen_slice) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + module->state = STATE_FRAME_DONE; + break; + } + + bytestream_peek_at( stream, module->unit_offset, header, sizeof(header)); + + /* Drop everything until we've seen a sequence header */ + if(header[3] != 0xB3 && !module->seen_sequence_header) + { + LOG_DEBUG(0, "waiting for sequence header, dropping %i bytes", module->frame_size); + module->state = STATE_UNIT_HEADER; + bytestream_skip(stream, module->frame_size); + module->unit_offset = module->frame_size = 0; + break; + } + + if(header[3] == 0x00) + module->state = STATE_UNIT_PICTURE; + else if(header[3] >= 0x01 && header[3] <= 0xAF) + module->state = STATE_UNIT_SLICE; + else if(header[3] == 0xB3) + module->state = STATE_UNIT_SEQUENCE; + else if(header[3] == 0xB8) + module->state = STATE_UNIT_GROUP; + else + module->state = STATE_UNIT_OTHER; + break; + + case STATE_UNIT_SEQUENCE: + status = mpgv_read_sequence_header(stream, module->unit_offset, &module->width, &module->height, + &module->frame_rate_num, &module->frame_rate_den, &module->aspect_ratio_num, &module->aspect_ratio_den); + if(status != VC_CONTAINER_SUCCESS && !module->seen_sequence_header) + { + /* We need a sequence header so drop everything until we see one */ + LOG_DEBUG(0, "invalid first sequence header, dropping %i bytes", module->frame_size); + bytestream_skip(stream, module->frame_size); + module->state = STATE_SYNC; + break; + } + mpgv_update_format(p_ctx); + module->seen_sequence_header = true; + vc_container_time_set_samplerate(time, module->frame_rate_num, module->frame_rate_den); + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_UNIT_PICTURE: + status = mpgv_read_picture_header(stream, module->unit_offset, &module->picture_type, &module->picture_temporal_ref); + if(status != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + module->seen_picture_header = true; + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_UNIT_SLICE: + module->seen_slice = true; + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_UNIT_GROUP: + case STATE_UNIT_OTHER: + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_FRAME_DONE: + bytestream_get_timestamps(stream, &module->pts, &module->dts, false); + + if(module->picture_type == PICTURE_CODING_TYPE_B || module->low_delay) + { + if(module->pts == VC_CONTAINER_TIME_UNKNOWN) + module->pts = module->dts; + if(module->dts == VC_CONTAINER_TIME_UNKNOWN) + module->dts = module->pts; + } + vc_container_time_set(time, module->pts); + + module->bytes_read = 0; + module->state = STATE_DATA; + module->seen_slice = false; + module->seen_picture_header = false; + +#if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) + LOG_DEBUG(0, "new frame, type %x, size %i, temp_ref %i)", module->picture_type, + module->frame_size, module->picture_temporal_ref); +#endif + /* fall through to the next state */ + + case STATE_DATA: + out->size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if(!module->bytes_read) + { + out->pts = module->pts; + out->dts = module->dts; + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if(flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if(flags & VC_PACKETIZER_FLAG_SKIP) + { + bytestream_skip( stream, out->size ); + } + else + { + out->size = MIN(out->size, out->buffer_size); + bytestream_get( stream, out->data, out->size ); + } + module->bytes_read += out->size; + + if(module->bytes_read == module->frame_size) + { + vc_container_time_add(time, 1); + module->state = STATE_UNIT_HEADER; + module->frame_size = 0; + module->unit_offset = 0; + } + else + out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return VC_CONTAINER_SUCCESS; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_MP1V && + p_ctx->in->codec != VC_CONTAINER_CODEC_MP2V) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + + vc_container_format_copy( p_ctx->out, p_ctx->in, 0); + p_ctx->max_frame_size = MAX_FRAME_SIZE; + p_ctx->priv->pf_close = mpgv_packetizer_close; + p_ctx->priv->pf_packetize = mpgv_packetizer_packetize; + p_ctx->priv->pf_reset = mpgv_packetizer_reset; + LOG_DEBUG(0, "using mpeg video packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(mpgv_packetizer_open, "mpgv"); diff --git a/containers/net/net_sockets.h b/containers/net/net_sockets.h new file mode 100755 index 0000000..e5a97a2 --- /dev/null +++ b/containers/net/net_sockets.h @@ -0,0 +1,263 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_NET_SOCKETS_H +#define VC_NET_SOCKETS_H + +/** \file net_sockets.h + * Abstraction layer for socket-style network communication, to enable porting + * between platforms. + * + * Does not support IPv6 multicast. + */ + +#include "containers/containers_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Status codes that can occur in a socket instance. */ +typedef enum { + VC_CONTAINER_NET_SUCCESS = 0, /**< No error */ + VC_CONTAINER_NET_ERROR_GENERAL, /**< An unrecognised error has occurred */ + VC_CONTAINER_NET_ERROR_INVALID_SOCKET, /**< Invalid socket passed to function */ + VC_CONTAINER_NET_ERROR_NOT_ALLOWED, /**< The operation requested is not allowed */ + VC_CONTAINER_NET_ERROR_INVALID_PARAMETER, /**< An invalid parameter was passed in */ + VC_CONTAINER_NET_ERROR_NO_MEMORY, /**< Failure due to lack of memory */ + VC_CONTAINER_NET_ERROR_ACCESS_DENIED, /**< Permission denied */ + VC_CONTAINER_NET_ERROR_TOO_BIG, /**< Too many handles already open */ + VC_CONTAINER_NET_ERROR_WOULD_BLOCK, /**< Asynchronous operation would block */ + VC_CONTAINER_NET_ERROR_IN_PROGRESS, /**< An operation is already in progress on this socket */ + VC_CONTAINER_NET_ERROR_IN_USE, /**< The address/port is already in use */ + VC_CONTAINER_NET_ERROR_NETWORK, /**< Network is unavailable */ + VC_CONTAINER_NET_ERROR_CONNECTION_LOST, /**< The connection has been lost, closed by network, etc. */ + VC_CONTAINER_NET_ERROR_NOT_CONNECTED, /**< The socket is not connected */ + VC_CONTAINER_NET_ERROR_TIMED_OUT, /**< Operation timed out */ + VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED, /**< Connection was refused by target */ + VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND, /**< Target address could not be resolved */ + VC_CONTAINER_NET_ERROR_TRY_AGAIN, /**< A temporary failure occurred that may clear */ +} vc_container_net_status_t; + +/** Operations that can be applied to sockets */ +typedef enum { + /** Set the buffer size used on the socket + * arg1: uint32_t - New buffer size in bytes */ + VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE = 1, + /** Set the timeout to be used on read operations + * arg1: uint32_t - New timeout in milliseconds, or INFINITE_TIMEOUT_MS */ + VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, +} vc_container_net_control_t; + +/** Container Input / Output Context. + * This is an opaque structure that defines the context for a socket instance. + * The details of the structure are contained within the platform implementation. */ +typedef struct vc_container_net_tag VC_CONTAINER_NET_T; + +/** \name Socket open flags + * The following flags can be used when opening a network socket. */ +/* @{ */ +typedef uint32_t vc_container_net_open_flags_t; +/** Connected stream socket, rather than connectionless datagram socket */ +#define VC_CONTAINER_NET_OPEN_FLAG_STREAM 1 +/** Force use of IPv4 addressing */ +#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 2 +/** Force use of IPv6 addressing */ +#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 6 +/** Use IPv4 broadcast address for datagram delivery */ +#define VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST 8 +/* @} */ + +/** Mask of bits used in forcing address type */ +#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK 6 + +/** Blocks until data is available, or an error occurs. + * Used with the VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS control operation. */ +#define INFINITE_TIMEOUT_MS 0xFFFFFFFFUL + + +/** Opens a network socket instance. + * The network address can be a host name, dotted IP4, hex IP6 address or NULL. Passing NULL + * signifies the socket is either to be used as a datagram receiver or a stream server, + * depending on the flags. + * \ref VC_CONTAINER_NET_OPEN_FLAG_STREAM will open the socket for connected streaming. The default + * is to use connectionless datagrams. + * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 will force the use of IPv4 addressing or fail to open + * the socket. The default is to pick the first available. + * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 will force the use of IPv6 addressing or fail to open + * the socket. The default is to pick the first available. + * \ref VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST will use IPv4 broadcast addressing for a datagram + * sender. Use with an IPv6 address, stream socket or datagram receiver will raise an error. + * If the p_status parameter is not NULL, the status code will be written to it to indicate the + * reason for failure, or VC_CONTAINER_NET_SUCCESS on success. + * Sockets shall be bound and connected as necessary. Stream server sockets shall further need + * to have vc_container_net_listen and vc_container_net_accept called on them before data can be transferred. + * + * \param address Network address or NULL. + * \param port Network port or well-known name. This is the local port for receivers/servers. + * \param flags Flags controlling socket type. + * \param p_status Optional pointer to variable to receive status of operation. + * \return The socket instance or NULL on error. */ +VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, + vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ); + +/** Closes a network socket instance. + * The p_ctx pointer must not be used after it has been closed. + * + * \param p_ctx The socket instance to close. + * \return The status code for closing the socket. */ +vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ); + +/** Query the latest status of the socket. + * + * \param p_ctx The socket instance. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ); + +/** Read data from the socket. + * The function will read up to the requested number of bytes into the buffer. + * If there is no data immediately available to read, the function will block + * until data arrives, an error occurs or the timeout is reached (if set). + * When the function returns zero, the socket may have been closed, an error + * may have occurred, a zero length datagram received, or the timeout reached. + * Check vc_container_net_status() to differentiate. + * Attempting to read on a datagram sender socket will trigger an error. + * + * \param p_ctx The socket instance. + * \param buffer The buffer into which bytes will be read. + * \param size The maximum number of bytes to read. + * \return The number of bytes actually read. */ +size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ); + +/** Write data to the socket. + * If the socket cannot send the requested number of bytes in one go, the function + * will return a value smaller than size. + * Attempting to write on a datagram receiver socket will trigger an error. + * + * \param p_ctx The socket instance. + * \param buffer The buffer from which bytes will be written. + * \param size The maximum number of bytes to write. + * \return The number of bytes actually written. */ +size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ); + +/** Start a stream server socket listening for connections from clients. + * Attempting to use this on anything other than a stream server socket shall + * trigger an error. + * + * \param p_ctx The socket instance. + * \param maximum_connections The maximum number of queued connections to allow. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ); + +/** Accept a client connection on a listening stream server socket. + * Attempting to use this on anything other than a listening stream server socket + * shall trigger an error. + * When a client connection is made, the new instance representing it is returned + * via pp_client_ctx. + * + * \param p_server ctx The server socket instance. + * \param pp_client_ctx The address where the pointer to the new client's socket + * instance is written. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ); + +/** Non-blocking check for data being available to read. + * If an error occurs, the function will return false and the error can be + * obtained using socket_status(). + * + * \param p_ctx The socket instance. + * \return True if there is data available to read immediately. */ +bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ); + +/** Returns the maximum size of a datagram in bytes, for sending or receiving. + * The limit for reading from or writing to stream sockets will generally be + * greater than this value, although the call can also be made on such sockets. + * + * \param p_ctx The socket instance. + * \return The maximum size of a datagram in bytes. */ +size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ); + +/** Get the DNS name or IP address of a stream server client, if connected. + * The length of the name will be limited by name_len, taking into account a + * terminating NUL character. + * Calling this function on a non-stream server instance, or one that is not + * connected to a client, will result in an error status. + * + * \param p_ctx The socket instance. + * \param name Pointer where the name should be written. + * \param name_len Maximum number of characters to write to name. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ); + +/** Get the port of a stream server client, if connected. + * The port is written to the address in host order. + * Calling this function on a non-stream server instance, or one that is not + * connected to a client, will result in an error status. + * + * \param p_ctx The socket instance. + * \param port Pointer where the port should be written. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ); + +/** Perform a control operation on the socket. + * See vc_container_net_control_t for more details. + * + * \param p_ctx The socket instance. + * \param operation The control operation to perform. + * \param args Variable list of additional arguments to the operation. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, vc_container_net_control_t operation, va_list args); + +/** Convert a 32-bit unsigned value from network order (big endian) to host order. + * + * \param value The value to be converted. + * \return The converted value. */ +uint32_t vc_container_net_to_host( uint32_t value ); + +/** Convert a 32-bit unsigned value from host order to network order (big endian). + * + * \param value The value to be converted. + * \return The converted value. */ +uint32_t vc_container_net_from_host( uint32_t value ); + +/** Convert a 16-bit unsigned value from network order (big endian) to host order. + * + * \param value The value to be converted. + * \return The converted value. */ +uint16_t vc_container_net_to_host_16( uint16_t value ); + +/** Convert a 16-bit unsigned value from host order to network order (big endian). + * + * \param value The value to be converted. + * \return The converted value. */ +uint16_t vc_container_net_from_host_16( uint16_t value ); + +#ifdef __cplusplus +} +#endif + +#endif /* VC_NET_SOCKETS_H */ diff --git a/containers/net/net_sockets_bsd.c b/containers/net/net_sockets_bsd.c new file mode 100755 index 0000000..4503a9a --- /dev/null +++ b/containers/net/net_sockets_bsd.c @@ -0,0 +1,117 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "net_sockets.h" +#include "net_sockets_priv.h" +#include "containers/core/containers_common.h" + +/*****************************************************************************/ + +/** Default maximum datagram size. + * This is based on the default Ethernet MTU size, less the IP and UDP headers. + */ +#define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8) + +/** Maximum socket buffer size to use. */ +#define MAXIMUM_BUFFER_SIZE 65536 + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_last_error() +{ + switch (errno) + { + case EACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED; + case EFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case EINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case EMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG; + case EWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK; + case EINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case EALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case EADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE; + case EADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case ENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case ENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case ENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case ENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + case ESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT; + case ECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED; + case ELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case ENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case EHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case EHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case EUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case EDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case ESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + /* All other errors are unexpected, so just map to a general purpose error code. */ + default: + return VC_CONTAINER_NET_ERROR_GENERAL; + } +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_init() +{ + /* No additional initialization required */ + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +void vc_container_net_private_deinit() +{ + /* No additional deinitialization required */ +} + +/*****************************************************************************/ +void vc_container_net_private_close( SOCKET_T sock ) +{ + close(sock); +} + +/*****************************************************************************/ +void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ) +{ + int opt = enable ? 1 : 0; + + (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); +} + +/*****************************************************************************/ +size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ) +{ + (void)sock; + + /* No easy way to determine this, just use the default. */ + return DEFAULT_MAXIMUM_DATAGRAM_SIZE; +} diff --git a/containers/net/net_sockets_bsd.h b/containers/net/net_sockets_bsd.h new file mode 100755 index 0000000..fd554c9 --- /dev/null +++ b/containers/net/net_sockets_bsd.h @@ -0,0 +1,43 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NET_SOCKETS_BSD_H_ +#define _NET_SOCKETS_BSD_H_ + +#include +#include +#include +#include +#include + +typedef int SOCKET_T; +typedef socklen_t SOCKADDR_LEN_T; +typedef void *SOCKOPT_CAST_T; +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#endif /* _NET_SOCKETS_BSD_H_ */ diff --git a/containers/net/net_sockets_common.c b/containers/net/net_sockets_common.c new file mode 100755 index 0000000..b3815f3 --- /dev/null +++ b/containers/net/net_sockets_common.c @@ -0,0 +1,619 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "net_sockets.h" +#include "net_sockets_priv.h" + +/*****************************************************************************/ + +struct vc_container_net_tag +{ + /** The underlying socket */ + SOCKET_T socket; + /** Last error raised on the socket instance. */ + vc_container_net_status_t status; + /** Simple socket type */ + vc_container_net_type_t type; + /** Socket address, used for sending datagrams. */ + union { + struct sockaddr_storage storage; + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; + } to_addr; + /** Number of bytes in to_addr that have been filled. */ + SOCKADDR_LEN_T to_addr_len; + /** Maximum size of datagrams. */ + size_t max_datagram_size; + /** Timeout to use when reading from a socket. INFINITE_TIMEOUT_MS waits forever. */ + uint32_t read_timeout_ms; +}; + +/*****************************************************************************/ +static void socket_clear_address(struct sockaddr *p_addr) +{ + switch (p_addr->sa_family) + { + case AF_INET: + { + struct sockaddr_in *p_addr_v4 = (struct sockaddr_in *)p_addr; + + memset(&p_addr_v4->sin_addr, 0, sizeof(p_addr_v4->sin_addr)); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *p_addr_v6 = (struct sockaddr_in6 *)p_addr; + + memset(&p_addr_v6->sin6_addr, 0, sizeof(p_addr_v6->sin6_addr)); + } + break; + default: + /* Invalid or unsupported address family */ + vc_container_assert(0); + } +} + +/*****************************************************************************/ +static vc_container_net_status_t socket_set_read_buffer_size(VC_CONTAINER_NET_T *p_ctx, + uint32_t buffer_size) +{ + int result; + const SOCKOPT_CAST_T optptr = (const SOCKOPT_CAST_T)&buffer_size; + + result = setsockopt(p_ctx->socket, SOL_SOCKET, SO_RCVBUF, optptr, sizeof(buffer_size)); + + if (result == SOCKET_ERROR) + return vc_container_net_private_last_error(); + + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +static vc_container_net_status_t socket_set_read_timeout_ms(VC_CONTAINER_NET_T *p_ctx, + uint32_t timeout_ms) +{ + p_ctx->read_timeout_ms = timeout_ms; + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +static bool socket_wait_for_data( VC_CONTAINER_NET_T *p_ctx, uint32_t timeout_ms ) +{ + int result; + fd_set set; + struct timeval tv; + + if (timeout_ms == INFINITE_TIMEOUT_MS) + return true; + + FD_ZERO(&set); + FD_SET(p_ctx->socket, &set); + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms - tv.tv_sec * 1000) * 1000; + result = select(p_ctx->socket + 1, &set, NULL, NULL, &tv); + + if (result == SOCKET_ERROR) + p_ctx->status = vc_container_net_private_last_error(); + else + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + return (result == 1); +} + +/*****************************************************************************/ +VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, + vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ) +{ + VC_CONTAINER_NET_T *p_ctx; + struct addrinfo hints, *info, *p; + int result; + vc_container_net_status_t status; + SOCKET_T sock = INVALID_SOCKET; + + status = vc_container_net_private_init(); + if (status != VC_CONTAINER_NET_SUCCESS) + { + LOG_ERROR(NULL, "vc_container_net_open: platform initialization failure: %d", status); + if (p_status) + *p_status = status; + return NULL; + } + + p_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T)); + if (!p_ctx) + { + if (p_status) + *p_status = VC_CONTAINER_NET_ERROR_NO_MEMORY; + + LOG_ERROR(NULL, "vc_container_net_open: malloc fail for VC_CONTAINER_NET_T"); + vc_container_net_private_deinit(); + return NULL; + } + + /* Initialize the net socket instance structure */ + memset(p_ctx, 0, sizeof(*p_ctx)); + p_ctx->socket = INVALID_SOCKET; + if (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) + p_ctx->type = address ? STREAM_CLIENT : STREAM_SERVER; + else + p_ctx->type = address ? DATAGRAM_SENDER : DATAGRAM_RECEIVER; + + /* Create the address info linked list from the data provided */ + memset(&hints, 0, sizeof(hints)); + switch (flags & VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK) + { + case 0: + hints.ai_family = AF_UNSPEC; + break; + case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4: + hints.ai_family = AF_INET; + break; + case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6: + hints.ai_family = AF_INET6; + break; + default: + status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + LOG_ERROR(NULL, "vc_container_net_open: invalid address forcing flag"); + goto error; + } + hints.ai_socktype = (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) ? SOCK_STREAM : SOCK_DGRAM; + + result = getaddrinfo(address, port, &hints, &info); + if (result) + { + status = vc_container_net_private_last_error(); + LOG_ERROR(NULL, "vc_container_net_open: unable to get address info: %d", status); + goto error; + } + + /* Not all address infos may be useable. Search for one that is by skipping any + * that provoke errors. */ + for(p = info; (p != NULL) && (sock == INVALID_SOCKET) ; p = p->ai_next) + { + sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sock == INVALID_SOCKET) + { + status = vc_container_net_private_last_error(); + continue; + } + + switch (p_ctx->type) + { + case STREAM_CLIENT: + /* Simply connect to the given address/port */ + if (connect(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) + status = vc_container_net_private_last_error(); + break; + + case DATAGRAM_SENDER: + /* Nothing further to do */ + break; + + case STREAM_SERVER: + /* Try to avoid socket reuse timing issues on TCP server sockets */ + vc_container_net_private_set_reusable(sock, true); + + /* Allow any source address */ + socket_clear_address(p->ai_addr); + + if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) + status = vc_container_net_private_last_error(); + break; + + case DATAGRAM_RECEIVER: + /* Allow any source address */ + socket_clear_address(p->ai_addr); + + if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) + status = vc_container_net_private_last_error(); + break; + } + + if (status == VC_CONTAINER_NET_SUCCESS) + { + /* Save addressing information for later use */ + p_ctx->to_addr_len = p->ai_addrlen; + memcpy(&p_ctx->to_addr, p->ai_addr, p->ai_addrlen); + } else { + vc_container_net_private_close(sock); /* Try next entry in list */ + sock = INVALID_SOCKET; + } + } + + freeaddrinfo(info); + + if (sock == INVALID_SOCKET) + { + LOG_ERROR(NULL, "vc_container_net_open: failed to open socket: %d", status); + goto error; + } + + p_ctx->socket = sock; + p_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(sock); + p_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS; + + if (p_status) + *p_status = VC_CONTAINER_NET_SUCCESS; + + return p_ctx; + +error: + if (p_status) + *p_status = status; + (void)vc_container_net_close(p_ctx); + return NULL; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (p_ctx->socket != INVALID_SOCKET) + { + vc_container_net_private_close(p_ctx->socket); + p_ctx->socket = INVALID_SOCKET; + } + free(p_ctx); + + vc_container_net_private_deinit(); + + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + return p_ctx->status; +} + +/*****************************************************************************/ +size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ) +{ + int result = 0; + + if (!p_ctx) + return 0; + + if (!buffer) + { + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + return 0; + } + + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + switch (p_ctx->type) + { + case STREAM_CLIENT: + case STREAM_SERVER: + /* Receive data from the stream */ + if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms)) + { + result = recv(p_ctx->socket, buffer, (int)size, 0); + if (!result) + p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + } else + p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT; + break; + + case DATAGRAM_RECEIVER: + { + /* Receive the packet */ + /* FIXME Potential for data loss, as rest of packet will be lost if buffer was not large enough */ + if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms)) + { + result = recvfrom(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, &p_ctx->to_addr_len); + if (!result) + p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + } else + p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT; + } + break; + + default: /* DATAGRAM_SENDER */ + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + result = 0; + break; + } + + if (result == SOCKET_ERROR) + { + p_ctx->status = vc_container_net_private_last_error(); + result = 0; + } + + return (size_t)result; +} + +/*****************************************************************************/ +size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ) +{ + int result; + + if (!p_ctx) + return 0; + + if (!buffer) + { + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + return 0; + } + + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + switch (p_ctx->type) + { + case STREAM_CLIENT: + case STREAM_SERVER: + /* Send data to the stream */ + result = send(p_ctx->socket, buffer, (int)size, 0); + break; + + case DATAGRAM_SENDER: + /* Send the datagram */ + + if (size > p_ctx->max_datagram_size) + size = p_ctx->max_datagram_size; + + result = sendto(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, p_ctx->to_addr_len); + break; + + default: /* DATAGRAM_RECEIVER */ + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + result = 0; + break; + } + + if (result == SOCKET_ERROR) + { + p_ctx->status = vc_container_net_private_last_error(); + result = 0; + } + + return (size_t)result; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + if (p_ctx->type == STREAM_SERVER) + { + if (listen(p_ctx->socket, maximum_connections) == SOCKET_ERROR) + p_ctx->status = vc_container_net_private_last_error(); + } else { + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + return p_ctx->status; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ) +{ + VC_CONTAINER_NET_T *p_client_ctx = NULL; + + if (!p_server_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (!pp_client_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + + *pp_client_ctx = NULL; + + if (p_server_ctx->type != STREAM_SERVER) + { + p_server_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + goto error; + } + + p_client_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T)); + if (!p_client_ctx) + { + p_server_ctx->status = VC_CONTAINER_NET_ERROR_NO_MEMORY; + goto error; + } + + /* Initialise the new context with the address information from the server context */ + memset(p_client_ctx, 0, sizeof(*p_client_ctx)); + memcpy(&p_client_ctx->to_addr, &p_server_ctx->to_addr, p_server_ctx->to_addr_len); + p_client_ctx->to_addr_len = p_server_ctx->to_addr_len; + + p_client_ctx->socket = accept(p_server_ctx->socket, &p_client_ctx->to_addr.sa, &p_client_ctx->to_addr_len); + + if (p_client_ctx->socket == INVALID_SOCKET) + { + p_server_ctx->status = vc_container_net_private_last_error(); + goto error; + } + + /* Need to bump up the initialisation count, as a new context has been created */ + p_server_ctx->status = vc_container_net_private_init(); + if (p_server_ctx->status != VC_CONTAINER_NET_SUCCESS) + goto error; + + p_client_ctx->type = STREAM_CLIENT; + p_client_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(p_client_ctx->socket); + p_client_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS; + p_client_ctx->status = VC_CONTAINER_NET_SUCCESS; + + *pp_client_ctx = p_client_ctx; + return VC_CONTAINER_NET_SUCCESS; + +error: + if (p_client_ctx) + free(p_client_ctx); + return p_server_ctx->status; +} + +/*****************************************************************************/ +bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ) +{ + if (!p_ctx) + return false; + + if (p_ctx->type == DATAGRAM_SENDER) + { + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + return false; + } + + return socket_wait_for_data(p_ctx, 0); +} + +/*****************************************************************************/ +size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ) +{ + return p_ctx ? p_ctx->max_datagram_size : 0; +} + +/*****************************************************************************/ +static vc_container_net_status_t translate_getnameinfo_error( int error ) +{ + switch (error) + { + case EAI_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN; + case EAI_FAIL: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + case EAI_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case EAI_NONAME: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + + /* All other errors are unexpected, so just map to a general purpose error code. */ + default: + return VC_CONTAINER_NET_ERROR_GENERAL; + } +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ) +{ + int result; + + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (p_ctx->socket == INVALID_SOCKET) + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + else if (!name || !name_len) + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + else if ((result = getnameinfo(&p_ctx->to_addr.sa, p_ctx->to_addr_len, name, name_len, NULL, 0, 0)) != 0) + p_ctx->status = translate_getnameinfo_error(result); + else + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + return p_ctx->status; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (p_ctx->socket == INVALID_SOCKET) + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + else if (!port) + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + else + { + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + switch (p_ctx->to_addr.sa.sa_family) + { + case AF_INET: + *port = ntohs(p_ctx->to_addr.in.sin_port); + break; + case AF_INET6: + *port = ntohs(p_ctx->to_addr.in6.sin6_port); + break; + default: + /* Highly unexepcted address family! */ + p_ctx->status = VC_CONTAINER_NET_ERROR_GENERAL; + } + } + + return p_ctx->status; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, + vc_container_net_control_t operation, + va_list args) +{ + vc_container_net_status_t status; + + switch (operation) + { + case VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE: + status = socket_set_read_buffer_size(p_ctx, va_arg(args, uint32_t)); + break; + case VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS: + status = socket_set_read_timeout_ms(p_ctx, va_arg(args, uint32_t)); + break; + default: + status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + return status; +} + +/*****************************************************************************/ +uint32_t vc_container_net_to_host( uint32_t value ) +{ + return ntohl(value); +} + +/*****************************************************************************/ +uint32_t vc_container_net_from_host( uint32_t value ) +{ + return htonl(value); +} + +/*****************************************************************************/ +uint16_t vc_container_net_to_host_16( uint16_t value ) +{ + return ntohs(value); +} + +/*****************************************************************************/ +uint16_t vc_container_net_from_host_16( uint16_t value ) +{ + return htons(value); +} diff --git a/containers/net/net_sockets_null.c b/containers/net/net_sockets_null.c new file mode 100755 index 0000000..a8fe8fb --- /dev/null +++ b/containers/net/net_sockets_null.c @@ -0,0 +1,175 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "net_sockets.h" +#include "containers/core/containers_common.h" + +/*****************************************************************************/ + +struct vc_container_net_tag +{ + uint32_t dummy; /* C requires structs not to be empty. */ +}; + +/*****************************************************************************/ +VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, + vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ) +{ + VC_CONTAINER_PARAM_UNUSED(address); + VC_CONTAINER_PARAM_UNUSED(port); + VC_CONTAINER_PARAM_UNUSED(flags); + + if (p_status) + *p_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + + return NULL; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + + return 0; +} + +/*****************************************************************************/ +size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + + return 0; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(maximum_connections); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_server_ctx); + VC_CONTAINER_PARAM_UNUSED(pp_client_ctx); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return false; +} + +/*****************************************************************************/ +size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return 0; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(name); + VC_CONTAINER_PARAM_UNUSED(name_len); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(port); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, + vc_container_net_control_t operation, + va_list args) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(operation); + VC_CONTAINER_PARAM_UNUSED(args); + + return VC_CONTAINER_NET_ERROR_NOT_ALLOWED; +} + +/*****************************************************************************/ +uint32_t vc_container_net_to_host( uint32_t value ) +{ + return value; +} + +/*****************************************************************************/ +uint32_t vc_container_net_from_host( uint32_t value ) +{ + return value; +} + +/*****************************************************************************/ +uint16_t vc_container_net_to_host_16( uint16_t value ) +{ + return value; +} + +/*****************************************************************************/ +uint16_t vc_container_net_from_host_16( uint16_t value ) +{ + return value; +} diff --git a/containers/net/net_sockets_priv.h b/containers/net/net_sockets_priv.h new file mode 100755 index 0000000..4186083 --- /dev/null +++ b/containers/net/net_sockets_priv.h @@ -0,0 +1,85 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NET_SOCKETS_PRIV_H_ +#define _NET_SOCKETS_PRIV_H_ + +#include "net_sockets.h" + +#ifdef WIN32 +#include "net_sockets_win32.h" +#else +#include "net_sockets_bsd.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + STREAM_CLIENT = 0, /**< TCP client */ + STREAM_SERVER, /**< TCP server */ + DATAGRAM_SENDER, /**< UDP sender */ + DATAGRAM_RECEIVER /**< UDP receiver */ +} vc_container_net_type_t; + + +/** Perform implementation-specific per-socket initialization. + * + * \return VC_CONTAINER_NET_SUCCESS or one of the error codes on failure. */ +vc_container_net_status_t vc_container_net_private_init( void ); + +/** Perform implementation-specific per-socket deinitialization. + * This function is always called once for each successful call to socket_private_init(). */ +void vc_container_net_private_deinit( void ); + +/** Return the last error from the socket implementation. */ +vc_container_net_status_t vc_container_net_private_last_error( void ); + +/** Implementation-specific internal socket close. + * + * \param sock Internal socket to be closed. */ +void vc_container_net_private_close( SOCKET_T sock ); + +/** Enable or disable socket address reusability. + * + * \param sock Internal socket to be closed. + * \param enable True to enable reusability, false to clear it. */ +void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ); + +/** Query the maximum datagram size for the socket. + * + * \param sock The socket to query. + * \return The maximum supported datagram size on the socket. */ +size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ); + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_SOCKETS_PRIV_H_ */ diff --git a/containers/net/net_sockets_win32.c b/containers/net/net_sockets_win32.c new file mode 100755 index 0000000..e0dd374 --- /dev/null +++ b/containers/net/net_sockets_win32.c @@ -0,0 +1,140 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "net_sockets.h" +#include "net_sockets_priv.h" +#include "containers/core/containers_common.h" + +#pragma comment(lib, "Ws2_32.lib") + +/*****************************************************************************/ + +/** Default maximum datagram size. + * This is based on the default Ethernet MTU size, less the IP and UDP headers. + */ +#define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8) + +/** Maximum socket buffer size to use. */ +#define MAXIMUM_BUFFER_SIZE 65536 + +/*****************************************************************************/ +static vc_container_net_status_t translate_error_status( int error ) +{ + switch (error) + { + case WSA_INVALID_HANDLE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + case WSA_NOT_ENOUGH_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSA_INVALID_PARAMETER: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED; + case WSAEFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG; + case WSAEWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK; + case WSAEINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case WSAEALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case WSAEADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE; + case WSAEADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + case WSAESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT; + case WSAECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED; + case WSAELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAEHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAEPROCLIM: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAEUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAEDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + case WSAEDISCON: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAHOST_NOT_FOUND: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + case WSATRY_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN; + case WSANO_RECOVERY: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + case WSANO_DATA: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + + /* All other errors are unexpected, so just map to a general purpose error code. */ + default: + return VC_CONTAINER_NET_ERROR_GENERAL; + } +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_last_error() +{ + return translate_error_status( WSAGetLastError() ); +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_init() +{ + WSADATA wsa_data; + int result; + + result = WSAStartup(MAKEWORD(2,2), &wsa_data); + if (result) + return translate_error_status( result ); + + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +void vc_container_net_private_deinit() +{ + WSACleanup(); +} + +/*****************************************************************************/ +void vc_container_net_private_close( SOCKET_T sock ) +{ + closesocket(sock); +} + +/*****************************************************************************/ +void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ) +{ + BOOL opt = enable ? TRUE : FALSE; + + (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); +} + +/*****************************************************************************/ +size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ) +{ + size_t max_datagram_size = DEFAULT_MAXIMUM_DATAGRAM_SIZE; + int opt_size = sizeof(max_datagram_size); + + /* Ignore errors and use the default if necessary */ + (void)getsockopt(sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&max_datagram_size, &opt_size); + + return max_datagram_size; +} diff --git a/containers/net/net_sockets_win32.h b/containers/net/net_sockets_win32.h new file mode 100755 index 0000000..86edc3a --- /dev/null +++ b/containers/net/net_sockets_win32.h @@ -0,0 +1,38 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NET_SOCKETS_WIN32_H_ +#define _NET_SOCKETS_WIN32_H_ + +#include +#include + +typedef SOCKET SOCKET_T; +typedef int SOCKADDR_LEN_T; +typedef char *SOCKOPT_CAST_T; + +#endif /* _NET_SOCKETS_WIN32_H_ */ diff --git a/containers/packetizers.h b/containers/packetizers.h new file mode 100755 index 0000000..735816d --- /dev/null +++ b/containers/packetizers.h @@ -0,0 +1,159 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_PACKETIZERS_H +#define VC_PACKETIZERS_H + +/** \file packetizers.h + * Public API for packetizing data (i.e. framing and timestamping) + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers.h" + +/** \defgroup VcPacketizerApi Packetizer API + * API for packetizers */ +/* @{ */ + +/** \name Packetizer flags + * \anchor packetizerflags + * The following flags describe properties of a packetizer */ +/* @{ */ +#define VC_PACKETIZER_FLAG_ES_CHANGED 0x1 /**< ES definition has changed */ +/* @} */ + +/** Definition of the packetizer type */ +typedef struct VC_PACKETIZER_T +{ + struct VC_PACKETIZER_PRIVATE_T *priv; /**< Private member used by the implementation */ + uint32_t flags; /**< Flags describing the properties of a packetizer. + * See \ref packetizerflags "Packetizer flags". */ + + VC_CONTAINER_ES_FORMAT_T *in; /**< Format of the input elementary stream */ + VC_CONTAINER_ES_FORMAT_T *out; /**< Format of the output elementary stream */ + + uint32_t max_frame_size; /**< Maximum size of a packetized frame */ + +} VC_PACKETIZER_T; + +/** Open a packetizer to convert the input format into the requested output format. + * This will create an an instance of a packetizer and its associated context. + * + * If no packetizer is found for the requested format, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param in Input elementary stream format + * \param out_variant Requested output variant for the output elementary stream format + * \param status Returns the status of the operation + * \return A pointer to the context of the new instance of the packetizer. + * Returns NULL on failure. + */ +VC_PACKETIZER_T *vc_packetizer_open(VC_CONTAINER_ES_FORMAT_T *in, VC_CONTAINER_FOURCC_T out_variant, + VC_CONTAINER_STATUS_T *status); + +/** Closes an instance of a packetizer. + * This will free all the resources associated with the context. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *context ); + +/** \name Packetizer flags + * The following flags can be passed during a packetize call */ +/* @{ */ +/** Type definition for the packetizer flags */ +typedef uint32_t VC_PACKETIZER_FLAGS_T; +/** Ask the packetizer to only return information on the next packet without reading it */ +#define VC_PACKETIZER_FLAG_INFO 0x1 +/** Ask the packetizer to skip the next packet */ +#define VC_PACKETIZER_FLAG_SKIP 0x2 +/** Ask the packetizer to flush any data being processed */ +#define VC_PACKETIZER_FLAG_FLUSH 0x4 +/** Force the packetizer to release an input packet */ +#define VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT 0x8 +/* @} */ + +/** Push a new packet of data to the packetizer. + * This is the mechanism used to feed data into the packetizer. Once a packet has been + * pushed into the packetizer it is owned by the packetizer until released by a call to + * \ref vc_packetizer_pop + * + * \param context Pointer to the context of the packetizer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * to push into the packetizer. + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T *packet); + +/** Pop a packet of data from the packetizer. + * This allows the client to retrieve consumed data from the packetizer. Packets returned by + * the packetizer in this manner can then be released / recycled by the client. + * It is possible for the client to retrieve non-consumed data by passing the + * VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT flag. This will however trigger some internal buffering + * inside the packetizer and thus is less efficient. + * + * \param context Pointer to the context of the packetizer to use + * \param packet Pointer used to return a consumed packet + * \param flags Miscellaneous flags controlling the operation + * + * \return VC_CONTAINER_SUCCESS if a consumed packet was retrieved, + * VC_CONTAINER_ERROR_INCOMPLETE_DATA if none is available. + */ +VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T **packet, VC_PACKETIZER_FLAGS_T flags); + +/** Read packetized data out of the packetizer. + * + * \param context Pointer to the context of the packetizer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * This might need to be partially filled before the call (buffer, buffer_size) + * depending on the flags used. + * \param flags Miscellaneous flags controlling the operation + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags); + +/** Reset packetizer state. + * This will reset the state of the packetizer as well as mark all data pushed to it as consumed. + * + * \param context Pointer to the context of the packetizer to reset + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *context ); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* VC_PACKETIZERS_H */ diff --git a/containers/pcm/pcm_packetizer.c b/containers/pcm/pcm_packetizer.c new file mode 100755 index 0000000..8dea398 --- /dev/null +++ b/containers/pcm/pcm_packetizer.c @@ -0,0 +1,269 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of a PCM packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_time.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" + +#define FRAME_SIZE (16*1024) /**< Arbitrary value which is neither too small nor too big */ +#define FACTOR_SHIFT 4 /**< Shift applied to the conversion factor */ + +VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +enum conversion { + CONVERSION_NONE = 0, + CONVERSION_U8_TO_S16L, + CONVERSION_UNKNOWN +}; + +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_NEW_PACKET = 0, + STATE_DATA + } state; + + unsigned int samples_per_frame; + unsigned int bytes_per_sample; + unsigned int max_frame_size; + + uint32_t bytes_read; + unsigned int frame_size; + + enum conversion conversion; + unsigned int conversion_factor; +} VC_PACKETIZER_MODULE_T; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T pcm_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T pcm_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->state = STATE_NEW_PACKET; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static void convert_pcm_u8_to_s16l( uint8_t **p_out, uint8_t *in, size_t size) +{ + int16_t *out = (int16_t *)*p_out; + uint8_t tmp; + + while(size--) + { + tmp = *in++; + *out++ = ((tmp - 128) << 8) | tmp; + } + *p_out = (uint8_t *)out; +} + +/*****************************************************************************/ +static void convert_pcm( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_BYTESTREAM_T *stream, size_t size, uint8_t *out ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + uint8_t tmp[256]; + size_t tmp_size; + + while(size) + { + tmp_size = MIN(sizeof(tmp), size); + bytestream_get(stream, tmp, tmp_size); + if (module->conversion == CONVERSION_U8_TO_S16L) + convert_pcm_u8_to_s16l(&out, tmp, tmp_size); + else + bytestream_skip(stream, tmp_size); + size -= tmp_size; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T pcm_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; + int64_t pts, dts; + size_t offset, size; + + while(1) switch (module->state) + { + case STATE_NEW_PACKET: + /* Make sure we've got enough data */ + if(bytestream_size(stream) < module->max_frame_size && + !(flags & VC_PACKETIZER_FLAG_FLUSH)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + if(!bytestream_size(stream)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + module->frame_size = bytestream_size(stream); + if(module->frame_size > module->max_frame_size) + module->frame_size = module->max_frame_size; + bytestream_get_timestamps_and_offset(stream, &pts, &dts, &offset, true); + vc_container_time_set(time, pts); + if(pts != VC_CONTAINER_TIME_UNKNOWN) + vc_container_time_add(time, offset / module->bytes_per_sample); + + module->bytes_read = 0; + module->state = STATE_DATA; + /* fall through to the next state */ + + case STATE_DATA: + size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + out->size = (size * module->conversion_factor) >> FACTOR_SHIFT; + + if(!module->bytes_read) + { + out->pts = out->dts = vc_container_time_get(time); + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if(flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if(flags & VC_PACKETIZER_FLAG_SKIP) + { + bytestream_skip( stream, size ); + } + else + { + out->size = MIN(out->size, out->buffer_size); + size = (out->size << FACTOR_SHIFT) / module->conversion_factor; + out->size = (size * module->conversion_factor) >> FACTOR_SHIFT; + + if(module->conversion != CONVERSION_NONE) + convert_pcm(p_ctx, stream, size, out->data); + else + bytestream_get(stream, out->data, out->size); + } + module->bytes_read += size; + + if(module->bytes_read == module->frame_size) + { + vc_container_time_add(time, module->samples_per_frame); + module->state = STATE_NEW_PACKET; + } + return VC_CONTAINER_SUCCESS; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + unsigned int bytes_per_sample = 0; + enum conversion conversion = CONVERSION_NONE; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_BE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_LE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_BE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_LE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_BE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_LE) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(p_ctx->in->type->audio.block_align) + bytes_per_sample = p_ctx->in->type->audio.block_align; + else if(p_ctx->in->type->audio.bits_per_sample && p_ctx->in->type->audio.channels) + bytes_per_sample = p_ctx->in->type->audio.bits_per_sample * + p_ctx->in->type->audio.channels / 8; + + if(!bytes_per_sample) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Check if we support any potential conversion we've been asked to do */ + if(p_ctx->out->codec_variant) + conversion = CONVERSION_UNKNOWN; + if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') && + p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE && + p_ctx->in->type->audio.bits_per_sample == 16) + conversion = CONVERSION_NONE; + if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') && + (p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_LE || + p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_BE) && + p_ctx->in->type->audio.bits_per_sample == 8) + conversion = CONVERSION_U8_TO_S16L; + if(conversion == CONVERSION_UNKNOWN) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + module->conversion = conversion; + module->conversion_factor = 1 << FACTOR_SHIFT; + + p_ctx->out->codec_variant = 0; + if(conversion == CONVERSION_U8_TO_S16L) + { + module->conversion_factor = 2 << FACTOR_SHIFT; + p_ctx->out->type->audio.bits_per_sample *= 2; + p_ctx->out->type->audio.block_align *= 2; + p_ctx->out->codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE; + } + + vc_container_time_set_samplerate(&p_ctx->priv->time, p_ctx->in->type->audio.sample_rate, 1); + + p_ctx->max_frame_size = FRAME_SIZE; + module->max_frame_size = (FRAME_SIZE << FACTOR_SHIFT) / module->conversion_factor; + module->bytes_per_sample = bytes_per_sample; + module->samples_per_frame = module->max_frame_size / bytes_per_sample; + p_ctx->priv->pf_close = pcm_packetizer_close; + p_ctx->priv->pf_packetize = pcm_packetizer_packetize; + p_ctx->priv->pf_reset = pcm_packetizer_reset; + + LOG_DEBUG(0, "using pcm audio packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(pcm_packetizer_open, "pcm"); diff --git a/containers/qsynth/CMakeLists.txt b/containers/qsynth/CMakeLists.txt new file mode 100755 index 0000000..b188e50 --- /dev/null +++ b/containers/qsynth/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_qsynth ${LIBRARY_TYPE} qsynth_reader.c) + +target_link_libraries(reader_qsynth containers) + +install(TARGETS reader_qsynth DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/qsynth/qsynth_reader.c b/containers/qsynth/qsynth_reader.c new file mode 100755 index 0000000..06a9d54 --- /dev/null +++ b/containers/qsynth/qsynth_reader.c @@ -0,0 +1,482 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3])) +#define BI16(b) (((b)[0]<<8)|((b)[1])) + +#define HEADER_LENGTH 14 +#define MAX_TRACKS 128 + +/****************************************************************************** +Type definitions +******************************************************************************/ +struct _QSYNTH_SEGMENT_T { + struct _QSYNTH_SEGMENT_T *next; + uint32_t len; + uint8_t *data; +}; +typedef struct _QSYNTH_SEGMENT_T QSYNTH_SEGMENT_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint32_t filesize; + QSYNTH_SEGMENT_T *seg; + QSYNTH_SEGMENT_T *pass; + uint32_t sent; + int64_t timestamp; + uint32_t seek; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T qsynth_read_header(uint8_t *data, uint32_t *tracks, + uint32_t *division, uint8_t *fps, uint8_t *dpf) +{ + if(data[0] != 'M' || data[1] != 'T' || data[2] != 'h' || data[3] != 'd' || + data[4] != 0 || data[5] != 0 || data[6] != 0 || data[7] != 6) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(data[12] < 0x80) + { + if(division) *division = BI16(data+12); + } + else + { + if(fps) *fps = 256-data[12]; + if(dpf) *dpf = data[13]; + } + + if(tracks) *tracks = BI16(data+10); + + return VC_CONTAINER_SUCCESS; +} + +static int qsynth_read_variable(uint8_t *data, uint32_t *val) +{ + int i = 0; + *val = 0; + do { + *val = (*val << 7) + (data[i] & 0x7f); + } while(data[i++] & 0x80); + + return i; +} + +static VC_CONTAINER_STATUS_T qsynth_read_event(uint8_t *data, uint32_t *used, uint8_t *last, + uint32_t *time, uint32_t *tempo, uint32_t *end) +{ + int read; + + // need at least 4 bytes here + read = qsynth_read_variable(data, time); + + if(data[read] == 0xff) // meta event + { + uint32_t len; + uint8_t type = data[read+1]; + + read += 2; + read += qsynth_read_variable(data+read, &len); + + if(type == 0x2f) // end of track + { + if(len != 0) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + *end = 1; + } + else if(type == 0x51) // tempo event + { + if(len != 3) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + *tempo = (data[read]<<16) | (data[read+1]<<8) | data[read+2]; + } + + read += len; + } + else if(data[read] == 0xf0 || data[read] == 0xf7) // sysex events + { + uint32_t len; + read += 1; + read += qsynth_read_variable(data+read, &len) + len; + } + else // midi event + { + uint8_t type; + + if(data[read] < 128) + type = *last; + else + { + type = data[read] >> 4; + *last = type; + read++; + } + + switch(type) { + case 8: case 9: case 0xa: case 0xb: case 0xe: + read += 2; + break; + case 0xc: case 0xd: + read += 1; + break; + default: + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + } + + *used = read; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T qsynth_read_track(QSYNTH_SEGMENT_T *seg, + uint32_t *ticks, int64_t *time, + uint32_t *us_perclock, uint32_t *tempo_ticks) +{ + uint32_t total_ticks = 0; + uint32_t used = 8; + uint8_t last = 0; + + *time = 0LL; + *tempo_ticks = 0; + + while(used < seg->len) + { + VC_CONTAINER_STATUS_T status; + uint32_t event_ticks, new_tempo = 0, end = 0, event_used; + if((status = qsynth_read_event(seg->data+used, &event_used, &last, &event_ticks, &new_tempo, &end)) != VC_CONTAINER_SUCCESS) + return status; + + used += event_used; + total_ticks += event_ticks; + + if(new_tempo != 0) + { + *time += ((int64_t) (total_ticks - *tempo_ticks)) * (*us_perclock); + *us_perclock = new_tempo; + *tempo_ticks = total_ticks; + } + + if(end) + break; + } + + *ticks = total_ticks; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T qsynth_get_duration(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + QSYNTH_SEGMENT_T **seg = &(module->seg); + uint32_t i, tracks, division = 0, max_ticks = 0, us_perclock = 500000; + uint32_t end_uspc = 0, end_ticks = 0; + int64_t end_time = 0; + uint8_t fps = 1, dpf = 1; + + if((*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + HEADER_LENGTH)) == NULL) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + (*seg)->next = NULL; + (*seg)->len = HEADER_LENGTH; + (*seg)->data = (uint8_t *) ((*seg) + 1); + + if(PEEK_BYTES(p_ctx, (*seg)->data, HEADER_LENGTH) != HEADER_LENGTH) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if((status = qsynth_read_header((*seg)->data, &tracks, &division, &fps, &dpf)) != VC_CONTAINER_SUCCESS) + return status; + + // if we have a suspiciously large number of tracks, this is probably a bad file + if(tracks > MAX_TRACKS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + SKIP_BYTES(p_ctx, HEADER_LENGTH); + + seg = &((*seg)->next); + module->filesize = HEADER_LENGTH; + + if(division == 0) + { + us_perclock = 1000000 / (fps * dpf); + division = 1; + } + + for(i=0; i (1<<20) || (*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + 8 + len)) == NULL) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + module->filesize += len+8; + (*seg)->next = NULL; + (*seg)->len = len + 8; + (*seg)->data = (uint8_t *) ((*seg) + 1); + + memcpy((*seg)->data, dummy, 8); + if(READ_BYTES(p_ctx, (*seg)->data+8, len) != len) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if((status = qsynth_read_track(*seg, &ticks, &time, &us_perclock, &tempo_ticks)) != VC_CONTAINER_SUCCESS) + return status; + + if(end_uspc == 0) + { + end_uspc = us_perclock; + end_ticks = tempo_ticks; + end_time = time; + } + + if(ticks > max_ticks) + max_ticks = ticks; + + seg = &((*seg)->next); + } + + if(end_uspc == 0) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + module->pass = module->seg; + module->sent = 0; + p_ctx->duration = (end_time + (((int64_t) (max_ticks - end_ticks)) * end_uspc)) / division; + module->track->format->extradata = (uint8_t *) &module->filesize; + module->track->format->extradata_size = 4; + return VC_CONTAINER_SUCCESS; +} + + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ +static VC_CONTAINER_STATUS_T qsynth_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, + uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + if(module->pass) + { + packet->size = module->pass->len - module->sent; + packet->dts = packet->pts = 0; + packet->track = 0; + packet->flags = module->sent ? 0 : VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + else + { + if(module->timestamp > p_ctx->duration) + return VC_CONTAINER_ERROR_EOS; + + packet->size = 5; + packet->dts = packet->pts = module->timestamp; + packet->track = 0; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME; + } + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + if(module->pass) + { + module->pass = module->pass->next; + module->sent = 0; + } + else + { + // if we're playing then we can't really skip, but have to simulate a seek instead + module->seek = 1; + module->timestamp += 40; + } + + return VC_CONTAINER_SUCCESS; + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + // read frame into packet->data + if(module->pass) + { + uint32_t copy = MIN(packet->size, packet->buffer_size); + memcpy(packet->data, module->pass->data + module->sent, copy); + packet->size = copy; + + if((module->sent += copy) == module->pass->len) + { + module->pass = module->pass->next; + module->sent = 0; + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + } + else + { + if(packet->buffer_size < packet->size) + return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; + + if(module->seek) + { + uint32_t current_time = module->timestamp / 1000; + + packet->data[0] = 'S'; + packet->data[1] = (uint8_t)((current_time >> 24) & 0xFF); + packet->data[2] = (uint8_t)((current_time >> 16) & 0xFF); + packet->data[3] = (uint8_t)((current_time >> 8) & 0xFF); + packet->data[4] = (uint8_t)((current_time ) & 0xFF); + module->seek = 0; + } + else + { + packet->data[0] = 'P'; + packet->data[1] = 0; + packet->data[2] = 0; + packet->data[3] = 0; + packet->data[4] = 40; + module->timestamp += 40 * 1000; + } + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T qsynth_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(flags); + + if (mode != VC_CONTAINER_SEEK_MODE_TIME) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if(*offset < 0) + *offset = 0; + else if(*offset > p_ctx->duration) + *offset = p_ctx->duration; + + module->timestamp = *offset; + module->seek = 1; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T qsynth_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + QSYNTH_SEGMENT_T *seg = module->seg; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + while(seg != NULL) + { + QSYNTH_SEGMENT_T *next = seg->next; + free(seg); + seg = next; + } + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + uint8_t header[HEADER_LENGTH]; + + /* Check the file header */ + if((PEEK_BYTES(p_ctx, header, HEADER_LENGTH) != HEADER_LENGTH) || + qsynth_read_header(header, 0, 0, 0, 0) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_MIDI; + p_ctx->tracks[0]->is_enabled = true; + + if((status = qsynth_get_duration(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + LOG_DEBUG(p_ctx, "using qsynth reader"); + + p_ctx->capabilities = VC_CONTAINER_CAPS_CAN_SEEK; + + p_ctx->priv->pf_close = qsynth_reader_close; + p_ctx->priv->pf_read = qsynth_reader_read; + p_ctx->priv->pf_seek = qsynth_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "qsynth: error opening stream (%i)", status); + if(module) qsynth_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function +********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open qsynth_reader_open +#endif diff --git a/containers/raw/CMakeLists.txt b/containers/raw/CMakeLists.txt new file mode 100755 index 0000000..0ada17e --- /dev/null +++ b/containers/raw/CMakeLists.txt @@ -0,0 +1,18 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_raw_video ${LIBRARY_TYPE} raw_video_reader.c) + +target_link_libraries(reader_raw_video containers) + +install(TARGETS reader_raw_video DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_raw_video ${LIBRARY_TYPE} raw_video_writer.c) + +target_link_libraries(writer_raw_video containers) + +install(TARGETS writer_raw_video DESTINATION ${VMCS_PLUGIN_DIR}) diff --git a/containers/raw/raw_video_common.h b/containers/raw/raw_video_common.h new file mode 100755 index 0000000..52d73ec --- /dev/null +++ b/containers/raw/raw_video_common.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef RAW_VIDEO_COMMON_H +#define RAW_VIDEO_COMMON_H + +static struct { + const char *id; + VC_CONTAINER_FOURCC_T codec; + unsigned int size_num; + unsigned int size_den; +} table[] = { + {"420", VC_CONTAINER_CODEC_I420, 3, 2}, + {0, 0, 0, 0} +}; + +STATIC_INLINE bool from_yuv4mpeg2(const char *id, VC_CONTAINER_FOURCC_T *codec, + unsigned int *size_num, unsigned int *size_den) +{ + unsigned int i; + for (i = 0; table[i].id; i++) + if (!strcmp(id, table[i].id)) + break; + if (codec) *codec = table[i].codec; + if (size_num) *size_num = table[i].size_num; + if (size_den) *size_den = table[i].size_den; + return !!table[i].id; +} + +STATIC_INLINE bool to_yuv4mpeg2(VC_CONTAINER_FOURCC_T codec, const char **id, + unsigned int *size_num, unsigned int *size_den) +{ + unsigned int i; + for (i = 0; table[i].id; i++) + if (codec == table[i].codec) + break; + if (id) *id = table[i].id; + if (size_num) *size_num = table[i].size_num; + if (size_den) *size_den = table[i].size_den; + return !!table[i].id; +} + +#endif /* RAW_VIDEO_COMMON_H */ diff --git a/containers/raw/raw_video_reader.c b/containers/raw/raw_video_reader.c new file mode 100755 index 0000000..00ac064 --- /dev/null +++ b/containers/raw/raw_video_reader.c @@ -0,0 +1,465 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "raw_video_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define FILE_HEADER_SIZE_MAX 1024 +#define FRAME_HEADER_SIZE_MAX 256 +#define OPTION_SIZE_MAX 32 + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_STATUS_T status; + + bool yuv4mpeg2; + bool non_standard; + char option[OPTION_SIZE_MAX]; + + bool frame_header; + unsigned int frame_header_size; + + int64_t data_offset; + unsigned int block_size; + unsigned int block_offset; + unsigned int frames; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T read_yuv4mpeg2_option( VC_CONTAINER_T *ctx, + unsigned int *bytes_left ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int size, i; + + /* Start by skipping spaces */ + while (*bytes_left && PEEK_U8(ctx) == ' ') + (*bytes_left)--, _SKIP_U8(ctx); + + size = PEEK_BYTES(ctx, module->option, + MIN(sizeof(module->option), *bytes_left)); + + /* The config option ends at next space or newline */ + for (i = 0; i < size; i++) + { + if (module->option[i] == ' ' || module->option[i] == 0x0a) + { + module->option[i] = 0; + break; + } + } + if (i == 0) + return VC_CONTAINER_ERROR_NOT_FOUND; + + *bytes_left -= i; + SKIP_BYTES(ctx, i); + + /* If option is too long, we just discard it */ + if (i == size) + { + while (*bytes_left && PEEK_U8(ctx) != ' ' && PEEK_U8(ctx) != 0x0a) + (*bytes_left)--, _SKIP_U8(ctx); + return VC_CONTAINER_ERROR_NOT_FOUND; + } + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T read_yuv4mpeg2_file_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int bytes_left = FILE_HEADER_SIZE_MAX - 10; + unsigned int value1, value2; + char codec[OPTION_SIZE_MAX] = "420"; + uint8_t h[10]; + + /* Check for the YUV4MPEG2 signature */ + if (READ_BYTES(ctx, h, sizeof(h)) != sizeof(h)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (memcmp(h, "YUV4MPEG2 ", sizeof(h))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Parse parameters */ + while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) + { + if (sscanf(module->option, "W%i", &value1) == 1) + ctx->tracks[0]->format->type->video.width = value1; + else if (sscanf(module->option, "H%i", &value1) == 1) + ctx->tracks[0]->format->type->video.height = value1; + else if (sscanf(module->option, "S%i", &value1) == 1) + module->block_size = value1; + else if (sscanf(module->option, "F%i:%i", &value1, &value2) == 2) + { + ctx->tracks[0]->format->type->video.frame_rate_num = value1; + ctx->tracks[0]->format->type->video.frame_rate_den = value2; + } + else if (sscanf(module->option, "A%i:%i", &value1, &value2) == 2) + { + ctx->tracks[0]->format->type->video.par_num = value1; + ctx->tracks[0]->format->type->video.par_den = value2; + } + else if (module->option[0] == 'C') + { + strcpy(codec, module->option+1); + } + } + + /* Check the end marker */ + if (_READ_U8(ctx) != 0x0a) + { + LOG_ERROR(ctx, "missing end of header marker"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Find out which codec we are dealing with */ + if (from_yuv4mpeg2(codec, &ctx->tracks[0]->format->codec, &value1, &value2)) + { + module->block_size = ctx->tracks[0]->format->type->video.width * + ctx->tracks[0]->format->type->video.height * value1 / value2; + } + else + { + memcpy(&ctx->tracks[0]->format->codec, codec, 4); + module->non_standard = true; + } + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T read_yuv4mpeg2_frame_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int bytes_left = FRAME_HEADER_SIZE_MAX - 5; + unsigned int value1; + char header[5]; + + if (READ_BYTES(ctx, header, sizeof(header)) != sizeof(header) || + memcmp(header, "FRAME", sizeof(header))) + { + LOG_ERROR(ctx, "missing frame marker"); + return STREAM_STATUS(ctx) != VC_CONTAINER_SUCCESS ? + STREAM_STATUS(ctx) : VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Parse parameters */ + while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) + { + if (module->non_standard && sscanf(module->option, "S%i", &value1) == 1) + module->block_size = value1; + } + + /* Check the end marker */ + if (_READ_U8(ctx) != 0x0a) + { + LOG_ERROR(ctx, "missing end of frame header marker"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + module->frame_header_size = FRAME_HEADER_SIZE_MAX - bytes_left - 1; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T rawvideo_parse_uri( VC_CONTAINER_T *ctx, + VC_CONTAINER_FOURCC_T *c, unsigned int *w, unsigned int *h, + unsigned int *fr_num, unsigned int *fr_den, unsigned *block_size ) +{ + VC_CONTAINER_FOURCC_T codec = 0; + unsigned int i, matches, width = 0, height = 0, fn = 0, fd = 0, size = 0; + const char *uri = ctx->priv->io->uri; + + /* Try and find a match for the string describing the format */ + for (i = 0; uri[i]; i++) + { + if (uri[i] != '_' && uri[i+1] != 'C') + continue; + + matches = sscanf(uri+i, "_C%4cW%iH%iF%i#%iS%i", (char *)&codec, + &width, &height, &fn, &fd, &size); + if (matches >= 3) + break; + } + if (!uri[i]) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (!size) + { + switch (codec) + { + case VC_CONTAINER_CODEC_I420: + case VC_CONTAINER_CODEC_YV12: + size = width * height * 3 / 2; + break; + default: break; + } + } + + if (!width || !height || !size) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (block_size) *block_size = size; + if (c) *c = codec; + if (w) *w = width; + if (h) *h = height; + if (fr_num) *fr_num = fn; + if (fr_den) *fr_den = fd; + if (block_size) *block_size = size; + + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_reader_read( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int size; + + if (module->status != VC_CONTAINER_SUCCESS) + return module->status; + + if (module->yuv4mpeg2 && !module->block_offset && + !module->frame_header) + { + module->status = read_yuv4mpeg2_frame_header(ctx); + if (module->status != VC_CONTAINER_SUCCESS) + return module->status; + + module->frame_header = true; + } + + if (!module->block_offset) + packet->pts = packet->dts = module->frames * INT64_C(1000000) * + ctx->tracks[0]->format->type->video.frame_rate_den / + ctx->tracks[0]->format->type->video.frame_rate_num; + else + packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END | + VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if (!module->block_offset) + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + packet->frame_size = module->block_size; + packet->size = module->block_size - module->block_offset; + packet->track = 0; + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(ctx, packet->size); + module->block_offset = 0; + module->frames++; + module->frame_header = 0; + module->status = STREAM_STATUS(ctx); + return module->status; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->block_size - module->block_offset, packet->buffer_size); + size = READ_BYTES(ctx, packet->data, size); + module->block_offset += size; + packet->size = size; + + if (module->block_offset == module->block_size) + { + module->block_offset = 0; + module->frame_header = 0; + module->frames++; + } + + module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(ctx); + return module->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(mode); + + module->frames = *offset * + ctx->tracks[0]->format->type->video.frame_rate_num / + ctx->tracks[0]->format->type->video.frame_rate_den / INT64_C(1000000); + module->block_offset = 0; + + if ((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && + module->frames * INT64_C(1000000) * + ctx->tracks[0]->format->type->video.frame_rate_den / + ctx->tracks[0]->format->type->video.frame_rate_num < *offset) + module->frames++; + + module->frame_header = 0; + + module->status = + SEEK(ctx, module->data_offset + module->frames * + (module->block_size + module->frame_header_size)); + + return module->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_reader_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + for (; ctx->tracks_num > 0; ctx->tracks_num--) + vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + bool yuv4mpeg2 = false; + uint8_t h[10]; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Check for the YUV4MPEG2 signature */ + if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if (!memcmp(h, "YUV4MPEG2 ", sizeof(h))) + yuv4mpeg2 = true; + + /* Or check if the extension is supported */ + if (!yuv4mpeg2 && + !(extension && !strcasecmp(extension, "yuv"))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using raw video reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks_num = 1; + ctx->tracks = &module->track; + ctx->tracks[0] = vc_container_allocate_track(ctx, 0); + if (!ctx->tracks[0]) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + ctx->tracks[0]->is_enabled = true; + ctx->tracks[0]->format->type->video.frame_rate_num = 25; + ctx->tracks[0]->format->type->video.frame_rate_den = 1; + ctx->tracks[0]->format->type->video.par_num = 1; + ctx->tracks[0]->format->type->video.par_den = 1; + + if (yuv4mpeg2) + { + status = read_yuv4mpeg2_file_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + module->data_offset = STREAM_POSITION(ctx); + + status = read_yuv4mpeg2_frame_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + module->frame_header = true; + } + else + { + VC_CONTAINER_FOURCC_T codec; + unsigned int width, height, fr_num, fr_den, block_size; + + status = rawvideo_parse_uri(ctx, &codec, &width, &height, + &fr_num, &fr_den, &block_size); + if (status != VC_CONTAINER_SUCCESS) + goto error; + ctx->tracks[0]->format->codec = codec; + ctx->tracks[0]->format->type->video.width = width; + ctx->tracks[0]->format->type->video.height = height; + if (fr_num && fr_den) + { + ctx->tracks[0]->format->type->video.frame_rate_num = fr_num; + ctx->tracks[0]->format->type->video.frame_rate_den = fr_den; + } + module->block_size = block_size; + } + + /* + * We now have all the information we really need to start playing the stream + */ + + LOG_INFO(ctx, "rawvideo %4.4s/%ix%i/fps:%i:%i/size:%i", + (char *)&ctx->tracks[0]->format->codec, + ctx->tracks[0]->format->type->video.width, + ctx->tracks[0]->format->type->video.height, + ctx->tracks[0]->format->type->video.frame_rate_num, + ctx->tracks[0]->format->type->video.frame_rate_den, module->block_size); + ctx->priv->pf_close = rawvideo_reader_close; + ctx->priv->pf_read = rawvideo_reader_read; + ctx->priv->pf_seek = rawvideo_reader_seek; + module->yuv4mpeg2 = yuv4mpeg2; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status); + rawvideo_reader_close(ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rawvideo_reader_open +#endif diff --git a/containers/raw/raw_video_writer.c b/containers/raw/raw_video_writer.c new file mode 100755 index 0000000..49b7c7c --- /dev/null +++ b/containers/raw/raw_video_writer.c @@ -0,0 +1,264 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "raw_video_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + bool yuv4mpeg2; + bool header_done; + bool non_standard; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_write_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int size; + char line[128]; + const char *id; + + size = snprintf(line, sizeof(line), "YUV4MPEG2 W%i H%i", + ctx->tracks[0]->format->type->video.width, + ctx->tracks[0]->format->type->video.height); + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + + if (ctx->tracks[0]->format->type->video.frame_rate_num && + ctx->tracks[0]->format->type->video.frame_rate_den) + { + size = snprintf(line, sizeof(line), " F%i:%i", + ctx->tracks[0]->format->type->video.frame_rate_num, + ctx->tracks[0]->format->type->video.frame_rate_den); + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + } + + if (ctx->tracks[0]->format->type->video.par_num && + ctx->tracks[0]->format->type->video.par_den) + { + size = snprintf(line, sizeof(line), " A%i:%i", + ctx->tracks[0]->format->type->video.par_num, + ctx->tracks[0]->format->type->video.par_den); + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + } + + if (to_yuv4mpeg2(ctx->tracks[0]->format->codec, &id, 0, 0)) + { + size = snprintf(line, sizeof(line), " C%s", id); + } + else + { + module->non_standard = true; + size = snprintf(line, sizeof(line), " C%4.4s", + (char *)&ctx->tracks[0]->format->codec); + } + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + + _WRITE_U8(ctx, 0x0a); + module->header_done = true; + return STREAM_STATUS(ctx); +} + +static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_STATUS_T status; + + /* Sanity check that we support the type of track being created */ + if (ctx->tracks_num) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + if (format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + + /* Allocate and initialise track data */ + ctx->tracks[0] = vc_container_allocate_track(ctx, 0); + if (!ctx->tracks[0]) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + status = vc_container_track_allocate_extradata(ctx, + ctx->tracks[0], format->extradata_size); + if(status != VC_CONTAINER_SUCCESS) + return status; + + vc_container_format_copy(ctx->tracks[0]->format, format, + format->extradata_size); + ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_writer_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + for (; ctx->tracks_num > 0; ctx->tracks_num--) + vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_writer_write( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + if (module->yuv4mpeg2 && !module->header_done) + { + status = rawvideo_write_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (module->yuv4mpeg2 && + (packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) + { + /* Write the metadata */ + WRITE_BYTES(ctx, "FRAME", sizeof("FRAME")-1); + + /* For formats not supported by the YUV4MPEG2 spec, we prepend + * each frame with its size */ + if (module->non_standard) + { + unsigned int size; + char line[32]; + size = snprintf(line, sizeof(line), " S%i", + packet->frame_size ? packet->frame_size : packet->size); + if (size < sizeof(line)) + WRITE_BYTES(ctx, line, size); + } + + _WRITE_U8(ctx, 0x0a); + } + + /* Write the elementary stream */ + WRITE_BYTES(ctx, packet->data, packet->size); + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_writer_control( VC_CONTAINER_T *ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_ES_FORMAT_T *format; + + switch (operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *); + return simple_write_add_track(ctx, format); + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return module->yuv4mpeg2 ? + rawvideo_write_header( ctx ) : VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module; + bool yuv4mpeg2 = false; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(!strcasecmp(extension, "y4m") || !strcasecmp(extension, "yuv4mpeg2")) + yuv4mpeg2 = true; + if(!yuv4mpeg2 && strcasecmp(extension, "yuv")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using rawvideo writer"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = &module->track; + module->yuv4mpeg2 = yuv4mpeg2; + + ctx->priv->pf_close = rawvideo_writer_close; + ctx->priv->pf_write = rawvideo_writer_write; + ctx->priv->pf_control = rawvideo_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open rawvideo_writer_open +#endif diff --git a/containers/rcv/CMakeLists.txt b/containers/rcv/CMakeLists.txt new file mode 100755 index 0000000..9407f58 --- /dev/null +++ b/containers/rcv/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_rcv ${LIBRARY_TYPE} rcv_reader.c) + +target_link_libraries(reader_rcv containers) + +install(TARGETS reader_rcv DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rcv/rcv_reader.c b/containers/rcv/rcv_reader.c new file mode 100755 index 0000000..4d4db27 --- /dev/null +++ b/containers/rcv/rcv_reader.c @@ -0,0 +1,358 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_index.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +#define LI32(b) (((b)[3]<<24)|((b)[2]<<16)|((b)[1]<<8)|((b)[0])) +#define LI24(b) (((b)[2]<<16)|((b)[1]<<8)|((b)[0])) + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct { + unsigned int num_frames : 24; + unsigned int constant_c5 : 8; + int constant_4; + uint32_t struct_c; + uint32_t vert_size; + uint32_t horiz_size; + int constant_c; + uint32_t struct_b[2]; + uint32_t framerate; +} RCV_FILE_HEADER_T; + +typedef struct { + unsigned int framesize : 24; + unsigned int res : 7; + unsigned int keyframe : 1; + uint32_t timestamp; +} RCV_FRAME_HEADER_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint8_t extradata[4]; + uint8_t mid_frame; + uint32_t frame_read; + RCV_FRAME_HEADER_T frame; + VC_CONTAINER_INDEX_T *index; /* index of key frames */ + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T rcv_read_header(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + RCV_FILE_HEADER_T header; + uint8_t dummy[36]; + + if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; + + header.num_frames = LI24(dummy); + header.constant_c5 = dummy[3]; + header.constant_4 = LI32(dummy+4); + + // extradata is just struct_c from the header + memcpy(module->extradata, dummy+8, 4); + module->track->format->extradata = module->extradata; + module->track->format->extradata_size = 4; + + module->track->format->type->video.height = LI32(dummy+12); + module->track->format->type->video.width = LI32(dummy+16); + + header.constant_c = LI32(dummy+20); + memcpy(header.struct_b, dummy+24, 8); + header.framerate = LI32(dummy+32); + + if(header.constant_c5 != 0xc5 || header.constant_4 != 0x4 || header.constant_c != 0xc) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(header.framerate != 0 && header.framerate != 0xffffffffUL) + { + module->track->format->type->video.frame_rate_num = header.framerate; + module->track->format->type->video.frame_rate_den = 1; + } + + // fill in general information + if(header.num_frames != (1<<24)-1 && header.framerate != 0 && header.framerate != 0xffffffffUL) + p_ctx->duration = ((int64_t) header.num_frames * 1000000LL) / (int64_t) header.framerate; + + // we're happy that this is an rcv file + SKIP_BYTES(p_ctx, sizeof(dummy)); + + return STREAM_STATUS(p_ctx); +} + +/***************************************************************************** + * Utility function to seek to the keyframe nearest the given timestamp. + * + * @param p_ctx Pointer to the container context. + * @param timestamp The requested time. On success, this is updated with the time of the selected keyframe. + * @param later If true, the selected frame is the earliest keyframe with a time greater or equal to timestamp. + * If false, the selected frame is the latest keyframe with a time earlier or equal to timestamp. + * @return Status code. + */ +static VC_CONTAINER_STATUS_T rcv_seek_nearest_keyframe(VC_CONTAINER_T *p_ctx, int64_t *timestamp, int later) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t prev_keyframe_offset = sizeof(RCV_FILE_HEADER_T); /* set to very first frame */ + int64_t prev_keyframe_timestamp = 0; + int use_prev_keyframe = !later; + + if(use_prev_keyframe || (module->frame.timestamp * 1000LL > *timestamp)) + { + /* A seek has been requested to an earlier keyframe, so rewind to the beginning + * of the stream since there's no information available on previous frames */ + SEEK(p_ctx, sizeof(RCV_FILE_HEADER_T)); + memset(&module->frame, 0, sizeof(RCV_FRAME_HEADER_T)); + module->mid_frame = 0; + module->frame_read = 0; + } + + if(module->mid_frame) + { + /* Seek back to the start of the current frame */ + SEEK(p_ctx, STREAM_POSITION(p_ctx) - module->frame_read - sizeof(RCV_FILE_HEADER_T)); + module->mid_frame = 0; + module->frame_read = 0; + } + + while(1) + { + if(PEEK_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T)) + { + status = VC_CONTAINER_ERROR_EOS; + break; + } + + if(module->frame.keyframe) + { + if(module->index) + vc_container_index_add(module->index, module->frame.timestamp * 1000LL, STREAM_POSITION(p_ctx)); + + if((module->frame.timestamp * 1000LL) >= *timestamp) + { + if((module->frame.timestamp * 1000LL) == *timestamp) + use_prev_keyframe = 0; + + *timestamp = module->frame.timestamp * 1000LL; + + break; + } + + prev_keyframe_offset = STREAM_POSITION(p_ctx); + prev_keyframe_timestamp = module->frame.timestamp * 1000LL; + } + + SKIP_BYTES(p_ctx, module->frame.framesize + sizeof(RCV_FRAME_HEADER_T)); + } + + if(use_prev_keyframe) + { + *timestamp = prev_keyframe_timestamp; + status = SEEK(p_ctx, prev_keyframe_offset); + } + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ +static VC_CONTAINER_STATUS_T rcv_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int size; + + if(!module->mid_frame) + { + /* Save the current position for updating the indexer */ + int64_t position = STREAM_POSITION(p_ctx); + + if(READ_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T)) + return VC_CONTAINER_ERROR_EOS; + module->mid_frame = 1; + module->frame_read = 0; + + if(module->index && module->frame.keyframe) + vc_container_index_add(module->index, (int64_t)module->frame.timestamp * 1000LL, position); + } + + packet->size = module->frame.framesize; + packet->dts = packet->pts = module->frame.timestamp * 1000LL; + packet->track = 0; + packet->flags = 0; + if(module->frame_read == 0) + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + if(module->frame.keyframe) + packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(p_ctx, module->frame.framesize - module->frame_read); + if((module->frame_read += size) == module->frame.framesize) + { + module->frame_read = 0; + module->mid_frame = 0; + } + return STREAM_STATUS(p_ctx); + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->frame.framesize - module->frame_read, packet->buffer_size); + size = READ_BYTES(p_ctx, packet->data, size); + if((module->frame_read += size) == module->frame.framesize) + { + module->frame_read = 0; + module->mid_frame = 0; + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + packet->size = size; + + return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rcv_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + int past = 1; + int64_t position; + int64_t timestamp = *offset; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(mode); + + if(module->index) + status = vc_container_index_get(module->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, ×tamp, &position, &past); + + if(status == VC_CONTAINER_SUCCESS && !past) + { + /* Indexed keyframe found */ + module->frame_read = 0; + module->mid_frame = 0; + *offset = timestamp; + status = SEEK(p_ctx, position); + } + else + { + /* No indexed keyframe found, so seek through all frames */ + status = rcv_seek_nearest_keyframe(p_ctx, offset, flags & VC_CONTAINER_SEEK_FLAG_FORWARD); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rcv_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + + if(module->index) + vc_container_index_free(module->index); + + free(module); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + uint8_t dummy[8]; + + /* Quick check for a valid file header */ + if((PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) || + dummy[3] != 0xc5 || LI32(dummy+4) != 0x4) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_WMV3; + p_ctx->tracks[0]->is_enabled = true; + + if((status = rcv_read_header(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + LOG_DEBUG(p_ctx, "using rcv reader"); + + if(vc_container_index_create(&module->index, 512) == VC_CONTAINER_SUCCESS) + vc_container_index_add(module->index, 0LL, STREAM_POSITION(p_ctx)); + + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + p_ctx->priv->pf_close = rcv_reader_close; + p_ctx->priv->pf_read = rcv_reader_read; + p_ctx->priv->pf_seek = rcv_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + if(module) rcv_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function +********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rcv_reader_open +#endif diff --git a/containers/rtp/CMakeLists.txt b/containers/rtp/CMakeLists.txt new file mode 100755 index 0000000..e6ba3a9 --- /dev/null +++ b/containers/rtp/CMakeLists.txt @@ -0,0 +1,17 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +set(rtp_SRCS ${rtp_SRCS} rtp_reader.c) +set(rtp_SRCS ${rtp_SRCS} rtp_h264.c) +set(rtp_SRCS ${rtp_SRCS} rtp_mpeg4.c) +set(rtp_SRCS ${rtp_SRCS} rtp_base64.c) +add_library(reader_rtp ${LIBRARY_TYPE} ${rtp_SRCS}) + +target_link_libraries(reader_rtp containers) + +install(TARGETS reader_rtp DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rtp/rtp_base64.c b/containers/rtp/rtp_base64.c new file mode 100755 index 0000000..f0e873f --- /dev/null +++ b/containers/rtp/rtp_base64.c @@ -0,0 +1,165 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "rtp_base64.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define LOWEST_BASE64_CHAR '+' +#define HIGHEST_BASE64_CHAR 'z' +#define IN_BASE64_RANGE(C) ((C) >= LOWEST_BASE64_CHAR && (C) <= HIGHEST_BASE64_CHAR) + +/** Used as a marker in the lookup table to indicate an invalid Base64 character */ +#define INVALID 0xFF + +/* Reduced lookup table for translating a character to a 6-bit value. The + * table starts at the lowest Base64 character, '+' */ +uint8_t base64_decode_lookup[] = { + 62, INVALID, 62, INVALID, 63, /* '+' to '/' */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* '0' to '9' */ + INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, /* ':' to '@' */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' to 'T' */ + 20, 21, 22, 23, 24, 25, /* 'U' to 'Z' */ + INVALID, INVALID, INVALID, INVALID, 63, INVALID, /* '[' to '`' */ + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, /* 'a' to 'r' */ + 44, 45, 46, 47, 48, 49, 50, 51 /* 's' to 'z' */ +}; + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/***************************************************************************** +Functions exported as part of the Base64 API + *****************************************************************************/ + +/*****************************************************************************/ +uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len) +{ + uint32_t character_count = 0; + uint32_t ii; + char cc; + + /* Scan through string until either a pad ('=') character or the end is + * reached. Ignore characters that are not part of the Base64 alphabet. + * Number of bytes should then be 3/4 of the character count. */ + + for (ii = 0; ii < str_len; ii++) + { + cc = *str++; + if (cc == '=') + break; /* Found a pad character: stop */ + + if (!IN_BASE64_RANGE(cc)) + continue; /* Ignore invalid character */ + + if (base64_decode_lookup[cc - LOWEST_BASE64_CHAR] != INVALID) + character_count++; + } + + return (character_count * 3) >> 2; +} + +/*****************************************************************************/ +uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len) +{ + uint32_t character_count = 0; + uint32_t value = 0; + uint32_t ii; + char cc; + uint8_t lookup; + + /* Build up sets of four characters (ignoring invalid ones) to generate + * triplets of bytes, until either the end of the string or the pad ('=') + * characters are reached. */ + + for (ii = 0; ii < str_len; ii++) + { + cc = *str++; + if (cc == '=') + break; /* Found a pad character: stop */ + + if (!IN_BASE64_RANGE(cc)) + continue; /* Ignore invalid character */ + + lookup = base64_decode_lookup[cc - LOWEST_BASE64_CHAR]; + if (lookup == INVALID) + continue; /* Ignore invalid character */ + + value = (value << 6) | lookup; + character_count++; + + if (character_count == 4) + { + if (buffer_len < 3) + return NULL; /* Not enough room in the output buffer */ + + *buffer++ = (uint8_t)(value >> 16); + *buffer++ = (uint8_t)(value >> 8); + *buffer++ = (uint8_t)(value ); + buffer_len -= 3; + + character_count = 0; + value = 0; + } + } + + /* If there were extra characters on the end, these need to be handled to get + * the last one or two bytes. */ + + switch (character_count) + { + case 0: /* Nothing more to do, the final bytes were converted in the loop */ + break; + case 2: /* One additional byte, padded with four zero bits */ + if (!buffer_len) + return NULL; + *buffer++ = (uint8_t)(value >> 4); + break; + case 3: /* Two additional bytes, padded with two zero bits */ + if (buffer_len < 2) + return NULL; + *buffer++ = (uint8_t)(value >> 10); + *buffer++ = (uint8_t)(value >> 2); + break; + default: /* This is an invalid Base64 encoding */ + return NULL; + } + + /* Return number of bytes written to the buffer */ + return buffer; +} diff --git a/containers/rtp/rtp_base64.h b/containers/rtp/rtp_base64.h new file mode 100755 index 0000000..2cb6a76 --- /dev/null +++ b/containers/rtp/rtp_base64.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_BASE64_H_ +#define _RTP_BASE64_H_ + +#include "containers/containers.h" + +/** Returns the number of bytes encoded by the given Base64 encoded string. + * + * \param str The Base64 encoded string. + * \param str_len The number of characters in the string. + * \return The number of bytes that can be decoded. */ +uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len); + +/** Decodes a Base64 encoded string into a byte buffer. + * + * \param str The Base64 encoded string. + * \param str_len The number of characters in the string. + * \param buffer The buffer to receive the decoded output. + * \param buffer_len The maximum number of bytes to put in the buffer. + * \return Pointer to byte after the last one converted, or NULL on error. */ +uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len); + +#endif /* _RTP_BASE64_H_ */ diff --git a/containers/rtp/rtp_h264.c b/containers/rtp/rtp_h264.c new file mode 100755 index 0000000..e7f0f83 --- /dev/null +++ b/containers/rtp/rtp_h264.c @@ -0,0 +1,803 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/containers.h" + +#include "containers/core/containers_logging.h" +#include "containers/core/containers_list.h" +#include "containers/core/containers_bits.h" +#include "rtp_priv.h" +#include "rtp_base64.h" +#include "rtp_h264.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/** H.264 payload flag bits */ +typedef enum +{ + H264F_NEXT_PACKET_IS_START = 0, + H264F_INSIDE_FRAGMENT, + H264F_OUTPUT_NAL_HEADER, +} h264_flag_bit_t; + +/** Bit mask to extract F zero bit from NAL unit header */ +#define NAL_UNIT_FZERO_MASK 0x80 +/** Bit mask to extract NAL unit type from NAL unit header */ +#define NAL_UNIT_TYPE_MASK 0x1F + +/** NAL unit type codes */ +enum +{ + /* 0 unspecified */ + NAL_UNIT_NON_IDR = 1, + NAL_UNIT_PARTITION_A = 2, + NAL_UNIT_PARTITION_B = 3, + NAL_UNIT_PARTITION_C = 4, + NAL_UNIT_IDR = 5, + NAL_UNIT_SEI = 6, + NAL_UNIT_SEQUENCE_PARAMETER_SET = 7, + NAL_UNIT_PICTURE_PARAMETER_SET = 8, + NAL_UNIT_ACCESS_UNIT_DELIMITER = 9, + NAL_UNIT_END_OF_SEQUENCE = 10, + NAL_UNIT_END_OF_STREAM = 11, + NAL_UNIT_FILLER = 12, + NAL_UNIT_EXT_SEQUENCE_PARAMETER_SET = 13, + NAL_UNIT_PREFIX = 14, + NAL_UNIT_SUBSET_SEQUENCE_PARAMETER_SET = 15, + /* 16 to 18 reserved */ + NAL_UNIT_AUXILIARY = 19, + NAL_UNIT_EXTENSION = 20, + /* 21 to 23 reserved */ + NAL_UNIT_STAP_A = 24, + NAL_UNIT_STAP_B = 25, + NAL_UNIT_MTAP16 = 26, + NAL_UNIT_MTAP24 = 27, + NAL_UNIT_FU_A = 28, + NAL_UNIT_FU_B = 29, + /* 30 to 31 unspecified */ +}; + +/** Fragment unit header indicator bits */ +typedef enum +{ + FRAGMENT_UNIT_HEADER_RESERVED = 5, + FRAGMENT_UNIT_HEADER_END = 6, + FRAGMENT_UNIT_HEADER_START = 7, +} fragment_unit_header_bit_t; + +#define MACROBLOCK_WIDTH 16 +#define MACROBLOCK_HEIGHT 16 + +/** H.264 RTP timestamp clock rate */ +#define H264_TIMESTAMP_CLOCK 90000 + +typedef enum +{ + CHROMA_FORMAT_MONO = 0, + CHROMA_FORMAT_YUV_420 = 1, + CHROMA_FORMAT_YUV_422 = 2, + CHROMA_FORMAT_YUV_444 = 3, + CHROMA_FORMAT_YUV_444_PLANAR = 4, + CHROMA_FORMAT_RGB = 5, +} CHROMA_FORMAT_T; + +uint32_t chroma_sub_width[] = { + 1, 2, 2, 1, 1, 1 +}; + +uint32_t chroma_sub_height[] = { + 1, 2, 1, 1, 1, 1 +}; + +/****************************************************************************** +Type definitions +******************************************************************************/ + +typedef struct h264_payload_tag +{ + uint32_t nal_unit_size; /**< Number of NAL unit bytes left to write */ + uint8_t flags; /**< H.264 payload flags */ + uint8_t header_bytes_to_write; /**< Number of start code bytes left to write */ + uint8_t nal_header; /**< Header for next NAL unit */ +} H264_PAYLOAD_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Remove emulation prevention bytes from a buffer. + * These are 0x03 bytes inserted to prevent misinterprentation of a byte + * sequence in a buffer as a start code. + * + * @param sprop The buffer from which bytes are to be removed. + * @param sprop_size The number of bytes in the buffer. + * @return The new number of bytes in the buffer. + */ +static uint32_t h264_remove_emulation_prevention_bytes(uint8_t *sprop, + uint32_t sprop_size) +{ + uint32_t offset = 0; + uint8_t nal_unit_type = sprop[offset++]; + uint32_t new_sprop_size = sprop_size; + uint8_t first_byte, second_byte; + + nal_unit_type &= 0x1F; /* Just keep NAL unit type bits */ + + /* Certain NAL unit types need a byte triplet passed first */ + if (nal_unit_type == NAL_UNIT_PREFIX || nal_unit_type == NAL_UNIT_EXTENSION) + offset += 3; + + /* Make sure there is enough data for there to be a 0x00 0x00 0x03 sequence */ + if (offset + 2 >= new_sprop_size) + return new_sprop_size; + + /* Keep a rolling set of the last couple of bytes */ + first_byte = sprop[offset++]; + second_byte = sprop[offset++]; + + while (offset < new_sprop_size) + { + uint8_t next_byte = sprop[offset]; + + if (!first_byte && !second_byte && next_byte == 0x03) + { + /* Remove the emulation prevention byte (0x03) */ + new_sprop_size--; + if (offset == new_sprop_size) /* No more data to check */ + break; + memmove(&sprop[offset], &sprop[offset + 1], new_sprop_size - offset); + next_byte = sprop[offset]; + } else + offset++; + + first_byte = second_byte; + second_byte = next_byte; + } + + return new_sprop_size; +} + +/**************************************************************************//** + * Skip a scaling list in a bit stream. + * + * @param p_ctx The container context. + * @param sprop The bit stream containing the scaling list. + * @param size_of_scaling_list The size of the scaling list. + */ +static void h264_skip_scaling_list(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_BITS_T *sprop, + uint32_t size_of_scaling_list) +{ + uint32_t last_scale = 8; + uint32_t next_scale = 8; + int32_t delta_scale; + uint32_t jj; + + /* Algorithm taken from H.264 section 7.3.2.1.1.1 */ + for (jj = 0; jj < size_of_scaling_list; jj++) + { + if (next_scale) + { + delta_scale = BITS_READ_S32_EXP(p_ctx, sprop, "delta_scale"); + next_scale = (last_scale + delta_scale + 256) & 0xFF; + + if (next_scale) + last_scale = next_scale; + } + } +} + +/**************************************************************************//** + * Get the chroma format from the bit stream. + * + * @param p_ctx The container context. + * @param sprop The bit stream containing the scaling list. + * @return The chroma format index. + */ +static uint32_t h264_get_chroma_format(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_BITS_T *sprop) +{ + uint32_t chroma_format_idc; + + chroma_format_idc = BITS_READ_U32_EXP(p_ctx, sprop, "chroma_format_idc"); + if (chroma_format_idc == 3 && BITS_READ_U32(p_ctx, sprop, 1, "separate_colour_plane_flag")) + chroma_format_idc = CHROMA_FORMAT_YUV_444_PLANAR; + + BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_luma_minus8"); + BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_chroma_minus8"); + BITS_SKIP(p_ctx, sprop, 1, "qpprime_y_zero_transform_bypass_flag"); + + if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_matrix_present_flag")) + { + uint32_t scaling_lists = (chroma_format_idc == 3) ? 12 : 8; + uint32_t ii; + + for (ii = 0; ii < scaling_lists; ii++) + { + if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_list_present_flag")) + h264_skip_scaling_list(p_ctx, sprop, (ii < 6) ? 16 : 64); + } + } + + return chroma_format_idc; +} + +/**************************************************************************//** + * Decode an H.264 sequence parameter set and update track information. + * + * @param p_ctx The RTP container context. + * @param track The track to be updated. + * @param sprop The bit stream containing the sequence parameter set. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_decode_sequence_parameter_set(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_BITS_T *sprop) +{ + VC_CONTAINER_VIDEO_FORMAT_T *video = &track->format->type->video; + uint32_t pic_order_cnt_type, chroma_format_idc; + uint32_t pic_width_in_mbs_minus1, pic_height_in_map_units_minus1, frame_mbs_only_flag; + uint32_t frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset; + uint8_t profile_idc; + + /* This structure is defined by H.264 section 7.3.2.1.1 */ + profile_idc = BITS_READ_U8(p_ctx, sprop, 8, "profile_idc"); + BITS_SKIP(p_ctx, sprop, 16, "Rest of profile_level_id"); + + BITS_READ_U32_EXP(p_ctx, sprop, "seq_parameter_set_id"); + + chroma_format_idc = CHROMA_FORMAT_RGB; + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || + profile_idc == 86 || profile_idc == 118 || profile_idc == 128) + { + chroma_format_idc = h264_get_chroma_format(p_ctx, sprop); + if (chroma_format_idc > CHROMA_FORMAT_YUV_444_PLANAR) + goto error; + } + + BITS_SKIP_EXP(p_ctx, sprop, "log2_max_frame_num_minus4"); + pic_order_cnt_type = BITS_READ_U32_EXP(p_ctx, sprop, "pic_order_cnt_type"); + if (pic_order_cnt_type == 0) + { + BITS_SKIP_EXP(p_ctx, sprop, "log2_max_pic_order_cnt_lsb_minus4"); + } + else if (pic_order_cnt_type == 1) + { + uint32_t num_ref_frames_in_pic_order_cnt_cycle; + uint32_t ii; + + BITS_SKIP(p_ctx, sprop, 1, "delta_pic_order_always_zero_flag"); + BITS_SKIP_EXP(p_ctx, sprop, "offset_for_non_ref_pic"); + BITS_SKIP_EXP(p_ctx, sprop, "offset_for_top_to_bottom_field"); + num_ref_frames_in_pic_order_cnt_cycle = BITS_READ_U32_EXP(p_ctx, sprop, "num_ref_frames_in_pic_order_cnt_cycle"); + + for (ii = 0; ii < num_ref_frames_in_pic_order_cnt_cycle; ii++) + BITS_SKIP_EXP(p_ctx, sprop, "offset_for_ref_frame"); + } + + BITS_SKIP_EXP(p_ctx, sprop, "max_num_ref_frames"); + BITS_SKIP(p_ctx, sprop, 1, "gaps_in_frame_num_value_allowed_flag"); + + pic_width_in_mbs_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_width_in_mbs_minus1"); + pic_height_in_map_units_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_height_in_map_units_minus1"); + frame_mbs_only_flag = BITS_READ_U32(p_ctx, sprop, 1, "frame_mbs_only_flag"); + + /* Can now set the overall width and height in pixels */ + video->width = (pic_width_in_mbs_minus1 + 1) * MACROBLOCK_WIDTH; + video->height = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * MACROBLOCK_HEIGHT; + + if (!frame_mbs_only_flag) + BITS_SKIP(p_ctx, sprop, 1, "mb_adaptive_frame_field_flag"); + BITS_SKIP(p_ctx, sprop, 1, "direct_8x8_inference_flag"); + + if (BITS_READ_U32(p_ctx, sprop, 1, "frame_cropping_flag")) + { + /* Visible area is restricted */ + frame_crop_left_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_left_offset"); + frame_crop_right_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_right_offset"); + frame_crop_top_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_top_offset"); + frame_crop_bottom_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_bottom_offset"); + + /* Need to adjust offsets for 4:2:0 and 4:2:2 chroma formats and field/frame flag */ + frame_crop_left_offset *= chroma_sub_width[chroma_format_idc]; + frame_crop_right_offset *= chroma_sub_width[chroma_format_idc]; + frame_crop_top_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag); + frame_crop_bottom_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag); + + if ((frame_crop_left_offset + frame_crop_right_offset) >= video->width || + (frame_crop_top_offset + frame_crop_bottom_offset) >= video->height) + { + LOG_ERROR(p_ctx, "H.264: frame crop offsets (%u, %u, %u, %u) larger than frame (%u, %u)", + frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, + frame_crop_bottom_offset, video->width, video->height); + goto error; + } + + video->x_offset = frame_crop_left_offset; + video->y_offset = frame_crop_top_offset; + video->visible_width = video->width - frame_crop_left_offset - frame_crop_right_offset; + video->visible_height = video->height - frame_crop_top_offset - frame_crop_bottom_offset; + } else { + video->visible_width = video->width; + video->visible_height = video->height; + } + + /* vui_parameters may follow, but these will not be decoded */ + + if (!BITS_VALID(p_ctx, sprop)) + goto error; + + return VC_CONTAINER_SUCCESS; + +error: + LOG_ERROR(p_ctx, "H.264: sequence_parameter_set failed to decode"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/**************************************************************************//** + * Decode an H.264 sprop and update track information. + * + * @param p_ctx The RTP container context. + * @param track The track to be updated. + * @param sprop The bit stream containing the sprop. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_decode_sprop(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_BITS_T *sprop) +{ + switch (BITS_READ_U32(p_ctx, sprop, 8, "nal_unit_header") & NAL_UNIT_TYPE_MASK) + { + case NAL_UNIT_SEQUENCE_PARAMETER_SET: + return h264_decode_sequence_parameter_set(p_ctx, track, sprop); + case NAL_UNIT_PICTURE_PARAMETER_SET: + /* Not handled, but valid */ + return VC_CONTAINER_SUCCESS; + default: + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } +} + +/**************************************************************************//** + * Decode the sprop parameter sets URI parameter and update track information. + * + * @param p_ctx The RTP container context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_get_sprop_parameter_sets(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_STATUS_T status; + PARAMETER_T param; + size_t str_len; + uint32_t extradata_size = 0; + uint8_t *sprop; + const char *set; + const char *comma; + + /* Get the value of sprop-parameter-sets, base64 decode the (comma separated) + * sets, store all of them in track->priv->extradata and also decode to + * validate and fill in video format info. */ + + param.name = "sprop-parameter-sets"; + if (!vc_containers_list_find_entry(params, ¶m) || !param.value) + { + LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets is required, but not found"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* First pass, calculate total size of buffer needed */ + set = param.value; + do { + comma = strchr(set, ','); + str_len = comma ? (size_t)(comma - set) : strlen(set); + /* Allow space for the NAL unit and a start code */ + extradata_size += rtp_base64_byte_length(set, str_len) + 4; + set = comma + 1; + } while (comma); + + if (!extradata_size) + { + LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets doesn't contain useful data"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size); + if(status != VC_CONTAINER_SUCCESS) return status; + + track->format->extradata_size = extradata_size; + sprop = track->priv->extradata; + + /* Now decode the data into the buffer, and validate / use it to fill in format */ + set = param.value; + do { + uint8_t *next_sprop; + uint32_t sprop_size; + VC_CONTAINER_BITS_T sprop_stream; + + comma = strchr(set, ','); + str_len = comma ? (size_t)(comma - set) : strlen(set); + + /* Insert a start code (0x00000001 in network order) */ + *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x01; + extradata_size -= 4; + + next_sprop = rtp_base64_decode(set, str_len, sprop, extradata_size); + if (!next_sprop) + { + LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets failed to decode"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + sprop_size = next_sprop - sprop; + if (sprop_size) + { + uint32_t new_sprop_size; + + /* Need to remove emulation prevention bytes before decoding */ + new_sprop_size = h264_remove_emulation_prevention_bytes(sprop, sprop_size); + + BITS_INIT(p_ctx, &sprop_stream, sprop, new_sprop_size); + status = h264_decode_sprop(p_ctx, track, &sprop_stream); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* If necessary, decode sprop again, to put back the emulation prevention bytes */ + if (new_sprop_size != sprop_size) + rtp_base64_decode(set, str_len, sprop, sprop_size); + + extradata_size -= sprop_size; + sprop = next_sprop; + } + + set = comma + 1; + } while (comma); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Check URI parameter list for unsupported features. + * + * @param p_ctx The RTP container context. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_check_unsupported_features(VC_CONTAINER_T *p_ctx, + const VC_CONTAINERS_LIST_T *params) +{ + uint32_t u32_unused; + + /* Limitation: interleaving not yet supported */ + if (rtp_get_parameter_u32(params, "sprop-interleaving-depth", &u32_unused) || + rtp_get_parameter_u32(params, "sprop-deint-buf-req", &u32_unused) || + rtp_get_parameter_u32(params, "sprop-init-buf-time", &u32_unused) || + rtp_get_parameter_u32(params, "sprop-max-don-diff", &u32_unused)) + { + LOG_ERROR(p_ctx, "H.264: Interleaved packetization is not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Get and check the packetization mode URI parameter. + * + * @param p_ctx The RTP container context. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_get_packetization_mode(VC_CONTAINER_T *p_ctx, + const VC_CONTAINERS_LIST_T *params) +{ + uint32_t packetization_mode; + + if (rtp_get_parameter_u32(params, "packetization-mode", &packetization_mode)) + { + /* Only modes 0 and 1 are supported, no interleaving */ + if (packetization_mode > 1) + { + LOG_ERROR(p_ctx, "H.264: Unsupported packetization mode: %u", packetization_mode); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Initialise payload bit stream for a new RTP packet. + * + * @param p_ctx The RTP container context. + * @param t_module The track module with the new RTP packet. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_new_rtp_packet(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_BITS_T *payload = &t_module->payload; + H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra; + uint8_t unit_header; + uint8_t fragment_header; + + /* Read the NAL unit type and process as necessary */ + unit_header = BITS_READ_U8(p_ctx, payload, 8, "nal_unit_header"); + + /* When the top bit is set, the NAL unit is invalid */ + if (unit_header & NAL_UNIT_FZERO_MASK) + { + LOG_DEBUG(p_ctx, "H.264: Invalid NAL unit (top bit of header set)"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* In most cases, a new packet means a new NAL unit, which will need a start code and the header */ + extra->header_bytes_to_write = 5; + extra->nal_header = unit_header; + extra->nal_unit_size = BITS_BYTES_AVAILABLE(p_ctx, payload); + + switch (unit_header & NAL_UNIT_TYPE_MASK) + { + case NAL_UNIT_STAP_A: + /* Single Time Aggregation Packet A */ + CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + /* Trigger reading NAL unit length and header */ + extra->nal_unit_size = 0; + break; + + case NAL_UNIT_FU_A: + /* Fragementation Unit A */ + fragment_header = BITS_READ_U8(p_ctx, payload, 8, "fragment_header"); + extra->nal_unit_size--; + + if (BIT_IS_CLEAR(fragment_header, FRAGMENT_UNIT_HEADER_START) || + BIT_IS_SET(extra->flags, H264F_INSIDE_FRAGMENT)) + { + /* This is a continuation packet, prevent start code and header from being output */ + extra->header_bytes_to_write = 0; + + /* If this is the end of a fragment, the next FU will be a new one */ + if (BIT_IS_SET(fragment_header, FRAGMENT_UNIT_HEADER_END)) + CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + } else { + /* Start of a new fragment. */ + SET_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + + /* Merge type from fragment header and the rest from NAL unit header to form real NAL unit header */ + fragment_header &= NAL_UNIT_TYPE_MASK; + fragment_header |= (unit_header & ~NAL_UNIT_TYPE_MASK); + extra->nal_header = fragment_header; + } + break; + + case NAL_UNIT_STAP_B: + case NAL_UNIT_MTAP16: + case NAL_UNIT_MTAP24: + case NAL_UNIT_FU_B: + LOG_ERROR(p_ctx, "H.264: Unsupported RTP NAL unit type: %u", unit_header & NAL_UNIT_TYPE_MASK); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + default: + /* Single NAL unit case */ + CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * H.264 payload handler. + * Extracts/skips data from the payload according to the NAL unit headers. + * + * @param p_ctx The RTP container context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_BITS_T *payload = &t_module->payload; + H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra; + uint32_t packet_flags = 0; + uint8_t header_bytes_to_write; + uint32_t size, offset; + uint8_t *data_ptr; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + bool last_nal_unit_in_packet = false; + + if (BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET)) + { + status = h264_new_rtp_packet(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (BIT_IS_SET(extra->flags, H264F_NEXT_PACKET_IS_START)) + { + packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + CLEAR_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); + } + + if (!extra->nal_unit_size && BITS_BYTES_AVAILABLE(p_ctx, payload)) + { + uint32_t stap_unit_header; + + /* STAP-A packet: read NAL unit size and header from payload */ + stap_unit_header = BITS_READ_U32(p_ctx, payload, 24, "STAP unit header"); + extra->nal_unit_size = stap_unit_header >> 8; + if (extra->nal_unit_size > BITS_BYTES_AVAILABLE(p_ctx, payload)) + { + LOG_ERROR(p_ctx, "H.264: STAP-A NAL unit size bigger than payload"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + extra->header_bytes_to_write = 5; + extra->nal_header = (uint8_t)stap_unit_header; + } + + header_bytes_to_write = extra->header_bytes_to_write; + size = extra->nal_unit_size + header_bytes_to_write; + + if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + { + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + /* In order to set the frame end flag correctly, need to work out if this + * is the only NAL unit or last in an aggregated packet */ + last_nal_unit_in_packet = (extra->nal_unit_size == BITS_BYTES_AVAILABLE(p_ctx, payload)); + } else { + offset = 0; + data_ptr = p_packet->data; + + if (size > p_packet->buffer_size) + { + /* Buffer not big enough */ + size = p_packet->buffer_size; + } + + /* Insert start code and header into the data stream */ + while (offset < size && header_bytes_to_write) + { + uint8_t header_byte; + + switch (header_bytes_to_write) + { + case 2: header_byte = 0x01; break; + case 1: header_byte = extra->nal_header; break; + default: header_byte = 0x00; + } + data_ptr[offset++] = header_byte; + header_bytes_to_write--; + } + extra->header_bytes_to_write = header_bytes_to_write; + + if (offset < size) + { + BITS_COPY_BYTES(p_ctx, payload, size - offset, data_ptr + offset, "Packet data"); + extra->nal_unit_size -= (size - offset); + } + + /* If we've read the final bytes of the packet, this must be the last (or only) + * NAL unit in it */ + last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload); + } + p_packet->size = size; + } else { + extra->header_bytes_to_write = 0; + BITS_SKIP_BYTES(p_ctx, payload, extra->nal_unit_size, "Packet data"); + last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload); + extra->nal_unit_size = 0; + } + + /* The marker bit on an RTP packet indicates the frame ends at the end of packet */ + if (last_nal_unit_in_packet && BIT_IS_SET(t_module->flags, TRACK_HAS_MARKER)) + { + packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + /* If this was the last packet of a frame, the next one must be the start */ + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); + } + + if (p_packet) + p_packet->flags = packet_flags; + + return status; +} + +/***************************************************************************** +Functions exported as part of the RTP parameter handler API + *****************************************************************************/ + +/**************************************************************************//** + * H.264 parameter handler. + * Parses the URI parameters to set up the track for an H.264 stream. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + H264_PAYLOAD_T *extra; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(params); + + /* See RFC3984, section 8.1, for parameter names and details. */ + extra = (H264_PAYLOAD_T *)malloc(sizeof(H264_PAYLOAD_T)); + if (!extra) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + track->priv->module->extra = extra; + memset(extra, 0, sizeof(H264_PAYLOAD_T)); + + /* Mandatory parameters */ + status = h264_get_sprop_parameter_sets(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Unsupported parameters */ + status = h264_check_unsupported_features(p_ctx, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Optional parameters */ + status = h264_get_packetization_mode(p_ctx, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + track->priv->module->payload_handler = h264_payload_handler; + SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); + + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + track->priv->module->timestamp_clock = H264_TIMESTAMP_CLOCK; + + return status; +} + diff --git a/containers/rtp/rtp_h264.h b/containers/rtp/rtp_h264.h new file mode 100755 index 0000000..ffd51da --- /dev/null +++ b/containers/rtp/rtp_h264.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_H264_H_ +#define _RTP_H264_H_ + +#include "containers/containers.h" +#include "containers/core/containers_list.h" + +/** H.264 parameter handler + * + * \param p_ctx Container context. + * \param track Track data. + * \param params Parameter list. + * \return Status of decoding the H.264 parameters. */ +VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +#endif /* _RTP_H264_H_ */ diff --git a/containers/rtp/rtp_mpeg4.c b/containers/rtp/rtp_mpeg4.c new file mode 100755 index 0000000..6a968e4 --- /dev/null +++ b/containers/rtp/rtp_mpeg4.c @@ -0,0 +1,788 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/containers.h" + +#include "containers/core/containers_logging.h" +#include "containers/core/containers_bits.h" +#include "containers/core/containers_list.h" +#include "rtp_priv.h" +#include "rtp_mpeg4.h" + +#ifdef _DEBUG +#define RTP_DEBUG 1 +#endif + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/** MPEG-4 stream types, ISO/IEC 14496-1:2010 Table 6 */ +typedef enum +{ + MPEG4_OBJECT_DESCRIPTOR_STREAM = 1, + MPEG4_CLOCK_REFERENCE_STREAM = 2, + MPEG4_SCENE_DESCRIPTION_STREAM = 3, + MPEG4_VISUAL_STREAM = 4, + MPEG4_AUDIO_STREAM = 5, + MPEG4_MPEG7_STREAM = 6, + MPEG4_IPMP_STREAM = 7, + MPEG4_OBJECT_CONTENT_INFO_STREAM = 8, + MPEG4_MPEGJ_STREAM = 9, + MPEG4_INTERACTION_STREAM = 10, + MPEG4_IPMP_TOOL_STREAM = 11, +} mp4_stream_type_t; + +/** MPEG-4 audio object types, ISO/IEC 14496-3:2009 Table 1.17 */ +typedef enum +{ + MPEG4A_AAC_MAIN = 1, + MPEG4A_AAC_LC = 2, + MPEG4A_AAC_SSR = 3, + MPEG4A_AAC_LTP = 4, + MPEG4A_SBR = 5, + MPEG4A_AAC_SCALABLE = 6, + MPEG4A_TWIN_VQ = 7, + MPEG4A_CELP = 8, + MPEG4A_HVXC = 9, + MPEG4A_TTSI = 12, + MPEG4A_MAIN_SYNTHETIC = 13, + MPEG4A_WAVETABLE_SYNTHESIS = 14, + MPEG4A_GENERAL_MIDI = 15, + MPEG4A_ALGORITHMIC_SYNTHESIS = 16, + MPEG4A_ER_AAC_LC = 17, + MPEG4A_ER_AAC_LTP = 19, + MPEG4A_ER_AAC_SCALABLE = 20, + MPEG4A_ER_TWIN_VQ = 21, + MPEG4A_ER_BSAC = 22, + MPEG4A_ER_AAC_LD = 23, + MPEG4A_ER_CELP = 24, + MPEG4A_ER_HVXC = 25, + MPEG4A_ER_HILN = 26, + MPEG4A_ER_PARAMETERIC = 27, + MPEG4A_SSC = 28, + MPEG4A_PS = 29, + MPEG4A_MPEG_SURROUND = 30, + MPEG4A_LAYER_1 = 32, + MPEG4A_LAYER_2 = 33, + MPEG4A_LAYER_3 = 34, + MPEG4A_DST = 35, + MPEG4A_ALS = 36, + MPEG4A_SLS = 37, + MPEG4A_SLS_NON_CORE = 38, + MPEG4A_ER_AAC_ELD = 39, + MPEG4A_SMR_SIMPLE = 40, + MPEG4A_SMR_MAIN = 41, +} mp4_audio_object_type_t; + +/** RTP MPEG-4 modes */ +typedef enum +{ + MP4_GENERIC_MODE = 0, + MP4_CELP_CBR_MODE, + MP4_CELP_VBR_MODE, + MP4_AAC_LBR_MODE, + MP4_AAC_HBR_MODE +} mp4_mode_t; + +typedef struct mp4_mode_detail_tag +{ + const char *name; + mp4_mode_t mode; +} MP4_MODE_ENTRY_T; + +/* RTP MPEG-4 mode look-up table. + * Note: case-insensitive sort by name */ +static MP4_MODE_ENTRY_T mp4_mode_array[] = { + { "aac-hbr", MP4_AAC_HBR_MODE }, + { "aac-lbr", MP4_AAC_LBR_MODE }, + { "celp-cbr", MP4_CELP_CBR_MODE }, + { "celp-vbr", MP4_CELP_VBR_MODE }, + { "generic", MP4_GENERIC_MODE }, +}; + +static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b); + +VC_CONTAINERS_STATIC_LIST(mp4_mode_lookup, mp4_mode_array, mp4_mode_comparator); + +typedef struct au_info_tag +{ + uint32_t available; + uint32_t index; + int32_t cts_delta; + int32_t dts_delta; +} AU_INFO_T; + +typedef struct mp4_payload_tag +{ + mp4_stream_type_t stream_type; + uint32_t profile_level_id; + mp4_mode_t mode; + uint32_t size_length; + uint32_t index_length; + uint32_t index_delta_length; + uint32_t cts_delta_length; + uint32_t dts_delta_length; + uint32_t object_type; + uint32_t constant_size; + uint32_t constant_duration; + uint32_t auxiliary_length; + VC_CONTAINER_BITS_T au_headers; + AU_INFO_T au_info; +} MP4_PAYLOAD_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Convert a hexadecimal character to a value between 0 and 15. + * Upper and lower case characters are supported. An invalid chacter return zero. + * + * @param hex The character to convert. + * @return The value of the character. + */ +static uint8_t hex_to_nybble(char hex) +{ + if (hex >= '0' && hex <= '9') + return hex - '0'; + if (hex >= 'A' && hex <= 'F') + return hex - 'A' + 10; + if (hex >= 'a' && hex <= 'f') + return hex - 'a' + 10; + return 0; /* Illegal character (not hex) */ +} + +/**************************************************************************//** + * Convert a sequence of hexadecimal characters to consecutive entries in a + * byte array. + * The string must contain at least twice as many characters as the number of + * bytes to convert. + * + * @param hex The hexadecimal string. + * @param buffer The buffer into which bytes are to be stored. + * @param bytes_to_convert The number of bytes in the array to be filled. + */ +static void hex_to_byte_buffer(const char *hex, + uint8_t *buffer, + uint32_t bytes_to_convert) +{ + uint8_t value; + + while (bytes_to_convert--) + { + value = hex_to_nybble(*hex++) << 4; + value |= hex_to_nybble(*hex++); + *buffer++ = value; + } +} + +/**************************************************************************//** + * Retrieves and checks the stream type in the URI parameters. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_get_stream_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + uint32_t stream_type; + VC_CONTAINER_ES_TYPE_T expected_es_type; + + if (!rtp_get_parameter_u32(params, "streamType", &stream_type)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + switch (stream_type) + { + case MPEG4_AUDIO_STREAM: + extra->stream_type = MPEG4_AUDIO_STREAM; + expected_es_type = VC_CONTAINER_ES_TYPE_AUDIO; + break; + default: + LOG_ERROR(p_ctx, "Unsupported MPEG-4 stream type: %u", stream_type); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + if (track->format->es_type != expected_es_type) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Decode and store audio configuration information from an MP4 audio + * configuration bit stream. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param bit_stream The bit stream containing the audio configuration. + * @return True if the configuration was decoded successfully, false otherwise. + */ +static bool mp4_decode_audio_config(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_BITS_T *bit_stream) +{ + static uint32_t mp4_audio_sample_rate[] = + { 96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350, 0, 0 }; + + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + uint32_t audio_object_type; + uint32_t sampling_frequency_index; + uint32_t channel_configuration; + + audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 5, "audioObjectType"); + if (audio_object_type == 31) + audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 6, "audioObjectTypeExt") + 32; + + sampling_frequency_index = BITS_READ_U32(p_ctx, bit_stream, 4, "samplingFrequencyIndex"); + if (sampling_frequency_index == 0xF) + audio->sample_rate = BITS_READ_U32(p_ctx, bit_stream, 24, "samplingFrequency"); + else + audio->sample_rate = mp4_audio_sample_rate[sampling_frequency_index]; + if (!audio->sample_rate) return false; + + track->priv->module->timestamp_clock = audio->sample_rate; + + channel_configuration = BITS_READ_U32(p_ctx, bit_stream, 4, "channelConfiguration"); + switch (channel_configuration) + { + case 1: /* 1 channel, centre front */ + case 2: /* 2 channel, stereo front */ + case 3: /* 3 channel, centre and stereo front */ + case 4: /* 4 channel, centre and stereo front, mono surround */ + case 5: /* 5 channel, centre and stereo front, stereo surround */ + case 6: /* 5.1 channel, centre and stereo front, stereo surround, low freq */ + audio->channels = channel_configuration; + break; + case 7: /* 7.1 channel, centre, stereo and stereo outside front, stereo surround, low freq */ + audio->channels = channel_configuration + 1; + break; + default: + LOG_DEBUG(p_ctx, "MPEG-4: Unsupported channel configuration (%u)", channel_configuration); + return false; + } + + switch (audio_object_type) + { + case MPEG4A_AAC_LC: + { + uint32_t ga_specific_config = BITS_READ_U32(p_ctx, bit_stream, 3, "GASpecificConfig"); + + /* Make sure there are no unexpected (and unsupported) additional configuration elements */ + if (ga_specific_config != 0) + { + LOG_DEBUG(p_ctx, "MPEG-4: Unexpected additional configuration data (%u)", ga_specific_config); + return false; + } + } + break; + /* Add any further supported codecs here */ + default: + LOG_DEBUG(p_ctx, "MPEG-4: Unsupported Audio Object Type (%u)", audio_object_type); + return false; + } + + return true; +} + +/**************************************************************************//** + * Get, store and decode the configuration information from the URI parameters. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_get_config(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + PARAMETER_T param; + uint32_t config_len; + VC_CONTAINER_STATUS_T status; + uint8_t *config; + VC_CONTAINER_BITS_T bit_stream; + + param.name = "config"; + if (!vc_containers_list_find_entry(params, ¶m) || !param.value) + { + LOG_ERROR(p_ctx, "MPEG-4: config parameter missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + config_len = strlen(param.value); + if (config_len & 1) + { + LOG_ERROR(p_ctx, "MPEG-4: config parameter invalid"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + config_len /= 2; + + /* Copy AudioSpecificConfig into track extradata, to be decoded by client */ + status = vc_container_track_allocate_extradata(p_ctx, track, config_len); + if(status != VC_CONTAINER_SUCCESS) return status; + + config = track->priv->extradata; + track->format->extradata_size = config_len; + hex_to_byte_buffer(param.value, config, config_len); + + /* Decode config locally, to determine sample rate, etc. */ + BITS_INIT(p_ctx, &bit_stream, config, config_len); + + switch (extra->stream_type) + { + case MPEG4_AUDIO_STREAM: + if (!mp4_decode_audio_config(p_ctx, track, &bit_stream)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + break; + default: + /* Other stream types not yet supported */ + LOG_ERROR(p_ctx, "MPEG-4: stream type %d not supported", extra->stream_type); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * MP4 mode comparison function. + * Compare two MP4 mode structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b) +{ + return strcasecmp(a->name, b->name); +} + +/**************************************************************************//** + * Get and store the MP4 mode, if recognised, from the URI parameters. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_get_mode(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + PARAMETER_T param; + MP4_MODE_ENTRY_T mode_entry; + + param.name = "mode"; + if (!vc_containers_list_find_entry(params, ¶m) || !param.value) + { + LOG_ERROR(p_ctx, "MPEG-4: mode parameter missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + +#ifdef RTP_DEBUG + vc_containers_list_validate(&mp4_mode_lookup); +#endif + + mode_entry.name = param.value; + if (!vc_containers_list_find_entry(&mp4_mode_lookup, &mode_entry)) + { + LOG_ERROR(p_ctx, "MPEG-4: Unrecognised mode parameter \"%s\"", mode_entry.name); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + extra->mode = mode_entry.mode; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Check URI parameters for unsupported features. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_check_unsupported_features(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + uint32_t u32_unused; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(track); + + /* Limitation: RAP flag not yet supported */ + if (rtp_get_parameter_u32(params, "randomAccessIndication", &u32_unused)) + { + LOG_ERROR(p_ctx, "MPEG-4: random access not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* Limitation: interleaving not yet supported */ + if (rtp_get_parameter_u32(params, "maxDisplacement", &u32_unused) || + rtp_get_parameter_u32(params, "de-interleaveBufferSize", &u32_unused)) + { + LOG_ERROR(p_ctx, "MPEG-4: interleaved packetization not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* Limitation: system streams not supported */ + if (rtp_get_parameter_u32(params, "streamStateIndication", &u32_unused)) + { + LOG_ERROR(p_ctx, "MPEG-4: system streams not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Validate parameters that have been read form the URI parameter list. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_check_parameters(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + + switch (extra->mode) + { + case MP4_CELP_CBR_MODE: + if (!extra->constant_size) + { + LOG_ERROR(p_ctx, "MPEG-4: CELP-cbr requires constantSize parameter."); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + break; + case MP4_CELP_VBR_MODE: + case MP4_AAC_LBR_MODE: + if (extra->size_length != 6 || extra->index_length != 2 || extra->index_delta_length != 2) + { + LOG_ERROR(p_ctx, "MPEG-4: CELP-vbr/AAC-lbr invalid lengths (%u/%u/%u)", + extra->size_length, extra->index_length, extra->index_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + break; + case MP4_AAC_HBR_MODE: + if (extra->size_length != 13 || extra->index_length != 3 || extra->index_delta_length != 3) + { + LOG_ERROR(p_ctx, "MPEG-4: AAC-hbr invalid lengths (%u/%u/%u)", + extra->size_length, extra->index_length, extra->index_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + break; + default: /* MP4_GENERIC_MODE */ + if (extra->size_length > 32 || extra->index_length > 32 || extra->index_delta_length > 32) + { + LOG_ERROR(p_ctx, "MPEG-4: generic invalid lengths (%u/%u/%u)", + extra->size_length, extra->index_length, extra->index_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + } + + if (extra->cts_delta_length > 32 || extra->dts_delta_length > 32) + { + LOG_ERROR(p_ctx, "MPEG-4: CTS/DTS invalid lengths (%u/%u)", + extra->cts_delta_length, extra->dts_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Initialise payload bit stream for a new RTP packet. + * + * @param p_ctx The RTP container context. + * @param t_module The track module with the new RTP packet. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_new_rtp_packet(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_BITS_T *payload = &t_module->payload; + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra; + VC_CONTAINER_BITS_T *au_headers = &extra->au_headers; + + /* There will be an AU header section if any of its fields are non-zero. */ + if (extra->size_length || extra->index_length || extra->cts_delta_length || extra->dts_delta_length) + { + uint32_t au_headers_length; + + /* Calculate how far to advance the payload, to get past the AU headers */ + au_headers_length = BITS_READ_U32(p_ctx, payload, 16, "AU headers length"); + au_headers_length = BITS_TO_BYTES(au_headers_length); /* Round up to bytes */ + + /* Record where the AU headers are in the payload */ + BITS_INIT(p_ctx, au_headers, BITS_CURRENT_POINTER(p_ctx, payload), au_headers_length); + BITS_SKIP_BYTES(p_ctx, &t_module->payload, au_headers_length, "Move payload past AU headers"); + } + + /* Skip the auxiliary section, if present */ + if (extra->auxiliary_length) + { + uint32_t auxiliary_data_size; + + auxiliary_data_size = BITS_READ_U32(p_ctx, payload, extra->auxiliary_length, "Auxiliary length"); + auxiliary_data_size = BITS_TO_BYTES(auxiliary_data_size); /* Round up to bytes */ + BITS_SKIP_BYTES(p_ctx, payload, auxiliary_data_size, "Auxiliary data"); + } + + return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/**************************************************************************//** + * Read a flagged delta from an AU header bit stream. + * A flagged delta is an optional value in the stream that is preceded by a + * flag bit that indicates whether the value is present in the stream. If the + * length of the value is zero bits, the flag is never present. + * + * @pre The delta_length must be 32 or less. + * + * @param p_ctx The container context. + * @param au_headers The AU header bit stream. + * @param delta_length The number of bits in the delta value. + * @return The delta value, or zero if not present. + */ +static int32_t mp4_flagged_delta(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_BITS_T *au_headers, + uint32_t delta_length) +{ + uint32_t value = 0; + + /* Flag is only present if the delta length is non-zero */ + if (delta_length && BITS_READ_U32(p_ctx, au_headers, 1, "CTS/DTS delta present")) + { + value = BITS_READ_U32(p_ctx, au_headers, delta_length, "CTS/DTS delta"); + + /* Sign extend value based on bit length */ + if (value & (1 << (delta_length - 1))) + value |= ~((1 << delta_length) - 1); + } + + return (int32_t)value; +} + +/**************************************************************************//** + * Read next AU header from the bit stream. + * + * @param p_ctx The RTP container context. + * @param extra The MP4-specific track module information. + * @param is_first_au True if the first AU header in the packet is being read. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_next_au_header(VC_CONTAINER_T *p_ctx, + MP4_PAYLOAD_T *extra, + bool is_first_au) +{ + VC_CONTAINER_BITS_T *au_headers = &extra->au_headers; + AU_INFO_T *au_info = &extra->au_info; + + /* See RFC3550 section 3.2.1.1 */ + + if (extra->constant_size) + au_info->available = extra->constant_size; + else + au_info->available = BITS_READ_U32(p_ctx, au_headers, extra->size_length, "AU size"); + + if (is_first_au) + au_info->index = BITS_READ_U32(p_ctx, au_headers, extra->index_length, "AU index"); + else + au_info->index += BITS_READ_U32(p_ctx, au_headers, extra->index_delta_length, "AU index delta") + 1; + + au_info->cts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->cts_delta_length); + au_info->dts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->dts_delta_length); + + /* RAP and stream state not supported yet */ + + return BITS_VALID(p_ctx, au_headers) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/**************************************************************************//** + * MP4 payload handler. + * Extracts/skips data from the payload according to the AU headers. + * + * @param p_ctx The RTP container context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_BITS_T *payload = &t_module->payload; + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra; + AU_INFO_T *au_info = &extra->au_info; + bool is_new_packet = BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET); + uint32_t bytes_left_in_payload; + uint32_t size; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (is_new_packet) + { + status = mp4_new_rtp_packet(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (!au_info->available) + { + status = mp4_next_au_header(p_ctx, extra, is_new_packet); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (p_packet) + { + /* Adjust the packet time stamps using deltas */ + p_packet->pts += au_info->cts_delta; + p_packet->dts += au_info->dts_delta; + } + + size = au_info->available; + bytes_left_in_payload = BITS_BYTES_AVAILABLE(p_ctx, payload); + if (size > bytes_left_in_payload) + { + /* AU is fragmented across RTP packets */ + size = bytes_left_in_payload; + } + + if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + { + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + { + if (size > p_packet->buffer_size) + size = p_packet->buffer_size; + + BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); + } + p_packet->size = size; + } else { + BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); + } + + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + au_info->available -= size; + + return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/***************************************************************************** +Functions exported as part of the RTP parameter handler API + *****************************************************************************/ + +/**************************************************************************//** + * MP4 parameter handler. + * Parses the URI parameters to set up the track for an MP4 stream. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra; + VC_CONTAINER_STATUS_T status; + + /* See RFC3640, section 4.1, for parameter names and details. */ + extra = (MP4_PAYLOAD_T *)malloc(sizeof(MP4_PAYLOAD_T)); + if (!extra) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + track->priv->module->extra = extra; + memset(extra, 0, sizeof(MP4_PAYLOAD_T)); + + /* Mandatory parameters */ + status = mp4_get_stream_type(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_get_config(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_get_mode(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Unsupported parameters */ + status = mp4_check_unsupported_features(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Optional parameters */ + rtp_get_parameter_u32(params, "sizeLength", &extra->size_length); + rtp_get_parameter_u32(params, "indexLength", &extra->index_length); + rtp_get_parameter_u32(params, "indexDeltaLength", &extra->index_delta_length); + rtp_get_parameter_u32(params, "CTSDeltaLength", &extra->cts_delta_length); + rtp_get_parameter_u32(params, "DTSDeltaLength", &extra->dts_delta_length); + rtp_get_parameter_u32(params, "objectType", &extra->object_type); + rtp_get_parameter_u32(params, "constantSize", &extra->constant_size); + rtp_get_parameter_u32(params, "constantDuration", &extra->constant_duration); + rtp_get_parameter_u32(params, "auxiliaryDataSizeLength", &extra->auxiliary_length); + + if (extra->constant_size && extra->size_length) + { + LOG_ERROR(p_ctx, "MPEG4: constantSize and sizeLength cannot both be set."); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + status = mp4_check_parameters(p_ctx, track); + if (status != VC_CONTAINER_SUCCESS) return status; + + track->priv->module->payload_handler = mp4_payload_handler; + + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/rtp/rtp_mpeg4.h b/containers/rtp/rtp_mpeg4.h new file mode 100755 index 0000000..99181bb --- /dev/null +++ b/containers/rtp/rtp_mpeg4.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_MPEG4_H_ +#define _RTP_MPEG4_H_ + +#include "containers/containers.h" +#include "containers/core/containers_list.h" + +/** MPEG-4 parameter handler + * + * \param p_ctx Container context. + * \param track Track data. + * \param params Parameter list. + * \return Status of decoding the MPEG-4 parameters. */ +VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +#endif /* _RTP_MPEG4_H_ */ diff --git a/containers/rtp/rtp_priv.h b/containers/rtp/rtp_priv.h new file mode 100755 index 0000000..c54bfe8 --- /dev/null +++ b/containers/rtp/rtp_priv.h @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_PRIV_H_ +#define _RTP_PRIV_H_ + +#include "containers/containers.h" + +#include "containers/core/containers_private.h" +#include "containers/core/containers_bits.h" +#include "containers/core/containers_list.h" + +typedef VC_CONTAINER_STATUS_T (*PAYLOAD_HANDLER_T)(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); + +/** Parameter list entry type. */ +typedef struct parameter_tag +{ + const char *name; + const char *value; +} PARAMETER_T; + +/** Prototype for MIME parameter handling. + * Each MIME type has a certain set of parameter names it uses, so a handler is + * needed for each type. This is that handler's prototype. + */ +typedef VC_CONTAINER_STATUS_T (*PARAMETER_HANDLER_T)(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +/** Track module flag bit numbers (up to seven) */ +typedef enum +{ + TRACK_SSRC_SET = 0, + TRACK_HAS_MARKER, + TRACK_NEW_PACKET, +} track_module_flag_bit_t; + +/** RTP track data */ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + PAYLOAD_HANDLER_T payload_handler; /**< Extracts the data from the payload */ + uint8_t *buffer; /**< Buffer into which the RTP packet is read */ + VC_CONTAINER_BITS_T payload; /**< Payload bit bit_stream */ + uint8_t flags; /**< Combination of track module flags */ + uint8_t payload_type; /**< The expected payload type */ + uint16_t max_seq_num; /**< Highest seq. number seen */ + uint32_t timestamp; /**< RTP timestamp of packet */ + uint32_t timestamp_base; /**< RTP timestamp value that equates to time zero */ + uint32_t last_timestamp_top; /**< Top two bits of RTP timestamp of previous packet */ + uint32_t timestamp_wraps; /**< Count of the times that the timestamp has wrapped */ + uint32_t timestamp_clock; /**< Clock frequency of RTP timestamp values */ + uint32_t expected_ssrc; /**< The expected SSRC, if set */ + uint32_t base_seq; /**< Base seq number */ + uint32_t bad_seq; /**< Last 'bad' seq number + 1 */ + uint32_t probation; /**< Sequential packets till source is valid */ + uint32_t received; /**< RTP packets received */ + void *extra; /**< Payload specific data */ +} VC_CONTAINER_TRACK_MODULE_T; + +/** Determine minimum number of bytes needed to hold a number of bits */ +#define BITS_TO_BYTES(X) (((X) + 7) >> 3) + +/** Collection of bit manipulation routines */ +/* @{ */ +#define SET_BIT(V, B) V |= (1 << (B)) +#define CLEAR_BIT(V, B) V &= ~(1 << (B)) +#define BIT_IS_SET(V, B) (!(!((V) & (1 << (B))))) +#define BIT_IS_CLEAR(V, B) (!((V) & (1 << (B)))) +/* }@ */ + + +/** Get a parameter's value as a decimal number. + * + * \param param_list The list of parameter name/value pairs. + * \param name The paramter's name. + * \param value Where to put the converted value. + * \return True if successful, false if the parameter was not found or didn't convert. */ +bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value); + +/** Get a parameter's value as a hexadecimal number. + * + * \param param_list The list of parameter name/value pairs. + * \param name The paramter's name. + * \param value Where to put the converted value. + * \return True if successful, false if the parameter was not found or didn't convert. */ +bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value); + +#endif /* _RTP_PRIV_H_ */ diff --git a/containers/rtp/rtp_reader.c b/containers/rtp/rtp_reader.c new file mode 100755 index 0000000..9ce4a59 --- /dev/null +++ b/containers/rtp/rtp_reader.c @@ -0,0 +1,1158 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_uri.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_bits.h" +#include "containers/core/containers_list.h" + +#include "rtp_priv.h" +#include "rtp_mpeg4.h" +#include "rtp_h264.h" + +#ifdef _DEBUG +/* Validates static sorted lists are correctly constructed */ +#define RTP_DEBUG 1 +#endif + +/****************************************************************************** +Configurable defines and constants. +******************************************************************************/ + +/** Maximum size of an RTP packet */ +#define MAXIMUM_PACKET_SIZE 2048 + +/** Maximum number of RTP packets that can be missed without restarting. */ +#define MAX_DROPOUT 3000 +/** Maximum number of out of sequence RTP packets that are accepted. */ +#define MAX_MISORDER 0 +/** Minimum number of sequential packets required for an acceptable connection + * when restarting. */ +#define MIN_SEQUENTIAL 2 + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define RTP_SCHEME "rtp:" + +/** The RTP PKT scheme is used with test pkt files */ +#define RTP_PKT_SCHEME "rtppkt:" + +/** \name RTP URI parameter names + * @{ */ +#define PAYLOAD_TYPE_NAME "rtppt" +#define MIME_TYPE_NAME "mime-type" +#define CHANNELS_NAME "channels" +#define RATE_NAME "rate" +#define SSRC_NAME "ssrc" +#define SEQ_NAME "seq" +/* @} */ + +/** A sentinel codec that is not supported */ +#define UNSUPPORTED_CODEC VC_FOURCC(0,0,0,0) + +/** Evaluates to true if the given payload type is in the supported static audio range. */ +#define IS_STATIC_AUDIO_TYPE(PT) ((PT) < countof(static_audio_payload_types)) + +/** Payload type number for the first static video type */ +#define FIRST_STATIC_VIDEO_TYPE 24 +/** Evaluates to true if the given payload type is in the supported static video range. */ +#define IS_STATIC_VIDEO_TYPE(PT) ((PT) >= FIRST_STATIC_VIDEO_TYPE && \ + (PT) < (FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types))) + +/** Evaluates to true if the given payload type is in the dynamic range. */ +#define IS_DYNAMIC_TYPE(PT) ((PT) >= 96 && (PT) < 128) + +/** All sequence numbers are modulo this value. */ +#define RTP_SEQ_MOD (1 << 16) + +/** All the static video payload types use a 90kHz timestamp clock */ +#define STATIC_VIDEO_TIMESTAMP_CLOCK 90000 + +/** Number of microseconds in a second, used to convert RTP timestamps to microseconds */ +#define MICROSECONDS_PER_SECOND 1000000 + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/** \name MIME type parameter handlers + * Function prototypes for payload parameter handlers */ +/* @{ */ +static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); +static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); +static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); +/* @} */ + +/** \name MIME type payload handlers */ +/* @{ */ +static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); +/* @} */ + +/** Static audio payload type data */ +typedef struct audio_payload_type_data_tag +{ + VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ + uint32_t channels; /**< Number of audio channels */ + uint32_t sample_rate; /**< Sample rate */ + uint32_t bits_per_sample; /**< Bits per sample, or 1 if not applicable */ + PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ + PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ +} AUDIO_PAYLOAD_TYPE_DATA_T; + +/** The details for the statically defined audio payload types from RFC3551 */ +static AUDIO_PAYLOAD_TYPE_DATA_T static_audio_payload_types[] = +{ + { VC_CONTAINER_CODEC_MULAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 0 - PCMU */ + { UNSUPPORTED_CODEC }, /* 1 - reserved */ + { UNSUPPORTED_CODEC }, /* 2 - reserved */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 3 - GSM */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 4 - G723 */ + { UNSUPPORTED_CODEC, 1, 8000, 4, NULL, NULL }, /* 5 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 16000, 4, NULL, NULL }, /* 6 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 7 - LPC */ + { VC_CONTAINER_CODEC_ALAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 8 - PCMA */ + { UNSUPPORTED_CODEC, 1, 8000, 8, NULL, NULL }, /* 9 - G722 */ + { VC_CONTAINER_CODEC_PCM_SIGNED, 2, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 10 - L16 */ + { VC_CONTAINER_CODEC_PCM_SIGNED, 1, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 11 - L16 */ + { VC_CONTAINER_CODEC_QCELP, 1, 8000, 16, NULL, NULL }, /* 12 - QCELP */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 13 - CN */ + { VC_CONTAINER_CODEC_MPGA, 1, 90000, 1, NULL, NULL }, /* 14 - MPA */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 15 - G728 */ + { UNSUPPORTED_CODEC, 1, 11025, 4, NULL, NULL }, /* 16 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 22050, 4, NULL, NULL }, /* 17 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 18 - G729 */ +}; + +/** Static video payload type data */ +typedef struct video_payload_type_data_tag +{ + VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ + PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ + PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ +} VIDEO_PAYLOAD_TYPE_DATA_T; + +/** The details for the statically defined video payload types from RFC3551 */ +static VIDEO_PAYLOAD_TYPE_DATA_T static_video_payload_types[] = +{ + { UNSUPPORTED_CODEC }, /* 24 - unassigned */ + { UNSUPPORTED_CODEC }, /* 25 - CelB */ + { UNSUPPORTED_CODEC }, /* 26 - JPEG */ + { UNSUPPORTED_CODEC }, /* 27 - unassigned */ + { UNSUPPORTED_CODEC }, /* 28 - nv */ + { UNSUPPORTED_CODEC }, /* 29 - unassigned */ + { UNSUPPORTED_CODEC }, /* 30 - unassigned */ + { UNSUPPORTED_CODEC }, /* 31 - H261 */ + { VC_CONTAINER_CODEC_MP2V, NULL, NULL }, /* 32 - MPV */ + { UNSUPPORTED_CODEC }, /* 33 - MP2T */ + { VC_CONTAINER_CODEC_H263, NULL, NULL } /* 34 - H263 */ +}; + +/** MIME type details */ +typedef struct mime_type_data_tag +{ + const char *name; /**< Name of MIME type */ + VC_CONTAINER_ES_TYPE_T es_type; /**< Elementary stream type */ + VC_CONTAINER_FOURCC_T codec; /**< Codec to be used */ + PARAMETER_HANDLER_T param_handler; /**< Parameter handler for this MIME type */ +} MIME_TYPE_DATA_T; + +/** Comparator for MIME type details. */ +static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b); + +/** Dynamic audio payload details + * Note: case-insensitive sort by name */ +static MIME_TYPE_DATA_T dynamic_mime_details[] = { + { "audio/l16", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l16_parameter_handler }, + { "audio/l8", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l8_parameter_handler }, + { "audio/mpeg4-generic", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A, mp4_parameter_handler }, + { "video/h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264, h264_parameter_handler }, + { "video/mpeg4-generic", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V, mp4_parameter_handler }, +}; + +/** Sorted list of dynamic MIME type details */ +VC_CONTAINERS_STATIC_LIST(dynamic_mime, dynamic_mime_details, mime_type_data_comparator); + +/** RTP reader data. */ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Parameter comparison function. + * Compare two parameter structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int parameter_comparator(const PARAMETER_T *first, const PARAMETER_T *second) +{ + return strcasecmp(first->name, second->name); +} + +/**************************************************************************//** + * Creates and populates a parameter list from a URI structure. + * The list does not copy the parameter strings themselves, so the URI structure + * must be retained (and its parameters unmodified) while the list is in use. + * + * @param uri The URI containing the parameters. + * @return List created from the parameters of the URI, or NULL on error. + */ +static VC_CONTAINERS_LIST_T *fill_parameter_list(VC_URI_PARTS_T *uri) +{ + uint32_t num_parameters = vc_uri_num_queries(uri); + VC_CONTAINERS_LIST_T *parameters; + uint32_t ii; + + parameters = vc_containers_list_create(num_parameters, sizeof(PARAMETER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)parameter_comparator); + if (!parameters) + return NULL; + + for (ii = 0; ii < num_parameters; ii++) + { + PARAMETER_T param; + + vc_uri_query(uri, ii, ¶m.name, ¶m.value); + if (!vc_containers_list_insert(parameters, ¶m, false)) + { + vc_containers_list_destroy(parameters); + return NULL; + } + } + +#ifdef RTP_DEBUG + vc_containers_list_validate(parameters); +#endif + + return parameters; +} + +/**************************************************************************//** + * Decodes a static audio payload type into track information. + * The static parameters may be overridden by URI parameters. + * + * @param p_ctx The reader context. + * @param track The track to be populated. + * @param param_list The URI parameter list. + * @param payload_type The static payload type. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_static_audio_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list, + uint32_t payload_type) +{ + VC_CONTAINER_ES_FORMAT_T *format = track->format; + AUDIO_PAYLOAD_TYPE_DATA_T *data = &static_audio_payload_types[payload_type]; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(param_list); + + vc_container_assert(payload_type < countof(static_audio_payload_types)); + + if (data->codec == UNSUPPORTED_CODEC) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + format->codec = data->codec; + format->type->audio.channels = data->channels; + format->type->audio.sample_rate = data->sample_rate; + format->type->audio.bits_per_sample = data->bits_per_sample; + format->type->audio.block_align = data->channels * BITS_TO_BYTES(data->bits_per_sample); + track->priv->module->timestamp_clock = format->type->audio.sample_rate; + track->priv->module->payload_handler = data->payload_handler; + + if (data->param_handler) + data->param_handler(p_ctx, track, param_list); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Decodes a static video payload type into track information. + * The static parameters may be overridden by URI parameters. + * + * @param p_ctx The reader context. + * @param track The track to be populated. + * @param param_list The URI parameter list. + * @param payload_type The static payload type. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_static_video_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list, + uint32_t payload_type) +{ + VC_CONTAINER_ES_FORMAT_T *format = track->format; + VIDEO_PAYLOAD_TYPE_DATA_T *data = &static_video_payload_types[payload_type - FIRST_STATIC_VIDEO_TYPE]; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(param_list); + + vc_container_assert(payload_type >= FIRST_STATIC_VIDEO_TYPE); + vc_container_assert(payload_type < FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types)); + + if (data->codec == UNSUPPORTED_CODEC) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + format->codec = data->codec; + track->priv->module->timestamp_clock = STATIC_VIDEO_TIMESTAMP_CLOCK; + track->priv->module->payload_handler = data->payload_handler; + + if (data->param_handler) + data->param_handler(p_ctx, track, param_list); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Compare two MIME type structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param a The first parameter structure to be compared. + * @param b The second parameter structure to be compared. + * @return Negative if a is less than b, positive if a is greater and zero if + * they are equal. + */ +static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b) +{ + return strcasecmp(a->name, b->name); +} + +/**************************************************************************//** + * Generic audio parameter handler. + * Updates the track information with generic audio parameters, such as "rate" + * and "channels". + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + /* See RFC3555. Generic audio parameters that can override static payload + * type defaults. */ + if (rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) + track->priv->module->timestamp_clock = audio->sample_rate; + if (rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) + audio->block_align = audio->channels * BITS_TO_BYTES(audio->bits_per_sample); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * L8 specific audio parameter handler. + * Updates the track information with audio parameters needed by the audio/L8 + * MIME type. For example, the "rate" parameter is mandatory. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + /* See RFC3555, section 4.1.14, for parameter names and details. */ + if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) + audio->channels = 1; + audio->bits_per_sample = 8; + audio->block_align = audio->channels; + track->priv->module->timestamp_clock = audio->sample_rate; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * L16 specific audio parameter handler. + * Updates the track information with audio parameters needed by the audio/L16 + * MIME type. For example, the "rate" parameter is mandatory. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + /* See RFC3555, section 4.1.15, for parameter names and details. */ + if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) + audio->channels = 1; + audio->bits_per_sample = 16; + audio->block_align = audio->channels * 2; + track->priv->module->timestamp_clock = audio->sample_rate; + track->priv->module->payload_handler = l16_payload_handler; + + /* TODO: add support for "channel-order" to set channel mapping */ + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Decode a dynamic payload type from parameters. + * Populates the track information with data for supported dynamic media types. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param param_list The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_dynamic_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list) +{ + VC_CONTAINER_ES_FORMAT_T *format = track->format; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + PARAMETER_T mime_type; + MIME_TYPE_DATA_T mime_details; + + /* Get MIME type parameter */ + mime_type.name = MIME_TYPE_NAME; + if (!vc_containers_list_find_entry(param_list, &mime_type)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + +#ifdef RTP_DEBUG + vc_containers_list_validate(&dynamic_mime); +#endif + + /* Look up MIME type to see if it can be handled */ + mime_details.name = mime_type.value; + if (!vc_containers_list_find_entry(&dynamic_mime, &mime_details)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + format->codec = mime_details.codec; + format->es_type = mime_details.es_type; + + /* Default number of channels for audio is one */ + if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO) + format->type->audio.channels = 1; + + /* Lete MIME type specific parameter handler deal with any other parameters */ + status = mime_details.param_handler(p_ctx, track, param_list); + + /* Ensure that the sample rate has been set for audio formats */ + if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO && !format->type->audio.sample_rate) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + return status; +} + +/**************************************************************************//** + * Decode the RTP payload type. + * Populates track information with data from static tables and the URI + * parameter list, according to the payload and MIME types. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_payload_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list, + uint32_t payload_type) +{ + VC_CONTAINER_TRACK_MODULE_T *module = track->priv->module; + VC_CONTAINER_STATUS_T status; + + if (IS_STATIC_AUDIO_TYPE(payload_type)) + status = decode_static_audio_type(p_ctx, track, param_list, payload_type); + else if (IS_STATIC_VIDEO_TYPE(payload_type)) + status = decode_static_video_type(p_ctx, track, param_list, payload_type); + else if (IS_DYNAMIC_TYPE(payload_type)) + status = decode_dynamic_type(p_ctx, track, param_list); + else + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + module->payload_type = (uint8_t)payload_type; + + return status; +} + +/**************************************************************************//** + * Initialises the RTP sequence number algorithm with a new sequence number. + * + * @param t_module The track module. + * @param seq The new sequence number. + */ +static void init_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, + uint16_t seq) +{ + t_module->base_seq = seq; + t_module->max_seq_num = seq; + t_module->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ + t_module->received = 0; +} + +/**************************************************************************//** + * Checks whether the sequence number for a packet is acceptable or not. + * The packet will be unacceptable if it is out of sequence by some degree, or + * if the packet sequence is still being established. + * + * @param t_module The track module. + * @param seq The new sequence number. + * @return True if the sequence number indicates the packet is acceptable + */ +static bool update_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, + uint16_t seq) +{ + uint16_t udelta = seq - t_module->max_seq_num; + + /* NOTE: This source is derived from the example code in RFC3550, section A.1 */ + + /* Source is not valid until MIN_SEQUENTIAL packets with + * sequential sequence numbers have been received. */ + if (t_module->probation) + { + /* packet is in sequence */ + if (seq == t_module->max_seq_num + 1) + { + t_module->probation--; + t_module->max_seq_num = seq; + LOG_INFO(0, "RTP: Probation, %u more packet(s) to go at 0x%4.4hx", t_module->probation, seq); + + if (!t_module->probation) + { + init_sequence_number(t_module, seq); + t_module->received++; + return 1; + } + } else { + t_module->probation = MIN_SEQUENTIAL - 1; + t_module->max_seq_num = seq; + LOG_INFO(0, "RTP: Probation reset, wait for %u packet(s) at 0x%4.4hx", t_module->probation, seq); + } + return 0; + } else if (udelta < MAX_DROPOUT) + { + if (!udelta) + { + /* Duplicate packet, drop it */ + LOG_INFO(0, "RTP: Drop duplicate packet at 0x%4.4hx", seq); + return 0; + } + if (udelta > 1) + { + LOG_INFO(0, "RTP: Jumped by %hu packets to 0x%4.4hx", udelta, seq); + } + /* in order, with permissible gap */ + t_module->max_seq_num = seq; + } else +#if (MAX_MISORDER != 0) + /* When MAX_MISORDER is zero, always treat as out of order */ + if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) +#endif + { + /* the sequence number made a very large jump */ + if (seq == t_module->bad_seq) + { + LOG_INFO(0, "RTP: Misorder restart at 0x%4.4hx", seq); + /* Two sequential packets -- assume that the other side + * restarted without telling us so just re-sync + * (i.e., pretend this was the first packet). */ + init_sequence_number(t_module, seq); + } else { + LOG_INFO(0, "RTP: Misorder at 0x%4.4hx, expected 0x%4.4hx", seq, t_module->max_seq_num); + t_module->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); + return 0; + } + } +#if (MAX_MISORDER != 0) + else { + /* duplicate or reordered packet */ + + /* TODO: handle out of order packets */ + } +#endif + t_module->received++; + return 1; +} + +/**************************************************************************//** + * Extract the fields of an RTP packet and validate it. + * + * @param p_ctx The reader context. + * @param t_module The track module. + * @return True if successful, false if there were not enough bits in the + * packet or the packet was invalid. + */ +static void decode_rtp_packet_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_BITS_T *payload = &t_module->payload; + uint32_t version, has_padding, has_extension, csrc_count, has_marker; + uint32_t payload_type, ssrc; + uint16_t seq_num; + + /* Break down fixed header area into component parts */ + version = BITS_READ_U32(p_ctx, payload, 2, "Version"); + has_padding = BITS_READ_U32(p_ctx, payload, 1, "Has padding"); + has_extension = BITS_READ_U32(p_ctx, payload, 1, "Has extension"); + csrc_count = BITS_READ_U32(p_ctx, payload, 4, "CSRC count"); + has_marker = BITS_READ_U32(p_ctx, payload, 1, "Has marker"); + payload_type = BITS_READ_U32(p_ctx, payload, 7, "Payload type"); + seq_num = BITS_READ_U16(p_ctx, payload, 16, "Sequence number"); + t_module->timestamp = BITS_READ_U32(p_ctx, payload, 32, "Timestamp"); + ssrc = BITS_READ_U32(p_ctx, payload, 32, "SSRC"); + + /* If there was only a partial header, abort immediately */ + if (!BITS_VALID(p_ctx, payload)) + return; + + /* Validate version, payload type, sequence number and SSRC, if set */ + if (version != 2 || payload_type != t_module->payload_type) + { + BITS_INVALIDATE(p_ctx, payload); + return; + } + if (BIT_IS_SET(t_module->flags, TRACK_SSRC_SET) && (ssrc != t_module->expected_ssrc)) + { + LOG_DEBUG(p_ctx, "RTP: Unexpected SSRC (0x%8.8X)", ssrc); + BITS_INVALIDATE(p_ctx, payload); + return; + } + + /* Check sequence number indicates packet is usable */ + if (!update_sequence_number(t_module, seq_num)) + { + BITS_INVALIDATE(p_ctx, payload); + return; + } + + /* Adjust to account for padding, CSRCs and extension */ + if (has_padding) + { + VC_CONTAINER_BITS_T bit_stream; + uint32_t available = BITS_BYTES_AVAILABLE(p_ctx, payload); + uint8_t padding; + + BITS_COPY_STREAM(p_ctx, &bit_stream, payload); + /* The last byte of the payload is the number of padding bytes, including itself */ + BITS_SKIP_BYTES(p_ctx, &bit_stream, available - 1, "Skip to padding length"); + padding = BITS_READ_U8(p_ctx, &bit_stream, 8, "Padding length"); + + BITS_REDUCE_BYTES(p_ctx, payload, padding, "Remove padding"); + } + + /* Each CSRC is 32 bits, so shift count up to skip the right number of bits */ + BITS_SKIP(p_ctx, payload, csrc_count << 5, "CSRC section"); + + if (has_extension) + { + uint32_t extension_bits; + + /* Extension header is 16-bit ID (which isn't needed), then 16-bit length in 32-bit words */ + BITS_SKIP(p_ctx, payload, 16, "Extension ID"); + extension_bits = BITS_READ_U32(p_ctx, payload, 16, "Extension length") << 5; + BITS_SKIP(p_ctx, payload, extension_bits, "Extension data"); + } + + /* Record whether or not this RTP packet had the marker bit set */ + if (has_marker) + SET_BIT(t_module->flags, TRACK_HAS_MARKER); + else + CLEAR_BIT(t_module->flags, TRACK_HAS_MARKER); + + /* If it hasn't been set independently, use the first timestamp as a baseline */ + if (!t_module->timestamp_base) + t_module->timestamp_base = t_module->timestamp; + t_module->timestamp -= t_module->timestamp_base; +} + +/**************************************************************************//** + * Generic payload handler. + * Copies/skips data verbatim from the packet payload. + * + * @param p_ctx The reader context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T generic_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_BITS_T *payload = &t_module->payload; + uint32_t size; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + if (!p_packet) + { + /* Skip the rest of this RTP packet */ + BITS_INVALIDATE(p_ctx, payload); + return VC_CONTAINER_SUCCESS; + } + + /* Copy as much as possible into the client packet buffer */ + size = BITS_BYTES_AVAILABLE(p_ctx, payload); + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); + else { + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + { + if (size > p_packet->buffer_size) + size = p_packet->buffer_size; + + BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); + } + p_packet->size = size; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * L16 payload handler. + * Copies/skips data from the packet payload. On copy, swaps consecutive bytes + * in the data in order to get expected byte order. + * + * @param p_ctx The reader context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_STATUS_T status; + + /* Most aspects are handled adequately by the generic handler */ + status = generic_payload_handler(p_ctx, track, p_packet, flags); + if (status != VC_CONTAINER_SUCCESS) + return status; + + if (p_packet && !(flags & (VC_CONTAINER_READ_FLAG_SKIP | VC_CONTAINER_READ_FLAG_INFO))) + { + uint8_t *ptr, *end_ptr; + + /* Ensure packet size is even */ + p_packet->size &= ~1; + + /* Swap bytes of each sample, to get host order instead of network order */ + for (ptr = p_packet->data, end_ptr = ptr + p_packet->size; ptr < end_ptr; ptr += 2) + { + uint8_t high_byte = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = high_byte; + } + } + + return status; +} + + +/***************************************************************************** +Utility functions for use by RTP payload handlers + *****************************************************************************/ + +/**************************************************************************//** + * Gets the value of a parameter as an unsigned 32-bit decimal integer. + * + * @param param_list The URI parameter list. + * @param name The name of the parameter. + * @param value Pointer to the variable to receive the value. + * @return True if the parameter value was read and stored correctly, false + * otherwise. + */ +bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, + const char *name, + uint32_t *value) +{ + PARAMETER_T param; + + param.name = name; + if (vc_containers_list_find_entry(param_list, ¶m) && param.value) + { + char *end; + + *value = strtoul(param.value, &end, 10); + return (end != param.value) && (*end == '\0'); + } + + return false; +} + +/**************************************************************************//** + * Gets the value of a parameter as an unsigned 32-bit hexadecimal integer. + * + * @param param_list The URI parameter list. + * @param name The name of the parameter. + * @param value Pointer to the variable to receive the value. + * @return True if the parameter value was read and stored correctly, false + * otherwise. + */ +bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, + const char *name, + uint32_t *value) +{ + PARAMETER_T param; + + param.name = name; + if (vc_containers_list_find_entry(param_list, ¶m) && param.value) + { + char *end; + + *value = strtoul(param.value, &end, 16); + return (end != param.value) && (*end == '\0'); + } + + return false; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +/**************************************************************************//** + * Read/skip data from the container. + * Can also be used to query information about the next block of data. + * + * @param p_ctx The reader context. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags ) +{ + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_TRACK_MODULE_T *t_module; + VC_CONTAINER_STATUS_T status; + + if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + track = p_ctx->tracks[0]; + t_module = track->priv->module; + + CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET); + + while (!BITS_AVAILABLE(p_ctx, &t_module->payload)) + { + uint32_t bytes_read; + + /* No data left from last RTP packet, get another one */ + bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE); + if (!bytes_read) + return STREAM_STATUS(p_ctx); + + BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read); + + decode_rtp_packet_header(p_ctx, t_module); + SET_BIT(t_module->flags, TRACK_NEW_PACKET); + } + + if (p_packet) + { + uint32_t timestamp_top = t_module->timestamp >> 30; + + /* Determine whether timestamp has wrapped forwards or backwards around zero */ + if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3)) + t_module->timestamp_wraps++; + else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0)) + t_module->timestamp_wraps--; + t_module->last_timestamp_top = timestamp_top; + + p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp; + p_packet->track = 0; + p_packet->flags = 0; + } + + status = t_module->payload_handler(p_ctx, track, p_packet, flags); + if (p_packet && status == VC_CONTAINER_SUCCESS) + { + /* Adjust timestamps from RTP clock rate to microseconds */ + p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; + p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; + } + + STREAM_STATUS(p_ctx) = status; + return status; +} + +/**************************************************************************//** + * Seek over data in the container. + * + * @param p_ctx The reader context. + * @param p_offset The seek offset. + * @param mode The seek mode. + * @param flags The seek flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(p_offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + /* RTP is a non-seekable container */ + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/**************************************************************************//** + * Apply a control operation to the container. + * + * @param p_ctx The reader context. + * @param operation The control operation. + * @param args Optional additional arguments for the operation. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_control( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, + va_list args) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[0]->priv->module; + + switch (operation) + { + case VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE: + { + t_module->timestamp_base = va_arg(args, uint32_t); + if (!t_module->timestamp_base) + t_module->timestamp_base = 1; /* Zero is used to mean "not set" */ + status = VC_CONTAINER_SUCCESS; + } + break; + case VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER: + { + init_sequence_number(t_module, (uint16_t)va_arg(args, uint32_t)); + t_module->probation = 0; + status = VC_CONTAINER_SUCCESS; + } + break; + case VC_CONTAINER_CONTROL_SET_SOURCE_ID: + { + t_module->expected_ssrc = va_arg(args, uint32_t); + SET_BIT(t_module->flags, TRACK_SSRC_SET); + status = VC_CONTAINER_SUCCESS; + } + break; + default: + status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } + + return status; +} + +/**************************************************************************//** + * Close the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + vc_container_assert(p_ctx->tracks_num < 2); + + if (p_ctx->tracks_num) + { + void *payload_extra; + + vc_container_assert(module); + vc_container_assert(module->track); + vc_container_assert(module->track->priv); + vc_container_assert(module->track->priv->module); + + payload_extra = module->track->priv->module->extra; + if (payload_extra) + free(payload_extra); + vc_container_free_track(p_ctx, module->track); + } + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) free(module); + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Open the container. + * Uses the I/O URI and/or data to configure the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_TRACK_T *track = 0; + VC_CONTAINER_TRACK_MODULE_T *t_module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINERS_LIST_T *parameters = NULL; + uint32_t payload_type; + uint32_t initial_seq_num; + + /* Check the URI scheme looks valid */ + if (!vc_uri_scheme(p_ctx->priv->uri) || + (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_SCHEME) && + strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_PKT_SCHEME))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Make the query/parameter list more easily searchable */ + parameters = fill_parameter_list(p_ctx->priv->uri); + if (!parameters) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + /* Payload type parameter is mandatory and must fit in 7 bits */ + if (!rtp_get_parameter_u32(parameters, PAYLOAD_TYPE_NAME, &payload_type) || payload_type > 127) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto error; + } + + /* Allocate our context */ + module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T)); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = &module->track; + + /* Allocate the track, including space for reading an RTP packet on the end */ + track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + MAXIMUM_PACKET_SIZE); + if (!track) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + module->track = track; + p_ctx->tracks_num = 1; + t_module = track->priv->module; + + /* Initialise the track data */ + t_module->buffer = (uint8_t *)(t_module + 1); + status = decode_payload_type(p_ctx, track, parameters, payload_type); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + vc_container_assert(t_module->timestamp_clock != 0); + + /* Default to a generic, unstructured payload handler */ + if (!t_module->payload_handler) + t_module->payload_handler = generic_payload_handler; + + if (rtp_get_parameter_x32(parameters, SSRC_NAME, &t_module->expected_ssrc)) + SET_BIT(t_module->flags, TRACK_SSRC_SET); + + t_module->probation = MIN_SEQUENTIAL; + if (rtp_get_parameter_u32(parameters, SEQ_NAME, &initial_seq_num)) + { + /* If an initial sequence number is provided, avoid probation period */ + t_module->max_seq_num = (uint16_t)initial_seq_num; + t_module->probation = 0; + } + + track->is_enabled = true; + + vc_containers_list_destroy(parameters); + + p_ctx->priv->pf_close = rtp_reader_close; + p_ctx->priv->pf_read = rtp_reader_read; + p_ctx->priv->pf_seek = rtp_reader_seek; + p_ctx->priv->pf_control = rtp_reader_control; + + return VC_CONTAINER_SUCCESS; + +error: + if (parameters) vc_containers_list_destroy(parameters); + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "error opening RTP (%i)", status); + rtp_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rtp_reader_open +#endif diff --git a/containers/rtsp/CMakeLists.txt b/containers/rtsp/CMakeLists.txt new file mode 100755 index 0000000..5c386f6 --- /dev/null +++ b/containers/rtsp/CMakeLists.txt @@ -0,0 +1,14 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +set(rtsp_SRCS ${rtsp_SRCS} rtsp_reader.c) +add_library(reader_rtsp ${LIBRARY_TYPE} ${rtsp_SRCS}) + +target_link_libraries(reader_rtsp containers) + +install(TARGETS reader_rtsp DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rtsp/rtsp_reader.c b/containers/rtsp/rtsp_reader.c new file mode 100755 index 0000000..2d96234 --- /dev/null +++ b/containers/rtsp/rtsp_reader.c @@ -0,0 +1,1983 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_list.h" +#include "containers/core/containers_uri.h" + +/****************************************************************************** +Configurable defines and constants. +******************************************************************************/ + +/** Maximum number of tracks allowed in an RTSP reader */ +#define RTSP_TRACKS_MAX 4 + +/** Space for sending requests and receiving responses */ +#define COMMS_BUFFER_SIZE 2048 + +/** Largest allowed RTSP URI. Must be substantially smaller than COMMS_BUFFER_SIZE + * to allow for the headers that may be sent. */ +#define RTSP_URI_LENGTH_MAX 1024 + +/** Maximum allowed length for the Session: header recevied in a SETUP response, + * This is to ensure comms buffer is not overflowed. */ +#define SESSION_HEADER_LENGTH_MAX 100 + +/** Number of milliseconds to block trying to read from the RTSP stream when no + * data is available from any of the tracks */ +#define DATA_UNAVAILABLE_READ_TIMEOUT_MS 1 + +/** Size of buffer for each track to use when receiving packets */ +#define UDP_READ_BUFFER_SIZE 520000 + +/* Arbitrary number of different dynamic ports to try */ +#define DYNAMIC_PORT_ATTEMPTS_MAX 16 + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define RTSP_SCHEME "rtsp:" +#define RTP_SCHEME "rtp" + +/** The RTSP PKT scheme is used with test pkt files */ +#define RTSP_PKT_SCHEME "rtsppkt:" + +#define RTSP_NETWORK_URI_START "rtsp://" +#define RTSP_NETWORK_URI_START_LENGTH (sizeof(RTSP_NETWORK_URI_START)-1) + +/** Initial capacity of header list */ +#define HEADER_LIST_INITIAL_CAPACITY 16 + +/** Format of the first line of an RTSP request */ +#define RTSP_REQUEST_LINE_FORMAT "%s %s RTSP/1.0\r\n" + +/** Format string for common headers used with all request methods. + * Note: includes double new line to terminate headers */ +#define TRAILING_HEADERS_FORMAT "CSeq: %u\r\nConnection: Keep-Alive\r\nUser-Agent: Broadcom/1.0\r\n\r\n" + +/** Format for the Transport: header */ +#define TRANSPORT_HEADER_FORMAT "Transport: RTP/AVP;unicast;client_port=%hu-%hu;mode=play\r\n" + +/** Format for including Session: header. */ +#define SESSION_HEADER_FORMAT "Session: %s\r\n" + +/** \name RTSP methods, used as the first item in the request line + * @{ */ +#define DESCRIBE_METHOD "DESCRIBE" +#define SETUP_METHOD "SETUP" +#define PLAY_METHOD "PLAY" +#define TEARDOWN_METHOD "TEARDOWN" +/* @} */ + +/** \name Names of headers used by the code + * @{ */ +#define CONTENT_PSEUDOHEADER_NAME ":" +#define CONTENT_LENGTH_NAME "Content-Length" +#define CONTENT_BASE_NAME "Content-Base" +#define CONTENT_LOCATION_NAME "Content-Location" +#define RTP_INFO_NAME "RTP-Info" +#define SESSION_NAME "Session" +/* @} */ + +/** Supported RTSP major version number */ +#define RTSP_MAJOR_VERSION 1 +/** Supported RTSP minor version number */ +#define RTSP_MINOR_VERSION 0 + +/** Lowest successful status code value */ +#define RTSP_STATUS_OK 200 +/** Next failure status code after the set of successful ones */ +#define RTSP_STATUS_MULTIPLE_CHOICES 300 + +/** Maximum size of a decimal string representation of a uint16_t, plus NUL */ +#define PORT_BUFFER_SIZE 6 +/** Start of private / dynamic port region */ +#define FIRST_DYNAMIC_PORT 0xC000 +/** End of private / dynamic port region */ +#define LAST_DYNAMIC_PORT 0xFFF0 + +/** Format of RTP track file extension */ +#define RTP_PATH_EXTENSION_FORMAT ".t%u.pkt" +/** Extra space need for creating an RTP track file name from an RTSP URI path */ +#define RTP_PATH_EXTRA 17 + +/** \name RTP URI parameter names + * @{ */ +#define PAYLOAD_TYPE_NAME "rtppt" +#define MIME_TYPE_NAME "mime-type" +#define SAMPLE_RATE_NAME "rate" +#define CHANNELS_NAME "channels" +/* @} */ + +/** Largest signed 64-bit integer */ +#define MAXIMUM_INT64 (int64_t)((1ULL << 63) - 1) + +/****************************************************************************** +Type definitions +******************************************************************************/ + +typedef int (*PARSE_IS_DELIMITER_FN_T)(int char_to_test); + +typedef struct rtsp_header_tag +{ + const char *name; + char *value; +} RTSP_HEADER_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + VC_CONTAINER_T *reader; /**< RTP reader for track */ + VC_URI_PARTS_T *reader_uri; /**< URI built up from SDP and used to open reader */ + char *control_uri; /**< URI used to control track playback */ + char *session_header; /**< Session header to be used when sending control requests */ + char *payload_type; /**< RTP payload type for track */ + char *media_type; /**< MIME type for track */ + VC_CONTAINER_PACKET_T info; /**< Latest track packet info block */ + unsigned short rtp_port; /**< UDP listener port being used in RTP reader */ +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[RTSP_TRACKS_MAX]; + char *comms_buffer; /**< Buffer used for sending and receiving RTSP messages */ + VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */ + uint32_t cseq_value; /**< CSeq header value for next request */ + uint16_t next_rtp_port; /**< Next RTP port to use when opening track reader */ + uint16_t media_item; /**< Current media item number during initialization */ + bool uri_has_network_info; /**< True if the RTSP URI contains network info */ + int64_t ts_base; /**< Base value for dts and pts */ + VC_CONTAINER_TRACK_MODULE_T *current_track; /**< Next track to be read, to keep info/data on same track */ +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second); + +VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Trim whitespace from the end and start of the string + * + * \param str String to be trimmed + * \return Trimmed string + */ +static char *rtsp_trim( char *str ) +{ + char *trim = str + strlen(str); + + /* Search backwards for first non-whitespace */ + while (--trim >= str && isspace((int)*trim)) + ; /* Everything done in the while */ + trim[1] = '\0'; + + /* Now move start of string forwards to first non-whitespace */ + trim = str; + while (isspace((int)*trim)) + trim++; + + return trim; +} + +/**************************************************************************//** + * Send out the data in the comms buffer. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t to_write; + uint32_t written; + const char *buffer = module->comms_buffer; + + /* When reading from a captured file, do not attempt to send data */ + if (!module->uri_has_network_info) + return VC_CONTAINER_SUCCESS; + + to_write = strlen(buffer); + + while (to_write) + { + written = vc_container_io_write(p_ctx->priv->io, buffer, to_write); + if (!written) + break; + to_write -= written; + buffer += written; + } + + return p_ctx->priv->io->status; +} + +/**************************************************************************//** + * Send a DESCRIBE request to the RTSP server. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_describe_request( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = p_ctx->priv->io->uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, DESCRIBE_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Send a SETUP request to the RTSP server. + * + * @param p_ctx The reader context. + * @param t_module The track module relating to the SETUP. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_setup_request( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = t_module->control_uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, SETUP_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRANSPORT_HEADER_FORMAT, t_module->rtp_port, t_module->rtp_port + 1); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Send a PLAY request to the RTSP server. + * + * @param p_ctx The reader context. + * @param t_module The track module relating to the PLAY. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_play_request( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = t_module->control_uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, PLAY_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Send a TEARDOWN request to the RTSP server. + * + * @param p_ctx The reader context. + * @param t_module The track module relating to the SETUP. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_teardown_request( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = t_module->control_uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, TEARDOWN_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Check a response status line to see if the response is usable or not. + * Reasons for invalidity include: + * - Incorrectly formatted + * - Unsupported version + * - Status code is not in the 2xx range + * + * @param p_ctx The reader context. + * @param status_line The response status line. + * @return The resulting status of the function. + */ +static bool rtsp_successful_response_status( VC_CONTAINER_T *p_ctx, + const char *status_line) +{ + unsigned int major_version, minor_version, status_code; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(status_line, "RTSP/%u.%u %u", &major_version, &minor_version, &status_code) != 3) + { + LOG_ERROR(p_ctx, "RTSP: Invalid response status line:\n%s", status_line); + return false; + } + + if (major_version != RTSP_MAJOR_VERSION || minor_version != RTSP_MINOR_VERSION) + { + LOG_ERROR(p_ctx, "RTSP: Unexpected response RTSP version: %u.%u", major_version, minor_version); + return false; + } + + if (status_code < RTSP_STATUS_OK || status_code >= RTSP_STATUS_MULTIPLE_CHOICES) + { + LOG_ERROR(p_ctx, "RTSP: Response status unsuccessful:\n%s", status_line); + return false; + } + + return true; +} + +/**************************************************************************//** + * Get the content length header from the response headers as an unsigned + * 32-bit integer. + * If the content length header is not found or badly formatted, zero is + * returned. + * + * @param header_list The response headers. + * @return The content length. + */ +static uint32_t rtsp_get_content_length( VC_CONTAINERS_LIST_T *header_list ) +{ + unsigned int content_length = 0; + RTSP_HEADER_T header; + + header.name = CONTENT_LENGTH_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + /* coverity[secure_coding] String is null-terminated */ + sscanf(header.value, "%u", &content_length); + + return content_length; +} + +/**************************************************************************//** + * Get the session header from the response headers. + * If the session header is not found, the empty string is returned. + * + * @param header_list The response headers. + * @return The session header. + */ +static const char *rtsp_get_session_header(VC_CONTAINERS_LIST_T *header_list) +{ + RTSP_HEADER_T header; + + header.name = SESSION_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + return header.value; + + return ""; +} + +/**************************************************************************//** + * Returns pointer to the string with any leading whitespace trimmed and + * terminated by a delimiter, as determined by is_delimiter_fn. The delimiter + * character replaced by NUL (to terminate the string) is returned in the + * variable pointed at by p_delimiter_replaced, if it is not NULL. + * + * The parse_str pointer is moved on to the character after the delimiter, or + * the end of the string if it is reached first. + * + * @param parse_str Pointer to the string pointer to parse. + * @param is_delimiter_fn Function to test if a character is a delimiter or not. + * @param p_delimiter_replaced Pointer to variable to receive delimiter character, or NULL. + * @return Pointer to extracted string. + */ +static char *rtsp_parse_extract(char **parse_str, + PARSE_IS_DELIMITER_FN_T is_delimiter_fn, + char *p_delimiter_replaced) +{ + char *ptr; + char *result; + + vc_container_assert(parse_str); + vc_container_assert(*parse_str); + vc_container_assert(is_delimiter_fn); + + ptr = *parse_str; + + while (isspace((int)*ptr)) + ptr++; + + result = ptr; + + while (*ptr && !(*is_delimiter_fn)(*ptr)) + ptr++; + if (p_delimiter_replaced) + *p_delimiter_replaced = *ptr; + if (*ptr) + *ptr++ = '\0'; + + *parse_str = ptr; + return result; +} + +/**************************************************************************//** + * Specialised form of rtsp_parse_extract() where the delimiter is whitespace. + * Returns pointer to the string with any leading whitespace trimmed and + * terminated by further whitespace. + * + * The parse_str pointer is moved on to the character after the delimiter, or + * the end of the string if it is reached first. + * + * @param parse_str Pointer to the string pointer to parse. + * @return Pointer to extracted string. + */ +static char *rtsp_parse_extract_ws(char **parse_str) +{ + char *ptr; + char *result; + + vc_container_assert(parse_str); + vc_container_assert(*parse_str); + + ptr = *parse_str; + + while (isspace((int)*ptr)) + ptr++; + + result = ptr; + + while (*ptr && !isspace((int)*ptr)) + ptr++; + if (*ptr) + *ptr++ = '\0'; + + *parse_str = ptr; + return result; +} + +/**************************************************************************//** + * Returns whether the given character is a parameter name delimiter or not. + * + * @param char_to_test The character under test. + * @return True if the character is a name delimiter, false if not. + */ +static int name_delimiter_fn(int char_to_test) +{ + switch (char_to_test) + { + case ' ': + case '\t': + case '=': + case ';': + return true; + default: + return false; + } +} + +/**************************************************************************//** + * Returns whether the given character is a parameter value delimiter or not. + * + * @param char_to_test The character under test. + * @return True if the character is a value delimiter, false if not. + */ +static int value_delimiter_fn(int char_to_test) +{ + switch (char_to_test) + { + case ' ': + case '\t': + case ';': + return true; + default: + return false; + } +} + +/**************************************************************************//** + * Extract a name/value pair from a given string. + * Each pair consists of a name, optionally followed by '=' and a value, with + * optional whitespace around either or both name and value. The parameter is + * terminated by a semi-colon, ';'. + * + * The parse_str pointer is moved on to the next parameter, or the end of the + * string if that is reached first. + * + * Name can be empty if there are two consecutive semi-colons, or a trailing + * semi-colon. + * + * @param parse_str Pointer to the string pointer to be parsed. + * @param p_name Pointer to where name string pointer shall be written. + * @param p_value Pointer to where value string pointer shall be written. + * @return True if the name is not empty. + */ +static bool rtsp_parse_extract_parameter(char **parse_str, char **p_name, char **p_value) +{ + char delimiter; + + vc_container_assert(parse_str); + vc_container_assert(*parse_str); + vc_container_assert(p_name); + vc_container_assert(p_value); + + /* General form of each parameter: + * [=] + * but allow for spaces before and after name and value */ + *p_name = rtsp_parse_extract(parse_str, name_delimiter_fn, &delimiter); + if (isspace((int)delimiter)) + { + /* Skip further spaces after parameter name */ + do { + delimiter = **parse_str; + if (delimiter) + (*parse_str)++; + } while (isspace((int)delimiter)); + } + + if (delimiter == '=') + { + /* Parameter value present (although may be empty) */ + *p_value = rtsp_parse_extract(parse_str, value_delimiter_fn, &delimiter); + if (isspace((int)delimiter)) + { + /* Skip spaces after parameter value */ + do { + delimiter = **parse_str; + if (delimiter) + (*parse_str)++; + } while (isspace((int)delimiter)); + } + } else { + *p_value = NULL; + } + + return (**p_name != '\0'); +} + +/**************************************************************************//** + * Parses RTP-Info header and stores relevant parts. + * + * @param header_list The response header list. + * @param t_module The track module relating to the response headers. + */ +static void rtsp_store_rtp_info(VC_CONTAINERS_LIST_T *header_list, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + RTSP_HEADER_T header; + char *ptr; + + header.name = RTP_INFO_NAME; + if (!vc_containers_list_find_entry(header_list, &header)) + return; + + ptr = header.value; + while (ptr && *ptr) + { + char *name; + char *value; + + if (!rtsp_parse_extract_parameter(&ptr, &name, &value)) + continue; + + if (strcasecmp(name, "rtptime") == 0) + { + unsigned int timestamp_base = 0; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(value, "%u", ×tamp_base) == 1) + (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, timestamp_base); + } + else if (strcasecmp(name, "seq") == 0) + { + unsigned short int sequence_number = 0; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(value, "%hu", &sequence_number) == 1) + (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, (uint32_t)sequence_number); + } + } +} + +/**************************************************************************//** + * Reads an RTSP response and parses it into headers and content. + * The headers and content remain stored in the comms buffer, but referenced + * by the module's header list. Content uses a special header name that cannot + * occur in the real headers. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_read_response( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_IO_T *p_ctx_io = p_ctx->priv->io; + char *next_read = module->comms_buffer; + uint32_t space_available = COMMS_BUFFER_SIZE - 1; /* Allow for a NUL */ + uint32_t received; + char *ptr = next_read; + bool found_content = false; + RTSP_HEADER_T header; + + vc_containers_list_reset(module->header_list); + + /* Response status line doesn't need to be stored, just checked */ + header.name = NULL; + header.value = next_read; + + while (space_available) + { + received = vc_container_io_read(p_ctx_io, next_read, space_available); + if (p_ctx_io->status != VC_CONTAINER_SUCCESS) + break; + + next_read += received; + space_available -= received; + + while (!found_content && ptr < next_read) + { + switch (*ptr) + { + case ':': + if (header.value) + { + /* Just another character in the value */ + ptr++; + } else { + /* End of name, expect value next */ + *ptr++ = '\0'; + header.value = ptr; + } + break; + + case '\n': + if (header.value) + { + /* End of line while parsing the value part of the header, add name/value pair to list */ + *ptr++ = '\0'; + header.value = rtsp_trim(header.value); + if (header.name) + { + if (!vc_containers_list_insert(module->header_list, &header, false)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> header to list", header.name); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } else { + /* Check response status line */ + if (!rtsp_successful_response_status(p_ctx, header.value)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + /* Ready for next header */ + header.name = ptr; + header.value = NULL; + } else { + uint32_t content_length; + + /* End of line while parsing the name of a header */ + *ptr++ = '\0'; + if (*header.name && *header.name != '\r') + { + /* A non-empty name is invalid, so fail */ + LOG_ERROR(p_ctx, "RTSP: Invalid name in header - no colon:\n%s", header.name); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* An empty name signifies the start of the content has been found */ + found_content = true; + + /* Make a pseudo-header for the content and add it to the list */ + header.name = CONTENT_PSEUDOHEADER_NAME; + header.value = ptr; + if (!vc_containers_list_insert(module->header_list, &header, false)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add content pseudoheader to list"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + /* Calculate how much content there is left to read, based on Content-Length header */ + content_length = rtsp_get_content_length(module->header_list); + if (ptr + content_length < next_read) + { + /* Final content byte already present, with extra data after it */ + space_available = 0; + } else { + uint32_t content_to_read = content_length - (next_read - ptr); + + if (content_to_read >= space_available) + { + LOG_ERROR(p_ctx, "RTSP: Not enough room to read content"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + /* Restrict further reading to the number of content bytes left */ + space_available = content_to_read; + } + } + break; + + default: + /* Just another character in either the name or the value */ + ptr++; + } + } + } + + if (!space_available) + { + if (found_content) + { + /* Terminate content region */ + *next_read = '\0'; + } else { + /* Ran out of buffer space and never found the content */ + LOG_ERROR(p_ctx, "RTSP: Response header section too big / content missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + } + + return p_ctx_io->status; +} + +/**************************************************************************//** + * Creates a new track from an SDP media field. + * Limitation: only the first payload type of the field is used. + * + * @param p_ctx The RTSP reader context. + * @param media The media field. + * @param p_track Pointer to the variable to receive the new track pointer. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_create_track_for_media_field(VC_CONTAINER_T *p_ctx, + char *media, + VC_CONTAINER_TRACK_T **p_track ) +{ + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_TRACK_MODULE_T *t_module = NULL; + char *ptr = media; + char *media_type; + char *rtp_port; + char *transport_type; + char *payload_type; + + *p_track = NULL; + if (p_ctx->tracks_num == RTSP_TRACKS_MAX) + { + LOG_DEBUG(p_ctx, "RTSP: Too many media items in SDP data, only %d are supported.", RTSP_TRACKS_MAX); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* Format of media item: + * m= + * Only RTP/AVP transport and the first payload type are supported */ + media_type = rtsp_parse_extract_ws(&ptr); + rtp_port = rtsp_parse_extract_ws(&ptr); + transport_type = rtsp_parse_extract_ws(&ptr); + payload_type = rtsp_parse_extract_ws(&ptr); + if (!*media_type || !*rtp_port || strcmp(transport_type, "RTP/AVP") || !*payload_type) + { + LOG_ERROR(p_ctx, "RTSP: Failure to parse media field"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T)); + if (!track) goto out_of_memory_error; + t_module = track->priv->module; + + /* If the port specifier is invalid, treat it as if it were zero */ + /* coverity[secure_coding] String is null-terminated */ + sscanf(rtp_port, "%hu", &t_module->rtp_port); + t_module->payload_type = payload_type; + t_module->media_type = media_type; + + t_module->reader_uri = vc_uri_create(); + if (!t_module->reader_uri) goto out_of_memory_error; + if (!vc_uri_set_scheme(t_module->reader_uri, RTP_SCHEME)) goto out_of_memory_error; + if (!vc_uri_add_query(t_module->reader_uri, PAYLOAD_TYPE_NAME, payload_type)) goto out_of_memory_error; + + p_ctx->tracks[p_ctx->tracks_num++] = track; + *p_track = track; + return VC_CONTAINER_SUCCESS; + +out_of_memory_error: + if (track) + { + if (t_module->reader_uri) + vc_uri_release(t_module->reader_uri); + vc_container_free_track(p_ctx, track); + } + LOG_ERROR(p_ctx, "RTSP: Memory allocation failure creating track"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; +} + +/**************************************************************************//** + * Returns whether the given character is a slash or not. + * + * @param char_to_test The character under test. + * @return True if the character is a slash, false if not. + */ +static int slash_delimiter_fn(int char_to_test) +{ + return char_to_test == '/'; +} + +/**************************************************************************//** + * Parse an rtpmap attribute and store values in the related track. + * + * @param p_ctx The RTSP reader context. + * @param track The track relating to the rtpmap. + * @param attribute The rtpmap attribute value. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_parse_rtpmap_attribute( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + char *attribute ) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + char *ptr = attribute; + char *payload_type; + char *mime_sub_type; + char *sample_rate; + char *full_mime_type; + char *channels; + + /* rtpmap attribute format: + * /[/] + * Payload type must match the one used in the media field */ + payload_type = rtsp_parse_extract_ws(&ptr); + if (strcmp(payload_type, t_module->payload_type)) + { + /* Ignore any unsupported secondary payload type attributes */ + LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported"); + return VC_CONTAINER_SUCCESS; + } + + mime_sub_type = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL); + if (!*mime_sub_type) + { + LOG_ERROR(p_ctx, "RTSP: rtpmap: MIME type missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + sample_rate = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL); + if (!*sample_rate) + { + LOG_ERROR(p_ctx, "RTSP: rtpmap: sample rate missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + full_mime_type = (char *)malloc(strlen(t_module->media_type) + strlen(mime_sub_type) + 2); + if (!full_mime_type) + { + LOG_ERROR(p_ctx, "RTSP: Failed to allocate space for full MIME type"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + /* coverity[secure_coding] String has been allocated of the right size */ + sprintf(full_mime_type, "%s/%s", t_module->media_type, mime_sub_type); + if (!vc_uri_add_query(t_module->reader_uri, MIME_TYPE_NAME, full_mime_type)) + { + free(full_mime_type); + LOG_ERROR(p_ctx, "RTSP: Failed to add MIME type to URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + free(full_mime_type); + + if (!vc_uri_add_query(t_module->reader_uri, SAMPLE_RATE_NAME, sample_rate)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add sample rate to URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + /* Optional channels specifier */ + channels = rtsp_parse_extract_ws(&ptr); + if (*channels) + { + if (!vc_uri_add_query(t_module->reader_uri, CHANNELS_NAME, channels)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add channels to URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Parse an fmtp attribute and store values in the related track. + * + * @param p_ctx The RTSP reader context. + * @param track The track relating to the fmtp. + * @param attribute The fmtp attribute value. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_parse_fmtp_attribute( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + char *attribute ) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + char *ptr = attribute; + char *payload_type; + + /* fmtp attribute format: + * + * The payload type must match the first one in the media field, parameters + * are semi-colon separated and may have additional whitespace around them. */ + + payload_type = rtsp_parse_extract_ws(&ptr); + if (strcmp(payload_type, t_module->payload_type)) + { + /* Ignore any unsupported secondary payload type attributes */ + LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported"); + return VC_CONTAINER_SUCCESS; + } + + while (*ptr) + { + char *name; + char *value; + + /* Only add the parameter if the name was not empty. This avoids problems with + * strings like ";;", ";" or ";=value;" */ + if (rtsp_parse_extract_parameter(&ptr, &name, &value)) + { + if (!vc_uri_add_query(t_module->reader_uri, name, value)) + { + if (value) + LOG_ERROR(p_ctx, "RTSP: Failed to add <%s>=<%s> query to URI", name, value); + else + LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> query to URI", name); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Merge base URI and relative URI strings into a merged URI string. + * Always creates a new string, even if the relative URI is actually absolute. + * + * @param p_ctx The RTSP reader context. + * @param base_uri_str The base URI string. + * @param relative_uri_str The relative URI string. + * @param p_merged_uri_str Pointer to where to put the merged string pointer. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_merge_uris( VC_CONTAINER_T *p_ctx, + const char *base_uri_str, + const char *relative_uri_str, + char **p_merged_uri_str) +{ + VC_URI_PARTS_T *base_uri = NULL; + VC_URI_PARTS_T *relative_uri = NULL; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + uint32_t merged_size; + + *p_merged_uri_str = NULL; + relative_uri = vc_uri_create(); + if (!relative_uri) goto tidy_up; + if (!vc_uri_parse(relative_uri, relative_uri_str)) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + if (vc_uri_scheme(relative_uri) != NULL) + { + /* URI is absolute, not relative, so return it as the merged URI */ + size_t len = strlen(relative_uri_str); + + *p_merged_uri_str = (char *)malloc(len + 1); + if (!*p_merged_uri_str) goto tidy_up; + + strncpy(*p_merged_uri_str, relative_uri_str, len); + status = VC_CONTAINER_SUCCESS; + goto tidy_up; + } + + base_uri = vc_uri_create(); + if (!base_uri) goto tidy_up; + if (!vc_uri_parse(base_uri, base_uri_str)) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + /* Build up merged URI in relative_uri, using base_uri as necessary */ + if (!vc_uri_merge(base_uri, relative_uri)) goto tidy_up; + + merged_size = vc_uri_build(relative_uri, NULL, 0) + 1; + *p_merged_uri_str = (char *)malloc(merged_size); + if (!*p_merged_uri_str) goto tidy_up; + + vc_uri_build(relative_uri, *p_merged_uri_str, merged_size); + + status = VC_CONTAINER_SUCCESS; + +tidy_up: + if (base_uri) vc_uri_release(base_uri); + if (relative_uri) vc_uri_release(relative_uri); + if (status != VC_CONTAINER_SUCCESS) + LOG_ERROR(p_ctx, "RTSP: Error merging URIs: %d", (int)status); + return status; +} + +/**************************************************************************//** + * Parse a control attribute and store it as an absolute URI. + * + * @param p_ctx The RTSP reader context. + * @param attribute The control attribute value. + * @param base_uri_str The base URI string. + * @param p_control_uri_str Pointer to where to put the control string pointer. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_parse_control_attribute( VC_CONTAINER_T *p_ctx, + const char *attribute, + const char *base_uri_str, + char **p_control_uri_str) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + /* control attribute format: + * + * The control URI is either absolute or relative to the base URI. If the + * control URI is just an asterisk, the control URI matches the base URI. */ + + if (!*attribute || strcmp(attribute, "*") == 0) + { + size_t len = strlen(base_uri_str); + + *p_control_uri_str = (char *)malloc(len + 1); + if (!*p_control_uri_str) + { + LOG_ERROR(p_ctx, "RTSP: Failed to allocate control URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + strncpy(*p_control_uri_str, base_uri_str, len); + } else { + status = rtsp_merge_uris(p_ctx, base_uri_str, attribute, p_control_uri_str); + } + + return status; +} + +/**************************************************************************//** + * Open a reader for the track using the URI that has been generated. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module for which a reader is needed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_open_track_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t uri_buffer_size; + char *uri_buffer; + + uri_buffer_size = vc_uri_build(t_module->reader_uri, NULL, 0) + 1; + uri_buffer = (char *)malloc(uri_buffer_size); + if (!uri_buffer) + { + LOG_ERROR(p_ctx, "RTSP: Failed to build RTP URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + vc_uri_build(t_module->reader_uri, uri_buffer, uri_buffer_size); + + t_module->reader = vc_container_open_reader(uri_buffer, &status, NULL, NULL); + free(uri_buffer); + + return status; +} + +/**************************************************************************//** + * Open a reader for the track using the network URI that has been generated. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module for which a reader is needed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_open_network_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + char port[PORT_BUFFER_SIZE] = {0}; + + if (!t_module->rtp_port) + { + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + t_module->rtp_port = module->next_rtp_port; + if (t_module->rtp_port > LAST_DYNAMIC_PORT) + { + LOG_ERROR(p_ctx, "RTSP: Out of dynamic ports"); + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + module->next_rtp_port += 2; + } + + snprintf(port, sizeof(port)-1, "%hu", t_module->rtp_port); + if (!vc_uri_set_port(t_module->reader_uri, port)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI port"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + return rtsp_open_track_reader(p_ctx, t_module); +} + +/**************************************************************************//** + * Open a reader for the track using the file URI that has been generated. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module for which a reader is needed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_open_file_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status; + VC_URI_PARTS_T *rtsp_uri = NULL; + const char *rtsp_path; + int len; + char *new_path = NULL; + char *extension; + + /* Use the RTSP URI's path, with the extension changed to ".t.pkt" + * where is the zero-based media item number. */ + + rtsp_uri = vc_uri_create(); + if (!rtsp_uri) + { + LOG_ERROR(p_ctx, "RTSP: Failed to create RTSP URI"); + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto tidy_up; + } + + if (!vc_uri_parse(rtsp_uri, p_ctx->priv->io->uri)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to parse RTSP URI <%s>", p_ctx->priv->io->uri); + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + rtsp_path = vc_uri_path(rtsp_uri); + if (!rtsp_path || !*rtsp_path) + { + LOG_ERROR(p_ctx, "RTSP: RTSP URI path missing <%s>", p_ctx->priv->io->uri); + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + len = strlen(rtsp_path); + new_path = (char *)calloc(1, len + RTP_PATH_EXTRA + 1); + if (!rtsp_uri) + { + LOG_ERROR(p_ctx, "RTSP: Failed to create buffer for RTP path"); + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto tidy_up; + } + + strncpy(new_path, rtsp_path, len); + extension = strrchr(new_path, '.'); /* Find extension, to replace it */ + if (!extension) + extension = new_path + strlen(new_path); /* No extension, so append instead */ + + snprintf(extension, len + RTP_PATH_EXTRA - (extension - new_path), + RTP_PATH_EXTENSION_FORMAT, p_ctx->priv->module->media_item); + if (!vc_uri_set_path(t_module->reader_uri, new_path)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to store RTP path <%s>", new_path); + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto tidy_up; + } + + free(new_path); + vc_uri_release(rtsp_uri); + + return rtsp_open_track_reader(p_ctx, t_module); + +tidy_up: + if (new_path) free(new_path); + if (rtsp_uri) vc_uri_release(rtsp_uri); + return status; +} + +/**************************************************************************//** + * Copy track information from the encapsulated track reader's track to the + * RTSP track. + * + * @param p_ctx The RTSP reader context. + * @param track The RTSP track requiring its information to be filled in. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_copy_track_data_from_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track ) +{ + VC_CONTAINER_T *reader = track->priv->module->reader; + VC_CONTAINER_ES_FORMAT_T *src_format, *dst_format; + + if (reader->tracks_num != 1) + { + LOG_ERROR(p_ctx, "RTSP: Expected track reader to have one track, has %d", reader->tracks_num); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + if (reader->tracks[0]->meta_num) + { + LOG_DEBUG(p_ctx, "RTSP: Track reader has meta data - not supported"); + } + + src_format = reader->tracks[0]->format; + dst_format = track->format; + + /* Copy fields individually to avoid problems with pointers (type and extradata). */ + dst_format->es_type = src_format->es_type; + dst_format->codec = src_format->codec; + dst_format->codec_variant = src_format->codec_variant; + *dst_format->type = *src_format->type; + dst_format->bitrate = src_format->bitrate; + memcpy(dst_format->language, src_format->language, sizeof(dst_format->language)); + dst_format->group_id = src_format->group_id; + dst_format->flags = src_format->flags; + + if (src_format->extradata) + { + VC_CONTAINER_STATUS_T status; + uint32_t extradata_size = src_format->extradata_size; + + status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size); + if (status != VC_CONTAINER_SUCCESS) + return status; + + memcpy(dst_format->extradata, src_format->extradata, extradata_size); + dst_format->extradata_size = extradata_size; + } + + track->is_enabled = reader->tracks[0]->is_enabled; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Finalise the creation of the RTSP track by opening its reader and filling in + * the track information. + * + * @param p_ctx The RTSP reader context. + * @param track The RTSP track being finalised. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_complete_track( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (!t_module->control_uri) + { + LOG_ERROR(p_ctx, "RTSP: Track control URI is missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + if (module->uri_has_network_info) + { + int ii; + + if (!vc_uri_set_host(t_module->reader_uri, "")) + { + LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI host"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + status = rtsp_open_network_reader(p_ctx, t_module); + + for (ii = 0; status == VC_CONTAINER_ERROR_URI_OPEN_FAILED && ii < DYNAMIC_PORT_ATTEMPTS_MAX; ii++) + { + /* Reset port to pick up next dynamic port */ + t_module->rtp_port = 0; + status = rtsp_open_network_reader(p_ctx, t_module); + } + + /* Change I/O to non-blocking, so that tracks can be polled */ + if (status == VC_CONTAINER_SUCCESS) + status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0); + /* Set a large read buffer, to avoid dropping bursts of large packets (e.g. hi-def video) */ + if (status == VC_CONTAINER_SUCCESS) + status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, UDP_READ_BUFFER_SIZE); + } else { + status = rtsp_open_file_reader(p_ctx, t_module); + } + + vc_uri_release(t_module->reader_uri); + t_module->reader_uri = NULL; + + if (status == VC_CONTAINER_SUCCESS) + status = rtsp_copy_track_data_from_reader(p_ctx, track); + + return status; +} + +/**************************************************************************//** + * Returns whether the given character is an attribute name delimiter or not. + * + * @param char_to_test The character under test. + * @return True if the character is an attribute name delimiter, false if not. + */ +static int attribute_name_delimiter_fn(int char_to_test) +{ + return (char_to_test == ':'); +} + +/**************************************************************************//** + * Create RTSP tracks from media fields in SDP formatted data. + * + * @param p_ctx The RTSP reader context. + * @param sdp_buffer The SDP data. + * @param base_uri The RTSP base URI. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_sdp( VC_CONTAINER_T *p_ctx, + char *sdp_buffer, + char *base_uri ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + char *session_base_uri = base_uri; + char *ptr; + char *next_line_ptr; + char *attribute; + + for (ptr = sdp_buffer; status == VC_CONTAINER_SUCCESS && *ptr; ptr = next_line_ptr) + { + /* Find end of line */ + char field = *ptr; + + next_line_ptr = ptr; + while (*next_line_ptr && *next_line_ptr != '\n') + next_line_ptr++; + + /* Terminate line */ + if (*next_line_ptr) + *next_line_ptr++ = '\0'; + + /* The format of the line has to be "=" where is a single + * character. Ignore anything else. */ + if (ptr[1] != '=') + continue; + ptr = rtsp_trim(ptr + 2); + + switch (field) + { + case 'm': + /* Start of media item */ + if (track) + { + /* Finish previous track */ + status = rtsp_complete_track(p_ctx, track); + track = NULL; + p_ctx->priv->module->media_item++; + if (status != VC_CONTAINER_SUCCESS) + break; + } + + status = rtsp_create_track_for_media_field(p_ctx, ptr, &track); + break; + case 'a': /* Attribute (either session or media level) */ + /* Attributes of the form "a=:" */ + attribute = rtsp_parse_extract(&ptr, attribute_name_delimiter_fn, NULL); + + if (track) + { + /* Media level attribute */ + + /* Look for known attributes */ + if (strcmp(attribute, "rtpmap") == 0) + status = rtsp_parse_rtpmap_attribute(p_ctx, track, ptr); + else if (strcmp(attribute, "fmtp") == 0) + status = rtsp_parse_fmtp_attribute(p_ctx, track, ptr); + else if (strcmp(attribute, "control") == 0) + { + char **track_control_uri = &track->priv->module->control_uri; + + if (*track_control_uri) + { + free(*track_control_uri); + *track_control_uri = NULL; + } + status = rtsp_parse_control_attribute(p_ctx, ptr, session_base_uri, track_control_uri); + } + /* Any other attributes are ignored */ + } else { + /* Session level attribute */ + if (strcmp(attribute, "control") == 0) + { + /* Only need to change the session_base_uri if it differs from the base URI */ + ptr = rtsp_trim(ptr); + if (session_base_uri != base_uri) + { + free(session_base_uri); + session_base_uri = base_uri; + } + if (strcmp(ptr, base_uri) != 0) + status = rtsp_parse_control_attribute(p_ctx, ptr, base_uri, &session_base_uri); + } + } + break; + default: /* Ignore any other field names */ + ; + } + } + + if (session_base_uri && session_base_uri != base_uri) + free(session_base_uri); + + /* Having no media fields is an error, since there will be nothing to play back */ + if (status == VC_CONTAINER_SUCCESS) + { + if (!p_ctx->tracks_num) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + else if (track) + { + /* Finish final track */ + status = rtsp_complete_track(p_ctx, track); + p_ctx->priv->module->media_item++; + } + } + + return status; +} + +/**************************************************************************//** + * Create RTSP tracks from the response to a DESCRIBE request. + * The response must have already been filled into the comms buffer and header + * list. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_response( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINERS_LIST_T *header_list = p_ctx->priv->module->header_list; + RTSP_HEADER_T header; + char *base_uri; + char *content; + + header.name = CONTENT_PSEUDOHEADER_NAME; + if (!vc_containers_list_find_entry(header_list, &header)) + { + LOG_ERROR(p_ctx, "RTSP: Content missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + content = header.value; + + /* The control URI may be relative to a base URI which is the first of these + * that is available: + * 1. Content-Base header + * 2. Content-Location header + * 3. Request URI + */ + header.name = CONTENT_BASE_NAME; + if (vc_containers_list_find_entry(header_list, &header)) + base_uri = header.value; + else { + header.name = CONTENT_LOCATION_NAME; + if (vc_containers_list_find_entry(header_list, &header)) + base_uri = header.value; + else + base_uri = p_ctx->priv->io->uri; + } + + return rtsp_create_tracks_from_sdp(p_ctx, content, base_uri); +} + +/**************************************************************************//** + * Header comparison function. + * Compare two header structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second) +{ + return strcasecmp(first->name, second->name); +} + +/**************************************************************************//** + * Make a DESCRIBE request to the server and create tracks from the response. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_describe( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + /* Send DESCRIBE request and get response */ + status = rtsp_send_describe_request(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + status = rtsp_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Create tracks from SDP content */ + status = rtsp_create_tracks_from_response(p_ctx); + + return status; +} + +/**************************************************************************//** + * Make a SETUP request to the server and get session from response. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module to be set up. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_setup( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + const char *session_header; + size_t session_header_len; + + status = rtsp_send_setup_request(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) return status; + status = rtsp_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + + session_header = rtsp_get_session_header(module->header_list); + session_header_len = strlen(session_header); + if (session_header_len > SESSION_HEADER_LENGTH_MAX) return VC_CONTAINER_ERROR_FORMAT_INVALID; + + t_module->session_header = (char *)malloc(session_header_len + 1); + if (!t_module->session_header) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + strncpy(t_module->session_header, session_header, session_header_len); + + return status; +} + +/**************************************************************************//** + * Make a SETUP request to the server and get session from response. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module to be set up. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_play( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + status = rtsp_send_play_request(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) return status; + status = rtsp_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + + rtsp_store_rtp_info(module->header_list, t_module); + + return status; +} + +/**************************************************************************//** + * Blocking read/skip data from a container. + * Can also be used to query information about the next block of data. + * + * @pre The container is set to non-blocking. + * @post The container is set to non-blocking. + * + * @param p_ctx The reader context. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_blocking_track_read(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_STATUS_T status; + + status = vc_container_read(p_ctx, p_packet, flags); + + /* The ..._ABORTED status corresponds to a timeout waiting for data */ + if (status == VC_CONTAINER_ERROR_ABORTED) + { + /* So switch to blocking temporarily to wait for some */ + (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, VC_CONTAINER_READ_TIMEOUT_BLOCK); + status = vc_container_read(p_ctx, p_packet, flags); + (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0); + } + + return status; +} + +/**************************************************************************//** + * Update the cached packet info blocks for all tracks. + * If one or more of the tracks has data, set the current track to the one with + * the earliest decode timestamp. + * + * @pre The track readers must not block when data is requested from them. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_update_track_info( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t tracks_num = p_ctx->tracks_num; + uint32_t track_idx; + int64_t earliest_dts = MAXIMUM_INT64; + VC_CONTAINER_TRACK_MODULE_T *earliest_track = NULL; + + /* Reset current track to unknown */ + p_ctx->priv->module->current_track = NULL; + + /* Collect each track's info and return the one with earliest timestamp. */ + for (track_idx = 0; track_idx < tracks_num; track_idx++) + { + VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[track_idx]->priv->module; + VC_CONTAINER_PACKET_T *info = &t_module->info; + + /* If this track has no data available, request more */ + if (!info->size) + { + /* This is a non-blocking read, so status will be ..._ABORTED if nothing available */ + status = vc_container_read(t_module->reader, info, VC_CONTAINER_READ_FLAG_INFO); + /* Adjust track index to be the RTSP index instead of the RTP one */ + info->track = track_idx; + } + + if (status == VC_CONTAINER_SUCCESS) + { + if (info->dts < earliest_dts) + { + earliest_dts = info->dts; + earliest_track = t_module; + } + } + else if (status != VC_CONTAINER_ERROR_ABORTED) + { + /* Not a time-out failure, so abort */ + return status; + } + } + + p_ctx->priv->module->current_track = earliest_track; + + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +/**************************************************************************//** + * Read/skip data from the container. + * Can also be used to query information about the next block of data. + * + * @param p_ctx The reader context. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_MODULE_T *current_track = module->current_track; + VC_CONTAINER_PACKET_T *info; + + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + vc_container_assert(p_packet); + vc_container_assert(p_packet->track < p_ctx->tracks_num); + current_track = p_ctx->tracks[p_packet->track]->priv->module; + module->current_track = current_track; + + if (!current_track->info.size) + { + status = rtsp_blocking_track_read(current_track->reader, ¤t_track->info, VC_CONTAINER_READ_FLAG_INFO); + if (status != VC_CONTAINER_SUCCESS) + goto error; + } + } + else if (!current_track || !current_track->info.size) + { + status = rtsp_update_track_info(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + while (!module->current_track) + { + /* Check RTSP stream to see if it has closed */ + status = rtsp_read_response(p_ctx); + if (status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_ABORTED) + { + /* No data from any track yet, so keep checking */ + status = rtsp_update_track_info(p_ctx); + } + if (status != VC_CONTAINER_SUCCESS) + goto error; + } + + current_track = module->current_track; + } + + info = ¤t_track->info; + vc_container_assert(info->size); + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + vc_container_assert(p_packet); + memcpy(p_packet, info, sizeof(*info)); + } else { + status = rtsp_blocking_track_read(current_track->reader, p_packet, flags); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + if (p_packet) + { + p_packet->track = info->track; + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + info->size = 0; + } else { + vc_container_assert(info->size >= p_packet->size); + info->size -= p_packet->size; + } + } else { + info->size = 0; + } + } + + if (p_packet) + { + /* Adjust timestamps to be relative to zero */ + if (!module->ts_base) + module->ts_base = p_packet->dts; + p_packet->dts -= module->ts_base; + p_packet->pts -= module->ts_base; + } + +error: + STREAM_STATUS(p_ctx) = status; + return status; +} + +/**************************************************************************//** + * Seek over data in the container. + * + * @param p_ctx The reader context. + * @param p_offset The seek offset. + * @param mode The seek mode. + * @param flags The seek flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(p_offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + /* RTSP is a non-seekable container */ + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/**************************************************************************//** + * Close the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[i]->priv->module; + + if (t_module->control_uri && t_module->session_header) + { + /* Send the teardown message and wait for a response, although it + * isn't important whether it was successful or not. */ + if (rtsp_send_teardown_request(p_ctx, t_module) == VC_CONTAINER_SUCCESS) + (void)rtsp_read_response(p_ctx); + } + + if (t_module->reader) + vc_container_close(t_module->reader); + if (t_module->reader_uri) + vc_uri_release(t_module->reader_uri); + if (t_module->control_uri) + free(t_module->control_uri); + if (t_module->session_header) + free(t_module->session_header); + vc_container_free_track(p_ctx, p_ctx->tracks[i]); /* Also need to close track's reader */ + } + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) + { + if (module->comms_buffer) + free(module->comms_buffer); + if (module->header_list) + vc_containers_list_destroy(module->header_list); + free(module); + } + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Open the container. + * Uses the I/O URI and/or data to configure the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t ii; + + /* Check the URI scheme looks valid */ + if (!vc_uri_scheme(p_ctx->priv->uri) || + (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_SCHEME) && + strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_PKT_SCHEME))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + if ((module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T))) == NULL) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + module->next_rtp_port = FIRST_DYNAMIC_PORT; + module->cseq_value = 0; + module->uri_has_network_info = + (strncasecmp(p_ctx->priv->io->uri, RTSP_NETWORK_URI_START, RTSP_NETWORK_URI_START_LENGTH) == 0); + module->comms_buffer = (char *)calloc(1, COMMS_BUFFER_SIZE+1); + if (!module->comms_buffer) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + /* header_list will contain pointers into the response_buffer, so take care in re-use */ + module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(RTSP_HEADER_T), + (VC_CONTAINERS_LIST_COMPARATOR_T)rtsp_header_comparator); + if (!module->header_list) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + status = rtsp_describe(p_ctx); + for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++) + status = rtsp_setup(p_ctx, p_ctx->tracks[ii]->priv->module); + for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++) + status = rtsp_play(p_ctx, p_ctx->tracks[ii]->priv->module); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + /* Set the RTSP stream to block briefly, to allow polling for closure as well as to avoid spinning CPU */ + vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, DATA_UNAVAILABLE_READ_TIMEOUT_MS); + + p_ctx->priv->pf_close = rtsp_reader_close; + p_ctx->priv->pf_read = rtsp_reader_read; + p_ctx->priv->pf_seek = rtsp_reader_seek; + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + +error: + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "error opening RTSP stream (%i)", status); + rtsp_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rtsp_reader_open +#endif diff --git a/containers/rv9/CMakeLists.txt b/containers/rv9/CMakeLists.txt new file mode 100755 index 0000000..f65a7af --- /dev/null +++ b/containers/rv9/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_rv9 ${LIBRARY_TYPE} rv9_reader.c) + +target_link_libraries(reader_rv9 containers) + +install(TARGETS reader_rv9 DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rv9/rv9_reader.c b/containers/rv9/rv9_reader.c new file mode 100755 index 0000000..cb4ca40 --- /dev/null +++ b/containers/rv9/rv9_reader.c @@ -0,0 +1,335 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3])) +#define BI16(b) (((b)[0]<<8)|((b)[1])) + +#define FRAME_HEADER_LEN 20 +#define MAX_NUM_SEGMENTS 64 + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct { + uint32_t len; + uint32_t timestamp; + uint16_t sequence; + uint16_t flags; + uint32_t num_segments; + uint32_t seg_offset; +} RV9_FRAME_HEADER_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint8_t mid_frame; + uint32_t frame_read; + uint32_t frame_len; + RV9_FRAME_HEADER_T hdr; + uint8_t data[FRAME_HEADER_LEN + (MAX_NUM_SEGMENTS<<3) + 1]; + uint32_t data_len; + uint8_t type; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T rv9_read_file_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T codec; + uint8_t dummy[12]; + uint32_t length; + + if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; + + length = BI32(dummy); + if(length < 12 || length > 1024) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(dummy[4] != 'V' || dummy[5] != 'I' || dummy[6] != 'D' || dummy[7] != 'O' || + dummy[8] != 'R' || dummy[9] != 'V' || dummy[11] != '0') + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + switch(dummy[10]) { + case '4': codec = VC_CONTAINER_CODEC_RV40; break; + case '3': codec = VC_CONTAINER_CODEC_RV30; break; + case '2': codec = VC_CONTAINER_CODEC_RV20; break; + case '1': codec = VC_CONTAINER_CODEC_RV10; break; + default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + if (!track) + return VC_CONTAINER_SUCCESS; + + status = vc_container_track_allocate_extradata(p_ctx, track, length); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(READ_BYTES(p_ctx, track->format->extradata, length) != length) return VC_CONTAINER_ERROR_EOS; + track->format->extradata_size = length; + + track->format->codec = codec; + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T rv9_read_frame_header(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t seg_offset = (uint32_t) -1; + uint8_t *buffer = module->data + FRAME_HEADER_LEN; + + if(READ_BYTES(p_ctx, module->data, FRAME_HEADER_LEN) != FRAME_HEADER_LEN) return VC_CONTAINER_ERROR_EOS; + module->data_len = FRAME_HEADER_LEN; + + module->hdr.len = BI32(module->data); + module->hdr.timestamp = BI32(module->data+4); + module->hdr.sequence = BI16(module->data+8); + module->hdr.flags = BI16(module->data+10); + module->hdr.num_segments = BI32(module->data+16); + + module->frame_len = FRAME_HEADER_LEN + (module->hdr.num_segments * 8) + module->hdr.len; + + // if we have space, we store up the segments in memory so we can tell the frame + // type, since most streams have their type byte as the first follow the segment information. + // if we don't have space, then we just don't know the frame type, so will not emit timestamp + // information as we don't know if it's reliable. + if(module->hdr.num_segments <= MAX_NUM_SEGMENTS) + { + uint32_t i; + + if(READ_BYTES(p_ctx, buffer, 8*module->hdr.num_segments) != 8*module->hdr.num_segments) return VC_CONTAINER_ERROR_EOS; + module->data_len += (module->hdr.num_segments * 8); + + for (i=0; ihdr.num_segments; i++) + { + uint32_t valid_seg; + uint32_t offset; + + valid_seg = BI32(buffer); + offset = BI32(buffer+4); + + if (valid_seg && seg_offset > offset) + seg_offset = offset; + + // this boolean field should have only 0 or 1 values + if(valid_seg > 1) return VC_CONTAINER_ERROR_FORMAT_INVALID; + + buffer += 8; + } + } + + if(seg_offset == 0) + { + if (READ_BYTES(p_ctx, buffer, 1) != 1) return VC_CONTAINER_ERROR_EOS; + module->data_len += 1; + + module->type = (*buffer >> 5) & 3; + } + else + module->type = (uint8_t) -1; + + return VC_CONTAINER_SUCCESS; +} + +static uint32_t rv9_get_frame_data(VC_CONTAINER_T *p_ctx, uint32_t len, uint8_t *dest) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t ret = 0; + + // we may have read some data before into the data array, so + // check whether we've copied all this data out first. + if(module->frame_read < module->data_len) + { + uint32_t copy = MIN(len, module->data_len - module->frame_read); + if(dest) + { + memcpy(dest, module->data + module->frame_read, copy); + dest += copy; + } + ret += copy; + len -= copy; + } + + // if there is still more to do, we need to access the IO to do this. + if(len > 0) + { + if(dest) + ret += READ_BYTES(p_ctx, dest, len); + else + ret += SKIP_BYTES(p_ctx, len); + } + + module->frame_read += ret; + return ret; +} + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ +static VC_CONTAINER_STATUS_T rv9_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, + uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + unsigned int size; + + if(!module->mid_frame) + { + if((status = rv9_read_frame_header(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + module->mid_frame = 1; + module->frame_read = 0; + } + + packet->size = module->frame_len; + packet->pts = module->type < 3 ? module->hdr.timestamp * 1000LL : VC_CONTAINER_TIME_UNKNOWN; + packet->dts = packet->pts; + packet->track = 0; + packet->flags = module->type < 2 ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0; + if(module->frame_read == 0) + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = rv9_get_frame_data(p_ctx, module->frame_len - module->frame_read, NULL); + if(module->frame_read == module->frame_len) + { + module->frame_read = 0; + module->mid_frame = 0; + } + return STREAM_STATUS(p_ctx); + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->frame_len - module->frame_read, packet->buffer_size); + size = rv9_get_frame_data(p_ctx, size, packet->data); + if(module->frame_read == module->frame_len) + { + module->frame_read = 0; + module->mid_frame = 0; + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + packet->size = size; + + return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rv9_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(flags); + + if(*offset == 0LL && mode == VC_CONTAINER_SEEK_MODE_TIME) + { + SEEK(p_ctx, module->track->format->extradata_size); + module->mid_frame = 0; + module->frame_read = 0; + return STREAM_STATUS(p_ctx); + } + else + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rv9_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Check the file header */ + if(rv9_read_file_header(p_ctx, 0) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_RV40; + p_ctx->tracks[0]->is_enabled = true; + + if((status = rv9_read_file_header(p_ctx, p_ctx->tracks[0])) != VC_CONTAINER_SUCCESS) goto error; + + LOG_DEBUG(p_ctx, "using rv9 reader"); + + p_ctx->priv->pf_close = rv9_reader_close; + p_ctx->priv->pf_read = rv9_reader_read; + p_ctx->priv->pf_seek = rv9_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "rv9: error opening stream (%i)", status); + if(module) rv9_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function +********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rv9_reader_open +#endif diff --git a/containers/simple/CMakeLists.txt b/containers/simple/CMakeLists.txt new file mode 100755 index 0000000..0fbdc11 --- /dev/null +++ b/containers/simple/CMakeLists.txt @@ -0,0 +1,18 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_simple ${LIBRARY_TYPE} simple_reader.c) + +target_link_libraries(reader_simple containers) + +install(TARGETS reader_simple DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_simple ${LIBRARY_TYPE} simple_writer.c) + +target_link_libraries(writer_simple containers) + +install(TARGETS writer_simple DESTINATION ${VMCS_PLUGIN_DIR}) diff --git a/containers/simple/simple_common.h b/containers/simple/simple_common.h new file mode 100755 index 0000000..b75242c --- /dev/null +++ b/containers/simple/simple_common.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef SIMPLE_COMMON_H +#define SIMPLE_COMMON_H + +#define SIGNATURE_STRING "S1MPL3" +#define SIGNATURE_END_STRING "3LPM1S" + +/** List of configuration options supported in the header */ +#define CONFIG_VARIANT "VARIANT" +#define CONFIG_URI "URI" +#define CONFIG_CODEC_VARIANT "CODEC_VARIANT" +#define CONFIG_BITRATE "BITRATE" +#define CONFIG_UNFRAMED "UNFRAMED" +#define CONFIG_VIDEO_CROP "VIDEO_CROP" +#define CONFIG_VIDEO_ASPECT "VIDEO_ASPECT" + +#endif /* SIMPLE_COMMON_H */ diff --git a/containers/simple/simple_reader.c b/containers/simple/simple_reader.c new file mode 100755 index 0000000..f0ef55a --- /dev/null +++ b/containers/simple/simple_reader.c @@ -0,0 +1,599 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "simple_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MAX_LINE_SIZE 512 +#define LINE_PADDING 3 /* 2 for newline + 1 for null */ + +#define MAX_TRACKS 4 +#define MAX_HEADER_LINES 512 + +typedef enum SIMPLE_VARIANT_T +{ + VARIANT_DEFAULT = 0, + VARIANT_MMAL, + VARIANT_OMX +} SIMPLE_VARIANT_T; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct SIMPLE_PACKET_STATE_T +{ + unsigned int track_num; + unsigned int flags; + + uint64_t metadata_offset; /* Offset in metadata stream */ + uint32_t data_size; /* Size of current data packet */ + uint32_t data_left; /* Data left to read in current packet */ + + int64_t pts; + +} SIMPLE_PACKET_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + SIMPLE_PACKET_STATE_T *state; + SIMPLE_PACKET_STATE_T local_state; + + VC_CONTAINER_IO_T *io; + uint64_t data_offset; /* Current offset in data stream */ + char uri[MAX_LINE_SIZE+1]; + + SIMPLE_VARIANT_T variant; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; + + char line[MAX_LINE_SIZE + LINE_PADDING]; + + int64_t metadata_offset; + + /* Shared packet state. This is used when the tracks are in sync, + * and for the track at the earliest position in the file when they are + * not in sync */ + SIMPLE_PACKET_STATE_T state; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T simple_read_line( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int i, bytes = PEEK_BYTES(ctx, module->line, sizeof(module->line)-1); + + if (!bytes) + return VC_CONTAINER_ERROR_EOS; + + /* Find new-line marker */ + for (i = 0; i < bytes; i++) + if (module->line[i] == '\n') + break; + + /* Bail out if line is bigger than the maximum allowed */ + if (i == sizeof(module->line)-1) + { + LOG_ERROR(ctx, "line too big"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if (i < bytes) + { + module->line[i++] = 0; + if (i < bytes && module->line[i] == '\r') + i++; + } + module->line[i] = 0; /* Make sure the line is null terminated */ + + SKIP_BYTES(ctx, i); + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T simple_read_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_FOURCC_T fourcc; + int matches, width, height, channels, samplerate, bps, blockalign, value; + unsigned int lines = 1; + + /* Skip the signature */ + if (simple_read_line(ctx) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_CORRUPTED; + + while (lines++ < MAX_HEADER_LINES && + simple_read_line(ctx) == VC_CONTAINER_SUCCESS) + { + /* Our exit condition is the end signature */ + if (!memcmp(module->line, SIGNATURE_END_STRING, sizeof(SIGNATURE_STRING)-1)) + { + if (track) ctx->tracks[ctx->tracks_num++] = track; + return VC_CONTAINER_SUCCESS; + } + + /* Start of track description */ + if (!memcmp(module->line, "TRACK ", sizeof("TRACK ")-1)) + { + /* Add track we were constructing */ + if (track) ctx->tracks[ctx->tracks_num++] = track; + track = NULL; + + if (ctx->tracks_num >= MAX_TRACKS) + { + LOG_ERROR(ctx, "too many tracks, ignoring: %s", module->line); + continue; + } + track = vc_container_allocate_track(ctx, sizeof(*track->priv->module)); + if (!track) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->is_enabled = true; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + + if ((matches = sscanf(module->line, + "TRACK video, %4c, %i, %i", + (char *)&fourcc, &width, &height)) > 0) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + track->format->codec = fourcc; + if (matches > 1) track->format->type->video.width = width; + if (matches > 2) track->format->type->video.height = height; + } + else if ((matches = sscanf(module->line, + "TRACK audio, %4c, %i, %i, %i, %i", + (char *)&fourcc, &channels, &samplerate, &bps, + &blockalign)) > 0) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + track->format->codec = fourcc; + if (matches > 1) track->format->type->audio.channels = channels; + if (matches > 2) track->format->type->audio.sample_rate = samplerate; + if (matches > 3) track->format->type->audio.bits_per_sample = bps; + if (matches > 4) track->format->type->audio.block_align = blockalign; + } + if ((matches = sscanf(module->line, + "TRACK subpicture, %4c, %i", + (char *)&fourcc, &value)) > 0) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + track->format->codec = fourcc; + if (matches > 1) track->format->type->subpicture.encoding = value; + } + } + + if (!track) + continue; /* Nothing interesting */ + + /* VARIANT of the syntax */ + if (sscanf(module->line, CONFIG_VARIANT" %i", &value) == 1) + { + track->priv->module->variant = value; + LOG_FORMAT(ctx, CONFIG_VARIANT": %i", value); + } + /* URI for elementary stream */ + else if (sscanf(module->line, CONFIG_URI" %s", track->priv->module->uri) == 1) + LOG_FORMAT(ctx, CONFIG_URI": %s", track->priv->module->uri); + /* COCDEC_VARIANT of elementary stream */ + else if (sscanf(module->line, CONFIG_CODEC_VARIANT" %4c", (char *)&fourcc) == 1) + { + track->format->codec_variant = fourcc; + LOG_FORMAT(ctx, CONFIG_CODEC_VARIANT": %4.4s", (char *)&fourcc); + } + /* BITRATE of elementary stream */ + else if (sscanf(module->line, CONFIG_BITRATE" %i", &value) == 1) + { + track->format->bitrate = value; + LOG_FORMAT(ctx, CONFIG_BITRATE": %i", value); + } + /* UNFRAMED elementary stream */ + else if (!memcmp(module->line, CONFIG_UNFRAMED, sizeof(CONFIG_UNFRAMED)-1)) + { + track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + LOG_FORMAT(ctx, CONFIG_UNFRAMED); + } + /* VIDEO_CROP information */ + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + sscanf(module->line, CONFIG_VIDEO_CROP" %i, %i", &width, &height) == 2) + { + track->format->type->video.visible_width = width; + track->format->type->video.visible_height = height; + LOG_FORMAT(ctx, CONFIG_VIDEO_CROP": %i, %i", width, height); + } + /* VIDEO_ASPECT information */ + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + sscanf(module->line, CONFIG_VIDEO_ASPECT" %i, %i", &width, &height) == 2) + { + track->format->type->video.par_num = width; + track->format->type->video.par_den = height; + LOG_FORMAT(ctx, CONFIG_VIDEO_ASPECT": %i, %i", width, height); + } + } + + if (track) vc_container_free_track(ctx, track); + return VC_CONTAINER_ERROR_CORRUPTED; +} + +static uint32_t simple_convert_packet_flags(VC_CONTAINER_T *ctx, + unsigned int track_num, uint32_t flags) +{ + typedef struct { uint32_t from; uint32_t to; } convert_from_t; + const convert_from_t convert_from_mmal[] = + { {1<<1, VC_CONTAINER_PACKET_FLAG_FRAME_START}, + {1<<2, VC_CONTAINER_PACKET_FLAG_FRAME_END}, + {1<<3, VC_CONTAINER_PACKET_FLAG_KEYFRAME}, + {1<<4, VC_CONTAINER_PACKET_FLAG_DISCONTINUITY}, + {1<<5, VC_CONTAINER_PACKET_FLAG_CONFIG}, + {1<<6, VC_CONTAINER_PACKET_FLAG_ENCRYPTED}, + {0, 0} }; + const convert_from_t convert_from_omx[] = + { {0x10, VC_CONTAINER_PACKET_FLAG_FRAME_END}, + {0x20, VC_CONTAINER_PACKET_FLAG_KEYFRAME}, + {0x80, VC_CONTAINER_PACKET_FLAG_CONFIG}, + {0, 0} }; + const convert_from_t *convert_from = NULL; + int i; + + switch (ctx->tracks[track_num]->priv->module->variant) + { + case VARIANT_MMAL: convert_from = convert_from_mmal; break; + case VARIANT_OMX: convert_from = convert_from_omx; break; + default: break; + } + + if (convert_from) + { + uint32_t new_flags = 0; + for (i = 0; convert_from[i].from; i++) + if (convert_from[i].from & flags) + new_flags |= convert_from[i].to; + return new_flags; + } + + return flags; +} + +static int64_t simple_convert_packet_pts(VC_CONTAINER_T *ctx, + unsigned int track_num, int64_t pts, uint32_t flags) +{ + if (ctx->tracks[track_num]->priv->module->variant == VARIANT_OMX && + flags & 0x100) + return VC_CONTAINER_TIME_UNKNOWN; + + return pts; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_reader_read( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + SIMPLE_PACKET_STATE_T *state; + + /* If a specific track has been selected, use the track packet state */ + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + state = ctx->tracks[packet->track]->priv->module->state; + else + state = &module->state; + + /* Switch to the next packet when the current one is empty */ + if (!state->data_left) + { + unsigned int track_num, size; + int64_t pts; + int flags; + + SEEK(ctx, state->metadata_offset); + status = simple_read_line(ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + + if (sscanf(module->line, "%u %u %"PRIi64" %i", + &track_num, &size, &pts, &flags) != 4 && + (track_num = 0, sscanf(module->line, "%u %"PRIi64" %i", + &size, &pts, &flags)) != 3) + { + LOG_ERROR(ctx, "invalid metadata: %s", module->line); + return VC_CONTAINER_ERROR_CORRUPTED; + } + state->metadata_offset = STREAM_POSITION(ctx); + + if (track_num >= ctx->tracks_num) + { + LOG_DEBUG(ctx, "skipping %i bytes for track %d/%d", + size, track_num, ctx->tracks_num); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we are reading from the global state (i.e. normal read or forced + read from the track on the global state), and the track we found is + not on the global state, reconnect the two */ + if (state == &module->state && + ctx->tracks[track_num]->priv->module->state != &module->state) + { + LOG_DEBUG(ctx, "reconnect track %u to the global state", track_num); + ctx->tracks[track_num]->priv->module->state = &module->state; + module->state = ctx->tracks[track_num]->priv->module->local_state; + return VC_CONTAINER_ERROR_CONTINUE; + } + + state->data_size = state->data_left = size; + state->track_num = track_num; + state->flags = simple_convert_packet_flags(ctx, track_num, flags); + state->pts = simple_convert_packet_pts(ctx, track_num, pts, flags); + + /* Discard empty packets */ + if (!state->data_size && !state->flags) + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If there is data from another track skip past it */ + if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && + state->track_num != packet->track) + { + LOG_DEBUG(ctx, "skipping track %d/%d as we are ignoring it", + state->track_num, ctx->tracks_num); + + track_module = ctx->tracks[packet->track]->priv->module; + + /* Handle disconnection from global state */ + if (state == &module->state && + ctx->tracks[state->track_num]->priv->module->state == &module->state) + { + /* Make a copy of the global state */ + LOG_DEBUG(ctx, "using local state on track %d", packet->track); + track_module->local_state = module->state; + track_module->state = &track_module->local_state; + } + + track_module->state->data_left = 0; + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* + * From this point we know we have the packet which was requested + */ + + /* !!!! If we aren't in the right position in the file go there now. */ + + track_module = ctx->tracks[state->track_num]->priv->module; + packet->track = state->track_num; + packet->size = state->data_left; + packet->frame_size = (state->flags & VC_CONTAINER_PACKET_FLAG_FRAME) ? + state->data_size : 0; + packet->flags = state->flags; + packet->pts = state->pts; + packet->dts = VC_CONTAINER_TIME_UNKNOWN; + if (state->data_left != state->data_size) + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + track_module->data_offset += state->data_left; + state->data_left = 0; + return VC_CONTAINER_SUCCESS; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + return VC_CONTAINER_SUCCESS; + } + + /* Now try to read data into buffer */ + vc_container_io_seek(track_module->io, track_module->data_offset); + + packet->size = vc_container_io_read(track_module->io, packet->data, + MIN(packet->buffer_size, state->data_left)); + state->data_left -= packet->size; + track_module->data_offset += packet->size; + + if (state->data_left) + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return track_module->io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_reader_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + + for (; ctx->tracks_num > 0; ctx->tracks_num--) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[ctx->tracks_num-1]; + if (track->priv->module->io) + vc_container_io_close(track->priv->module->io); + vc_container_free_track(ctx, track); + } + + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint8_t h[sizeof(SIGNATURE_STRING)]; + unsigned int i; + + /* Check for the signature */ + if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h) || + memcmp(h, SIGNATURE_STRING, sizeof(SIGNATURE_STRING)-1)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using simple reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = module->tracks; + + status = simple_read_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + /* Open all the elementary streams */ + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + char *uri; + + track->priv->module->io = vc_container_io_open(track->priv->module->uri, + VC_CONTAINER_IO_MODE_READ, &status); + + /* URI might be relative to the path of the metadata file so + * try again with that new path */ + if (!track->priv->module->io && + (uri = malloc(strlen(ctx->priv->io->uri) + + strlen(track->priv->module->uri) + 1)) != NULL) + { + char *end; + + strcpy(uri, ctx->priv->io->uri); + + /* Find the last directory separator */ + for (end = uri + strlen(ctx->priv->io->uri) + 1; end != uri; end--) + if (*(end-1) == '/' || *(end-1) == '\\') + break; + strcpy(end, track->priv->module->uri); + + track->priv->module->io = vc_container_io_open(uri, + VC_CONTAINER_IO_MODE_READ, &status); + if (!track->priv->module->io) + LOG_ERROR(ctx, "could not open elementary stream: %s", uri); + free(uri); + } + if (!track->priv->module->io) + { + LOG_ERROR(ctx, "could not open elementary stream: %s", + track->priv->module->uri); + goto error; + } + } + + /* + * We now have all the information we really need to start playing the stream + */ + + module->metadata_offset = STREAM_POSITION(ctx); + + /* Initialise state for all tracks */ + module->state.metadata_offset = module->metadata_offset; + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + track->priv->module->state = &module->state; + } + + /* Look for the codec configuration data for each track so + * we can store it in the track format */ + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + VC_CONTAINER_PACKET_T packet; + packet.track = i; + status = VC_CONTAINER_ERROR_CONTINUE; + + while (status == VC_CONTAINER_ERROR_CONTINUE) + status = simple_reader_read(ctx, &packet, + VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); + if (status != VC_CONTAINER_SUCCESS) + continue; + + status = vc_container_track_allocate_extradata(ctx, track, packet.size); + if (status != VC_CONTAINER_SUCCESS) + continue; + + packet.data = track->format->extradata; + packet.buffer_size = packet.size; + packet.size = 0; + status = simple_reader_read(ctx, &packet, + VC_CONTAINER_READ_FLAG_FORCE_TRACK); + if (status != VC_CONTAINER_SUCCESS) + continue; + + track->format->extradata_size = packet.size; + } + + ctx->priv->pf_close = simple_reader_close; + ctx->priv->pf_read = simple_reader_read; + ctx->priv->pf_seek = simple_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_ERROR(ctx, "simple: error opening stream (%i)", status); + simple_reader_close(ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open simple_reader_open +#endif diff --git a/containers/simple/simple_writer.c b/containers/simple/simple_writer.c new file mode 100755 index 0000000..e0cf0f5 --- /dev/null +++ b/containers/simple/simple_writer.c @@ -0,0 +1,348 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "simple_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MAX_TRACKS 4 +#define MAX_LINE_SIZE 512 + +#define ES_SUFFIX "%s.%2.2i.%4.4s" +#define ES_SUFFIX_SIZE 8 + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + VC_CONTAINER_IO_T *io; + char *uri; + + bool config_done; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + char line[MAX_LINE_SIZE + 1]; + + VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; + bool header_done; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); +static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T simple_write_line( VC_CONTAINER_T *ctx, + const char *format, ...) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + va_list args; + int result; + + va_start(args, format); + result = vsnprintf(module->line, sizeof(module->line), format, args); + va_end(args); + + if (result >= (int)sizeof(module->line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + WRITE_BYTES(ctx, module->line, result); + _WRITE_U8(ctx, '\n'); + return STREAM_STATUS(ctx); +} + +static VC_CONTAINER_STATUS_T simple_write_header( VC_CONTAINER_T *ctx ) +{ + unsigned int i; + + simple_write_line(ctx, SIGNATURE_STRING); + + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + + if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + simple_write_line(ctx, "TRACK video, %4.4s, %i, %i", + (char *)&track->format->codec, + (int)track->format->type->video.width, + (int)track->format->type->video.height); + if ((track->format->type->video.visible_width && + track->format->type->video.visible_width != + track->format->type->video.width) || + (track->format->type->video.visible_height && + track->format->type->video.visible_height != + track->format->type->video.height)) + simple_write_line(ctx, CONFIG_VIDEO_CROP" %i, %i", + track->format->type->video.visible_width, + track->format->type->video.visible_height); + if (track->format->type->video.par_num && + track->format->type->video.par_den) + simple_write_line(ctx, CONFIG_VIDEO_ASPECT" %i, %i", + track->format->type->video.par_num, + track->format->type->video.par_den); + } + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + simple_write_line(ctx, "TRACK audio, %4.4s, %i, %i, %i, %i", + (char *)&track->format->codec, + (int)track->format->type->audio.channels, + (int)track->format->type->audio.sample_rate, + (int)track->format->type->audio.bits_per_sample, + (int)track->format->type->audio.block_align); + } + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + simple_write_line(ctx, "TRACK subpicture, %4.4s, %i", + (char *)&track->format->codec, + (int)track->format->type->subpicture.encoding); + } + else + { + simple_write_line(ctx, "TRACK unknown, %4.4s", + (char *)&track->format->codec); + } + + simple_write_line(ctx, CONFIG_URI" %s", track->priv->module->io->uri); + if (track->format->codec_variant) + simple_write_line(ctx, CONFIG_CODEC_VARIANT" %4.4s", + (char *)&track->format->codec_variant); + if (track->format->bitrate) + simple_write_line(ctx, CONFIG_BITRATE" %i", track->format->bitrate); + if (!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + simple_write_line(ctx, CONFIG_UNFRAMED); + } + + simple_write_line(ctx, SIGNATURE_END_STRING); + + ctx->priv->module->header_done = true; + return STREAM_STATUS(ctx); +} + +static VC_CONTAINER_STATUS_T simple_write_config( VC_CONTAINER_T *ctx, + unsigned int track_num, VC_CONTAINER_PACKET_T *pkt) +{ + VC_CONTAINER_TRACK_T *track = ctx->tracks[track_num]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T packet; + + track->priv->module->config_done = true; + + if (track->format->extradata_size) + { + packet.size = track->format->extradata_size; + packet.data = track->format->extradata; + packet.track = track_num; + packet.pts = pkt ? pkt->pts : VC_CONTAINER_TIME_UNKNOWN; + packet.flags = 0; + packet.flags |= VC_CONTAINER_PACKET_FLAG_CONFIG; + + status = simple_writer_write(ctx, &packet); + } + + return status; +} + +static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_STATUS_T status; + const char *uri = vc_uri_path(ctx->priv->uri); + unsigned int uri_size = strlen(uri); + + /* Allocate and initialise track data */ + if (ctx->tracks_num >= MAX_TRACKS) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + ctx->tracks[ctx->tracks_num] = track = + vc_container_allocate_track(ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + + uri_size + ES_SUFFIX_SIZE + 1); + if (!track) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if (format->extradata_size) + { + status = vc_container_track_allocate_extradata(ctx, track, format->extradata_size); + if (status != VC_CONTAINER_SUCCESS) + goto error; + } + vc_container_format_copy(track->format, format, format->extradata_size); + + track->priv->module->uri = (char *)&track->priv->module[1]; + snprintf(track->priv->module->uri, uri_size + ES_SUFFIX_SIZE + 1, + ES_SUFFIX, uri, ctx->tracks_num, (char *)&track->format->codec); + + LOG_DEBUG(ctx, "opening elementary stream: %s", track->priv->module->uri); + track->priv->module->io = vc_container_io_open(track->priv->module->uri, + VC_CONTAINER_IO_MODE_WRITE, &status); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(ctx, "error opening elementary stream: %s", + track->priv->module->uri); + goto error; + } + + ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + if (track) + vc_container_free_track(ctx, track); + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_writer_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + for (; ctx->tracks_num > 0; ctx->tracks_num--) + { + vc_container_io_close(ctx->tracks[ctx->tracks_num-1]->priv->module->io); + vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); + } + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_STATUS_T status; + + if (!ctx->priv->module->header_done) + { + status = simple_write_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (!ctx->tracks[packet->track]->priv->module->config_done) + { + status = simple_write_config(ctx, packet->track, packet); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + /* Write the metadata */ + status = simple_write_line(ctx, "%i %i %"PRIi64" 0x%x", + (int)packet->track, (int)packet->size, packet->pts, packet->flags); + if (status != VC_CONTAINER_SUCCESS) + return status; + + /* Write the elementary stream */ + vc_container_io_write(ctx->tracks[packet->track]->priv->module->io, + packet->data, packet->size); + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_writer_control( VC_CONTAINER_T *ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_ES_FORMAT_T *format; + + switch (operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *); + return simple_write_add_track(ctx, format); + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + simple_write_header( ctx ); + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "smpl") && strcasecmp(extension, "simple")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using simple writer"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = module->tracks; + + ctx->priv->pf_close = simple_writer_close; + ctx->priv->pf_write = simple_writer_write; + ctx->priv->pf_control = simple_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(ctx, "simple: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open simple_writer_open +#endif diff --git a/containers/test/CMakeLists.txt b/containers/test/CMakeLists.txt new file mode 100755 index 0000000..7d36352 --- /dev/null +++ b/containers/test/CMakeLists.txt @@ -0,0 +1,66 @@ +# Generate test application +add_executable(containers_test test.c) +target_link_libraries(containers_test -Wl,--no-whole-archive containers) +install(TARGETS containers_test DESTINATION bin) + +# Generate test application +add_executable(containers_check_frame_int check_frame_int.c) +target_link_libraries(containers_check_frame_int -Wl,--no-whole-archive containers) +install(TARGETS containers_check_frame_int DESTINATION bin) + +# Generate autotest application +#add_executable(containers_autotest autotest.cpp crc_32.c) +#target_link_libraries(containers_autotest -Wl,--no-whole-archive containers}) +#install(TARGETS containers_autotest DESTINATION bin) + +# Helper code to provide non-blocking console input +if (WIN32) +set( NB_IO_SOURCE nb_io_win32.c ) +elseif (UNIX) +set( NB_IO_SOURCE nb_io_unix.c ) +endif (WIN32) +set(extra_test_SRCS nb_io_win32.c autotest.cpp crc_32.c) +add_custom_target(containers_test_extra + COMMAND touch ${extra_test_SRCS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/test) +add_dependencies(containers_test containers_test_extra) + +# Generate net test applications +add_executable(containers_stream_client stream_client.c ${NB_IO_SOURCE}) +target_link_libraries(containers_stream_client containers) +install(TARGETS containers_stream_client DESTINATION bin) + +add_executable(containers_stream_server stream_server.c) +target_link_libraries(containers_stream_server containers) +install(TARGETS containers_stream_server DESTINATION bin) + +add_executable(containers_datagram_sender datagram_sender.c) +target_link_libraries(containers_datagram_sender containers) +install(TARGETS containers_datagram_sender DESTINATION bin) + +add_executable(containers_datagram_receiver datagram_receiver.c) +target_link_libraries(containers_datagram_receiver containers) +install(TARGETS containers_datagram_receiver DESTINATION bin) + +add_executable(containers_rtp_decoder rtp_decoder.c ${NB_IO_SOURCE}) +target_link_libraries(containers_rtp_decoder containers) +install(TARGETS containers_rtp_decoder DESTINATION bin) + +# Generate URI test application +add_executable(containers_test_uri test_uri.c) +target_link_libraries(containers_test_uri containers) +install(TARGETS containers_test_uri DESTINATION bin) + +# Generate URI pipe application +add_executable(containers_uri_pipe uri_pipe.c ${NB_IO_SOURCE}) +target_link_libraries(containers_uri_pipe containers) +install(TARGETS containers_uri_pipe DESTINATION bin) + +# Generate bit stream test application +add_executable(containers_test_bits test_bits.c) +target_link_libraries(containers_test_bits containers) +install(TARGETS containers_test_bits DESTINATION bin) + +# Generate packet file dump application +add_executable(containers_dump_pktfile dump_pktfile.c) +install(TARGETS containers_dump_pktfile DESTINATION bin) diff --git a/containers/test/autotest.cpp b/containers/test/autotest.cpp new file mode 100755 index 0000000..2a9a3cb --- /dev/null +++ b/containers/test/autotest.cpp @@ -0,0 +1,1958 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MS compilers require __cdecl calling convention on some callbacks. Other compilers reject it. +#if (!defined(_MSC_VER) && !defined(__cdecl)) +#define __cdecl +#endif + +extern "C" +{ +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_io.h" + +// Declare the CRC32 function from Snippets.org (obtained from the wayback machine, see crc_32.c) + uint32_t crc32buf(const uint8_t *buf, size_t len); +} + +// Error logger. It looks a little like std::cout, but it will stop the program if required on an end-of-line, +// returning an error to the caller. This is intended to allow automated tests to run to first fail, +// but interactive tests to run all the way through (using -k) +class ERROR_LOGGER_T +{ +public: + ERROR_LOGGER_T(): error_is_fatal(true), had_any_error(false) + {} + + ~ERROR_LOGGER_T() + { + // Flush out any bits of message that have been assembled, but not yet flushed out. + std::cerr << stream.str(); + + if (had_any_error) + { + exit(VC_CONTAINER_ERROR_FAILED); + } + } + + // Tell the logger that we're going to keep going after an error if we possibly can + void set_errors_not_fatal() + { + error_is_fatal = false; + } + + // Tell the error logger to redirect its output to this stream instead of the default stderr. + // This is used when dumping is enabled, so that errors become part of the stream description. + void set_output(std::ostream* new_stream) + { + output_stream = new_stream; + } + + // Generic inserter. Always calls through to the inserter for the base class. + template ERROR_LOGGER_T& operator<<(const T& object) + { + stream << object; + had_any_error = true; + return *this; + } + + // specialised inserter for iomanip type objects. Ensures that this object, and not the contained stream, + // is passed to the object. + ERROR_LOGGER_T& operator<<(ERROR_LOGGER_T& (__cdecl *_Pfn)(ERROR_LOGGER_T&)) + { + return _Pfn(*this); + } + + // implementation of flush. This is called by endl, and if the flags are set the program will stop. + ERROR_LOGGER_T& flush() + { + // If required send it to the quoted stream + if (output_stream) + { + *output_stream << stream.str(); + } + else + { + // just send it to stderr + std::cerr << stream.str(); + } + + stream.clear(); // reset any odd flags + stream.str(""); // empty the string + + if (error_is_fatal) + { + exit(VC_CONTAINER_ERROR_FAILED); + } + + return *this; + } + +private: + // set true if should stop on first error (usual for smoke testing) + // or keep going (usual if you want to look at the logs). Controlled by config -k flag (as for make) + bool error_is_fatal; + + // Set true if we've ever had an error. This way the app will return an error even if it kept going after it. + bool had_any_error; + + // The current error message + std::ostringstream stream; + + // The output stream, if not defaulted + std::ostream* output_stream; +}; + +namespace std +{ + // GCC insists I say this twice: + ERROR_LOGGER_T& ends(ERROR_LOGGER_T& logger); + + // implementation of std::ends for the error logger - flushes the message. + ERROR_LOGGER_T& ends(ERROR_LOGGER_T& logger) + { + logger << "\n"; + logger.flush(); + return logger; + } +} + +// Internal to this file, PTS is stored in one of these. other code uses int64_t directly. +typedef int64_t PTS_T; + +struct PACKET_DATA_T +{ + VC_CONTAINER_PACKET_T info; // copy of the API's data + std::vector buffer; // copy of the data contained, or empty if we have exceeded configuration.mem_max + uint32_t crc; // CRC32 of the data. + + // Constructor. Zeroes the whole content. + PACKET_DATA_T(): /* info((VC_CONTAINER_PACKET_T){0}), */ buffer(), crc(0) + { + // The syntax above for initialising info is probably correct according to C++ 0x. + // Unfortunately the MS C++ compiler in VS2012 isn't really C++0x compliant. + memset(&info, 0, sizeof(info)); + } +}; + +typedef uint32_t STREAM_T; + +// Packets in a file in an arbitrary order, usually the order they were read. +typedef std::list ALL_PACKETS_T; + +// Packets keyed by time (excludes packets with no PTS). This collection must be on a per-stream basis. +typedef std::map TIMED_PACKETS_T; + +// Packets with times. Must be split per-stream as multiple streams may have the same PTS. +// (It's actually unusual for more than one stream to have key frames) +typedef std::map STREAM_TIMED_PACKETS_T; + +// structure parsing and holding configuration information for the program. +struct CONFIGURATION_T +{ + // maximum size of in-memory buffers holding file data. Set by -m + size_t mem_max; + + // The source file or URL being processed + std::string source_name; + + // trace verbosity, to reflect the older test application. Set by -v. + int32_t verbosity, verbosity_input, verbosity_output; + + // fatal errors flag set by -k + bool errors_not_fatal; + + // dump-path for packet summary set by -d. May be std::cout. + mutable std::ostream* dump_packets; + + // tolerance values set by -t. + // Ideally when we seek to a time all the tracks would be right on it, but that's not always possible. + // When we don't have seek_forwards set: + // - The video must not be after the requested time + // - The video must not be more than + PTS_T tolerance_video_early; + // microseconds before the desired time. A 'good' container will hit it bang on if the supplied time is a video key frame. + PTS_T tolerance_video_late; + + // - The other tracks must not be more than + PTS_T tolerance_other_early; + PTS_T tolerance_other_late; + // from the time of the video frame. If forcing is not supported we have to take what's in the file. + + // If set by -p a test is performed re-reading the file using a packet buffer smaller than the expected packet: + // If <1 it's a proportion (e.g 0.5 will read half the packet, then the other half on a subsequent read) + // If 1 or more it's a size (1 will read 1 byte at a time; 100 not more than 100 bytes in each read) + // Defaults to -ve value, which means don't do this test. + double packet_buffer_size; + + // Constructor + CONFIGURATION_T(int argc, char** argv) + : mem_max(0x40000000) // 1Gb for 32-bit system friendliness. + , verbosity(0) // no trace + , verbosity_input(0) + , verbosity_output(0) + , errors_not_fatal(false) + , dump_packets(nullptr) + , tolerance_video_early(100000) // 100k uS = 100mS + , tolerance_video_late(0) + , tolerance_other_early(100000) // 100k uS = 100mS + , tolerance_other_late(1000000) // 1000k uS = 1S + , packet_buffer_size(-1) + { + if (argc < 2) + { + std::cout << + "-d produce packet dump (to file if specified)" << std::endl << + "-k keep going on errors" << std::endl << + "-m max memory buffer for packet data (default 1Gb). If the file is large not all will be validated properly." << std::endl << + "-p re-read using a small packet buffer. If >= 1 this is a size; if < 1 a proportion of the packet" << std::endl << + "-v verbose mode." << std::endl << + " -vi input verbosity only" << std::endl << + " -vo output verbosity only" << std::endl << + " add more vvv to make it more verbose" << std::endl << + "-t seek error tolerance in microseconds" << std::endl << + " tv video streams" << std::endl << + " to other streams" << std::endl << + " te, tl all streams earliness, lateness" << std::endl << + " tvl, tol video, other lateness" << std::endl << + " toe, tve video, other earliness" << std::endl << std::endl << + + "example: autotest -k 1-128.wmv -vvvvv -t1000000" << std::endl << + " tests 1-128.wmv. Keeps going on errors. Very verbose. Tolerant of errors up to 1s in seeks." << std::endl; + exit(0); + } + // Parse each argument + for (int arg = 1; arg > mem_max; + if (!number.eof()) + { + error(arg, argstr, "Size cannot be parsed"); + } + } + break; + + case 'p': + { + std::istringstream number(argstr); + + // throw away the p + number.ignore(1); + + if (number.eof()) + { + error(arg, argstr, "Packet re-read size not supplied"); + } + + // read the number + number >> packet_buffer_size; + if (!number.eof()) + { + error(arg, argstr, "Size cannot be parsed"); + } + } + break; + + case 't': + // error tolerance + { + std::istringstream stream(argstr); + + stream.ignore(1); // throw away the t + + // flags for which tolerance we are processing. + bool video = true, other = true, earliness=true, lateness = true; + int64_t tolerance; + + // If there's a v or an o it's video or other only + if (!stream.eof()) + { + switch (stream.peek()) + { + case 'v': + other = false; // if he said video we don't touch the other params + stream.ignore(1); // throw away the letter + break; + + case 'o': + video = false; + stream.ignore(1); // throw away the letter + break; + + // do nothing on other chars. + } + } + + // If there's an l or an e it's late or early only + if (!stream.eof()) + { + switch (stream.peek()) + { + case 'l': + earliness = false; + stream.ignore(1); // throw away the letter + break; + + case 'e': + lateness = false; + stream.ignore(1); // throw away the letter + break; + + // do nothing on other chars. + } + } + + // read the number that follows + if (stream.eof()) + { + error(arg, argstr, "tolerance not supplied"); + } + else + { + // read the number + stream >> tolerance; + if (!stream.eof()) + { + error(arg, argstr, "Number cannot be parsed"); + } + + if (video && earliness) + { + tolerance_video_early = tolerance; + } + if (video && lateness) + { + tolerance_video_late = tolerance; + } + if (other && earliness) + { + tolerance_other_early = tolerance; + } + if (other && lateness) + { + tolerance_other_late = tolerance; + } + } + } + break; + + // verbosity. v, vi or vo followed by zero or more extra v. + case 'v': + process_vees(arg, argstr); + break; + + // anything else + default: + error(arg, argstr, "is not understood"); + } + } + } + + if (source_name.empty()) + { + std::cerr << "No source name supplied"; + exit(VC_CONTAINER_ERROR_URI_NOT_FOUND); + } + + if (verbosity != 0) + { + if (verbosity_input == 0) + { + verbosity_input = verbosity; + } + + if (verbosity_output == 0) + { + verbosity_output = verbosity; + } + } + + std::cout << "Source: " << source_name << std::endl; + std::cout << "Max buffer size: " << mem_max << " 0x" << std::hex << mem_max << std::dec << std::endl; + std::cout << "Verbosity: " << verbosity << std::endl; + std::cout << "Input verbosity: " << verbosity_input << std::endl; + std::cout << "Output verbosity: " << verbosity_output << std::endl; + std::cout << "Continue on errors: " << (errors_not_fatal ? 'Y' : 'N') << std::endl; + std::cout << "Seek tolerance (uS)" << std::endl; + std::cout << " Video Early: " << tolerance_video_early << std::endl; + std::cout << " Video Late: " << tolerance_video_late << std::endl; + std::cout << " Other Early: " << tolerance_other_early << std::endl; + std::cout << " Other Late: " << tolerance_other_late << std::endl; + std::cout << "Dump Summary: " << (dump_packets ? 'Y' : 'N') << std::endl; + } +private: + + // processing for -v parameter. + void process_vees(int arg, const std::string& argstr) + { + std::istringstream vees(argstr); + + // we know we have v, so we can drop it. + vees.ignore(1); + + int32_t* which_param = &verbosity; + int32_t value = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + + // process the rest of the characters, if any + switch (vees.peek()) + { + case 'v': + // do nothing yet + break; + + case 'i': + which_param = &verbosity_input; + vees.ignore(1); + break; + + case 'o': + which_param = &verbosity_output; + vees.ignore(1); + break; + + default: + if (vees.peek() != std::char_traits::eof()) + { + error(arg, argstr, "verbosity is not understood"); + } + break; + } + + while (1) + { + int next = vees.get(); + if (next == 'v') + { + // add verbosity + value = (value << 1) | 1; + } + else if (next == std::char_traits::eof()) + { + break; + } + else + { + error(arg, argstr, "verbosity is not understood"); + } + } + + // store what we parsed. + *which_param = value; + } + + // error handling function. Does not return. + void error(int arg, const std::string& argstr, const std::string& msg) + { + std::cerr << "Argument " << arg << " \"" << argstr << "\" " << msg; + exit(VC_CONTAINER_ERROR_INVALID_ARGUMENT); + } +}; + +// Most of the functionality of the program is in this class, or its subsidiaries. +class TESTER_T +{ +public: + // Construct, passing command line parameters + TESTER_T(int argc, char**argv) + : configuration(argc, argv) + , video_stream(std::numeric_limits::max()) + { + // If the configuration said keep going on errors tell the logger. + if (configuration.errors_not_fatal) + { + error_logger.set_errors_not_fatal(); + } + + // Tell it where any -d routed the file summary + error_logger.set_output(configuration.dump_packets); + } + + // Run the tests + VC_CONTAINER_STATUS_T run() + { + /* Set the verbosity */ + vc_container_log_set_verbosity(0, configuration.verbosity_input); + + // Open the container + p_ctx = vc_container_open_reader(configuration.source_name.c_str(), &status, 0, 0); + + if(!p_ctx || (status != VC_CONTAINER_SUCCESS)) + { + error_logger << "error opening file " << configuration.source_name << " Code " << status << std::ends; + } + + // Find the video stream. + for(size_t i = 0; i < p_ctx->tracks_num; i++) + { + if (p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + if (video_stream == std::numeric_limits::max()) + { + video_stream = i; + } + else + { + // we've found two video streams. This is not necessarily an error, but it's certainly odd - perhaps it's the angles + // from a DVD. We don't expect to see it, but report it anyway. Don't stop, just assume that the first one is the one we want. + error_logger << "Both track " << video_stream << " and " << i << " are marked as video streams" << std::ends; + } + } + } + + if (video_stream == std::numeric_limits::max()) + { + error_logger << "No video track found" << std::ends; + } + + // Read all the packets sequentially. This will gve us all the metadata of the packets, + // and (up to configuration.mem_max) will also store the packet data. + read_sequential(); + + // Now we have some packets we can initialise our internal RNG. + init_crg(); + + // Find all the keyframes in the collection we just read. + find_key_packets(); + + // Seeking tests. Only if the container supports it. + // We do this first, because one of the checks is whether or not the container supports seeking, + // and we want to be able to seek back between tests. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_CAN_SEEK) + { + /// Seek to PTS 0 (this ought to be the beginning) + PTS_T beginning = 0; + status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Container failed to seek to the beginning - status " << status << std::ends; + + // This is fatal. + exit(status); + } + + // Ask for info about the first packet + PACKET_DATA_T actual; + status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Read info failed after seek to the beginning - status " << status << std::ends; + } + else + { + PACKET_DATA_T& expected = all_packets.front(); + // Compare some selected data to check that this genuinely is the first packet. + // We don't actually do a read. + if ((expected.info.pts != actual.info.pts) + || (expected.info.dts != actual.info.dts) + || (expected.info.size != actual.info.size) + || (expected.info.frame_size != actual.info.frame_size) + || (expected.info.track != actual.info.track) + || (expected.info.flags != actual.info.flags)) + { + // Copy the CRC from the expected value. That will stop compare_packets whinging. + actual.crc = expected.crc; + + // report the discrepancy + error_logger << "Seek to time zero did not arrive at beginning: " + << compare_packets(expected, actual) << std::ends; + } + } + + // Perform seeks to find all the packets that a seek will go to. + check_indices(0); + check_indices(VC_CONTAINER_SEEK_FLAG_FORWARD); + + // If it supports forcing then seek to each of those locations, and force read all the tracks. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) + { + check_seek_then_force(0); + check_seek_then_force(VC_CONTAINER_SEEK_FLAG_FORWARD); + } + + seek_randomly(0); + seek_randomly(VC_CONTAINER_SEEK_FLAG_FORWARD); + + // todo more? + } + else + { + // The file claims not to support seeking. Perform a seek, just to check this out. + PTS_T beginning = 0; + + status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); + + if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + { + error_logger << "Container did not reject seek when its capabilities said it is not supported" << std::ends; + } + } + + // If the config didn't ask for this test don't do it. + if (configuration.packet_buffer_size > 0) + { + re_seek_to_beginning(); + + // Read through the file, reading the packet in several bites (bite size controlled by -p parameter) + check_partial_reads(); + + // Check forcing (this doesn't require seek) + if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) + { + re_seek_to_beginning(); + check_forcing(); + } + } + + std::cout << "Test Complete" << std::endl; + + // If anything failed then the error logger will replace this error code. So always return 0 from here. + return VC_CONTAINER_SUCCESS; + } +private: + // read packets from the file, and stash them away. + void read_sequential() + { + // Use the same buffer for reading each of the packets. It's cheaper to copy the (usually small) data + // from this buffer than to create a new one for each packet, then resize it. + // 256k is the size used in the other program, and is bigger than any known packet. Increase it + // if a bigger one is found. + std::vector buffer(256*1024, 0); + + std::map packet_counts; + + // Count of memory used for packet buffers. + size_t memory_buffered = 0; + + if (configuration.dump_packets) + { + *configuration.dump_packets << "pts" << ",\t" << "track" << ",\t" << "size" << ",\t" << "crc" << std::endl; + } + + while (1) // We break out at EOF. + { + // Packet object + PACKET_DATA_T packet; + + // Use the shared buffer for the moment, + packet.info.data = &buffer[0]; + packet.info.buffer_size = buffer.size(); + + // Read data. + status = vc_container_read(p_ctx, &packet.info, 0); + + if (packet.info.size >= buffer.size()) + { + std::cerr << "Packet size limit exceeeded. Increase internal buffer size!"; + exit(VC_CONTAINER_ERROR_FAILED); + } + + if (status == VC_CONTAINER_SUCCESS) + { + // Calculate the CRC of the packet + packet.crc = crc32buf(&buffer[0], packet.info.size); + + // If there is any data, and we haven't exceeded our size limit... + if ((packet.info.size > 0) && (memory_buffered < configuration.mem_max)) + { + // Copy the data we read across from the big buffer to our local one + packet.buffer.assign(buffer.begin(), buffer.begin() + packet.info.size); + + // count how much we have buffered + memory_buffered += packet.info.size; + + // wipe the bit of the big buffer we used + memset(&buffer[0], 0, packet.info.size); + } + else + { + // not storing any data, either because there is none or we have no space. + packet.buffer.clear(); + } + + // Clear the pointer in our data. It won't be valid later. + packet.info.data = 0; + + // Set the size of the buffer to match what is really there. + packet.info.buffer_size = packet.info.size; + + // count the packet + ++packet_counts[packet.info.track]; + + // store it + all_packets.push_back(packet); + + // perhaps dump it + if (configuration.dump_packets) + { + if (packet.info.pts < 0) + { + *configuration.dump_packets << "-"; + } + else + { + *configuration.dump_packets << packet.info.pts; + } + + *configuration.dump_packets + << ",\t" << packet.info.track << ",\t" << packet.info.size << ",\t" << packet.crc << std::endl; + } + } + else if (status == VC_CONTAINER_ERROR_EOS) + { + break; + } + else + { + error_logger << "error reading file " << configuration.source_name << " Code " << status << std::ends; + } + } + + std::cout << std::dec << "File has " << packet_counts.size() << " tracks." << std::endl; + + // Print the packet count for each stream. Also, while iterating, save all the stream numbers. + for(std::map::const_iterator stream = packet_counts.begin(); stream != packet_counts.end(); ++stream) + { + // Print the packet count + std::cout << "Stream " << stream->first << " has " << stream->second << " packets." << std::endl; + + // Note that we have a stream with this number. + all_streams.insert(stream->first); + }; + + if (p_ctx->tracks_num != packet_counts.size()) + { + error_logger << "The file header claims " << p_ctx->tracks_num + << " but " << packet_counts.size() << " streams were found" << std::ends; + } + } + + // Search the all_packets collection for key frames, and store them in all_key_packets + void find_key_packets() + { + // The last known PTS for each stream. + std::map last_pts_in_stream; + + for (ALL_PACKETS_T::const_iterator packet = all_packets.begin(); packet != all_packets.end(); ++packet) + { + PTS_T pts = packet->info.pts; + STREAM_T stream = packet->info.track; + + // If it has a PTS check they are an ascending sequence. + if (pts >= 0) + { + // Find the last PTS, if any, for this stream + std::map::const_iterator last_known_pts = last_pts_in_stream.find(stream); + + if ((last_known_pts != last_pts_in_stream.end()) && (pts <= last_known_pts->second)) + { + // the PTS isn't bigger than the previous best. This is an error. + error_logger << "Out of sequence PTS " << pts << " found. Previous largest was " + << last_pts_in_stream[stream] << std::ends; + } + + // store it (even if bad) + last_pts_in_stream[stream] = pts; + + // Store in the collection of packets with PTSes + all_pts_packets[stream].insert(std::make_pair(pts, packet)); + + // if it is also a keyframe + if (packet->info.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) + { + // Put it into the collection for its track. + all_key_packets[stream].insert(std::make_pair(pts, packet)); + } + } + } + } + + // Check which locations can be accessed by a seek, with or without VC_CONTAINER_SEEK_FLAG_FORWARD. + void check_indices(VC_CONTAINER_SEEK_FLAGS_T direction) + { + STREAM_TIMED_PACKETS_T& index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; + // Go through each stream that contains key packets (usually only one) + for (STREAM_TIMED_PACKETS_T::const_iterator stream_keys = all_key_packets.begin(); + stream_keys != all_key_packets.end(); + ++stream_keys) + { + // Start with a PTS just after the last key packet, and repeat until we fall off the beginning. + for (PTS_T target_pts = stream_keys->second.rbegin()->first + 1; target_pts >= 0; /* no decrement */) + { + // copy the PTS value to pass to vc_container_seek + PTS_T actual_pts = target_pts; + + // Seek to that position in the file. + status = vc_container_seek(p_ctx, &actual_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " seeking to PTS " << target_pts << std::ends; + continue; // if errors are not fatal we'll try again 1uS earlier. + } + + // Check whether this seek reported that it went somewhere sensible + check_correct_seek(direction, actual_pts, target_pts); + + // Validate that the place it said we arrived at is correct - read info for the first packet in any stream + { + PACKET_DATA_T packet; + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading info for packet at PTS " << target_pts << std::ends; + continue; // stop trying to read at this PTS. + } + + if (packet.info.pts != actual_pts) + { + error_logger << "Incorrect seek. Container claimed to have arrived at " + << actual_pts << " but first packet "; + + if (packet.info.pts < 0) + { + error_logger << " had no PTS"; + } + else + { + error_logger << "was at " << packet.info.pts; + } + + error_logger << std::ends; + } + } + + // We'll perform reads until we've had a packet with PTS from every stream. + for (std::set unfound_streams = all_streams; + !unfound_streams.empty(); + /* unfound_streams.erase(packet.info.track) */ ) + { + // Read a packet. We can't be sure what track it will be from, so first read the info... + PACKET_DATA_T packet; + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading info for packet following PTS " << target_pts << std::ends; + break; // stop trying to read at this PTS. + } + + // allocate a buffer. + packet.buffer.resize(packet.info.size); + packet.info.data = &packet.buffer[0]; + packet.info.buffer_size = packet.info.size; + + // perform the read + status = vc_container_read(p_ctx, &packet.info, 0); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading packet following PTS " << target_pts << std::ends; + break; // stop trying to read at this PTS. + } + + STREAM_T stream = packet.info.track; + + // If it has no PTS we can't use it. Read another one. + if (packet.info.pts < 0) + { + // The first packet we found for a stream has no PTS. + // That's odd, because we wouldn't be able to play it. However, + // if the stream doesn't permit forcing it may be inevitable. + if ((unfound_streams.find(stream) != unfound_streams.end()) + && ((p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) != 0)) + { + error_logger << "Packet in stream " << stream + << " has no PTS after seeking to PTS " << target_pts << std::ends; + } + continue; + } + + // Search for the PTS collection for this stream + STREAM_TIMED_PACKETS_T::iterator stream_packets = all_pts_packets.find(stream); + + if (stream_packets == all_pts_packets.end()) + { + error_logger << "Packet from unknown stream " << stream + << "found when reading packet at PTS " << target_pts << std::ends; + continue; // try reading another packet. + } + + // Look for the packet for this PTS in this stream. + TIMED_PACKETS_T::const_iterator expected_packet = stream_packets->second.find(packet.info.pts); + + if (expected_packet == stream_packets->second.end()) + { + error_logger << "Read packet from stream " << stream << " has unknown PTS " << packet.info.pts << std::ends; + continue; + } + + // calculate the CRC + packet.crc = crc32buf(&packet.buffer[0], packet.info.size); + + // Validate that the data is the data we found when we did the sequential reads. + std::string anyerror = compare_packets(*expected_packet->second, packet); + + if (!anyerror.empty()) + { + error_logger << "Incorrect data found at PTS " << actual_pts << anyerror << std::ends; + } + + // If this is the first packet we found for this stream + if (unfound_streams.find(stream) != unfound_streams.end()) + { + // Store the packet we found. Note we key it by the PTS we used for the seek, + // not its own PTS. This should mean next time we seek to that PTS we'll get this packet. + index_positions[stream].insert(std::make_pair(target_pts, expected_packet->second)); + + // Check whether the time on the packet is reasonable - it ought to be close to the time achieved. + check_seek_tolerance(packet, actual_pts); + + // Now we've found a packet from this stream we're done with it, so note we don't care about it any more. + unfound_streams.erase(stream); + } + } // repeat until we've had a packet for every track + + // Adjust the target location + if (actual_pts > target_pts) + { + // Find the next packet down from where we looked, and try there. + TIMED_PACKETS_T this_stream_keys = stream_keys->second; + TIMED_PACKETS_T::const_iterator next_packet = this_stream_keys.lower_bound(target_pts); + + // If there is such a packet that it has more than this PTS, and it isn't the first packet + if ((next_packet != this_stream_keys.begin()) && + (next_packet != this_stream_keys.end())) + { + // pull out the PTS from the one before, and ask for a microsecond past it. + PTS_T new_target_pts = (--next_packet)->first + 1; + if (new_target_pts >= target_pts) + { + --target_pts; + } + else + { + target_pts = new_target_pts; + } + } + else + { + // There's no packet earlier than where we are looking. + // First time try 1 next time try zero. + target_pts = target_pts != 1 ? 1 : 0; + } + } + else if (actual_pts < (target_pts - 1)) + { + // The place we arrived at is well before where we wanted. + // Next time go just after where we arrived. + target_pts = actual_pts + 1; + } + else + { + // If the place we asked for is where we arrived, next time try one microsecond down. + --target_pts; + } + } + } + } + + // Seek to all feasible locations and perform force reads on all possible tracks + void check_seek_then_force(VC_CONTAINER_SEEK_FLAGS_T direction) + { + // Depending on whether we are doing forward or reverse seeks pick a collection of places a seek can go to. + STREAM_TIMED_PACKETS_T index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; + + // Go through all the streams + for (STREAM_TIMED_PACKETS_T::const_iterator index_stream = index_positions.begin(); + index_stream != index_positions.end(); + ++index_stream) + { + // Go through all the packets in each stream that can be indexed + for (TIMED_PACKETS_T::const_iterator location = index_stream->second.begin(); + location != index_stream->second.end(); + ++location + ) + { + // This is the time we expect + PTS_T actual_pts = location->first; + + // Seek to that position in the file. + status = vc_container_seek(p_ctx, &actual_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " seeking to PTS " << location->first << std::ends; + continue; // if errors are not fatal we'll try the next position. + } + + // We'll perform force-reads until we've had a packet from every stream that has any PTS in it. + for (STREAM_TIMED_PACKETS_T::const_iterator stream = all_pts_packets.begin(); stream != all_pts_packets.end(); ++stream) + { + // Read a packet. + PACKET_DATA_T packet; + packet.info.track = stream->first; + + // This loop repeats occasionally with a continue, but usually stops on the break at the end first time around. + while(true) + { + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " force-reading info for stream " + << location->first << " after seeking to PTS " << stream->first << std::ends; + + // If we can't read the info there's no point in anything else on this stream + break; + } + + // allocate a buffer. + packet.buffer.resize(packet.info.size); + packet.info.data = &packet.buffer[0]; + packet.info.buffer_size = packet.info.size; + + // perform the actual read + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " force-reading stream " + << location->first << " after seeking to PTS " << stream->first << std::ends; + + // If we didn't read successfully we can't check the data. Try the next stream. + break; + } + + // If it has no PTS we can't use it - and it can't be played either. + if (packet.info.pts < 0) + { + error_logger << "Packet force-read on stream " << stream->first + << " has no PTS after seeking to PTS " << location->first << std::ends; + + // Try force-reading another. + continue; + } + + // Make sure that the packet is near the time we wanted. + check_seek_tolerance(packet, actual_pts); + + // Look for the packet for this PTS in this stream. + TIMED_PACKETS_T::const_iterator expected_packet = stream->second.find(packet.info.pts); + + if (expected_packet == stream->second.end()) + { + error_logger << "Packet force-read on stream " << stream->first + << " has unknown PTS " << packet.info.pts << std::ends; + + // Try force-reading another. + continue; + } + + packet.crc = crc32buf(packet.info.data, packet.info.size); + + // Validate that the data is the data we found when we did the sequential reads. + std::string anyerror = compare_packets(*expected_packet->second, packet); + + if (!anyerror.empty()) + { + error_logger << "Incorrect data found at PTS " << actual_pts << anyerror << std::ends; + } + + // If we arrive here we're done for this stream. + break; + } + } + // repeat until we've had a packet for every stream at this index position + } + // repeat for every index position in the stream + } + // repeat for every stream that has index positions + } + + // seek to a random selection of places, and see if we get the correct data + void seek_randomly(VC_CONTAINER_SEEK_FLAGS_T direction) + { + // Depending on whether we are doing forward or reverse seeks pick a collection of places a seek can go to. + const STREAM_TIMED_PACKETS_T& index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; + + // Go through each stream in that collection + for (STREAM_TIMED_PACKETS_T::const_iterator i_all_index_packets = index_positions.begin(); + i_all_index_packets != index_positions.end(); + ++i_all_index_packets) + { + // These are the index packets in the stream. + const TIMED_PACKETS_T& all_index_packets = i_all_index_packets->second; + + // this is the number of seeks we'll perform. + size_t seek_count = all_index_packets.size(); + + // If there are more than 100 locations limit it to 100. + if (seek_count > 100) + { + seek_count = 100; + } + + // We want an unsorted list of PTSes. + std::list selected_index_packets; + { + // Picking 100 at random out of a potentially large collection of timestamps in a set is going + // to be an expensive operation - search by key is really fast, but search by position is not. + // This is an attempt to come up with something reasonably efficient even if the collection is large. + std::vector all_ptses; + + // Reserve enough memory to hold every PTS. This collection should be several orders of magnitude smaller than the file. + all_ptses.reserve(all_index_packets.size()); + + // Copy all the PTS values into the vector + std::transform( + all_index_packets.begin(), + all_index_packets.end(), + std::back_inserter(all_ptses), + [](const TIMED_PACKETS_T::value_type& source){ return source.first; } + ); + + // Based roughly on the Knuth shuffle, get a random subset of the PTS to the front of the collection. + std::vector::iterator some_pts; + size_t count; + for (count = 0, some_pts = all_ptses.begin(); count < seek_count; ++count, ++some_pts) + { + // Swap some random PTS into one of the first seek_count locations. + // Note this may well be another of the first seek_count locations, especially if this is most of them + std::iter_swap(some_pts, (all_ptses.begin() + rand() % seek_count)); + } + + // Throw away the ones we don't want + all_ptses.resize(seek_count); + + // Copy the iterators for each packet with a selected PTS into the list. + std::transform( + all_ptses.begin(), + all_ptses.end(), + std::back_inserter(selected_index_packets), + [&all_index_packets](PTS_T time){ return *all_index_packets.find(time); }); + + // End scope. That will free the rest of all_ptses. + } + + // Loop through the selected packets. + for (std::list::iterator expected = selected_index_packets.begin(); + expected != selected_index_packets.end(); + ++expected) + { + // Seek to their position & check we got there + const PACKET_DATA_T& target = *expected->second; + PTS_T target_pts = expected->first; + + status = vc_container_seek(p_ctx, &target_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); + check_correct_seek(direction, target_pts, expected->first); + + // Start by initialising the new packet object to match the old one - it's mostly right. + PACKET_DATA_T found_packet = target; + + // If forcing is supported, read the first packet & check it's OK. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) + { + // There might not be a buffer if we ran out of memory + found_packet.buffer.resize(found_packet.info.size); + + // Set the address + found_packet.info.data = &found_packet.buffer[0]; + + // Perform the read + status = vc_container_read(p_ctx, &found_packet.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); + } + else + { + // as forcing is not supported repeat reads until a packet is found for this track. + do + { + status = vc_container_read(p_ctx, &found_packet.info, VC_CONTAINER_READ_FLAG_INFO); + + // Give up on any error + if (status != VC_CONTAINER_SUCCESS) break; + + // Set the buffer to match the actual packet + found_packet.info.buffer_size = found_packet.info.size; + found_packet.buffer.resize(found_packet.info.size); + found_packet.info.data = &found_packet.buffer[0]; + status = vc_container_read(p_ctx, &found_packet.info, 0); + + if (status != VC_CONTAINER_SUCCESS) break; + } + while (found_packet.info.track != target.info.track); + } + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " after random seek to PTS " << target_pts << std::ends; + } + else + { + found_packet.crc = crc32buf(&found_packet.buffer[0], found_packet.info.size); + + // We now have a packet from the right stream - it ought to be the one we asked for. + const std::string& compare_result = compare_packets(found_packet, target); + + if (!compare_result.empty()) + { + error_logger << "Incorrect result from reading packet after random seek: " << compare_result << std::ends; + } + } + } + } + } + + // Seek back to the beginning of the file, either with a seek or by closing and re-opening it + void re_seek_to_beginning() + { + // If the container supports seek do it. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_CAN_SEEK) + { + PTS_T beginning = 0; + + status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Failed to seek back to the beginning" << std::ends; + } + } + else + { + // The file claims not to support seeking. Close it, and re-open - this should do it. + status = vc_container_close(p_ctx); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " Failed to close the container." << std::ends; + } + + p_ctx = vc_container_open_reader(configuration.source_name.c_str(), &status, 0, 0); + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " Failed to re-open the container." << std::ends; + + // Even with -k this is a fatal error. Give up. + exit(status); + } + } + } + + // Read through the file, checking to see that the packets we get match the ones we read first time around - + // given that we're going to read with a buffer of a different size, either a fixed value or a proportion + // of the actual packet size. + void check_partial_reads() + { + // This is used for some reads, and for compares. + PACKET_DATA_T actual; + + // Repeat until we meet EOF, which will be in the middle of the loop somewhere. + ALL_PACKETS_T::const_iterator expected; + for (expected = all_packets.begin(); expected != all_packets.end(); ++expected) + { + // Ask for info about the first packet + status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading info for partial packet" << std::ends; + // Not much point in carrying on if we can't read - but usually this is the end-of-file break. + break; + } + + size_t whole_packet = actual.info.size; + + // Work out how big a read we want to do. + size_t wanted_read_size = (configuration.packet_buffer_size >= 1.0) + ? (size_t)configuration.packet_buffer_size + : (size_t)(configuration.packet_buffer_size * (double)whole_packet); + + // Make sure it's at least 1 byte (a small proportion of a small packet might round down to zero) + if (wanted_read_size == 0) + { + wanted_read_size = 1; + } + + // Ensure our buffer is at least big enough to contain the _whole_ packet. + if (whole_packet > actual.buffer.size()) + { + actual.buffer.resize(whole_packet); + } + + // We'll need to collect some data from the bits of the packet as they go by. + PTS_T first_valid_pts = std::numeric_limits::min(); + PTS_T first_valid_dts = std::numeric_limits::min(); + uint32_t total_flags = 0; + + size_t amount_read; + + // Loop around several times, reading part of the packet each time + for (amount_read = 0; amount_read < whole_packet; /* increment elsewhere */) + { + // Somewhere in the buffer + actual.info.data = &actual.buffer[amount_read]; + + // Not more than our calculated size + actual.info.buffer_size = wanted_read_size; + + // read some data. + status = vc_container_read(p_ctx, &actual.info, 0); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Unable to read " << wanted_read_size << " bytes from packet of size " + << expected->info.size << " error " << status << std::ends; + + // Guess we're at end of packet. We _might_ be able to recover from this. + break; + } + + amount_read += actual.info.size; + + // validate the amount read is less than the request + if (actual.info.size > wanted_read_size) + { + error_logger << "Too much data read from packet. Request size was " << whole_packet + << " but " << actual.info.size << "read in" << std::ends; + } + + // and that the total amount read for this packet is not too much + if (amount_read > whole_packet) + { + error_logger << "Too much data read from packet. Total size is " << whole_packet + << " but " << amount_read << "read in" << std::ends; + } + + // OR all the flags together + total_flags |= actual.info.flags; + + // Save the PTS if we don't have one yet + if (first_valid_pts < 0) + { + first_valid_pts = actual.info.pts; + } + + // Ditto the DTS + if (first_valid_dts < 0) + { + first_valid_dts = actual.info.dts; + } + } + + // The buffer should now contain all the correct data. However, the size field + // reflects the last read only - so correct it before the compare. + actual.info.size = amount_read; + + // store back the other stuff we got in the loop + actual.info.flags = total_flags; + actual.info.pts = first_valid_pts; + actual.info.dts = first_valid_dts; + + // Calculate the CRC of the whole lot of data + actual.crc = crc32buf(&actual.buffer[0], amount_read); + + // It's possible that the packet read isn't the one we expected. + // (This happens with the Supremecy3_20sec_WMV_MainProfile_VGA@29.97fps_2mbps_WMA9.2.wmv sample, + // where the first packet has no PTS and is not a key frame - seek won't go there) + if ((expected->info.pts != actual.info.pts) + && (actual.info.pts >= 0)) + { + ALL_PACKETS_T::const_iterator candidate = std::find_if + (all_packets.begin(), + all_packets.end(), + [&actual](ALL_PACKETS_T::value_type& expected) -> bool + { + // If we find one with the correct track and PTS, that will do. + return (expected.info.pts == actual.info.pts) + && (expected.info.track == actual.info.track); + }); + + if (candidate != all_packets.end()) + { + // We've seen the packet before + error_logger << "Partial read returned an unexpected packet. Expect PTS " << expected->info.pts + << " but got PTS " << actual.info.pts << std::ends; + + // switch to expecting this packet + expected = candidate; + } + + // If we didn't find the packet anywhere in the expected list do nothing, and let the compare fail. + } + + const std::string& compare_result = compare_packets(*expected, actual); + + if (!compare_result.empty()) + { + error_logger << "Incorrect result from reading packet in parts: " << compare_result << std::ends; + } + } + + // check end reached if we were OK this far + if (status == VC_CONTAINER_SUCCESS) + { + // We should be at the end of the expected collection + if (expected != all_packets.end()) + { + error_logger << "Failed to read entire file when reading partial packets" << std::ends; + } + + // We should not be able to read any more. + status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status == VC_CONTAINER_ERROR_EOS) + { + status = VC_CONTAINER_SUCCESS; + } + else + { + error_logger << "Should have reached end of stream, but got status " + << status << " while reading partial packets." << std::ends; + } + } + } + + // Information used in the next function about where we are in each stream. + struct FORCING_INFO : public PACKET_DATA_T + { + void new_packet() + { + first_valid_pts = std::numeric_limits::min(); + first_valid_dts = std::numeric_limits::min(); + total_flags = 0; + + // Adjust the buffer to hold the amount we want. + // (note - resize smaller does not re-allocate, + // it'll grow until it reaches the biggest packet then stay at that size) + buffer.resize(info.buffer_size); + + info.data = &buffer[0]; + } + + PTS_T first_valid_pts; + PTS_T first_valid_dts; + uint32_t total_flags; + ALL_PACKETS_T::const_iterator position; + }; + + // Check forcing. Read bits of the streams so that they get out of sync, and make sure the data is correct. + void check_forcing() + { + // Data for each stream + std::map stream_positions; + std::map::iterator stream_pos; + + // Set stream_positions to the first packet for each stream + for (std::set::const_iterator stream = all_streams.begin(); + stream != all_streams.end(); + ++stream) + { + const STREAM_T stream_number = *stream; + FORCING_INFO stream_data; + stream_data.position = all_packets.begin(); + + // Insert an entry for this stream, starting at the beginning + stream_pos = stream_positions.insert(std::make_pair(stream_number, stream_data)).first; + + // Then search for a packet in this stream. + next_in_stream(stream_pos); + + stream_pos->second.info.track = stream_number; + } + + // Repeat until explicit break when all have reached last packet + bool alldone = false; + while (!alldone) + { + // go through each stream in turn and get some data. At end of packet check it. + for (stream_pos = stream_positions.begin(); stream_pos != stream_positions.end(); ++stream_pos) + { + FORCING_INFO& stream_data = stream_pos->second; + if (stream_data.position == all_packets.end()) + { + // This stream has reached the end of the expected packets. Force-read an info, + // to make sure it really is at the end. + status = vc_container_read(p_ctx, &stream_data.info, + VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + if (status != VC_CONTAINER_ERROR_EOS) + { + error_logger << "Reading from stream " << stream_pos->first + << " after end gave error " << status << "instead of EOS" << std::ends; + + // Erase the buffer so we don't repeat the check. We'd just end up with loads of errors. + stream_positions.erase(stream_pos); + + // Then reset the iterator, as that made it invalid. + stream_pos = stream_positions.begin(); + } + } + else + { + // This stream has not reached the end. Read some more, maybe check it. + + // If we haven't read anything in yet + if (stream_data.info.size == 0) + { + if (configuration.packet_buffer_size <= 0) + { + // if unspecified read whole packets. + stream_data.info.buffer_size = stream_data.position->info.size; + } + else if (configuration.packet_buffer_size < 1.0) + { + // If a proportion work it out + stream_data.info.buffer_size + = (uint32_t)(configuration.packet_buffer_size * stream_data.position->info.size); + + if (stream_data.info.buffer_size < 1) + { + // but never less than 1 byte + stream_data.info.buffer_size = 1; + } + } + else + { + // Use the amount specified + stream_data.info.buffer_size = (uint32_t)configuration.packet_buffer_size; + } + + stream_data.new_packet(); + } + else + { + // We've already read part of the packet. Read some more. + size_t read_so_far = stream_data.buffer.size(); + + // expand the buffer to hold another lump of data + stream_data.buffer.resize(read_so_far + stream_data.info.buffer_size); + + // point the read at the new part. + stream_data.info.data = &stream_data.buffer[read_so_far]; + } + + // Read some data + status = vc_container_read(p_ctx, &stream_data.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + stream_data.total_flags |= stream_data.info.flags; + // Save the PTS if we don't have one yet + if (stream_data.first_valid_pts < 0) + { + stream_data.first_valid_pts = stream_data.info.pts; + } + + // Ditto the DTS + if (stream_data.first_valid_dts < 0) + { + stream_data.first_valid_dts = stream_data.info.dts; + } + + // work out how much we have. + uint32_t total_read = (stream_data.info.data + stream_data.info.size) - &stream_data.buffer[0]; + + if (total_read > stream_data.position->info.size) + { + // we've read more than a packet. That's an error. + error_logger << "Read more data than expected from a packet - read " << total_read + << " Expect " << stream_data.position->info.size; + } + + if (total_read < stream_data.position->info.size) + { + // more to read. Just go back around the loop. + continue; + } + + stream_data.info.size = total_read; + + stream_data.crc = crc32buf(&stream_data.buffer[0], total_read); + + stream_data.info.flags = stream_data.total_flags; + stream_data.info.pts = stream_data.first_valid_pts; + stream_data.info.dts = stream_data.first_valid_dts; + + // Validate that the data is the data we found when we did the sequential reads. + std::string anyerror = compare_packets(*stream_data.position, stream_data); + + if (!anyerror.empty()) + { + error_logger << "Incorrect data found at PTS " << stream_data.info.pts << anyerror << std::ends; + } + + // Move to the next packet + ++stream_data.position; + + // Then search until we get one for this stream, or end. + next_in_stream(stream_pos); + + // Note we've read nothing of the new packet yet. + stream_data.info.size = 0; + } + } + + // go through each stream in turn. If they have all reached the end we are complete. + for (alldone = true, stream_pos = stream_positions.begin(); alldone && stream_pos != stream_positions.end(); ++stream_pos) + { + if (stream_pos->second.position != all_packets.end()) + { + // If any stream has not reached the end we have more to do + alldone = false; + } + } + } + } + + // Helper for the above: Advance the supplied FORCING_INFO to the next packet in the stream + void next_in_stream(std::map::iterator& pos) + { + // We're searching for this stream + const STREAM_T track = pos->first; + + ALL_PACKETS_T::const_iterator& position = pos->second.position; + + // Loop until no more packets, or found one for this stream + while ((position != all_packets.end()) + && (position->info.track != track)) + { + // advance this iterator to the next packet. + ++position; + } + }; + + // Compare two packets, and return a non-empty string if they don't match + std::string compare_packets(const PACKET_DATA_T& expected, const PACKET_DATA_T& actual) + { + std::ostringstream errors; + + // Check the fields in the structure + if (expected.info.size != actual.info.size) + { + errors << "size mismatch. Expect " << expected.info.size << " actual " << actual.info.size << std::endl; + } + + if (expected.info.frame_size != actual.info.frame_size) + { + errors << "frame_size mismatch. Expect " << expected.info.frame_size << " actual " << actual.info.frame_size << std::endl; + } + + if (expected.info.pts != actual.info.pts) + { + errors << "pts mismatch. Expect " << expected.info.pts << " actual " << actual.info.pts << std::endl; + } + + if (expected.info.dts != actual.info.dts) + { + errors << "dts mismatch. Expect " << expected.info.dts << " actual " << actual.info.dts << std::endl; + } + + if (expected.info.track != actual.info.track) + { + errors << "track mismatch. Expect " << expected.info.track << " actual " << actual.info.track << std::endl; + } + + if (expected.info.flags != actual.info.flags) + { + errors << "flags mismatch. Expect " << expected.info.flags << " actual " << actual.info.flags << std::endl; + } + + // check the buffer. We won't bother if there isn't one in either - if the expected hasn't got one, + // it's probably because we hit our memory limit. If the actual hasn't, the there should be a size error. + for (size_t i = 0, err_count = 0; i < expected.buffer.size() && i < actual.buffer.size(); ++i) + { + if (expected.buffer[i] != actual.buffer[i]) + { + errors << "Data mismatch at " + << std::setw(8) << std::hex << i + << std::setw(3) << (unsigned)expected.buffer[i] + << std::setw(3)<< (unsigned)actual.buffer[i] << std::endl; + + if (++err_count > 20) + { + errors << "Too many errors to report" << std::endl; + break; + } + } + } + + if (expected.crc != actual.crc) + { + errors << "CRC mismatch. Expect " << expected.crc << " actual " << actual.crc << std::endl; + } + + // If there were errors put a new line on the front. This aids formatting. + const std::string& error_list = errors.str(); + + if (error_list.empty()) + { + // There were no errors + return error_list; + } + else + { + return std::string("\n") + error_list; + } + } + + void check_correct_seek(VC_CONTAINER_SEEK_FLAGS_T direction, PTS_T actual_pts, PTS_T target_pts) + { + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " returned by seek" << std::ends; + } + if (direction) + { + if (actual_pts < target_pts) + { + // We shouldn't normally arrive at a time earlier than we requested. + // However there's a special case - when the time requested is after the last keyframe + PTS_T highest_pts_in_video = all_key_packets.at(video_stream).rbegin()->first; + + if (actual_pts < highest_pts_in_video) + { + error_logger << "Incorrect forward seek. Should not be before " + << target_pts << " but arrived at " << actual_pts << std::ends; + } + } + } + else + { + if (actual_pts > target_pts) + { + // We shouldn't normally arrive at a time later than we requested. + // However there's a special case - when the time requested is before the first keyframe in the file. + PTS_T lowest_pts_in_video = all_key_packets.at(video_stream).begin()->first; + + if (actual_pts > lowest_pts_in_video) + { + error_logger << "Incorrect reverse seek. Should not be after " + << target_pts << " but arrived at " << actual_pts << std::ends; + } + } + } + } + + // Check whether a packet is close enough to the PTS supplied. Used after a seek. + void check_seek_tolerance(const PACKET_DATA_T& packet, PTS_T actual_pts) + { + // Check whether the time on the packet is reasonable - it ought to be close to the time achieved. + if (packet.info.track == video_stream) + { + if ((packet.info.pts + configuration.tolerance_video_early) < actual_pts) + { + error_logger << "Video stream seek location is bad " << actual_pts - packet.info.pts << "uS early" << std::ends; + } + + if ((packet.info.pts - configuration.tolerance_video_late) > actual_pts) + { + // We shouldn't normally arrive at a time later than we requested. + // However there's a special case - when the time requested is before the first PTS in the file. + PTS_T lowest_pts_in_video = all_pts_packets.at(video_stream).begin()->first; + + if (actual_pts > lowest_pts_in_video) + { + error_logger << "Video stream seek location is bad " << packet.info.pts - actual_pts << "uS late" << std::ends; + } + } + } + else + { + if ((packet.info.pts + configuration.tolerance_other_early) < actual_pts) + { + error_logger << "Non-video stream seek location is bad " << actual_pts - packet.info.pts << "uS early" << std::ends; + } + + if ((packet.info.pts - configuration.tolerance_other_late) > actual_pts) + { + error_logger << "Non-video stream seek location is bad " << packet.info.pts - actual_pts << "uS late" << std::ends; + } + } + } + + // Initialise a Congruential Random Number Generator from the file contents. + // Used rather than rand() for the complete control that this offers - this app + // should behave the same regardless of the platform and word size. + void init_crg() + { + if (all_packets.empty()) + { + static const char* msg = "You must read some packets before initialising the RNG"; + assert(!msg); + std::cerr << msg; + exit(1); + } + + rng_value = 0; + + // XOR all the CRCs together to act as a file-specific seed. + for (ALL_PACKETS_T::const_iterator packet = all_packets.begin(); packet != all_packets.end(); ++packet) + { + rng_value ^= packet->crc; + } + } + + // Get a pseudo-random number. + uint32_t rand() + { + // Constants from "Numerical recipes in 'C'" + return rng_value = 1664525 * rng_value + 1013904223; + } + + // configuration + const CONFIGURATION_T configuration; + + // Error logger. + class ERROR_LOGGER_T error_logger; + + // The status from the last call made + VC_CONTAINER_STATUS_T status; + + // Pointer to the file being processed + VC_CONTAINER_T *p_ctx; + + // All the packets in the file + ALL_PACKETS_T all_packets; + + // All the streams + std::set all_streams; + + // All the streams and all the packets in them with a PTS, whether or not they are keyframes. + STREAM_TIMED_PACKETS_T all_pts_packets; + + // Subset of the above that hold key frames. + STREAM_TIMED_PACKETS_T all_key_packets; + + // Places where a seek without VC_CONTAINER_SEEK_FLAG_FORWARD can go to. A subset of all_key_packets. + STREAM_TIMED_PACKETS_T reverse_index_positions; + + // Places where a seek with VC_CONTAINER_SEEK_FLAG_FORWARD can go to. + STREAM_TIMED_PACKETS_T forward_index_positions; + + // The first stream containing video packets + STREAM_T video_stream; + + // The stored value of the RNG + uint32_t rng_value; +}; + +int main(int argc, char** argv) +{ + // Read and parse the configuration information from the command line. + TESTER_T test(argc, argv); + + // Run the tests and return their error code + return test.run(); +} diff --git a/containers/test/check_frame_int.c b/containers/test/check_frame_int.c new file mode 100755 index 0000000..1819c47 --- /dev/null +++ b/containers/test/check_frame_int.c @@ -0,0 +1,348 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_io.h" + +#define BUFFER_SIZE 256*1024 +#define MAX_TRACKS 16 +#define MAX_SEEKS 16 + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); +static int container_test_parse_cmdline(int argc, char **argv); + +static const char *psz_in = 0; +static long packets_num = 0; +static long track_num = -1; +static int fps = 30; +static int margin = 5; // margin for frame interval, percent + +static struct +{ + uint32_t mapping; + uint32_t frames; + uint32_t packets; + uint64_t bytes; + uint32_t frame_size; + int64_t first_dts; + int64_t first_pts; + int64_t last_dts; + int64_t last_pts; +} tracks[MAX_TRACKS]; + +static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + +/*****************************************************************************/ +int main(int argc, char **argv) +{ + int retval = 0; + VC_CONTAINER_T *p_ctx = 0; + VC_CONTAINER_STATUS_T status; + unsigned int i; + uint8_t *buffer = malloc(BUFFER_SIZE); + int32_t interval; + int64_t last_packet_pts = -1; + int32_t max_interval = 0, max_interval_after_first = 0; + int fail = 0; + + if(container_test_parse_cmdline(argc, argv)) + goto error_silent; + + LOG_INFO (0, "Require that no frame interval greater than 1/%d seconds (%d%% margin)", fps, margin); + + /* Set the general verbosity */ + vc_container_log_set_verbosity(0, verbosity); + vc_container_log_set_default_verbosity(verbosity); + + p_ctx = vc_container_open_reader(psz_in, &status, 0, 0); + + if(!p_ctx) + { + LOG_ERROR(0, "error opening file %s (%i)", psz_in, status); + goto error; + } + + if (verbosity & VC_CONTAINER_LOG_DEBUG) + { + container_test_info(p_ctx, true); + } + LOG_INFO (0, "Search for video track only"); + + /* Disabling tracks which are not requested and enable packetisation if requested */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + track->is_enabled = (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO); + } + + + LOG_DEBUG(0, "TEST start reading"); + for(i = 0; !packets_num || (long)i < packets_num; i++) + { + VC_CONTAINER_PACKET_T packet = {0}; + int32_t frame_num = 0; + + status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;} + + if(packet.track < MAX_TRACKS) + { + if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) + { + tracks[packet.track].frames++; + tracks[packet.track].frame_size = 0; + } + frame_num = tracks[packet.track].frames; + } + + tracks[packet.track].frame_size += packet.size; + +// LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i", +// packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags, +// (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "", +// frame_num-1); + + if (last_packet_pts != -1) + interval = packet.pts - last_packet_pts; + else + interval = 0; // packet.pts; + last_packet_pts = packet.pts; + max_interval = MAX (max_interval, interval); + if (i >= 2) + max_interval_after_first = MAX (max_interval_after_first, interval); + + /* Check if interval (in us) exceeds 1/fps, with percentage margin */ + if (interval * fps > 1e4 * (100+margin)) + { + LOG_INFO (0, "Frame %d, interval %.3f FAILED", i, interval / 1000.0f); + fail = 1; + } + + LOG_DEBUG (0, "Frame %d, interval %.3f", i, interval / 1000.0f); + + if(track_num >= 0 && packet.track != (uint32_t)track_num) + { + status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;} + continue; + } + + packet.buffer_size = BUFFER_SIZE; + packet.data = buffer; + packet.size = 0; + status = vc_container_read(p_ctx, &packet, 0); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;} + + // LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags); + + if (tracks[packet.track].packets) + { + tracks[packet.track].last_dts = packet.dts; + tracks[packet.track].last_pts = packet.pts; + } else { + tracks[packet.track].first_dts = packet.dts; + tracks[packet.track].first_pts = packet.pts; + } + + if(packet.track < MAX_TRACKS) + { + tracks[packet.track].packets++; + tracks[packet.track].bytes += packet.size; + } + + } + LOG_DEBUG(0, "TEST stop reading"); + + /* Output stats */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes", + i, tracks[i].frames, tracks[i].packets, tracks[i].bytes); + LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)", + tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts); + } + + LOG_INFO (0, "---\nMax interval = %.3f ms; max interval (after first) = %.3f ms\n", (float) max_interval / 1000.0, (float) max_interval_after_first / 1000.0); + + end: + if(p_ctx) vc_container_close(p_ctx); + free(buffer); + +#ifdef _MSC_VER + getchar(); +#endif + + retval = fail; + if (fail) + { + LOG_INFO (0, "TEST FAILED: highest frame interval = %.3f ms", max_interval / 1000.0f); + } + else + LOG_INFO (0, "TEST PASSED"); + return retval; + + error: + LOG_ERROR(0, "TEST FAILED TO RUN"); + error_silent: + retval = -1; + goto end; +} + +static int container_test_parse_cmdline(int argc, char **argv) +{ + int i, j, k; + int32_t *p_verbosity; + + /* Parse the command line arguments */ + for(i = 1; i < argc; i++) + { + if(!argv[i]) continue; + + if(argv[i][0] != '-') + { + /* Not an option argument so will be the input URI */ + psz_in = argv[i]; + continue; + } + + /* We are now dealing with command line options */ + switch(argv[i][1]) + { + case 'v': + j = 2; + p_verbosity = &verbosity; + *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++) + *p_verbosity = (*p_verbosity << 1) | 1 ; + break; + case 'f': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + fps = strtol(argv[++i], 0, 0); + break; + case 'h': goto usage; + default: goto invalid_option; + } + continue; + } + + /* Sanity check that we have at least an input uri */ + if(!psz_in) + { + LOG_ERROR(0, "missing uri argument"); + goto usage; + } + + return 0; + + invalid_option: + LOG_ERROR(0, "invalid command line option (%s)", argv[i]); + + usage: + psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++; + if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;} + if(!psz_in) psz_in = argv[0]; + LOG_INFO(0, ""); + LOG_INFO(0, "usage: %s [options] uri", psz_in); + LOG_INFO(0, "options list:"); + LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')"); + LOG_INFO(0, " -f : required frame rate/second (frame interval must not exceed 1/f)"); + LOG_INFO(0, " -h : help"); + return 1; +} + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader) +{ + const char *name_type; + unsigned int i; + + LOG_INFO(0, ""); + if(b_reader) LOG_INFO(0, "----Reader Information----"); + else LOG_INFO(0, "----Writer Information----"); + + LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size); + LOG_INFO(0, "capabilities: %x", ctx->capabilities); + LOG_INFO(0, ""); + + for(i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break; + case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; + default: name_type = "unknown"; break; + } + + if (!strcmp (name_type, "video")) + { + LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec); + LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate, + !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled); + LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata); + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: + LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)", + track->format->type->video.width, track->format->type->video.height, + track->format->type->video.x_offset, track->format->type->video.y_offset, + track->format->type->video.visible_width, track->format->type->video.visible_height); + LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i", + track->format->type->video.par_num, track->format->type->video.par_den, + track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den); + break; + + default: break; + } + } + } + + for (i = 0; i < ctx->meta_num; ++i) + { + const char *name, *value; + if (i == 0) LOG_INFO(0, ""); + name = vc_container_metadata_id_to_string(ctx->meta[i]->key); + value = ctx->meta[i]->value; + if(!name) continue; + LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value); + } + + LOG_INFO(0, "--------------------------"); + LOG_INFO(0, ""); + + return 0; +} + + diff --git a/containers/test/crc_32.c b/containers/test/crc_32.c new file mode 100755 index 0000000..1d2dbaa --- /dev/null +++ b/containers/test/crc_32.c @@ -0,0 +1,227 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include + +#ifndef Boolean_T +typedef unsigned Boolean_T; +#endif +#ifndef Error_ +#define Error_ 1 +#endif + +#ifndef Success_ +#define Success_ 0 +#endif + +#define UPDC32(octet,crc) (crc_32_tab[((crc)\ + ^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8)) + +#ifdef __TURBOC__ + #pragma warn -cln +#endif + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + +/* Need an unsigned type capable of holding 32 bits; */ + +typedef uint32_t UNS_32_BITS; + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static UNS_32_BITS crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, +0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, +0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, +0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, +0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, +0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, +0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, +0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, +0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, +0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, +0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, +0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, +0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, +0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, +0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, +0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, +0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, +0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, +0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, +0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, +0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, +0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, +0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, +0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, +0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, +0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, +0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, +0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, +0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, +0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, +0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, +0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, +0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, +0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, +0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, +0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, +0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, +0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, +0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, +0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, +0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, +0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, +0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t updateCRC32(unsigned char ch, uint32_t crc) +{ + return UPDC32(ch, crc); +} + +Boolean_T crc32file(char *name, uint32_t *crc, long *charcnt) +{ + register FILE *fin; + register uint32_t oldcrc32; + register int c; + + oldcrc32 = 0xFFFFFFFF; *charcnt = 0; +#ifdef MSDOS + if ((fin=fopen(name, "rb"))==NULL) +#else + if ((fin=fopen(name, "r"))==NULL) +#endif + { + perror(name); + return Error_; + } + while ((c=getc(fin))!=EOF) + { + ++*charcnt; + oldcrc32 = UPDC32(c, oldcrc32); + } + + if (ferror(fin)) + { + perror(name); + *charcnt = -1; + } + fclose(fin); + + *crc = oldcrc32 = ~oldcrc32; + + return Success_; +} + +uint32_t crc32buf(const uint8_t *buf, size_t len) +{ + register uint32_t oldcrc32; + + oldcrc32 = 0xFFFFFFFF; + + for ( ; len; --len, ++buf) + { + oldcrc32 = UPDC32(*buf, oldcrc32); + } + + return ~oldcrc32; +} + +#ifdef TEST + +main(int argc, char *argv[]) +{ + uint32_t crc; + long charcnt; + register errors = 0; + + while(--argc > 0) + { + errors |= crc32file(*++argv, &crc, &charcnt); + printf("%08lX %7ld %s\n", crc, charcnt, *argv); + } + return(errors != 0); +} + +#endif /* TEST */ diff --git a/containers/test/datagram_receiver.c b/containers/test/datagram_receiver.c new file mode 100755 index 0000000..30d661f --- /dev/null +++ b/containers/test/datagram_receiver.c @@ -0,0 +1,80 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/net/net_sockets.h" + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *sock; + vc_container_net_status_t status; + char *buffer; + size_t buffer_size; + size_t received; + + if (argc < 2) + { + printf("Usage:\n%s \n", argv[0]); + return 1; + } + + sock = vc_container_net_open(NULL, argv[1], 0, &status); + if (!sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + buffer_size = vc_container_net_maximum_datagram_size(sock); + buffer = (char *)malloc(buffer_size); + if (!buffer) + { + vc_container_net_close(sock); + printf("Failure allocating buffer\n"); + return 3; + } + + while ((received = vc_container_net_read(sock, buffer, buffer_size)) != 0) + { + char *ptr = buffer; + + while (received--) + putchar(*ptr++); + } + + if (vc_container_net_status(sock) != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_read failed: %d\n", vc_container_net_status(sock)); + } + + vc_container_net_close(sock); + + return 0; +} diff --git a/containers/test/datagram_sender.c b/containers/test/datagram_sender.c new file mode 100755 index 0000000..6ed7b15 --- /dev/null +++ b/containers/test/datagram_sender.c @@ -0,0 +1,77 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/net/net_sockets.h" + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *sock; + vc_container_net_status_t status; + char *buffer; + size_t buffer_size; + + if (argc < 3) + { + printf("Usage:\n%s
\n", argv[0]); + return 1; + } + + sock = vc_container_net_open(argv[1], argv[2], 0, &status); + if (!sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + buffer_size = vc_container_net_maximum_datagram_size(sock); + buffer = (char *)malloc(buffer_size); + if (!buffer) + { + vc_container_net_close(sock); + printf("Failure allocating buffer\n"); + return 3; + } + + printf("Don't enter more than %d characters in one line!\n", (int)buffer_size); + + while (fgets(buffer, buffer_size, stdin)) + { + if (!vc_container_net_write(sock, buffer, strlen(buffer))) + { + printf("vc_container_net_write failed: %d\n", vc_container_net_status(sock)); + break; + } + } + + vc_container_net_close(sock); + + return 0; +} diff --git a/containers/test/dump_pktfile.c b/containers/test/dump_pktfile.c new file mode 100755 index 0000000..eddf197 --- /dev/null +++ b/containers/test/dump_pktfile.c @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#define PACKET_BUFFER_SIZE 32768 + +enum { + SUCCESS = 0, + SHOWED_USAGE, + FAILED_TO_OPEN_PKTFILE, + FAILED_TO_OPEN_OUTPUT_FILE, + BYTE_ORDER_MARK_MISSING, + INVALID_BYTE_ORDER_MARK, + MEMORY_ALLOCATION_FAILURE, + PACKET_TOO_BIG, +}; + +/** Native byte order word */ +#define NATIVE_BYTE_ORDER 0x50415753U +/** Reverse of native byte order - need to swap bytes around */ +#define SWAP_BYTE_ORDER 0x53574150U + +static unsigned long reverse_byte_order( unsigned long value ) +{ + /* Reverse the order of the bytes in the word */ + return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24)); +} + +int main(int argc, char **argv) +{ + int status = SUCCESS; + FILE *pktfile = NULL, *output_file = NULL; + uint32_t byte_order, packet_size, packet_read; + char *buffer = NULL; + + if (argc < 2) + { + printf("\ +Usage:\n\ + %s []\n\ + is the file to read.\n\ +, if given, will receive the unpacketized data.\n", argv[0]); + status = SHOWED_USAGE; + goto end_program; + } + + pktfile = fopen(argv[1], "rb"); + if (!pktfile) + { + printf("Failed to open pkt file <%s> for reading.\n", argv[1]); + status = FAILED_TO_OPEN_PKTFILE; + goto end_program; + } + + if (fread(&byte_order, 1, sizeof(byte_order), pktfile) != sizeof(byte_order)) + { + printf("Failed to read byte order header from pkt file.\n"); + status = BYTE_ORDER_MARK_MISSING; + goto end_program; + } + + if (byte_order != NATIVE_BYTE_ORDER && byte_order != SWAP_BYTE_ORDER) + { + printf("Invalid byte order header: 0x%08x (%u)\n", byte_order, byte_order); + status = INVALID_BYTE_ORDER_MARK; + goto end_program; + } + + buffer = (char *)malloc(PACKET_BUFFER_SIZE); + if (!buffer) + { + printf("Memory allocation failed\n"); + status = MEMORY_ALLOCATION_FAILURE; + goto end_program; + } + + if (argc > 2) + { + output_file = fopen(argv[2], "wb"); + if (!output_file) + { + printf("Failed to open <%s> for output.\n", argv[2]); + status = FAILED_TO_OPEN_OUTPUT_FILE; + goto end_program; + } + } + + while (fread(&packet_size, 1, sizeof(packet_size), pktfile) == sizeof(packet_size)) + { + if (byte_order == SWAP_BYTE_ORDER) + packet_size = reverse_byte_order(packet_size); + + if (packet_size >= PACKET_BUFFER_SIZE) + { + printf("*** Packet size is bigger than buffer (%u > %u)\n", packet_size, PACKET_BUFFER_SIZE - 1); + status = PACKET_TOO_BIG; + goto end_program; + } + + packet_read = fread(buffer, 1, packet_size, pktfile); + + if (output_file) + { + fwrite(buffer, 1, packet_size, output_file); + } else { + buffer[packet_size] = '\0'; + printf("%s", buffer); + getchar(); + } + + if (packet_read != packet_size) + printf("*** Invalid packet size (expected %u, got %u)\n", packet_size, packet_read); + } + +end_program: + if (pktfile) fclose(pktfile); + if (output_file) fclose(output_file); + if (buffer) free(buffer); + return -status; +} diff --git a/containers/test/nb_io.h b/containers/test/nb_io.h new file mode 100755 index 0000000..2058fe6 --- /dev/null +++ b/containers/test/nb_io.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NB_IO_H_ +#define _NB_IO_H_ + +/** Set whether console input is non-blocking or not. + * + * \param enable Pass true to set input as non-blocking, false to set it as blocking again.*/ +void nb_set_nonblocking_input(int enable); + +/** \return Non-zero when there is a character available to read using nb_get_char(). */ +int nb_char_available(void); + +/** \return The next character available from the console, or zero. */ +char nb_get_char(void); + +/** Put the character out to the console. + * + * \param ch The character to be output. */ +void nb_put_char(char ch); + +#endif /* _NB_IO_H_ */ diff --git a/containers/test/nb_io_unix.c b/containers/test/nb_io_unix.c new file mode 100755 index 0000000..63e1a1f --- /dev/null +++ b/containers/test/nb_io_unix.c @@ -0,0 +1,80 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "nb_io.h" + +void nb_set_nonblocking_input(int enable) +{ + struct termios ttystate; + + //get the terminal state + tcgetattr(STDIN_FILENO, &ttystate); + + if (enable) + { + //turn off canonical mode + ttystate.c_lflag &= ~ICANON; + //minimum of number input read. + ttystate.c_cc[VMIN] = 1; + } + else + { + //turn on canonical mode + ttystate.c_lflag |= ICANON; + } + + //set the terminal attributes. + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); +} + +int nb_char_available(void) +{ + struct timeval tv; + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); + return (FD_ISSET(0, &fds)); +} + +char nb_get_char(void) +{ + return getchar(); +} + +void nb_put_char(char ch) +{ + putchar(ch); +} diff --git a/containers/test/nb_io_win32.c b/containers/test/nb_io_win32.c new file mode 100755 index 0000000..a402bcb --- /dev/null +++ b/containers/test/nb_io_win32.c @@ -0,0 +1,53 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include /* For _kbhit() */ + +#include "nb_io.h" + +void nb_set_nonblocking_input(int enable) +{ + /* No need to do anything in Win32 */ + (void)enable; +} + +int nb_char_available() +{ + return _kbhit(); +} + +char nb_get_char() +{ + char c = _getch(); + _putch(c); /* Echo back */ + return c; +} + +void nb_put_char(char ch) +{ + _putch(ch); +} diff --git a/containers/test/rtp_decoder.c b/containers/test/rtp_decoder.c new file mode 100755 index 0000000..c089576 --- /dev/null +++ b/containers/test/rtp_decoder.c @@ -0,0 +1,449 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_io.h" + +#include "nb_io.h" + +#define MAXIMUM_BUFFER_SIZE 65000 +#define MINIMUM_BUFFER_SPACE 1500 + +#define INITIAL_READ_BUFFER_SIZE 8000 +#define MAXIMUM_READ_BUFFER_SIZE 64000 + +#define BYTES_PER_ROW 32 + +#define HAS_PADDING 0x20 +#define HAS_EXTENSION 0x10 +#define CSRC_COUNT_MASK 0x0F + +#define HAS_MARKER 0x80 +#define PAYLOAD_TYPE_MASK 0x7F + +#define EXTENSION_LENGTH_MASK 0x0000FFFF +#define EXTENSION_ID_SHIFT 16 + +#define LOWEST_VERBOSITY 1 +#define BASIC_HEADER_VERBOSITY 2 +#define FULL_HEADER_VERBOSITY 3 +#define FULL_PACKET_VERBOSITY 4 + +#define ESCAPE_CHARACTER 0x1B + +static bool seen_first_packet; +static uint16_t expected_next_seq_num; + +static bool do_print_usage; +static uint32_t verbosity; +static const char *read_uri; +static const char *packet_save_file; +static bool packet_save_is_pktfile; + +static uint16_t network_to_host_16(const uint8_t *buffer) +{ + return (buffer[0] << 8) | buffer[1]; +} + +static uint32_t network_to_host_32(const uint8_t *buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +/** Avoid alignment problems when writing a word value to the buffer */ +static void store_u32(uint8_t *buffer, uint32_t value) +{ + buffer[0] = (uint8_t)value; + buffer[1] = (uint8_t)(value >> 8); + buffer[2] = (uint8_t)(value >> 16); + buffer[3] = (uint8_t)(value >> 24); +} + +/** Avoid alignment problems when reading a word value from the buffer */ +static uint32_t fetch_u32(uint8_t *buffer) +{ + return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; +} + +static bool marker_bit_set(const uint8_t *buffer, size_t buffer_len) +{ + if (buffer_len < 2) + return false; + + return (buffer[1] & HAS_MARKER); +} + +static void dump_bytes(const uint8_t *buffer, size_t buffer_len) +{ + char dump_str[3 * BYTES_PER_ROW + 1]; + int in_row = 0; + + while (buffer_len--) + { + sprintf(dump_str + 3 * in_row, "%2.2X ", *buffer++); + if (++in_row == BYTES_PER_ROW) + { + LOG_INFO(NULL, dump_str); + in_row = 0; + } + } + + if (in_row) + { + LOG_INFO(NULL, dump_str); + } +} + +static bool decode_packet(const uint8_t *buffer, size_t buffer_len) +{ + uint8_t flags; + uint8_t payload_type; + uint16_t seq_num; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc_count; + + if (buffer_len < 12) + { + LOG_ERROR(NULL, "Packet too small: basic header missing"); + return false; + } + + flags = buffer[0]; + payload_type = buffer[1]; + seq_num = network_to_host_16(buffer + 2); + timestamp = network_to_host_32(buffer + 4); + ssrc = network_to_host_32(buffer + 8); + + if (seen_first_packet && seq_num != expected_next_seq_num) + { + int16_t missing_packets = seq_num - expected_next_seq_num; + + LOG_INFO(NULL, "*** Sequence break, expected %hu, got %hu ***", expected_next_seq_num, seq_num); + if (missing_packets > 0) + LOG_INFO(NULL, "*** Jumped forward %hd packets ***", missing_packets); + else + LOG_INFO(NULL, "*** Jumped backward %hd packets ***", -missing_packets); + } + seen_first_packet = true; + expected_next_seq_num = seq_num + 1; + + /* Dump the basic header information */ + if (verbosity >= BASIC_HEADER_VERBOSITY) + { + LOG_INFO(NULL, "Version: %d\nPayload type: %d%s\nSequence: %d\nTimestamp: %u\nSSRC: 0x%8.8X", + flags >> 6, payload_type & PAYLOAD_TYPE_MASK, + (const char *)((payload_type & HAS_MARKER) ? " (M)" : ""), + seq_num, timestamp, ssrc); + } + + buffer += 12; + buffer_len -= 12; + + if (verbosity >= FULL_HEADER_VERBOSITY) + { + /* Dump the CSRCs, if any */ + csrc_count = flags & CSRC_COUNT_MASK; + if (csrc_count) + { + uint32_t ii; + + if (buffer_len < (csrc_count * 4)) + { + LOG_ERROR(NULL, "Packet too small: CSRCs missing"); + return false; + } + + LOG_INFO(NULL, "CSRCs:"); + for (ii = 0; ii < csrc_count; ii++) + { + LOG_INFO(NULL, " 0x%8.8X", network_to_host_32(buffer)); + buffer += 4; + buffer_len -= 4; + } + } + + /* Dump any extension, if present */ + if (flags & HAS_EXTENSION) + { + uint32_t extension_hdr; + uint32_t extension_id; + size_t extension_len; + + if (buffer_len < 4) + { + LOG_ERROR(NULL, "Packet too small: extension header missing"); + return false; + } + + extension_hdr = network_to_host_32(buffer); + buffer += 4; + buffer_len -= 4; + + extension_len = (size_t)(extension_hdr & EXTENSION_LENGTH_MASK); + extension_id = extension_hdr >> EXTENSION_ID_SHIFT; + + if (buffer_len < extension_len) + { + LOG_ERROR(NULL, "Packet too small: extension content missing"); + return false; + } + + LOG_INFO(NULL, "Extension: 0x%4.4X (%u bytes)", extension_id, (unsigned)extension_len); + dump_bytes(buffer, extension_len); + buffer += extension_len; + buffer_len -= extension_len; + } + } + + /* And finally the payload data */ + if (verbosity >= FULL_PACKET_VERBOSITY) + { + LOG_INFO(NULL, "Data: (%u bytes)", (unsigned)buffer_len); + dump_bytes(buffer, buffer_len); + } + + return true; +} + +static void increase_read_buffer_size(VC_CONTAINER_IO_T *p_ctx) +{ + uint32_t buffer_size = INITIAL_READ_BUFFER_SIZE; + + /* Iteratively enlarge read buffer until either operation fails or maximum is reached. */ + while (vc_container_io_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, buffer_size) == VC_CONTAINER_SUCCESS) + { + buffer_size <<= 1; /* Double and try again */ + if (buffer_size > MAXIMUM_READ_BUFFER_SIZE) + break; + } +} + +static void parse_command_line(int argc, char **argv) +{ + int arg = 1; + + while (arg < argc) + { + if (*argv[arg] != '-') /* End of options, next should be URI */ + break; + + switch (argv[arg][1]) + { + case 'h': + do_print_usage = true; + break; + case 's': + arg++; + if (arg >= argc) + break; + packet_save_file = argv[arg]; + packet_save_is_pktfile = (strncmp(packet_save_file, "pktfile:", 8) == 0); + break; + case 'v': + { + const char *ptr = &argv[arg][2]; + + verbosity = 1; + while (*ptr++ == 'v') + verbosity++; + } + break; + default: LOG_ERROR(NULL, "Unrecognised option: %s", argv[arg]); return; + } + + arg++; + } + + if (arg < argc) + read_uri = argv[arg]; +} + +static void print_usage(char *program_name) +{ + LOG_INFO(NULL, "Usage:"); + LOG_INFO(NULL, " %s [opts] ", program_name); + LOG_INFO(NULL, "Reads RTP packets from , decodes to standard output."); + LOG_INFO(NULL, "Press the escape key to terminate the program."); + LOG_INFO(NULL, "Options:"); + LOG_INFO(NULL, " -h Print this information"); + LOG_INFO(NULL, " -s x Save packets to URI x"); + LOG_INFO(NULL, " -v Dump standard packet header"); + LOG_INFO(NULL, " -vv Dump entire header"); + LOG_INFO(NULL, " -vvv Dump entire header and data"); +} + +int main(int argc, char **argv) +{ + int result = 0; + uint8_t *buffer = NULL; + VC_CONTAINER_IO_T *read_io = NULL; + VC_CONTAINER_IO_T *write_io = NULL; + VC_CONTAINER_STATUS_T status; + size_t received_bytes; + bool ready = true; + uint32_t available_bytes; + uint8_t *packet_ptr; + + parse_command_line(argc, argv); + + if (do_print_usage || !read_uri) + { + print_usage(argv[0]); + result = 1; goto tidyup; + } + + buffer = (uint8_t *)malloc(MAXIMUM_BUFFER_SIZE); + if (!buffer) + { + LOG_ERROR(NULL, "Allocating %d bytes for the buffer failed", MAXIMUM_BUFFER_SIZE); + result = 2; goto tidyup; + } + + read_io = vc_container_io_open(read_uri, VC_CONTAINER_IO_MODE_READ, &status); + if (!read_io) + { + LOG_ERROR(NULL, "Opening <%s> for read failed: %d", read_uri, status); + result = 3; goto tidyup; + } + + increase_read_buffer_size(read_io); + + if (packet_save_file) + { + write_io = vc_container_io_open(packet_save_file, VC_CONTAINER_IO_MODE_WRITE, &status); + if (!write_io) + { + LOG_ERROR(NULL, "Opening <%s> for write failed: %d", packet_save_file, status); + result = 4; goto tidyup; + } + if (!packet_save_is_pktfile) + { + store_u32(buffer, 0x50415753); + vc_container_io_write(write_io, buffer, sizeof(uint32_t)); + } + } + + /* Use non-blocking I/O for both network and console */ + vc_container_io_control(read_io, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 20); + nb_set_nonblocking_input(1); + + packet_ptr = buffer; + available_bytes = MAXIMUM_BUFFER_SIZE - sizeof(uint32_t); + while (ready) + { + /* Read a packet and store its length in the word before it */ + received_bytes = vc_container_io_read(read_io, packet_ptr + sizeof(uint32_t), available_bytes); + if (received_bytes) + { + bool packet_has_marker; + + store_u32(packet_ptr, received_bytes); + packet_ptr += sizeof(uint32_t); + packet_has_marker = marker_bit_set(packet_ptr, received_bytes); + packet_ptr += received_bytes; + available_bytes -= received_bytes + sizeof(uint32_t); + + if (packet_has_marker || (available_bytes < MINIMUM_BUFFER_SPACE)) + { + uint8_t *decode_ptr; + + if (write_io && !packet_save_is_pktfile) + { + uint32_t total_bytes = packet_ptr - buffer; + if (vc_container_io_write(write_io, buffer, total_bytes) != total_bytes) + { + LOG_ERROR(NULL, "Error saving packets to file"); + break; + } + if (verbosity >= LOWEST_VERBOSITY) + LOG_INFO(NULL, "Written %u bytes to file", total_bytes); + } + + for (decode_ptr = buffer; decode_ptr < packet_ptr;) + { + received_bytes = fetch_u32(decode_ptr); + decode_ptr += sizeof(uint32_t); + + if (write_io && packet_save_is_pktfile) + { + if (vc_container_io_write(write_io, buffer, received_bytes) != received_bytes) + { + LOG_ERROR(NULL, "Error saving packets to file"); + break; + } + if (verbosity >= LOWEST_VERBOSITY) + LOG_INFO(NULL, "Written %u bytes to file", received_bytes); + } + + if (!decode_packet(decode_ptr, received_bytes)) + { + LOG_ERROR(NULL, "Failed to decode packet"); + break; + } + decode_ptr += received_bytes; + } + + /* Reset to start of buffer */ + packet_ptr = buffer; + available_bytes = MAXIMUM_BUFFER_SIZE - sizeof(uint32_t); + } + } + + if (nb_char_available()) + { + if (nb_get_char() == ESCAPE_CHARACTER) + ready = false; + } + + switch (read_io->status) + { + case VC_CONTAINER_SUCCESS: + case VC_CONTAINER_ERROR_CONTINUE: + break; + default: + ready = false; + } + } + + nb_set_nonblocking_input(0); + +tidyup: + if (write_io) + vc_container_io_close(write_io); + if (read_io) + vc_container_io_close(read_io); + if (buffer) + free(buffer); + + return result; +} diff --git a/containers/test/stream_client.c b/containers/test/stream_client.c new file mode 100755 index 0000000..b1c015e --- /dev/null +++ b/containers/test/stream_client.c @@ -0,0 +1,145 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/net/net_sockets.h" + +#include "nb_io.h" + +#define MAX_BUFFER_LEN 1024 +/* Time in milliseconds to yield when nothing is happening */ +#define YIELD_PERIOD_MS 33 + + +static vc_container_net_status_t local_net_control(VC_CONTAINER_NET_T *sock, + vc_container_net_control_t operation, ...) +{ + vc_container_net_status_t result; + va_list args; + + va_start(args, operation); + result = vc_container_net_control(sock, operation, args); + va_end(args); + + return result; +} + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *sock; + vc_container_net_status_t status; + char send_buffer[MAX_BUFFER_LEN]; + char recv_buffer[MAX_BUFFER_LEN]; + int ready = 1; + int to_send = 0; + size_t received; + + if (argc < 3) + { + printf("Usage:\n%s
\n", argv[0]); + return 1; + } + + sock = vc_container_net_open(argv[1], argv[2], VC_CONTAINER_NET_OPEN_FLAG_STREAM, &status); + if (!sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + /* Use non-blocking I/O for both network and console */ + local_net_control(sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, YIELD_PERIOD_MS); + nb_set_nonblocking_input(1); + + while (ready) + { + if (nb_char_available()) + { + char c = nb_get_char(); + + if (c == 26) /* CTRL+Z */ + break; + + send_buffer[to_send++] = c; + + if (c == '\r') /* Translate CR into CRLF */ + { + c = '\n'; + nb_put_char(c); + send_buffer[to_send++] = c; + } + + if (c == '\n' || to_send == sizeof(send_buffer) - 1) /* Allow for next character needing translation */ + { + int already_sent = 0, sent; + + /* Send a line at a time */ + while (to_send) + { + sent = vc_container_net_write(sock, send_buffer + already_sent, to_send); + if (!sent) + { + printf("vc_container_net_write failed: %d\n", vc_container_net_status(sock)); + to_send = 0; + ready = 0; + break; + } + to_send -= sent; + already_sent += sent; + } + } + } + + /* Read back and print any data from the server */ + received = vc_container_net_read(sock, recv_buffer, sizeof(recv_buffer) - 1); + if (received) + { + char *ptr = recv_buffer; + + while (received--) + nb_put_char(*ptr++); + } else { + vc_container_net_status_t net_status = vc_container_net_status(sock); + + if (net_status != VC_CONTAINER_NET_ERROR_TIMED_OUT && net_status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_read failed: %d\n", net_status); + ready = 0; + } + } + } + + nb_set_nonblocking_input(0); + + vc_container_net_close(sock); + + return 0; +} diff --git a/containers/test/stream_server.c b/containers/test/stream_server.c new file mode 100755 index 0000000..8dead8f --- /dev/null +++ b/containers/test/stream_server.c @@ -0,0 +1,148 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "containers/net/net_sockets.h" + +#define MAX_BUFFER_LEN 1024 +#define MAX_NAME_LEN 256 +#define MAX_PORT_LEN 32 + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *server_sock, *sock; + vc_container_net_status_t status; + char buffer[MAX_BUFFER_LEN]; + char name[MAX_NAME_LEN]; + unsigned short port; + int ii, connections = 1; + size_t received; + + if (argc < 2) + { + printf("Usage:\n%s []\n", argv[0]); + return 1; + } + + server_sock = vc_container_net_open(NULL, argv[1], VC_CONTAINER_NET_OPEN_FLAG_STREAM, &status); + if (!server_sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + if (argc > 2) + { + sscanf(argv[2], "%d", &connections); + } + + status = vc_container_net_listen(server_sock, connections); + if (status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_listen failed: %d\n", status); + vc_container_net_close(server_sock); + return 3; + } + + for (ii = 0; ii < connections; ii++) + { + status = vc_container_net_accept(server_sock, &sock); + if (status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_accept failed: %d\n", status); + vc_container_net_close(server_sock); + return 3; + } + + strcpy(name, ""); + vc_container_net_get_client_name(sock, name, sizeof(name)); + vc_container_net_get_client_port(sock, &port); + printf("Connection from %s:%hu\n", name, port); + + while ((received = vc_container_net_read(sock, buffer, sizeof(buffer) - 1)) != 0) + { + char *ptr = buffer; + size_t jj; + + printf("Rx:"); + + /* Flip case and echo data back to client */ + for (jj = 0; jj < received; jj++, ptr++) + { + char c = *ptr; + + putchar(c); + if (isalpha((unsigned char)c)) + *ptr ^= 0x20; /* Swaps case of ASCII alphabetic characters */ + } + + ptr = buffer; + + printf("Tx:"); + while (received) + { + size_t sent; + + sent = vc_container_net_write(sock, ptr, received); + if (!sent) + { + status = vc_container_net_status(sock); + printf("vc_container_net_write failed: %d\n", status); + break; + } + + /* Print out bytes actually sent */ + while (sent--) + { + received--; + putchar(*ptr++); + } + } + } + + status = vc_container_net_status(sock); + + if (status != VC_CONTAINER_NET_SUCCESS && status != VC_CONTAINER_NET_ERROR_CONNECTION_LOST) + break; + + vc_container_net_close(sock); + } + + if (status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_read failed: %d\n", status); + } + + vc_container_net_close(server_sock); + + return 0; +} diff --git a/containers/test/test.c b/containers/test/test.c new file mode 100755 index 0000000..f6a5b08 --- /dev/null +++ b/containers/test/test.c @@ -0,0 +1,623 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_io.h" + +#define BUFFER_SIZE 256*1024 +#define MAX_TRACKS 16 +#define MAX_SEEKS 16 + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); +static int container_test_parse_cmdline(int argc, char **argv); + +/* Client i/o wrapper */ +static VC_CONTAINER_IO_T *client_io_open(const char *, VC_CONTAINER_STATUS_T *); + +static bool b_info = 0, b_seek = 0, b_dump = 0; +static bool b_audio = 1, b_video = 1, b_subs = 1, b_errorcode = 1; +static const char *psz_in = 0, *psz_out = 0; +static long packets_num = 0; +static long track_num = -1; +static FILE *dump_file = 0; +static bool b_client_io = 0; +static bool b_packetize = 0; + +static struct +{ + uint32_t mapping; + uint32_t frames; + uint32_t packets; + uint64_t bytes; + uint32_t frame_size; + int64_t first_dts; + int64_t first_pts; + int64_t last_dts; + int64_t last_pts; +} tracks[MAX_TRACKS]; + +static unsigned int seeks = 0; +static long seek_offsets[MAX_SEEKS]; +static long seek_flags[MAX_SEEKS]; +static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; +static int32_t verbosity_input = -1, verbosity_output = -1; + +/*****************************************************************************/ +int main(int argc, char **argv) +{ + int retval = 0; + VC_CONTAINER_T *p_ctx = 0, *p_writer_ctx = 0; + VC_CONTAINER_STATUS_T status; + unsigned int i, j; + uint8_t *buffer = malloc(BUFFER_SIZE); + int64_t seek_time; + + if(container_test_parse_cmdline(argc, argv)) + goto error_silent; + + /* Set the general verbosity */ + vc_container_log_set_verbosity(0, verbosity); + + if(verbosity_input < 0) verbosity_input = verbosity; + if(verbosity_output < 0) verbosity_output = verbosity; + + /* Open a dump file if it was requested */ + if(b_dump) + { + char *psz_dump; + + if (psz_out) + { + psz_dump = vcos_strdup(psz_out); + } else { + psz_dump = strrchr(psz_in, '\\'); if(psz_dump) psz_dump++; + if(!psz_dump) {psz_dump = strrchr(psz_in, '/'); if(psz_dump) psz_dump++;} + if(!psz_dump) psz_dump = vcos_strdup(psz_in); + else psz_dump = vcos_strdup(psz_dump); + psz_dump[strlen(psz_dump)-1] = '1'; + } + dump_file = fopen(psz_dump, "wb"); + if(!dump_file) LOG_ERROR(0, "error opening dump file %s", psz_dump); + else LOG_INFO(0, "data packets will dumped to %s", psz_dump); + free(psz_dump); + if(!dump_file) goto error; + } + + /* Open a writer if an output was requested */ + if(psz_out && !b_dump) + { + vc_container_log_set_default_verbosity(verbosity_output); + p_writer_ctx = vc_container_open_writer(psz_out, &status, 0, 0); + if(!p_writer_ctx) + { + LOG_ERROR(0, "error opening file %s (%i)", psz_out, status); + goto error; + } + } + + vc_container_log_set_default_verbosity(verbosity_input); + + /* Open the container */ + if(b_client_io) + { + VC_CONTAINER_IO_T *p_io; + + LOG_INFO(0, "Using client I/O for %s", psz_in); + p_io = client_io_open(psz_in, &status); + if(!p_io) + { + LOG_ERROR(0, "error creating io for %s (%i)", psz_in, status); + goto error; + } + + p_ctx = vc_container_open_reader_with_io(p_io, psz_in, &status, 0, 0); + if(!p_ctx) + vc_container_io_close(p_io); + } + else + { + p_ctx = vc_container_open_reader(psz_in, &status, 0, 0); + } + + if(!p_ctx) + { + LOG_ERROR(0, "error opening file %s (%i)", psz_in, status); + goto error; + } + + /* Disabling tracks which are not requested and enable packetisation if requested */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + unsigned int disable = 0; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: if(!b_video) disable = 1; break; + case VC_CONTAINER_ES_TYPE_AUDIO: if(!b_audio) disable = 1; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: if(!b_subs) disable = 1; break; + default: break; + } + if(disable) + { + track->is_enabled = 0; + LOG_INFO(0, "disabling track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); + } + + if(track->is_enabled && b_packetize && !(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + { + status = vc_container_control(p_ctx, VC_CONTAINER_CONTROL_TRACK_PACKETIZE, i, track->format->codec_variant); + if(status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(0, "packetization not supported on track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); + track->is_enabled = 0; + } + } + } + + container_test_info(p_ctx, true); + if(b_info) goto end; + + if(p_writer_ctx) + { + LOG_INFO(0, "----Writer Information----"); + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + if(!track->is_enabled) continue; + tracks[p_writer_ctx->tracks_num].mapping = i; + LOG_INFO(0, "adding track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); + status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD, track->format); + if(status) + { + LOG_INFO(0, "unsupported track type (%i, %i)", status, i); + track->is_enabled = 0; /* disable track */ + } + } + if(p_writer_ctx->tracks_num) + { + status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD_DONE); + if(status) LOG_INFO(0, "could not add tracks (%i)", status); + } + LOG_INFO(0, "--------------------------"); + LOG_INFO(0, ""); + } + + for(i = 0; i < seeks; i++) + { + LOG_DEBUG(0, "TEST seek to %ims", seek_offsets[i]); + seek_time = ((int64_t)(seek_offsets[i])) * 1000; + status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, seek_flags[i]); + LOG_DEBUG(0, "TEST seek done (%i) to %ims", status, (int)(seek_time/1000)); + } + + LOG_DEBUG(0, "TEST start reading"); + for(i = 0; !packets_num || (long)i < packets_num; i++) + { + VC_CONTAINER_PACKET_T packet = {0}; + int32_t frame_num = 0; + + status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;} + + if(packet.track < MAX_TRACKS) + { + if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) + { + tracks[packet.track].frames++; + tracks[packet.track].frame_size = 0; + } + frame_num = tracks[packet.track].frames; + } + + tracks[packet.track].frame_size += packet.size; + + LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i", + packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags, + (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "", + frame_num-1); + + if(track_num >= 0 && packet.track != (uint32_t)track_num) + { + status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;} + continue; + } + + packet.buffer_size = BUFFER_SIZE; + packet.data = buffer; + packet.size = 0; + status = vc_container_read(p_ctx, &packet, 0); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;} + + LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags); + + if (tracks[packet.track].packets) + { + tracks[packet.track].last_dts = packet.dts; + tracks[packet.track].last_pts = packet.pts; + } else { + tracks[packet.track].first_dts = packet.dts; + tracks[packet.track].first_pts = packet.pts; + } + + if(packet.track < MAX_TRACKS) + { + tracks[packet.track].packets++; + tracks[packet.track].bytes += packet.size; + } + + if(dump_file) fwrite(packet.data, packet.size, 1, dump_file); + + if(p_writer_ctx) + { + packet.track = tracks[packet.track].mapping; + status = vc_container_write(p_writer_ctx, &packet); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST write status: %i", status); break;} + } + } + LOG_DEBUG(0, "TEST stop reading"); + + if(b_seek) + { + LOG_DEBUG(0, "TEST start seeking"); + for(j = 0, seek_time = 100; j < 20; j++) + { + LOG_DEBUG(0, "seeking to %ims", (int)(seek_time/1000)); + status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_FORWARD); + LOG_DEBUG(0, "seek done (%i) to %ims", status, (int)(seek_time/1000)); + + for(i = 0; i < 1; i++) + { + VC_CONTAINER_PACKET_T packet = {0}; + packet.buffer_size = BUFFER_SIZE; + packet.data = buffer; + + status = vc_container_read(p_ctx, &packet, 0); + if(status) LOG_DEBUG(0, "TEST read status: %i", status); + if(status == VC_CONTAINER_ERROR_EOS) break; + if(status == VC_CONTAINER_ERROR_CORRUPTED) break; + if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) break; + seek_time = packet.pts + 800000; + } + } + LOG_DEBUG(0, "TEST stop seeking"); + } + + /* Output stats */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes", + i, tracks[i].frames, tracks[i].packets, tracks[i].bytes); + LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)", + tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts); + } + + end: + if(p_ctx) vc_container_close(p_ctx); + if(p_writer_ctx) + { + container_test_info(p_writer_ctx, false); + vc_container_close(p_writer_ctx); + } + if(dump_file) fclose(dump_file); + free(buffer); + +#ifdef _MSC_VER + getchar(); +#endif + + LOG_ERROR(0, "TEST ENDED (%i)", retval); + return b_errorcode ? retval : 0; + + error: + LOG_ERROR(0, "TEST FAILED"); + error_silent: + retval = -1; + goto end; +} + +static int container_test_parse_cmdline(int argc, char **argv) +{ + int i, j, k; + int32_t *p_verbosity; + + /* Parse the command line arguments */ + for(i = 1; i < argc; i++) + { + if(!argv[i]) continue; + + if(argv[i][0] != '-') + { + /* Not an option argument so will be the input URI */ + psz_in = argv[i]; + continue; + } + + /* We are now dealing with command line options */ + switch(argv[i][1]) + { + case 'i': b_info = 1; break; + case 'S': b_seek = 1; break; + case 'd': b_dump = 1; break; + case 'c': b_client_io = 1; break; + case 'v': + if(argv[i][2] == 'i') {j = 3; p_verbosity = &verbosity_input;} + else if(argv[i][2] == 'o') {j = 3; p_verbosity = &verbosity_output;} + else {j = 2; p_verbosity = &verbosity;} + *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++) + *p_verbosity = (*p_verbosity << 1) | 1 ; + break; + case 's': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + if(seeks >= MAX_SEEKS) goto invalid_option; + seek_flags[seeks] = argv[i][2] == 'f' ? VC_CONTAINER_SEEK_FLAG_FORWARD : 0; + seek_offsets[seeks] = strtol(argv[++i], 0, 0); + if(seek_offsets[seeks] < 0 || seek_offsets[seeks] == LONG_MAX) goto invalid_option; + seeks++; + break; + case 'n': + if(argv[i][2] == 'a') b_audio = 0; + else if(argv[i][2] == 'v') b_video = 0; + else if(argv[i][2] == 's') b_subs = 0; + else if(argv[i][2] == 'r') b_errorcode = 0; + else goto invalid_option; + break; + case 'e': + if(argv[i][2] == 'p') b_packetize = 1; + else goto invalid_option; + break; + case 'o': + if(i+1 == argc || !argv[i+1] || argv[i+1][0] == '-') goto invalid_option; + psz_out = argv[++i]; + break; + case 'p': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + packets_num = strtol(argv[++i], 0, 0); + if(packets_num < 0 || packets_num == LONG_MAX) goto invalid_option; + break; + case 't': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + track_num = strtol(argv[++i], 0, 0); + if(track_num == LONG_MIN || track_num == LONG_MAX) goto invalid_option; + break; + case 'h': goto usage; + default: goto invalid_option; + } + continue; + } + + /* Sanity check that we have at least an input uri */ + if(!psz_in) + { + LOG_ERROR(0, "missing uri argument"); + goto usage; + } + + return 0; + + invalid_option: + LOG_ERROR(0, "invalid command line option (%s)", argv[i]); + + usage: + psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++; + if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;} + if(!psz_in) psz_in = argv[0]; + LOG_INFO(0, ""); + LOG_INFO(0, "usage: %s [options] uri", psz_in); + LOG_INFO(0, "options list:"); + LOG_INFO(0, " -i : only print information on the container"); + LOG_INFO(0, " -p X : read only X packets from the container"); + LOG_INFO(0, " -t X : read only packets from track X"); + LOG_INFO(0, " -s X : seek to X milliseconds before starting reading"); + LOG_INFO(0, " -sf X : seek forward to X milliseconds before starting reading"); + LOG_INFO(0, " -S : do seek testing"); + LOG_INFO(0, " -d : dump the data read from the container to files (-o to name file)"); + LOG_INFO(0, " -o uri: output to another uri (i.e. re-muxing)"); + LOG_INFO(0, " -na : disable audio"); + LOG_INFO(0, " -nv : disable video"); + LOG_INFO(0, " -ns : disable subtitles"); + LOG_INFO(0, " -nr : always return an error code of 0 (even in case of failure)"); + LOG_INFO(0, " -ep : enable packetization if data is not already packetized"); + LOG_INFO(0, " -c : use the client i/o functions"); + LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')"); + LOG_INFO(0, " -vixx : verbosity specific to the input container"); + LOG_INFO(0, " -voxx : verbosity specific to the output container"); + LOG_INFO(0, " -h : help"); + return 1; +} + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader) +{ + const char *name_type; + unsigned int i; + + LOG_INFO(0, ""); + if(b_reader) LOG_INFO(0, "----Reader Information----"); + else LOG_INFO(0, "----Writer Information----"); + + LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size); + LOG_INFO(0, "capabilities: %x", ctx->capabilities); + LOG_INFO(0, ""); + + for(i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break; + case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; + default: name_type = "unknown"; break; + } + + LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec); + LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate, + !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled); + LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata); + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_AUDIO: + LOG_INFO(0, " samplerate: %i, channels: %i, bps: %i, block align: %i", + track->format->type->audio.sample_rate, track->format->type->audio.channels, + track->format->type->audio.bits_per_sample, track->format->type->audio.block_align); + LOG_INFO(0, " gapless delay: %i gapless padding: %i", + track->format->type->audio.gap_delay, track->format->type->audio.gap_padding); + LOG_INFO(0, " language: %4.4s", track->format->language); + break; + + case VC_CONTAINER_ES_TYPE_VIDEO: + LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)", + track->format->type->video.width, track->format->type->video.height, + track->format->type->video.x_offset, track->format->type->video.y_offset, + track->format->type->video.visible_width, track->format->type->video.visible_height); + LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i", + track->format->type->video.par_num, track->format->type->video.par_den, + track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den); + break; + + case VC_CONTAINER_ES_TYPE_SUBPICTURE: + LOG_INFO(0, " language: %4.4s, encoding: %i", track->format->language, + track->format->type->subpicture.encoding); + break; + + default: break; + } + } + + for (i = 0; i < ctx->meta_num; ++i) + { + const char *name, *value; + if (i == 0) LOG_INFO(0, ""); + name = vc_container_metadata_id_to_string(ctx->meta[i]->key); + value = ctx->meta[i]->value; + if(!name) continue; + LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value); + } + + LOG_INFO(0, "--------------------------"); + LOG_INFO(0, ""); + + return 0; +} + +/***************************************************************************** + * Client I/O wrapper. Only used when the right cmd line option is passed. + *****************************************************************************/ +static VC_CONTAINER_STATUS_T client_io_close( VC_CONTAINER_IO_T *p_ctx ) +{ + FILE *fd = (FILE *)p_ctx->module; + fclose(fd); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t client_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + FILE *fd = (FILE *)p_ctx->module; + size_t ret = fread(buffer, 1, size, fd); + if(ret != size) + { + /* Sanity check return value. Some platforms (e.g. Android) can return -1 */ + if( ((int)ret) < 0 ) ret = 0; + + if( feof(fd) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + } + LOG_DEBUG( 0, "read: %i", ret ); + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T client_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FILE *fd = (FILE *)p_ctx->module; + int ret; + + //FIXME: no large file support + if (offset > (int64_t)UINT_MAX) + { + LOG_ERROR( 0, "no large file support"); + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return VC_CONTAINER_ERROR_EOS; + } + + ret = fseek(fd, (long)offset, SEEK_SET); + if(ret) + { + if( feof(fd) ) status = VC_CONTAINER_ERROR_EOS; + else status = VC_CONTAINER_ERROR_FAILED; + } + + p_ctx->status = status; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_IO_T *client_io_open( const char *psz_uri, VC_CONTAINER_STATUS_T *status ) +{ + VC_CONTAINER_IO_T *p_io; + VC_CONTAINER_IO_CAPABILITIES_T capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; + FILE *fd; + + fd = fopen(psz_uri, "rb"); + if (!fd) + { + *status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + return NULL; + } + + p_io = vc_container_io_create( psz_uri, 0, capabilities, status ); + if(!p_io) + { + LOG_ERROR(0, "error creating io (%i)", *status); + fclose(fd); + return NULL; + } + + p_io->module = (struct VC_CONTAINER_IO_MODULE_T *)fd; + p_io->pf_close = client_io_close; + p_io->pf_read = client_io_read; + p_io->pf_seek = client_io_seek; + + //FIXME: no large file support + fseek(fd, 0, SEEK_END); + p_io->size = ftell(fd); + fseek(fd, 0, SEEK_SET); + + *status = VC_CONTAINER_SUCCESS; + return p_io; +} diff --git a/containers/test/test_bits.c b/containers/test/test_bits.c new file mode 100755 index 0000000..7ce1651 --- /dev/null +++ b/containers/test/test_bits.c @@ -0,0 +1,600 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#define BITS_LOG_INDENT(ctx) indent_level +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_bits.h" + +uint32_t indent_level; + +/** Bit stream containing the values 0 to 10, with each value in that many bits. + * At the end there is one further zero bit before the end of the stream. */ +static uint8_t bits_0_to_10[] = { + 0xCD, 0x0A, 0x30, 0x70, 0x80, 0x48, 0x14 +}; + +/** Bit stream containing the values 0 to 10, encoded using Exp-Golomb. + * At the end there is one further one bit before the end of the stream. */ +static uint8_t exp_golomb_0_to_10[] = { + 0xA6, 0x42, 0x98, 0xE2, 0x04, 0x8A, 0x17 +}; + +/** Array of signed values for the Exp-Golomb encoding of each index. */ +static int32_t exp_golomb_values[] = { + 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5 +}; + +/** Bit stream containing two large 32-bit values, encoded using Exp-Golomb. */ +static uint8_t exp_golomb_large[] = { + 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +/** Bit stream containing a 33-bit value, encoded using Exp-Golomb. */ +static uint8_t exp_golomb_oversize[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80 +}; + + +static const char *plural_ext(uint32_t val) +{ + return (val == 1) ? "" : "s"; +} + +static int test_reset_and_available(void) +{ + VC_CONTAINER_BITS_T bit_stream; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_reset and vc_container_bits_available"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + if (!BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected initialised stream to contain bits"); + error_count++; + } + + BITS_RESET(NULL, &bit_stream); + + if (BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected reset stream not to contain bits"); + error_count++; + } + + return error_count; +} + +static int test_read_u32(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii, value; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_get_u32"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + value = BITS_READ_U32(NULL, &bit_stream, ii, "test_read_u32"); + if (value != ii) + { + LOG_ERROR(NULL, "Expected %u, got %u", ii, value); + error_count++; + } + } + + value = BITS_READ_U32(NULL, &bit_stream, 1, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Failed to get final bit"); + error_count++; + } + value = BITS_READ_U32(NULL, &bit_stream, 1, "Beyond final bit"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); + error_count++; + } + + return error_count; +} + +static int test_skip(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int error_count = 0; + uint32_t last_bits_left, bits_left; + + LOG_DEBUG(NULL, "Testing vc_container_bits_skip"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + last_bits_left = BITS_AVAILABLE(NULL, &bit_stream); + for (ii = 0; ii < 11; ii++) + { + BITS_SKIP(NULL, &bit_stream, ii, "test_skip"); + bits_left = BITS_AVAILABLE(NULL, &bit_stream); + if (bits_left + ii != last_bits_left) + { + int32_t actual = last_bits_left - bits_left; + LOG_ERROR(NULL, "Tried to skip %u bit%s, actually skipped %d bit%s", + ii, plural_ext(ii), actual, plural_ext(actual)); + error_count++; + } + last_bits_left = bits_left; + } + + BITS_SKIP(NULL, &bit_stream, 1, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to skip final bit"); + error_count++; + } + if (BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "End of stream not reached by skipping"); + error_count++; + } + + BITS_SKIP(NULL, &bit_stream, 1, "Beyond final bit"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded skipping beyond expected end of stream"); + error_count++; + } + return error_count; +} + +static int test_ptr_and_skip_bytes(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + const uint8_t *expected_ptr; + int error_count = 0; + uint32_t last_bytes_left, bytes_left; + + LOG_DEBUG(NULL, "Testing vc_container_bits_current_pointer, vc_container_bits_skip_bytes and vc_container_bits_bytes_available"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + last_bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (last_bytes_left != countof(bits_0_to_10)) + { + LOG_ERROR(NULL, "Expected bytes available to initially match given size"); + error_count++; + } + + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) + { + LOG_ERROR(NULL, "Expected initial current pointer to match original buffer"); + error_count++; + } + + expected_ptr = bits_0_to_10; + for (ii = 0; ii < 4; ii++) + { + BITS_SKIP_BYTES(NULL, &bit_stream, ii, "test_ptr_and_skip_bytes"); + + expected_ptr += ii; + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != expected_ptr) + { + LOG_ERROR(NULL, "Expected current pointer to have moved by %u byte%s", ii, plural_ext(ii)); + error_count++; + } + + bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (bytes_left + ii != last_bytes_left) + { + int32_t actual = last_bytes_left - bytes_left; + LOG_ERROR(NULL, "Tried to skip %u byte%s, actually skipped %d byte%s", + ii, plural_ext(ii), actual, plural_ext(actual)); + error_count++; + } + + last_bytes_left = bytes_left; + } + + if (!bytes_left) + { + LOG_ERROR(NULL, "Reached end of stream too soon"); + error_count++; + } + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to be valid"); + error_count++; + } + + BITS_SKIP_BYTES(NULL, &bit_stream, bytes_left + 1, "Beyond end of stream"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded skipping bytes beyond end of stream"); + error_count++; + } + if (BITS_BYTES_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to have been reset"); + error_count++; + } + + return error_count; +} + +static int test_reduce_bytes(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int error_count = 0; + uint32_t last_bytes_left, bytes_left; + + LOG_DEBUG(NULL, "Testing vc_container_bits_reduce_bytes"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + last_bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (last_bytes_left != countof(bits_0_to_10)) + { + LOG_ERROR(NULL, "Expected bytes available to initially match given size"); + error_count++; + } + + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) + { + LOG_ERROR(NULL, "Expected initial current pointer to match original buffer"); + error_count++; + } + + for (ii = 0; ii < 4; ii++) + { + BITS_REDUCE_BYTES(NULL, &bit_stream, ii, "test_reduce_bytes"); + + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) + { + LOG_ERROR(NULL, "Did not expect current pointer to have moved"); + error_count++; + } + + bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (bytes_left + ii != last_bytes_left) + { + int32_t actual = last_bytes_left - bytes_left; + LOG_ERROR(NULL, "Tried to reduce by %u byte%s, actually reduced by %d byte%s", + ii, plural_ext(ii), actual, plural_ext(actual)); + error_count++; + } + + last_bytes_left = bytes_left; + } + + if (!bytes_left) + { + LOG_ERROR(NULL, "Reached end of stream too soon"); + error_count++; + } + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to be valid"); + error_count++; + } + + BITS_REDUCE_BYTES(NULL, &bit_stream, bytes_left + 1, "Reducing an empty stream"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reducing by too many bytes"); + error_count++; + } + if (BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to have been reset"); + error_count++; + } + + return error_count; +} + +static int test_copy_bytes(void) +{ + VC_CONTAINER_BITS_T bit_stream; + int error_count = 0; + uint8_t buffer[countof(bits_0_to_10)]; + uint32_t ii; + + LOG_DEBUG(NULL, "Testing vc_container_bits_copy_bytes"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + memset(buffer, 0, sizeof(buffer)); + + /* Copy whole buffer in one go */ + BITS_COPY_BYTES(NULL, &bit_stream, countof(buffer), buffer, "Copy whole buffer"); + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to copy the whole buffer"); + error_count++; + } + + if (memcmp(buffer, bits_0_to_10, countof(bits_0_to_10)) != 0) + { + LOG_ERROR(NULL, "Single copy doesn't match original"); + error_count++; + } + + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + memset(buffer, 0, sizeof(buffer)); + + /* Copy whole buffer one byte at a time */ + for (ii = 0; ii < countof(bits_0_to_10); ii++) + { + BITS_COPY_BYTES(NULL, &bit_stream, 1, buffer + ii, "Copy buffer piecemeal"); + } + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to copy the buffer piecemeal"); + error_count++; + } + + if (memcmp(buffer, bits_0_to_10, countof(bits_0_to_10)) != 0) + { + LOG_ERROR(NULL, "Multiple copy doesn't match original"); + error_count++; + } + + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + memset(buffer, 0, sizeof(buffer)); + + /* Copy part of buffer */ + BITS_SKIP_BYTES(NULL, &bit_stream, 1, "Copy part of buffer"); + BITS_REDUCE_BYTES(NULL, &bit_stream, 1, "Copy part of buffer"); + BITS_COPY_BYTES(NULL, &bit_stream, countof(buffer) - 2, buffer, "Copy part of buffer"); + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to copy part of buffer"); + error_count++; + } + + if (memcmp(buffer, bits_0_to_10 + 1, countof(bits_0_to_10) - 2) != 0) + { + LOG_ERROR(NULL, "Partial copy doesn't match original"); + error_count++; + } + + return error_count; +} + +static int test_skip_exp_golomb(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_skip_exp_golomb"); + BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); + + for (ii = 0; ii < 12; ii++) + { + BITS_SKIP_EXP(NULL, &bit_stream, "test_skip_exp_golomb"); + } + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to skip through buffer"); + error_count++; + } + + BITS_SKIP_EXP(NULL, &bit_stream, "Skip beyond end of stream"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded skipping beyond expected end of stream"); + error_count++; + } + + return error_count; +} + +static int test_read_u32_exp_golomb(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii, value; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_get_u32_exp_golomb"); + BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + value = BITS_READ_U32_EXP(NULL, &bit_stream, "test_read_u32_exp_golomb"); + if (value != ii) + { + LOG_ERROR(NULL, "Expected %u, got %u", ii, value); + error_count++; + } + } + + value = BITS_READ_U32(NULL, &bit_stream, 1, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream) || !value) + { + LOG_ERROR(NULL, "Failed to get final bit (expected a 1)"); + error_count++; + } + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Beyond end of stream"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); + error_count++; + } + + /* Test getting two large (32 bit) Exp-Golomb values */ + BITS_INIT(NULL, &bit_stream, exp_golomb_large, countof(exp_golomb_large)); + + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Second largest 32-bit value"); + if (value != 0xFFFFFFFE) + { + LOG_ERROR(NULL, "Failed to get second largest 32-bit value"); + error_count++; + } + + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Largest 32-bit value"); + if (value != 0xFFFFFFFF) + { + LOG_ERROR(NULL, "Failed to get largest 32-bit value"); + error_count++; + } + + /* Test getting an oversize (33 bit) Exp-Golomb value */ + BITS_INIT(NULL, &bit_stream, exp_golomb_oversize, countof(exp_golomb_oversize)); + + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Unsigned 33-bit value"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly got 33-bit value: %u", value); + error_count++; + } + + return error_count; +} + +static int test_read_s32_exp_golomb(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int32_t value; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_get_s32_exp_golomb"); + BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + value = BITS_READ_S32_EXP(NULL, &bit_stream, "test_read_s32_exp_golomb"); + if (value != exp_golomb_values[ii]) + { + LOG_ERROR(NULL, "Expected %u, got %u", ii, value); + error_count++; + } + } + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Failed to get final Exp-Golomb value (expected a zero)"); + error_count++; + } + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Beyond final bit"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); + error_count++; + } + + /* Test getting two large (32 bit) Exp-Golomb values */ + BITS_INIT(NULL, &bit_stream, exp_golomb_large, countof(exp_golomb_large)); + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Largest signed 32-bit value"); + if (!BITS_VALID(NULL, &bit_stream) || value != -0x7FFFFFFF) + { + LOG_ERROR(NULL, "Failed to get largest signed 32-bit value: %d", value); + error_count++; + } + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Just too large signed 33-bit value"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly got slightly too large signed 32-bit value: %d", value); + error_count++; + } + + /* Test getting an oversize (33 bit) Exp-Golomb value */ + BITS_INIT(NULL, &bit_stream, exp_golomb_oversize, countof(exp_golomb_oversize)); + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Larger signed 33-bit value"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly got signed 33-bit value: %d", value); + error_count++; + } + + return error_count; +} + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +static int test_indentation(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + + LOG_DEBUG(NULL, "Testing logging indentation"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + indent_level = ii; + BITS_READ_U32(NULL, &bit_stream, ii, "test_indentation (unit)"); + } + + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + indent_level = ii * 10; + BITS_READ_U32(NULL, &bit_stream, ii, "test_indentation (tens)"); + } + return 0; +} +#endif + +int main(int argc, char **argv) +{ + int error_count = 0; + + VC_CONTAINER_PARAM_UNUSED(argc); + VC_CONTAINER_PARAM_UNUSED(argv); + + error_count += test_reset_and_available(); + error_count += test_read_u32(); + error_count += test_skip(); + error_count += test_ptr_and_skip_bytes(); + error_count += test_reduce_bytes(); + error_count += test_copy_bytes(); + error_count += test_skip_exp_golomb(); + error_count += test_read_u32_exp_golomb(); + error_count += test_read_s32_exp_golomb(); +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + error_count += test_indentation(); +#endif + + if (error_count) + { + LOG_ERROR(NULL, "*** %d errors reported", error_count); + getchar(); + } + + return error_count; +} diff --git a/containers/test/test_uri.c b/containers/test/test_uri.c new file mode 100755 index 0000000..0586182 --- /dev/null +++ b/containers/test/test_uri.c @@ -0,0 +1,824 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_uri.h" + +#define TEST_CHAR '.' +#define TEST_STRING "test" +#define TEST_NAME "name" +#define TEST_VALUE "value" + +#define ARRAY_SIZE(X) (sizeof(X)/sizeof(*(X))) + +struct +{ + const char *before; + const char *after; +} test_parse_uris[] = { + {"", NULL}, + {"C:\\test\\filename", NULL}, + {"/usr/local/nix/filename", NULL}, + {"scheme-only:", NULL}, + {"scheme:/and/path", NULL}, + {"scheme:/and/path#with_fragment", NULL}, + {"scheme:/and/path?query=true", NULL}, + {"scheme:/and/path?multiple+queries=true&more=false", NULL}, + {"scheme:/and/path?multiple+queries=true&more=false#and+a+fragment,+too", NULL}, + {"scheme:C:\\Windows\\path", "scheme:C:%5CWindows%5Cpath"}, + {"scheme:C:\\Windows\\path#with_fragment", "scheme:C:%5CWindows%5Cpath#with_fragment"}, + {"scheme:C:\\Windows\\path?query=true", "scheme:C:%5CWindows%5Cpath?query=true"}, + {"scheme:C:\\Windows\\path?query#and+fragment", "scheme:C:%5CWindows%5Cpath?query#and+fragment"}, + {"scheme://just.host", NULL}, + {"scheme://host/and/path", NULL}, + {"scheme://host:port", NULL}, + {"scheme://host:port/and/path", NULL}, + {"scheme://127.0.0.1:port/and/path", NULL}, + {"scheme://userinfo@host:port/and/path?query#fragment", NULL}, + {"HTTP://www.EXAMPLE.com/", "http://www.example.com/"}, + {"%48%54%54%50://%54%45%53%54/", "http://test/"}, + {"scheme:esc%", "scheme:esc%25"}, + {"scheme:esc%%", "scheme:esc%25%25"}, + {"scheme:esc%%%", "scheme:esc"}, + {"scheme:esc%1%", "scheme:esc%10"}, + {"scheme:esc%%1", "scheme:esc%01"}, + {"s+-.1234567890:", NULL}, + {"scheme:///", NULL}, + {"scheme://:just_port", NULL}, + {"scheme://:port/and/path", NULL}, + {"scheme://just_userinfo@", NULL}, + {"scheme://userinfo@/and/path", NULL}, + {"scheme://userinfo@:port/and/path", NULL}, + {"%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f:", "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F:"}, + {"%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F:", "%20%21%22%23%24%25%26%27%28%29%2A+%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F:"}, + {"scheme://%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F@", "scheme://%20!%22%23$%25&'()*+,-.%2F:;%3C=%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F@"}, + {"scheme://%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://%20!%22%23$%25&'()*+,-.%2F:;%3C=%3E%3F%40[%5C]%5E_%60%7B%7C%7D~%7F"}, + {"scheme://:%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://:%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme:///%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme:///%20!%22%23$%25&'()*+,-./:;%3C=%3E%3F@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme://?%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://?%20!%22%23$%25&'()*+,-./:;%3C=%3E?@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme://#%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://#%20!%22%23$%25&'()*+,-./:;%3C=%3E?@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme://[v6.1234:5678]/", NULL}, + {"scheme://[::1]/", NULL}, + {"scheme://[1234:5678:90ab:cdef:1234:5678:90ab:cdef]/", NULL}, + {"scheme://[::1]:1/", NULL}, + {"scheme://?", "scheme://"}, + {"scheme://?#", "scheme://#"}, + {"scheme://?q&&;&n=&=v&=&n=v", "scheme://?q&n=&n=v"}, + {"ldap://[2001:db8::7]/c=GB?objectClass?one", NULL}, + {"mailto:John.Doe@example.com", NULL}, + {"news:comp.infosystems.www.servers.unix", NULL}, + {"tel:+1-816-555-1212", NULL}, + {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", NULL}, +}; + +typedef struct build_uri_tag +{ + const char *expected_uri; + const char *scheme; + const char *userinfo; + const char *host; + const char *port; + const char *path; + const char *fragment; + const char **queries; /* Held as name then value. NULL values allowed. NULL name terminates list */ +} BUILD_URI_T; + +/* Test query lists */ +const char *no_queries[] = { NULL }; +const char *query_true[] = { "query", "true", NULL }; +const char *multiple_queries[] = { "multiple+queries", "true", "more", "false", NULL }; +const char *just_query[] = {"query", NULL, NULL}; +const char *complex_query[] = { "q", NULL, "n", "", "n", "v", NULL }; +const char *objectClass_one[] = { "objectClass?one", NULL, NULL }; + +BUILD_URI_T test_build_uris[] = { + {"", NULL, NULL, NULL, NULL, NULL, NULL, no_queries }, + {"C:\\test\\filename", NULL, NULL, NULL, NULL, "C:\\test\\filename", NULL, no_queries}, + {"/usr/local/nix/filename", NULL, NULL, NULL, NULL, "/usr/local/nix/filename", NULL, no_queries}, + {"scheme-only:", "scheme-only", NULL, NULL, NULL, NULL, NULL, no_queries}, + {"scheme:/and/path", "scheme", NULL, NULL, NULL, "/and/path", NULL, no_queries}, + {"scheme:/and/path#with_fragment", "scheme", NULL, NULL, NULL, "/and/path", "with_fragment", no_queries}, + {"scheme:/and/path?query=true", "scheme", NULL, NULL, NULL, "/and/path", NULL, query_true}, + {"scheme:/and/path?multiple+queries=true&more=false", "scheme", NULL, NULL, NULL, "/and/path", NULL, multiple_queries}, + {"scheme:/and/path?multiple+queries=true&more=false#and+a+fragment,+too", "scheme", NULL, NULL, NULL, "/and/path", "and+a+fragment,+too", multiple_queries}, + {"scheme://just.host", "scheme", NULL, "just.host", NULL, NULL, NULL, no_queries}, + {"scheme://host/and/path", "scheme", NULL, "host", NULL, "/and/path", NULL, no_queries}, + {"scheme://host:port", "scheme", NULL, "host", "port", NULL, NULL, no_queries}, + {"scheme://host:port/and/path", "scheme", NULL, "host", "port", "/and/path", NULL, no_queries}, + {"scheme://127.0.0.1:port/and/path", "scheme", NULL, "127.0.0.1", "port", "/and/path", NULL, no_queries}, + {"scheme://userinfo@host:port/and/path?query#fragment", "scheme", "userinfo", "host", "port", "/and/path", "fragment", just_query}, + {"scheme:///", "scheme", NULL, "", NULL, "/", NULL, no_queries }, + {"scheme://:just_port", "scheme", NULL, "", "just_port", NULL, NULL, no_queries }, + {"scheme://:port/and/path", "scheme", NULL, "", "port", "/and/path", NULL, no_queries }, + {"scheme://just_userinfo@", "scheme", "just_userinfo", "", NULL, NULL, NULL, no_queries }, + {"scheme://userinfo@/and/path", "scheme", "userinfo", "", NULL, "/and/path", NULL, no_queries }, + {"scheme://userinfo@:port/and/path", "scheme", "userinfo", "", "port", "/and/path", NULL, no_queries }, + {"scheme://", "scheme", NULL, "", NULL, NULL, NULL, no_queries }, + {"scheme://#", "scheme", NULL, "", NULL, NULL, "", no_queries }, + {"scheme://?q&n=&n=v", "scheme", NULL, "", NULL, NULL, NULL, complex_query}, + {"ldap://[2001:db8::7]/c=GB?objectClass?one", "ldap", NULL, "[2001:db8::7]", NULL, "/c=GB", NULL, objectClass_one}, + {"mailto:John.Doe@example.com", "mailto", NULL, NULL, NULL, "John.Doe@example.com", NULL, no_queries }, + {"news:comp.infosystems.www.servers.unix", "news", NULL, NULL, NULL, "comp.infosystems.www.servers.unix", NULL, no_queries }, + {"tel:+1-816-555-1212", "tel", NULL, NULL, NULL, "+1-816-555-1212", NULL, no_queries }, + {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", "urn", NULL, NULL, NULL, "oasis:names:specification:docbook:dtd:xml:4.1.2", NULL, no_queries }, +}; + +typedef struct merge_uri_tag +{ + const char *base; + const char *relative; + const char *merged; +} MERGE_URI_T; + +MERGE_URI_T test_merge_uris[] = { + /* Normal examples */ + { "http://a/b/c/d;p?q#f", "ftp:h", "ftp:h" }, + { "http://a/b/c/d;p?q#f", "g", "http://a/b/c/g" }, + { "http://a/b/c/d;p?q#f", "./g", "http://a/b/c/g" }, + { "http://a/b/c/d;p?q#f", "g/", "http://a/b/c/g/" }, + { "http://a/b/c/d;p?q#f", "/g", "http://a/g" }, + { "http://a/b/c/d;p?q#f", "//g", "http://g" }, + { "http://a/b/c/d;p?q#f", "?y", "http://a/b/c/d;p?y" }, + { "http://a/b/c/d;p?q#f", "g?y", "http://a/b/c/g?y" }, + { "http://a/b/c/d;p?q#f", "g?y/./x", "http://a/b/c/g?y/./x" }, + { "http://a/b/c/d;p?q#f", "#s", "http://a/b/c/d;p?q#s" }, + { "http://a/b/c/d;p?q#f", "g#s", "http://a/b/c/g#s" }, + { "http://a/b/c/d;p?q#f", "g#s/./x", "http://a/b/c/g#s/./x" }, + { "http://a/b/c/d;p?q#f", "g?y#s", "http://a/b/c/g?y#s" }, + { "http://a/b/c/d;p?q#f", ";x", "http://a/b/c/d;x" }, + { "http://a/b/c/d;p?q#f", "g;x", "http://a/b/c/g;x" }, + { "http://a/b/c/d;p?q#f", "g;x?y#s", "http://a/b/c/g;x?y#s" }, + { "http://a/b/c/d;p?q#f", ".", "http://a/b/c/" }, + { "http://a/b/c/d;p?q#f", "./", "http://a/b/c/" }, + { "http://a/b/c/d;p?q#f", "..", "http://a/b/" }, + { "http://a/b/c/d;p?q#f", "../", "http://a/b/" }, + { "http://a/b/c/d;p?q#f", "../g", "http://a/b/g" }, + { "http://a/b/c/d;p?q#f", "../..", "http://a/" }, + { "http://a/b/c/d;p?q#f", "../../", "http://a/" }, + { "http://a/b/c/d;p?q#f", "../../g", "http://a/g" }, + /* Normal examples, without base network info */ + { "http:/b/c/d;p?q#f", "g", "http:/b/c/g" }, + { "http:/b/c/d;p?q#f", "./g", "http:/b/c/g" }, + { "http:/b/c/d;p?q#f", "g/", "http:/b/c/g/" }, + { "http:/b/c/d;p?q#f", "/g", "http:/g" }, + { "http:/b/c/d;p?q#f", "//g", "http://g" }, + { "http:/b/c/d;p?q#f", "?y", "http:/b/c/d;p?y" }, + { "http:/b/c/d;p?q#f", "g?y", "http:/b/c/g?y" }, + { "http:/b/c/d;p?q#f", "g?y/./x", "http:/b/c/g?y/./x" }, + { "http:/b/c/d;p?q#f", "#s", "http:/b/c/d;p?q#s" }, + { "http:/b/c/d;p?q#f", "g#s", "http:/b/c/g#s" }, + { "http:/b/c/d;p?q#f", "g#s/./x", "http:/b/c/g#s/./x" }, + { "http:/b/c/d;p?q#f", "g?y#s", "http:/b/c/g?y#s" }, + { "http:/b/c/d;p?q#f", ";x", "http:/b/c/d;x" }, + { "http:/b/c/d;p?q#f", "g;x", "http:/b/c/g;x" }, + { "http:/b/c/d;p?q#f", "g;x?y#s", "http:/b/c/g;x?y#s" }, + { "http:/b/c/d;p?q#f", ".", "http:/b/c/" }, + { "http:/b/c/d;p?q#f", "./", "http:/b/c/" }, + { "http:/b/c/d;p?q#f", "..", "http:/b/" }, + { "http:/b/c/d;p?q#f", "../", "http:/b/" }, + { "http:/b/c/d;p?q#f", "../g", "http:/b/g" }, + { "http:/b/c/d;p?q#f", "../..", "http:/" }, + { "http:/b/c/d;p?q#f", "../../", "http:/" }, + { "http:/b/c/d;p?q#f", "../../g", "http:/g" }, + /* Normal examples, without base network info or path root */ + { "http:b/c/d;p?q#f", "g", "http:b/c/g" }, + { "http:b/c/d;p?q#f", "./g", "http:b/c/g" }, + { "http:b/c/d;p?q#f", "g/", "http:b/c/g/" }, + { "http:b/c/d;p?q#f", "/g", "http:/g" }, + { "http:b/c/d;p?q#f", "//g", "http://g" }, + { "http:b/c/d;p?q#f", "?y", "http:b/c/d;p?y" }, + { "http:b/c/d;p?q#f", "g?y", "http:b/c/g?y" }, + { "http:b/c/d;p?q#f", "g?y/./x", "http:b/c/g?y/./x" }, + { "http:b/c/d;p?q#f", "#s", "http:b/c/d;p?q#s" }, + { "http:b/c/d;p?q#f", "g#s", "http:b/c/g#s" }, + { "http:b/c/d;p?q#f", "g#s/./x", "http:b/c/g#s/./x" }, + { "http:b/c/d;p?q#f", "g?y#s", "http:b/c/g?y#s" }, + { "http:b/c/d;p?q#f", ";x", "http:b/c/d;x" }, + { "http:b/c/d;p?q#f", "g;x", "http:b/c/g;x" }, + { "http:b/c/d;p?q#f", "g;x?y#s", "http:b/c/g;x?y#s" }, + { "http:b/c/d;p?q#f", ".", "http:b/c/" }, + { "http:b/c/d;p?q#f", "./", "http:b/c/" }, + { "http:b/c/d;p?q#f", "..", "http:b/" }, + { "http:b/c/d;p?q#f", "../", "http:b/" }, + { "http:b/c/d;p?q#f", "../g", "http:b/g" }, + { "http:b/c/d;p?q#f", "../..", "http:" }, + { "http:b/c/d;p?q#f", "../../", "http:" }, + { "http:b/c/d;p?q#f", "../../g", "http:g" }, + /* Normal examples, without base path */ + { "http://a?q#f", "g", "http://a/g" }, + { "http://a?q#f", "./g", "http://a/g" }, + { "http://a?q#f", "g/", "http://a/g/" }, + { "http://a?q#f", "/g", "http://a/g" }, + { "http://a?q#f", "//g", "http://g" }, + { "http://a?q#f", "?y", "http://a?y" }, + { "http://a?q#f", "g?y", "http://a/g?y" }, + { "http://a?q#f", "g?y/./x", "http://a/g?y/./x" }, + { "http://a?q#f", "#s", "http://a?q#s" }, + { "http://a?q#f", "g#s", "http://a/g#s" }, + { "http://a?q#f", "g#s/./x", "http://a/g#s/./x" }, + { "http://a?q#f", "g?y#s", "http://a/g?y#s" }, + { "http://a?q#f", ";x", "http://a/;x" }, + { "http://a?q#f", "g;x", "http://a/g;x" }, + { "http://a?q#f", "g;x?y#s", "http://a/g;x?y#s" }, + { "http://a?q#f", ".", "http://a/" }, + { "http://a?q#f", "./", "http://a/" }, + /* Normal examples, without base network info or path */ + { "http:?q#f", "g", "http:g" }, + { "http:?q#f", "./g", "http:g" }, + { "http:?q#f", "g/", "http:g/" }, + { "http:?q#f", "/g", "http:/g" }, + { "http:?q#f", "//g", "http://g" }, + { "http:?q#f", "?y", "http:?y" }, + { "http:?q#f", "g?y", "http:g?y" }, + { "http:?q#f", "g?y/./x", "http:g?y/./x" }, + { "http:?q#f", "#s", "http:?q#s" }, + { "http:?q#f", "g#s", "http:g#s" }, + { "http:?q#f", "g#s/./x", "http:g#s/./x" }, + { "http:?q#f", "g?y#s", "http:g?y#s" }, + { "http:?q#f", ";x", "http:;x" }, + { "http:?q#f", "g;x", "http:g;x" }, + { "http:?q#f", "g;x?y#s", "http:g;x?y#s" }, + /* Abnormal (but valid) examples */ + { "http://a/b/c/d;p?q#f", "../../../g", "http://a/../g" }, + { "http://a/b/c/d;p?q#f", "../../../../g", "http://a/../../g" }, + { "http://a/b/c/d;p?q#f", "/./g", "http://a/./g" }, + { "http://a/b/c/d;p?q#f", "/../g", "http://a/../g" }, + { "http://a/b/c/d;p?q#f", "g.", "http://a/b/c/g." }, + { "http://a/b/c/d;p?q#f", ".g", "http://a/b/c/.g" }, + { "http://a/b/c/d;p?q#f", "g..", "http://a/b/c/g.." }, + { "http://a/b/c/d;p?q#f", "..g", "http://a/b/c/..g" }, + { "http://a/b/c/d;p?q#f", "./../g", "http://a/b/g" }, + { "http://a/b/c/d;p?q#f", "./g/.", "http://a/b/c/g/" }, + { "http://a/b/c/d;p?q#f", "g/./h", "http://a/b/c/g/h" }, + { "http://a/b/c/d;p?q#f", "g/../h", "http://a/b/c/h" }, + { "http://a/b/c/d;p?q#f", "./g:h", "http://a/b/c/g:h" }, + { "http://a/b/c/d;p?q#f", "g/..", "http://a/b/c/" }, + /* Abnormal examples without base path */ + { "http://a?q#f", "../g", "http://a/../g" }, + { "http://a?q#f", "./../g", "http://a/../g" }, + /* Abnormal examples without base network info or path */ + { "http:?q#f", "../g", "http:../g" }, + { "http:?q#f", "./../g", "http:../g" }, + { "http:?q#f", ".", "http:" }, + { "http:?q#f", "./", "http:" }, +}; + + +/** Dump a URI structure to the log. + * + * \param uri URI structure to be dumped. */ +static void dump_uri(VC_URI_PARTS_T *uri) +{ + const char *str; + uint32_t query_count, ii; + + str = vc_uri_scheme(uri); + if (str) + LOG_DEBUG(NULL, "Scheme: <%s>", str); + + str = vc_uri_userinfo(uri); + if (str) + LOG_DEBUG(NULL, "Userinfo: <%s>", str); + + str = vc_uri_host(uri); + if (str) + LOG_DEBUG(NULL, "Host: <%s>", str); + + str = vc_uri_port(uri); + if (str) + LOG_DEBUG(NULL, "Port: <%s>", str); + + str = vc_uri_path(uri); + if (str) + LOG_DEBUG(NULL, "Path: <%s>", str); + + query_count = vc_uri_num_queries(uri); + for (ii = 0; ii < query_count; ii++) + { + const char *value; + + vc_uri_query(uri, ii, &str, &value); + if (str) + { + if (value) + LOG_DEBUG(NULL, "Query %d: <%s>=<%s>", ii, str, value); + else + LOG_DEBUG(NULL, "Query %d: <%s>", ii, str); + } + } + + str = vc_uri_fragment(uri); + if (str) + LOG_DEBUG(NULL, "Fragment: <%s>", str); +} + +static int check_uri(VC_URI_PARTS_T *uri, const char *expected) +{ + uint32_t built_len; + char *built; + + built_len = vc_uri_build(uri, NULL, 0) + 1; + built = (char *)malloc(built_len); + if (!built) + { + LOG_ERROR(NULL, "*** Unexpected memory allocation failure: %d bytes", built_len); + return 1; + } + + vc_uri_build(uri, built, built_len); + + if (strcmp(built, expected) != 0) + { + LOG_ERROR(NULL, "*** Built URI <%s>\nexpected <%s>", built, expected); + free(built); + return 1; + } + + free(built); + + return 0; +} + +static int check_null_uri_pointer(void) +{ + int error_count = 0; + char buffer[1]; + + /* Check NULL URI can be passed without failure to all routines */ + vc_uri_release( NULL ); + vc_uri_clear( NULL ); + if (vc_uri_parse( NULL, NULL )) + error_count++; + if (vc_uri_parse( NULL, "" )) + error_count++; + if (vc_uri_build( NULL, NULL, 0 ) != 0) + error_count++; + buffer[0] = TEST_CHAR; + if (vc_uri_build( NULL, buffer, sizeof(buffer) ) != 0) + error_count++; + if (buffer[0] != TEST_CHAR) + error_count++; + if (vc_uri_scheme( NULL )) + error_count++; + if (vc_uri_userinfo( NULL )) + error_count++; + if (vc_uri_host( NULL )) + error_count++; + if (vc_uri_port( NULL )) + error_count++; + if (vc_uri_path( NULL )) + error_count++; + if (vc_uri_fragment( NULL )) + error_count++; + if (vc_uri_num_queries( NULL ) != 0) + error_count++; + vc_uri_query( NULL, 0, NULL, NULL ); + if (vc_uri_set_scheme( NULL, NULL )) + error_count++; + if (vc_uri_set_userinfo( NULL, NULL )) + error_count++; + if (vc_uri_set_host( NULL, NULL )) + error_count++; + if (vc_uri_set_port( NULL, NULL )) + error_count++; + if (vc_uri_set_path( NULL, NULL )) + error_count++; + if (vc_uri_set_fragment( NULL, NULL )) + error_count++; + if (vc_uri_add_query( NULL, NULL, NULL )) + error_count++; + + if (error_count) + LOG_ERROR(NULL, "NULL URI parameter testing failed"); + + return error_count; +} + +static int check_parse_parameters(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + + if (vc_uri_parse( uri, NULL )) + { + LOG_ERROR(NULL, "Parsing NULL URI failed"); + error_count++; + } + + return error_count; +} + +static int check_build_parameters(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + char buffer[1]; + + vc_uri_set_path( uri, TEST_STRING ); + + if (vc_uri_build( uri, NULL, 0 ) != strlen(TEST_STRING)) + { + LOG_ERROR(NULL, "Retrieving URI length failed"); + error_count++; + } + + buffer[0] = TEST_CHAR; + if (vc_uri_build( uri, buffer, 1 ) != strlen(TEST_STRING)) + { + LOG_ERROR(NULL, "Building URI to small buffer failed"); + error_count++; + } + if (buffer[0] != TEST_CHAR) + { + LOG_ERROR(NULL, "Building URI to small buffer modified buffer"); + error_count++; + } + + vc_uri_set_path( uri, NULL ); /* Reset uri */ + + return error_count; +} + +static int check_get_defaults(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + const char *name = NULL, *value = NULL; + char buffer[1]; + + if (vc_uri_scheme( uri )) + error_count++; + if (vc_uri_userinfo( uri )) + error_count++; + if (vc_uri_host( uri )) + error_count++; + if (vc_uri_port( uri )) + error_count++; + if (vc_uri_path( uri )) + error_count++; + if (vc_uri_fragment( uri )) + error_count++; + if (vc_uri_num_queries( uri ) != 0) + error_count++; + + vc_uri_query( uri, 0, &name, &value ); + if (name != NULL || value != NULL) + error_count++; + + if (vc_uri_build(uri, NULL, 0) != 0) + error_count++; + buffer[0] = ~*TEST_STRING; /* Initialize with something */ + vc_uri_build(uri, buffer, sizeof(buffer)); + if (buffer[0] != '\0') /* Expect empty string */ + error_count++; + + if (error_count) + LOG_ERROR(NULL, "Getting default values gave unexpected values"); + + return error_count; +} + +static int check_accessors(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + const char *str; + + if (vc_uri_set_scheme( uri, TEST_STRING )) + { + str = vc_uri_scheme(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_scheme( uri, NULL )) + error_count++; + if (vc_uri_scheme(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_userinfo( uri, TEST_STRING )) + { + str = vc_uri_userinfo(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_userinfo( uri, NULL )) + error_count++; + if (vc_uri_userinfo(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_host( uri, TEST_STRING )) + { + str = vc_uri_host(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_host( uri, NULL )) + error_count++; + if (vc_uri_host(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_port( uri, TEST_STRING )) + { + str = vc_uri_port(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_port( uri, NULL )) + error_count++; + if (vc_uri_port(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_path( uri, TEST_STRING )) + { + str = vc_uri_path(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_path( uri, NULL )) + error_count++; + if (vc_uri_path(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_fragment( uri, TEST_STRING )) + { + str = vc_uri_fragment(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_fragment( uri, NULL )) + error_count++; + if (vc_uri_fragment(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_add_query( uri, NULL, NULL )) + error_count++; + if (vc_uri_add_query( uri, NULL, TEST_VALUE )) + error_count++; + if (!vc_uri_add_query( uri, TEST_STRING, NULL )) + error_count++; + if (!vc_uri_add_query( uri, TEST_NAME, TEST_VALUE )) + error_count++; + + if (vc_uri_num_queries(uri) == 2) + { + const char *name = NULL, *value = NULL; + + vc_uri_query(uri, 0, &name, &value); + if (!name || strcmp(TEST_STRING, name)) + error_count++; + if (value) + error_count++; + + vc_uri_query(uri, 1, &name, &value); + if (!name || strcmp(TEST_NAME, name)) + error_count++; + if (!value || strcmp(TEST_VALUE, value)) + error_count++; + } else + error_count++; + + if (error_count) + LOG_ERROR(NULL, "Accessors failed"); + + return error_count; +} + +/** Test parameter validation + * + * \param uri Pre-created URI structure. + * \return 1 on error, 0 on success. */ +static int test_parameter_validation(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + + error_count += check_null_uri_pointer(); + error_count += check_get_defaults(uri); + error_count += check_parse_parameters(uri); + error_count += check_build_parameters(uri); + error_count += check_accessors(uri); + + return error_count; +} + +/** Test parsing and rebuilding a URI. + * + * \param uri Pre-created URI structure. + * \param original The original URI string. + * \param expected The expected, re-built string, or NULL if original is expected. + * \return 1 on error, 0 on success. */ +static int test_parsing_uri(VC_URI_PARTS_T *uri, const char *original, const char *expected) +{ + bool parsed; + + LOG_INFO(NULL, "URI: <%s>", original); + + parsed = vc_uri_parse( uri, original ); + if (!parsed) + { + LOG_ERROR(NULL, "*** Expected <%s> to parse, but it didn't", original); + return 1; + } + + dump_uri(uri); + + return check_uri(uri, expected ? expected : original); +} + +/** Test building a URI from component parts. + * + * \param uri Pre-created URI structure. + * \param uri_data The data for building the URI and the expected output. + * \return 1 on error, 0 on success. */ +static int test_building_uri(VC_URI_PARTS_T *uri, BUILD_URI_T *uri_data) +{ + const char **p_str; + const char *name, *value; + + LOG_INFO(NULL, "Building URI <%s>", uri_data->expected_uri); + + vc_uri_clear(uri); + + if (!vc_uri_set_scheme(uri, uri_data->scheme)) + { + LOG_ERROR(NULL, "*** Failed to set scheme"); + return 1; + } + + if (!vc_uri_set_userinfo(uri, uri_data->userinfo)) + { + LOG_ERROR(NULL, "*** Failed to set userinfo"); + return 1; + } + + if (!vc_uri_set_host(uri, uri_data->host)) + { + LOG_ERROR(NULL, "*** Failed to set host"); + return 1; + } + + if (!vc_uri_set_port(uri, uri_data->port)) + { + LOG_ERROR(NULL, "*** Failed to set port"); + return 1; + } + + if (!vc_uri_set_path(uri, uri_data->path)) + { + LOG_ERROR(NULL, "*** Failed to set path"); + return 1; + } + + if (!vc_uri_set_fragment(uri, uri_data->fragment)) + { + LOG_ERROR(NULL, "*** Failed to set fragment"); + return 1; + } + + p_str = uri_data->queries; + name = *p_str++; + + while (name) + { + value = *p_str++; + if (!vc_uri_add_query(uri, name, value)) + { + LOG_ERROR(NULL, "*** Failed to add query"); + return 1; + } + name = *p_str++; + } + + dump_uri(uri); + + return check_uri(uri, uri_data->expected_uri); +} + +/** Test merging a relative URI with a base URI. + * + * \param uri Pre-created URI structure. + * \param uri_data The nase and relative URIs and the expected output. + * \return 1 on error, 0 on success. */ +static int test_merging_uri(VC_URI_PARTS_T *uri, MERGE_URI_T *uri_data) +{ + VC_URI_PARTS_T *base_uri; + + LOG_INFO(NULL, "Base <%s>, relative <%s>, expect <%s>", uri_data->base, uri_data->relative, uri_data->merged); + + vc_uri_clear(uri); + base_uri = vc_uri_create(); + if (!base_uri) + { + LOG_ERROR(NULL, "*** Failed to allocate base URI structure"); + return 1; + } + + if (!vc_uri_parse(base_uri, uri_data->base)) + { + LOG_ERROR(NULL, "*** Failed to parse base URI structure"); + return 1; + } + if (!vc_uri_parse(uri, uri_data->relative)) + { + LOG_ERROR(NULL, "*** Failed to parse relative URI structure"); + return 1; + } + + if (!vc_uri_merge(base_uri, uri)) + { + LOG_ERROR(NULL, "*** Failed to merge base and relative URIs"); + return 1; + } + + vc_uri_release(base_uri); + + return check_uri(uri, uri_data->merged); +} + +int main(int argc, char **argv) +{ + VC_URI_PARTS_T *uri; + int error_count = 0; + size_t ii; + + uri = vc_uri_create(); + if (!uri) + { + LOG_ERROR(NULL, "*** Failed to create URI structure."); + return 1; + } + + LOG_INFO(NULL, "Test parameter validation"); + error_count += test_parameter_validation(uri); + + LOG_INFO(NULL, "Test parsing URIs:"); + for (ii = 0; ii < ARRAY_SIZE(test_parse_uris); ii++) + { + error_count += test_parsing_uri(uri, test_parse_uris[ii].before, test_parse_uris[ii].after); + } + + LOG_INFO(NULL, "Test building URIs:"); + for (ii = 0; ii < ARRAY_SIZE(test_build_uris); ii++) + { + error_count += test_building_uri(uri, &test_build_uris[ii]); + } + + LOG_INFO(NULL, "Test merging URIs:"); + for (ii = 0; ii < ARRAY_SIZE(test_merge_uris); ii++) + { + error_count += test_merging_uri(uri, &test_merge_uris[ii]); + } + + if (argc > 1) + { + LOG_INFO(NULL, "Test parsing URIs from command line:"); + + while (argc-- > 1) + { + /* Test URIs passed on the command line are expected to parse and to + * match themselves when rebuilt. */ + error_count += test_parsing_uri(uri, argv[argc], NULL); + } + } + + vc_uri_release(uri); + + if (error_count) + LOG_ERROR(NULL, "*** %d errors reported", error_count); + +#ifdef _MSC_VER + LOG_INFO(NULL, "Press return to complete test."); + getchar(); +#endif + + return error_count; +} diff --git a/containers/test/uri_pipe.c b/containers/test/uri_pipe.c new file mode 100755 index 0000000..6dd6072 --- /dev/null +++ b/containers/test/uri_pipe.c @@ -0,0 +1,116 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "containers/containers.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_io.h" + +#include "nb_io.h" + +#define MAX_BUFFER_SIZE 2048 + +int main(int argc, char **argv) +{ + char buffer[MAX_BUFFER_SIZE]; + VC_CONTAINER_IO_T *read_io, *write_io; + VC_CONTAINER_STATUS_T status; + size_t received; + bool ready = true; + + if (argc < 3) + { + LOG_INFO(NULL, "Usage:\n%s \n", argv[0]); + return 1; + } + + read_io = vc_container_io_open(argv[1], VC_CONTAINER_IO_MODE_READ, &status); + if (!read_io) + { + LOG_INFO(NULL, "Opening <%s> for read failed: %d\n", argv[1], status); + return 2; + } + + write_io = vc_container_io_open(argv[2], VC_CONTAINER_IO_MODE_WRITE, &status); + if (!write_io) + { + vc_container_io_close(read_io); + LOG_INFO(NULL, "Opening <%s> for write failed: %d\n", argv[2], status); + return 3; + } + + nb_set_nonblocking_input(1); + + while (ready) + { + size_t total_written = 0; + + received = vc_container_io_read(read_io, buffer, sizeof(buffer)); + while (ready && total_written < received) + { + total_written += vc_container_io_write(write_io, buffer + total_written, received - total_written); + ready &= (write_io->status == VC_CONTAINER_SUCCESS); + } + ready &= (read_io->status == VC_CONTAINER_SUCCESS); + + if (nb_char_available()) + { + char c = nb_get_char(); + + switch (c) + { + case 'q': + case 'Q': + case 0x04: /* CTRL+D */ + case 0x1A: /* CTRL+Z */ + case 0x1B: /* Escape */ + ready = false; + break; + default: + ;/* Do nothing */ + } + } + } + + if (read_io->status != VC_CONTAINER_SUCCESS && read_io->status != VC_CONTAINER_ERROR_EOS) + { + LOG_INFO(NULL, "Read failed: %d\n", read_io->status); + } + + if (write_io->status != VC_CONTAINER_SUCCESS) + { + LOG_INFO(NULL, "Write failed: %d\n", write_io->status); + } + + vc_container_io_close(read_io); + vc_container_io_close(write_io); + + nb_set_nonblocking_input(0); + + return 0; +} diff --git a/containers/wav/CMakeLists.txt b/containers/wav/CMakeLists.txt new file mode 100755 index 0000000..aed61c6 --- /dev/null +++ b/containers/wav/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_wav ${LIBRARY_TYPE} wav_reader.c) + +target_link_libraries(reader_wav containers) + +install(TARGETS reader_wav DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/wav/wav_reader.c b/containers/wav/wav_reader.c new file mode 100755 index 0000000..0b279ba --- /dev/null +++ b/containers/wav/wav_reader.c @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_LITTLE_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_waveformat.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define WAV_EXTRADATA_MAX 16 +#define BLOCK_SIZE (16*1024) + +/****************************************************************************** +GUID list for the different codecs +******************************************************************************/ +static const GUID_T atracx_guid = {0xbfaa23e9, 0x58cb, 0x7144, {0xa1, 0x19, 0xff, 0xfa, 0x01, 0xe4, 0xce, 0x62}}; +static const GUID_T pcm_guid = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + uint64_t data_offset; /**< Offset to the start of the data packets */ + int64_t data_size; /**< Size of the data contained in the data element */ + uint32_t block_size; /**< Size of a block of audio data */ + int64_t position; + uint64_t frame_data_left; + + VC_CONTAINER_TRACK_T *track; + uint8_t extradata[WAV_EXTRADATA_MAX]; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T wav_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t packet_flags = 0, size, data_size; + int64_t pts; + + pts = module->position * 8000000 / p_ctx->tracks[0]->format->bitrate; + data_size = module->frame_data_left; + if(!data_size) + { + data_size = module->block_size; + packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + module->frame_data_left = 0; + + if(module->position + data_size > module->data_size) + data_size = module->data_size - module->position; + if(data_size == 0) return VC_CONTAINER_ERROR_EOS; + + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ + { + size = SKIP_BYTES(p_ctx, data_size); + module->frame_data_left = data_size - size; + module->position += size; + return STREAM_STATUS(p_ctx); + } + + p_packet->flags = packet_flags; + p_packet->dts = p_packet->pts = pts; + p_packet->track = 0; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(p_ctx, data_size); + module->frame_data_left = data_size - size; + if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + module->position += size; + p_packet->size += size; + return STREAM_STATUS(p_ctx); + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(data_size, p_packet->buffer_size - p_packet->size); + size = READ_BYTES(p_ctx, p_packet->data, size); + module->frame_data_left = data_size - size; + if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + module->position += size; + p_packet->size += size; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T wav_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t position; + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + position = *p_offset * p_ctx->tracks[0]->format->bitrate / 8000000; + if(p_ctx->tracks[0]->format->type->audio.block_align) + position = position / p_ctx->tracks[0]->format->type->audio.block_align * + p_ctx->tracks[0]->format->type->audio.block_align; + if(position > module->data_size) position = module->data_size; + + module->position = position; + module->frame_data_left = 0; + + if(position >= module->data_size) return VC_CONTAINER_ERROR_EOS; + return SEEK(p_ctx, module->data_offset + position); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T wav_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_FOURCC_T codec; + int64_t chunk_size, chunk_pos; + uint32_t format, channels, samplerate, bitrate, block_align, bps, cbsize = 0; + uint8_t buffer[12]; + + /* Check the RIFF chunk descriptor */ + if( PEEK_BYTES(p_ctx, buffer, 12) != 12 ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( VC_FOURCC(buffer[0], buffer[1], buffer[2], buffer[3]) != + VC_FOURCC('R','I','F','F') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( VC_FOURCC(buffer[8], buffer[9], buffer[10], buffer[11]) != + VC_FOURCC('W','A','V','E') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We are dealing with a WAV file + */ + SKIP_FOURCC(p_ctx, "Chunk ID"); + SKIP_U32(p_ctx, "Chunk size"); + SKIP_FOURCC(p_ctx, "WAVE ID"); + + /* We're looking for the 'fmt' sub-chunk */ + do { + chunk_pos = STREAM_POSITION(p_ctx) + 8; + if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('f','m','t',' ') ) break; + + /* Not interested in this chunk. Skip it. */ + chunk_size = READ_U32(p_ctx, "Chunk size"); + SKIP_BYTES(p_ctx, chunk_size); + } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* 'fmt' not found */ + + /* Parse the 'fmt' sub-chunk */ + chunk_size = READ_U32(p_ctx, "Chunk size"); + format = READ_U16(p_ctx, "wFormatTag"); + channels = READ_U16(p_ctx, "nChannels"); + samplerate = READ_U32(p_ctx, "nSamplesPerSec"); + bitrate = READ_U32(p_ctx, "nAvgBytesPerSec") * 8; + block_align = READ_U16(p_ctx, "nBlockAlign"); + bps = READ_U16(p_ctx, "wBitsPerSample"); + + if(STREAM_POSITION(p_ctx) - chunk_pos <= chunk_size - 2) + cbsize = READ_U16(p_ctx, "cbSize"); + + if(format == WAVE_FORMAT_EXTENSIBLE && + chunk_size >= 18 + 22 && cbsize >= 22) + { + GUID_T guid; + codec = VC_CONTAINER_CODEC_UNKNOWN; + + SKIP_U16(p_ctx, "wValidBitsPerSample"); + SKIP_U32(p_ctx, "dwChannelMask"); + READ_GUID(p_ctx, &guid, "SubFormat"); + + if(!memcmp(&guid, &pcm_guid, sizeof(guid))) + codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE; + else if(!memcmp(&guid, &atracx_guid, sizeof(guid))) + codec = VC_CONTAINER_CODEC_ATRAC3; + + cbsize -= 22; + + /* TODO: deal with channel mapping */ + } + else + { + codec = waveformat_to_codec(format); + } + + /* Bail out if we don't recognise the codec */ + if(codec == VC_CONTAINER_CODEC_UNKNOWN) + return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; + + /* Do some sanity checking on the info we got */ + if(!channels || !samplerate || !block_align || !bitrate) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + if(codec == VC_CONTAINER_CODEC_ATRAC3 && channels > 2) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + p_ctx->tracks[0]->format->codec = codec; + p_ctx->tracks[0]->format->type->audio.channels = channels; + p_ctx->tracks[0]->format->type->audio.sample_rate = samplerate; + p_ctx->tracks[0]->format->type->audio.block_align = block_align; + p_ctx->tracks[0]->format->type->audio.bits_per_sample = bps; + p_ctx->tracks[0]->format->bitrate = bitrate; + p_ctx->tracks[0]->is_enabled = true; + p_ctx->tracks[0]->format->extradata_size = 0; + p_ctx->tracks[0]->format->extradata = module->extradata; + module->block_size = block_align; + + /* Prepare the codec extradata */ + if(codec == VC_CONTAINER_CODEC_ATRAC3) + { + uint16_t h, mode; + + SKIP_U16(p_ctx, "len"); + SKIP_U16(p_ctx, "layer"); + SKIP_U32(p_ctx, "bytes_per_frame"); + mode = READ_U16(p_ctx, "mode"); + SKIP_U16(p_ctx, "mode_ext"); + SKIP_U16(p_ctx, "num_subframes"); + SKIP_U16(p_ctx, "flags"); + + h = (1 << 15); + if(channels == 2) + { + h |= (1 << 13); + if(mode == 1) h |= (1 << 14); + } + h |= block_align & 0x7ff; + + p_ctx->tracks[0]->format->extradata[0] = h >> 8; + p_ctx->tracks[0]->format->extradata[1] = h & 255; + p_ctx->tracks[0]->format->extradata_size = 2; + } + else if(codec == VC_CONTAINER_CODEC_ATRACX && cbsize >= 6) + { + SKIP_BYTES(p_ctx, 2); + p_ctx->tracks[0]->format->extradata_size = + READ_BYTES(p_ctx, p_ctx->tracks[0]->format->extradata, 6); + } + else if(codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE) + { + /* Audioplus can no longer be given anything other than a multiple-of-16 number of samples */ + block_align *= 16; + module->block_size = (BLOCK_SIZE / block_align) * block_align; + } + + /* Skip the rest of the 'fmt' sub-chunk */ + SKIP_BYTES(p_ctx, chunk_pos + chunk_size - STREAM_POSITION(p_ctx)); + + /* We also need the 'data' sub-chunk */ + do { + chunk_pos = STREAM_POSITION(p_ctx) + 8; + if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('d','a','t','a') ) break; + + /* Not interested in this chunk. Skip it. */ + chunk_size = READ_U32(p_ctx, "Chunk size"); + SKIP_BYTES(p_ctx, chunk_size); + } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; /* 'data' not found */; + goto error; + } + + module->data_offset = chunk_pos; + module->data_size = READ_U32(p_ctx, "Chunk size"); + p_ctx->duration = module->data_size * 8000000 / bitrate; + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + /* + * We now have all the information we really need to start playing the stream + */ + + p_ctx->priv->pf_close = wav_reader_close; + p_ctx->priv->pf_read = wav_reader_read; + p_ctx->priv->pf_seek = wav_reader_seek; + + /* Seek back to the start of the data */ + status = SEEK(p_ctx, module->data_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "wav: error opening stream (%i)", status); + if(module) wav_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open wav_reader_open +#endif diff --git a/helpers/dtoverlay/CMakeLists.txt b/helpers/dtoverlay/CMakeLists.txt new file mode 100755 index 0000000..b3bd30f --- /dev/null +++ b/helpers/dtoverlay/CMakeLists.txt @@ -0,0 +1,25 @@ +# ============================================================================= +# Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +# All rights reserved. +# +# FILE DESCRIPTION +# CMake build file for dtoverlay library. +# ============================================================================= + +cmake_minimum_required (VERSION 2.8) + +include_directories (${VIDEOCORE_ROOT} + ${VIDEOCORE_ROOT}/helpers + ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt + ${VIDEOCORE_HEADERS_BUILD_DIR}) + +if (CMAKE_COMPILER_IS_GNUCC) + add_definitions (-ffunction-sections) +endif () + +add_library (dtovl ${SHARED} + dtoverlay.c) + +target_link_libraries(dtovl fdt) + +install (TARGETS dtovl DESTINATION lib) diff --git a/helpers/dtoverlay/dtoverlay.c b/helpers/dtoverlay/dtoverlay.c new file mode 100755 index 0000000..29e1195 --- /dev/null +++ b/helpers/dtoverlay/dtoverlay.c @@ -0,0 +1,2006 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "dtoverlay.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +typedef enum +{ + FIXUP_ABSOLUTE, + FIXUP_RELATIVE +} fixup_type_t; + +#define DTOVERRIDE_END 0 +#define DTOVERRIDE_INTEGER 1 +#define DTOVERRIDE_BOOLEAN 2 +#define DTOVERRIDE_STRING 3 +#define DTOVERRIDE_OVERLAY 4 + +static int dtoverlay_extract_override(const char *override_name, + int *phandle_ptr, + const char **data_ptr, int *len_ptr, + const char **namep, int *namelenp, + int *offp, int *sizep); + +static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, + const char *name); + +static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, + const char *fmt, va_list args); + +#define phandle_debug if (0) dtoverlay_debug + +static DTOVERLAY_LOGGING_FUNC *dtoverlay_logging_func = dtoverlay_stdio_logging; +static int dtoverlay_debug_enabled = 0; + +static uint32_t dtoverlay_read_u32(const void *src, int off) +{ + const unsigned char *p = src; + return (p[off + 0] << 24) | (p[off + 1] << 16) | + (p[off + 2] << 8) | (p[off + 3] << 0); +} + +static void dtoverlay_write_u8(void *dst, int off, uint32_t val) +{ + unsigned char *p = dst; + p[off] = (val >> 0) & 0xff; +} + +static void dtoverlay_write_u16(void *dst, int off, uint32_t val) +{ + unsigned char *p = dst; + p[off + 0] = (val >> 8) & 0xff; + p[off + 1] = (val >> 0) & 0xff; +} + +static void dtoverlay_write_u32(void *dst, int off, uint32_t val) +{ + unsigned char *p = dst; + p[off + 0] = (val >> 24) & 0xff; + p[off + 1] = (val >> 16) & 0xff; + p[off + 2] = (val >> 8) & 0xff; + p[off + 3] = (val >> 0) & 0xff; +} + +static void dtoverlay_write_u64(void *dst, int off, uint64_t val) +{ + unsigned char *p = dst; + p[off + 0] = (val >> 56) & 0xff; + p[off + 1] = (val >> 48) & 0xff; + p[off + 2] = (val >> 40) & 0xff; + p[off + 3] = (val >> 32) & 0xff; + p[off + 4] = (val >> 24) & 0xff; + p[off + 5] = (val >> 16) & 0xff; + p[off + 6] = (val >> 8) & 0xff; + p[off + 7] = (val >> 0) & 0xff; +} + +// Returns the offset of the node indicated by the absolute path, creating +// it and any intermediates as necessary, or a negative error code. +int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_path, int path_len) +{ + const char *path_ptr; + const char *path_end; + int node_off = 0; + + if (!path_len) + path_len = strlen(node_path); + + path_ptr = node_path; + path_end = node_path + path_len; + + if ((path_len > 0) && (path_ptr[path_len - 1] == '/')) + path_end--; + + while (path_ptr < path_end) + { + const char *path_next; + int subnode_off; + + if (*path_ptr != '/') + return -FDT_ERR_BADPATH; + + // find the next path separator (or the end of the string) + path_ptr++; + for (path_next = path_ptr; + (path_next != path_end) && (*path_next != '/'); + path_next++) + continue; + + subnode_off = fdt_subnode_offset_namelen(dtb->fdt, node_off, path_ptr, + path_next - path_ptr); + if (subnode_off >= 0) + node_off = subnode_off; + else + node_off = fdt_add_subnode_namelen(dtb->fdt, node_off, path_ptr, + path_next - path_ptr); + if (node_off < 0) + break; + + path_ptr = path_next; + } + + if ((node_off >= 0) && (path_ptr != path_end)) + return -FDT_ERR_BADPATH; + + return node_off; +} + +// Returns 0 on success, otherwise <0 error code +int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_path, int path_len) +{ + int node_off = 0; + if (!path_len) + path_len = strlen(node_path); + + dtoverlay_debug("delete_node(%.*s)", path_len, node_path); + node_off = fdt_path_offset_namelen(dtb->fdt, node_path, path_len); + if (node_off < 0) + return node_off; + return fdt_del_node(dtb->fdt, node_off); +} + +// Returns the offset of the node indicated by the absolute path or a negative +// error code. +int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len) +{ + if (!path_len) + path_len = strlen(node_path); + return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); +} + +// Returns 0 on success, otherwise <0 error code +int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, + DTOVERLAY_PARAM_T *properties, + unsigned int num_properties) +{ + int err = 0; + int node_off; + + node_off = fdt_path_offset(dtb->fdt, node_path); + if (node_off < 0) + node_off = dtoverlay_create_node(dtb, node_path, 0); + if (node_off >= 0) + { + int i; + for (i = 0; (i < num_properties) && (err == 0); i++) + { + DTOVERLAY_PARAM_T *p; + + p = properties + i; + err = fdt_setprop(dtb->fdt, node_off, p->param, p->b, p->len); + } + } + else + err = node_off; + return err; +} + +struct dynstring +{ + char *buf; + int size; + int len; +}; + +static void dynstring_init(struct dynstring *ds) +{ + ds->size = 0; + ds->len = 0; + ds->buf = NULL; +} + +static int dynstring_init_size(struct dynstring *ds, int initial_size) +{ + if (initial_size < 32) + initial_size = 32; + ds->size = initial_size; + ds->len = 0; + ds->buf = malloc(initial_size); + if (!ds->buf) + { + dtoverlay_error(" out of memory"); + return -FDT_ERR_NOSPACE; + } + return 0; +} + +static int dynstring_set_size(struct dynstring *ds, int size) +{ + if (size > ds->size) + { + size = (size * 5)/4; // Add a 25% headroom + ds->buf = realloc(ds->buf, size); + if (!ds->buf) + { + dtoverlay_error(" out of memory"); + return -FDT_ERR_NOSPACE; + } + ds->size = size; + } + return 0; +} + +static int dynstring_dup(struct dynstring *ds, const char *src, int len) +{ + int err = 0; + + if (!len) + len = strlen(src); + + err = dynstring_set_size(ds, len + 1); + if (!err) + { + memcpy(ds->buf, src, len + 1); + ds->len = len; + } + + return err; +} + +static int dynstring_patch(struct dynstring *ds, int pos, int width, + const char *src, int len) +{ + int newlen = ds->len + (len - width); + int err = dynstring_set_size(ds, newlen + 1); + if (!err) + { + if (width != len) + { + // Move any data following the patch + memmove(ds->buf + pos + len, ds->buf + pos + width, + ds->len + 1 - (pos + width)); + ds->len = newlen; + } + memcpy(ds->buf + pos, src, len); + } + return err; +} + +static int dynstring_grow(struct dynstring *ds) +{ + return dynstring_set_size(ds, (3*ds->size)/2); +} + +static void dynstring_free(struct dynstring *ds) +{ + free(ds->buf); + dynstring_init(ds); +} + +static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, + const char *name) +{ + struct dynstring path_buf; + struct dynstring prop_buf; + char *old_path; + const char *old_name; + const char *fixup_nodes[] = + { + "/__fixups__", + "/__local_fixups__", // For old-style dtbos + "/__symbols__" // Just in case the kernel cares + }; + int old_name_len; + int old_path_len; // All of it + int dir_len; // Excluding the node name, but with the trailling slash + int name_len; + int offset; + int fixup_idx; + int err = 0; + + // Fixups and local-fixups both use node names, so this + // function must be patch them up when a node is renamed + // unless the fixups have already been applied. + // Calculating a node's name is expensive, so only do it if + // necessary. Since renaming a node can move things around, + // don't use node_off afterwards. + err = dynstring_init_size(&path_buf, 100); + if (err) + return err; + + if (!dtb->fixups_applied) + { + while (1) + { + err = fdt_get_path(dtb->fdt, node_off, path_buf.buf, path_buf.size); + if (!err) + break; + if (err != -FDT_ERR_NOSPACE) + return err; + dynstring_grow(&path_buf); + } + } + old_path = path_buf.buf; + + err = fdt_set_name(dtb->fdt, node_off, name); + if (err || dtb->fixups_applied) + goto clean_up; + + // Find the node name in old_path + old_name = strrchr(old_path, '/'); + assert(old_name); + if (!old_name) + return -FDT_ERR_INTERNAL; + old_name++; + old_name_len = strlen(old_name); + dir_len = old_name - old_path; + old_path_len = dir_len + old_name_len; + + // Short-circuit the case where the name isn't changing + if (strcmp(name, old_name) == 0) + goto clean_up; + + name_len = strlen(name); + + // Search the fixups and symbols for the old path (including as + // a parent) and replace with the new name + + dynstring_init(&prop_buf); + for (fixup_idx = 0; fixup_idx < ARRAY_SIZE(fixup_nodes); fixup_idx++) + { + int prop_off; + + offset = fdt_path_offset(dtb->fdt, fixup_nodes[fixup_idx]); + if (offset > 0) + { + + // Iterate through the properties + for (prop_off = fdt_first_property_offset(dtb->fdt, offset); + (prop_off >= 0) && (err == 0); + prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) + { + const char *prop_name; + const char *prop_val; + int prop_len; + int pos; + int changed = 0; + + prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, + &prop_name, &prop_len); + err = dynstring_dup(&prop_buf, prop_val, prop_len); + if (err) + break; + + // Scan each property for matching paths + pos = 0; + while (pos < prop_len) + { + if ((pos + old_path_len < prop_len) && + (memcmp(prop_buf.buf + pos, old_path, old_path_len) == 0) && + ((prop_buf.buf[pos + old_path_len] == ':') || + (prop_buf.buf[pos + old_path_len] == '/') || + (prop_buf.buf[pos + old_path_len] == '\0'))) + { + // Patch the string, replacing old name with new + err = dynstring_patch(&prop_buf, pos + dir_len, old_name_len, + name, name_len); + if (err) + break; + + prop_len += name_len - old_name_len; + changed = 1; + } + pos += strlen(prop_buf.buf + pos) + 1; + } + + if (!err && changed) + { + // Caution - may change offsets, but only by shuffling everything + // afterwards, i.e. the offset to this node or property does not + // change. + err = fdt_setprop(dtb->fdt, offset, prop_name, prop_buf.buf, + prop_len); + } + } + } + } + + dynstring_free(&prop_buf); + + if (err) + goto clean_up; + + // Then look for a "/__local_fixups__" node, and rename + // that as well. + offset = fdt_path_offset(dtb->fdt, "/__local_fixups__"); + if (offset > 0) + { + const char *p, *end; + + p = old_path; + end = old_path + old_path_len; + while (p < end) + { + const char *q; + + while (*p == '/') { + p++; + if (p == end) + break; + } + q = memchr(p, '/', end - p); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(dtb->fdt, offset, p, q-p); + if (offset < 0) + break; + + p = q; + } + + if (offset > 0) + err = fdt_set_name(dtb->fdt, offset, name); + } + + // __overrides__ don't need patching because nodes are identified + // using phandles, which are unaffected by renaming and resizing nodes. + +clean_up: + dynstring_free(&path_buf); + + return err; +} + +// Returns 0 on success, otherwise <0 error code +int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, + const char *prop_name, const void *prop_data, + int prop_len) +{ + char fragment_name[20]; + int frag_off, ovl_off; + int ret; + snprintf(fragment_name, sizeof(fragment_name), "fragment@%u", idx); + frag_off = fdt_add_subnode(dtb->fdt, 0, fragment_name); + if (frag_off < 0) + return frag_off; + ret = fdt_setprop_u32(dtb->fdt, frag_off, "target", target_phandle); + if (ret < 0) + return ret; + ovl_off = fdt_add_subnode(dtb->fdt, frag_off, "__overlay__"); + if (ovl_off < 0) + return ovl_off; + return fdt_setprop(dtb->fdt, ovl_off, prop_name, prop_data, prop_len); +} + +// Returns 0 on success, otherwise <0 error code +static int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off, + const DTBLOB_T *overlay_dtb, + int overlay_off, int depth) +{ + int prop_off, subnode_off; + int err = 0; + + if (dtoverlay_debug_enabled) + { + char base_path[256]; + char overlay_path[256]; + fdt_get_path(base_dtb->fdt, target_off, base_path, sizeof(base_path)); + fdt_get_path(overlay_dtb->fdt, overlay_off, overlay_path, + sizeof(overlay_path)); + + dtoverlay_debug("merge_fragment(%s,%s)", base_path, + overlay_path); + } + + // Merge each property of the node + for (prop_off = fdt_first_property_offset(overlay_dtb->fdt, overlay_off); + (prop_off >= 0) && (err == 0); + prop_off = fdt_next_property_offset(overlay_dtb->fdt, prop_off)) + { + const char *prop_name; + const void *prop_val; + int prop_len; + struct fdt_property *target_prop; + int target_len; + + prop_val = fdt_getprop_by_offset(overlay_dtb->fdt, prop_off, + &prop_name, &prop_len); + + /* Skip these system properties (only phandles in the first level) */ + if ((strcmp(prop_name, "name") == 0) || + ((depth == 0) && ((strcmp(prop_name, "phandle") == 0) || + (strcmp(prop_name, "linux,phandle") == 0)))) + continue; + + dtoverlay_debug(" +prop(%s)", prop_name); + + if ((strcmp(prop_name, "bootargs") == 0) && + ((target_prop = fdt_get_property_w(base_dtb->fdt, target_off, prop_name, &target_len)) != NULL) && + (target_len > 0) && *target_prop->data) + { + target_prop->data[target_len - 1] = ' '; + err = fdt_appendprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); + } + else + err = fdt_setprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); + } + + // Merge each subnode of the node + for (subnode_off = fdt_first_subnode(overlay_dtb->fdt, overlay_off); + (subnode_off >= 0) && (err == 0); + subnode_off = fdt_next_subnode(overlay_dtb->fdt, subnode_off)) + { + const char *subnode_name; + int name_len; + int subtarget_off; + + subnode_name = fdt_get_name(overlay_dtb->fdt, subnode_off, &name_len); + + subtarget_off = fdt_subnode_offset_namelen(base_dtb->fdt, target_off, + subnode_name, name_len); + if (subtarget_off < 0) + subtarget_off = fdt_add_subnode_namelen(base_dtb->fdt, target_off, + subnode_name, name_len); + + if (subtarget_off >= 0) + { + err = dtoverlay_merge_fragment(base_dtb, subtarget_off, + overlay_dtb, subnode_off, + depth + 1); + } + else + { + err = subtarget_off; + } + } + + dtoverlay_debug("merge_fragment() end"); + + return err; +} + +static int dtoverlay_phandle_relocate(DTBLOB_T *dtb, int node_off, + const char *prop_name, + uint32_t phandle_increment) +{ + int len; + const fdt32_t *prop_val = fdt_getprop(dtb->fdt, node_off, prop_name, &len); + int err = 0; // The absence of the property is not an error + + if (prop_val) + { + uint32_t phandle; + + if (len < 4) + { + dtoverlay_error("%s property too small", prop_name); + return -FDT_ERR_BADSTRUCTURE; + } + + phandle = fdt32_to_cpu(*prop_val) + phandle_increment; + phandle_debug(" phandle_relocate %d->%d", fdt32_to_cpu(*prop_val), phandle); + + err = fdt_setprop_inplace_u32(dtb->fdt, node_off, prop_name, phandle); + } + + return err; +} + +// Returns 0 on success, or an FDT error code +static int dtoverlay_apply_fixups(DTBLOB_T *dtb, const char *fixups_stringlist, + uint32_t phandle, fixup_type_t type) +{ + // The fixups arrive as a sequence of NUL-terminated strings, of the form: + // "path:property:offset" + // Use an empty string as an end marker, since: + // 1) all tags begin 0x00 0x00 0x00, + // 2) all string properties must be followed by a tag, + // 3) an empty string is not a valid fixup, and + // 4) the code is simpler as a result. + + const char *fixup = fixups_stringlist; + + while (fixup[0]) + { + const char *prop_name, *offset_str; + char *offset_end; + const void *prop_ptr; + int prop_len; + int node_off; + unsigned long offset; + uint32_t patch; + + prop_name = strchr(fixup, ':'); + if (!prop_name) + return -FDT_ERR_BADSTRUCTURE; + prop_name++; + + offset_str = strchr(prop_name, ':'); + if (!offset_str) + return -FDT_ERR_BADSTRUCTURE; + offset_str++; + + offset = strtoul(offset_str, &offset_end, 10); + if ((offset_end == offset_str) || (offset_end[0] != 0)) + return -FDT_ERR_BADSTRUCTURE; + + node_off = fdt_path_offset_namelen(dtb->fdt, fixup, prop_name - 1 - fixup); + if (node_off < 0) + return node_off; + + prop_ptr = fdt_getprop_namelen(dtb->fdt, node_off, prop_name, + offset_str - 1 - prop_name, &prop_len); + if (!prop_ptr) + return prop_len; + if (offset > (prop_len - 4)) + return -FDT_ERR_BADSTRUCTURE; + + // Now apply the patch. Yes, prop_ptr is a const void *, but the + // alternative (copying the whole property, patching, then updating as + // a whole) is ridiculous. + if (type == FIXUP_RELATIVE) + { + patch = phandle + dtoverlay_read_u32(prop_ptr, offset); + phandle_debug(" phandle fixup %d+%d->%d", phandle, patch - phandle, patch); + } + else + { + patch = phandle; + phandle_debug(" phandle ref '%s'->%d", prop_name, patch); + } + + dtoverlay_write_u32((void *)prop_ptr, offset, patch); + + fixup = offset_end + 1; + } + + return 0; +} + +// Returns 0 on success, or an FDT error code +static int dtoverlay_apply_fixups_node(DTBLOB_T *dtb, int fix_off, + int target_off, uint32_t phandle_offset) +{ + // The fixups are arranged as a subtree mirroring the structure of the + // overall tree. Walk this tree in order. Each property is an array of cells + // containing offsets to patch within the corresponding node/property of + // the target tree. + int err = 0; + int prop_off; + int subfix_off; + + // Merge each property of the node + for (prop_off = fdt_first_property_offset(dtb->fdt, fix_off); + (prop_off >= 0) && (err == 0); + prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) + { + const char *prop_name; + const void *prop_val; + int prop_len; + void *target_ptr; + int target_len; + int off; + + prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, + &prop_name, &prop_len); + if (!prop_val) + return -FDT_ERR_INTERNAL; + + target_ptr = fdt_getprop_w(dtb->fdt, target_off, prop_name, &target_len); + if (!target_ptr) + return -FDT_ERR_BADSTRUCTURE; + + for (off = 0; (off + 4) <= prop_len; off += 4) + { + uint32_t patch; + int patch_offset = dtoverlay_read_u32(prop_val, off); + if ((patch_offset + 4) > target_len) + return -FDT_ERR_BADSTRUCTURE; + + patch = phandle_offset + dtoverlay_read_u32(target_ptr, patch_offset); + phandle_debug(" phandle fixup %d+%d->%d", phandle_offset, patch - phandle_offset, patch); + + dtoverlay_write_u32(target_ptr, patch_offset, patch); + } + } + + // Merge each subnode of the node + for (subfix_off = fdt_first_subnode(dtb->fdt, fix_off); + (subfix_off >= 0) && (err == 0); + subfix_off = fdt_next_subnode(dtb->fdt, subfix_off)) + { + const char *subnode_name; + int name_len; + int subtarget_off; + + subnode_name = fdt_get_name(dtb->fdt, subfix_off, &name_len); + + subtarget_off = fdt_subnode_offset_namelen(dtb->fdt, target_off, + subnode_name, name_len); + + if (subtarget_off >= 0) + { + err = dtoverlay_apply_fixups_node(dtb, subfix_off, subtarget_off, + phandle_offset); + } + else + { + err = subtarget_off; + } + } + + return err; +} + +// Returns 0 on success, or a negative FDT error. +static int dtoverlay_resolve_phandles(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + int local_fixups_off; + int node_off; + int err = 0; + + // First find and update the phandles in the overlay + + for (node_off = 0; + node_off >= 0; + node_off = fdt_next_node(overlay_dtb->fdt, node_off, NULL)) + { + dtoverlay_phandle_relocate(overlay_dtb, node_off, "phandle", + base_dtb->max_phandle); + dtoverlay_phandle_relocate(overlay_dtb, node_off, "linux,phandle", + base_dtb->max_phandle); + } + + local_fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__local_fixups__"); + if (local_fixups_off >= 0) + { + const char *fixups_stringlist; + + // Update the references to local phandles using the local fixups. + // The property name is "fixup". + // The value is a NUL-separated stringlist of descriptors of the form: + // path:property:offset + fixups_stringlist = + fdt_getprop(overlay_dtb->fdt, local_fixups_off, "fixup", &err); + if (fixups_stringlist) + { + // Relocate the overlay phandle references + err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, + base_dtb->max_phandle, FIXUP_RELATIVE); + } + else + { + err = dtoverlay_apply_fixups_node(overlay_dtb, local_fixups_off, + 0, base_dtb->max_phandle); + } + if (err < 0) + { + dtoverlay_error("error applying local fixups"); + return err; + } + } + + overlay_dtb->max_phandle += base_dtb->max_phandle; + phandle_debug(" +overlay max phandle +%d -> %d", base_dtb->max_phandle, overlay_dtb->max_phandle); + + return err; +} + +// Returns 0 on success, or an FDT error code +static int dtoverlay_resolve_fixups(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + int fixups_off; + int err = 0; + + fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__fixups__"); + + if (fixups_off >= 0) + { + int fixup_off, symbols_off = -1; + + fixup_off = fdt_first_property_offset(overlay_dtb->fdt, fixups_off); + + if (fixup_off >= 0) + { + // Find the symbols, which will be needed to resolve the fixups + symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); + + if (symbols_off < 0) + { + dtoverlay_error("No symbols found"); + return -FDT_ERR_NOTFOUND; + } + } + + for (; + fixup_off >= 0; + fixup_off = fdt_next_property_offset(overlay_dtb->fdt, fixup_off)) + { + const char *fixups_stringlist, *symbol_name, *target_path; + const char *ref_type; + int target_off; + uint32_t target_phandle; + + // The property name identifies a symbol (or alias) in the base. + // The value is a comma-separated list of descriptors of the form: + // path:property:offset + fixups_stringlist = fdt_getprop_by_offset(overlay_dtb->fdt, fixup_off, + &symbol_name, &err); + if (!fixups_stringlist) + { + dtoverlay_error("__fixups__ are borked"); + break; + } + + // 1) Find the target node. + if (symbol_name[0] == '/') + { + /* This is a new-style path reference */ + target_path = symbol_name; + ref_type = "path"; + } + else + { + target_path = fdt_getprop(base_dtb->fdt, symbols_off, symbol_name, + &err); + if (!target_path) + { + dtoverlay_error("can't find symbol '%s'", symbol_name); + break; + } + + ref_type = "symbol"; + } + + target_off = fdt_path_offset(base_dtb->fdt, target_path); + if (target_off < 0) + { + dtoverlay_error("%s '%s' is invalid", ref_type, symbol_name); + err = target_off; + break; + } + + // 2) Ensure that the target node has a phandle. + target_phandle = fdt_get_phandle(base_dtb->fdt, target_off); + if (!target_phandle) + { + // It doesn't, so give it one + fdt32_t temp; + target_phandle = ++base_dtb->max_phandle; + temp = cpu_to_fdt32(target_phandle); + + err = fdt_setprop(base_dtb->fdt, target_off, "phandle", + &temp, 4); + + if (err != 0) + { + dtoverlay_error("failed to add a phandle"); + break; + } + phandle_debug(" phandle '%s'->%d", target_path, target_phandle); + + // The symbols may have moved, so recalculate + symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); + } + + // Now apply the valid target_phandle to the items in the fixup string + + err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, + target_phandle, FIXUP_ABSOLUTE); + if (err) + break; + } + } + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + // Merge each fragment node + int frag_off; + + for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0); + frag_off >= 0; + frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off)) + { + const char *node_name, *target_path; + const char *frag_name; + int target_off, overlay_off; + int len, err; + + node_name = fdt_get_name(overlay_dtb->fdt, frag_off, NULL); + + if (strncmp(node_name, "fragment@", 9) != 0) + continue; + frag_name = node_name + 9; + + dtoverlay_debug("Found fragment %s (offset %d)", frag_name, frag_off); + + // Find the target and overlay nodes + overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); + if (overlay_off < 0) + { + if (fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__dormant__")) + dtoverlay_debug("fragment %s disabled", frag_name); + else + dtoverlay_error("no overlay in fragment %s", frag_name); + continue; + } + + target_path = fdt_getprop(overlay_dtb->fdt, frag_off, "target-path", &len); + if (target_path) + { + if (len && (target_path[len - 1] == '\0')) + len--; + target_off = fdt_path_offset_namelen(base_dtb->fdt, target_path, len); + if (target_off < 0) + { + dtoverlay_error("invalid target-path '%.*s'", len, target_path); + return NON_FATAL(target_off); + } + } + else + { + const void *target_prop; + target_prop = fdt_getprop(overlay_dtb->fdt, frag_off, "target", &len); + if (!target_prop) + { + dtoverlay_error("no target or target-path"); + return NON_FATAL(len); + } + + if (len != 4) + return NON_FATAL(FDT_ERR_BADSTRUCTURE); + + target_off = + fdt_node_offset_by_phandle(base_dtb->fdt, + fdt32_to_cpu(*(fdt32_t *)target_prop)); + if (target_off < 0) + { + dtoverlay_error("invalid target"); + return NON_FATAL(target_off); + } + } + + // Now do the merge + err = dtoverlay_merge_fragment(base_dtb, target_off, overlay_dtb, + overlay_off, 0); + if (err != 0) + { + dtoverlay_error("merge failed"); + return err; + } + } + + base_dtb->max_phandle = overlay_dtb->max_phandle; + + return 0; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + int err; + + // To do: Check the "compatible" string? + + err = dtoverlay_resolve_fixups(base_dtb, overlay_dtb); + + if (err >= 0) + err = dtoverlay_resolve_phandles(base_dtb, overlay_dtb); + + overlay_dtb->fixups_applied = 1; + + return NON_FATAL(err); +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, + unsigned int num_params) +{ + int err = 0; + unsigned int i; + + for (i=0; (iparam; + slash = strrchr(node_name, '/'); + + if (!slash) + { + err = NON_FATAL(FDT_ERR_BADPATH); + break; + } + + // Ensure that root properties ("/xxx") work + if (slash == node_name) + path_len = 1; + else + path_len = slash - node_name; + + // find node, create if it does not exist yet + node_off = dtoverlay_create_node(dtb, node_name, path_len); + if (node_off >= 0) + { + const char *prop_name = slash + 1; + int prop_len; + struct fdt_property *prop; + + if ((strcmp(prop_name, "bootargs") == 0) && + ((prop = fdt_get_property_w(dtb->fdt, node_off, prop_name, &prop_len)) != NULL) && + (prop_len > 0) && *prop->data) + { + prop->data[prop_len - 1] = ' '; + err = fdt_appendprop(dtb->fdt, node_off, prop_name, p->b, p->len); + } + else + err = fdt_setprop(dtb->fdt, node_off, prop_name, p->b, p->len); + } + else + err = node_off; + } + + return err; +} + +/* Returns a pointer to the override data and (through data_len) its length. + On error, sets *data_len to be the error code. */ +const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, + int *data_len) +{ + int overrides_off; + const char *data; + int len; + + // Find the table of overrides + overrides_off = fdt_path_offset(dtb->fdt, "/__overrides__"); + + if (overrides_off < 0) + { + dtoverlay_debug("/__overrides__ node not found"); + *data_len = overrides_off; + return NULL; + } + + // Locate the property + data = fdt_getprop(dtb->fdt, overrides_off, override_name, &len); + *data_len = len; + if (data) + dtoverlay_debug("Found override %s", override_name); + else + dtoverlay_debug("/__overrides__ has no %s property", override_name); + + return data; +} + +int dtoverlay_override_one_target(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value) +{ + const char *override_value = callback_value; + int err = 0; + + if (override_type == DTOVERRIDE_STRING) + { + char *prop_val; + int prop_len; + + /* Replace the whole property with the string */ + if (strcmp(prop_name, "status") == 0) + { + /* Convert booleans to okay/disabled */ + if ((strcmp(override_value, "y") == 0) || + (strcmp(override_value, "yes") == 0) || + (strcmp(override_value, "on") == 0) || + (strcmp(override_value, "true") == 0) || + (strcmp(override_value, "enable") == 0) || + (strcmp(override_value, "1") == 0)) + override_value = "okay"; + else if ((strcmp(override_value, "n") == 0) || + (strcmp(override_value, "no") == 0) || + (strcmp(override_value, "off") == 0) || + (strcmp(override_value, "false") == 0) || + (strcmp(override_value, "0") == 0)) + override_value = "disabled"; + } + + if ((strcmp(prop_name, "bootargs") == 0) && + ((prop_val = fdt_getprop_w(dtb->fdt, node_off, prop_name, + &prop_len)) != NULL) && + (prop_len > 0) && prop_val[0]) + { + prop_val[prop_len - 1] = ' '; + err = fdt_appendprop_string(dtb->fdt, node_off, prop_name, override_value); + } + else + err = fdt_setprop_string(dtb->fdt, node_off, prop_name, override_value); + } + else if (override_type != DTOVERRIDE_END) + { + const char *p; + char *end; + char *prop_val; + char *prop_buf = NULL; + int prop_len; + int new_prop_len; + uint64_t override_int; + uint32_t frag_num; + + /* Parse as an integer */ + override_int = strtoull(override_value, &end, 0); + if (end[0] != '\0') + { + if ((strcmp(override_value, "y") == 0) || + (strcmp(override_value, "yes") == 0) || + (strcmp(override_value, "on") == 0) || + (strcmp(override_value, "true") == 0) || + (strcmp(override_value, "down") == 0)) + override_int = 1; + else if ((strcmp(override_value, "n") == 0) || + (strcmp(override_value, "no") == 0) || + (strcmp(override_value, "off") == 0) || + (strcmp(override_value, "false") == 0)) + override_int = 0; + else if (strcmp(override_value, "up") == 0) + override_int = 2; + else + { + dtoverlay_error("invalid override value '%s' - ignored", + override_value); + return NON_FATAL(FDT_ERR_INTERNAL); + } + } + + switch (override_type) + { + case DTOVERRIDE_INTEGER: + /* Patch a word within the property */ + prop_val = (void *)fdt_getprop(dtb->fdt, node_off, prop_name, + &prop_len); + new_prop_len = target_off + target_size; + if (prop_len < new_prop_len) + { + /* This property either doesn't exist or isn't long enough. + Create a buffer containing any existing property data + with zero padding, which will later be patched and written + back. */ + prop_buf = calloc(1, new_prop_len); + if (!prop_buf) + { + dtoverlay_error(" out of memory"); + return NON_FATAL(FDT_ERR_NOSPACE); + } + if (prop_len > 0) + memcpy(prop_buf, prop_val, prop_len); + prop_val = prop_buf; + } + + switch (target_size) + { + case 1: + dtoverlay_write_u8(prop_val, target_off, (uint32_t)override_int); + break; + case 2: + dtoverlay_write_u16(prop_val, target_off, (uint32_t)override_int); + break; + case 4: + dtoverlay_write_u32(prop_val, target_off, (uint32_t)override_int); + break; + case 8: + dtoverlay_write_u64(prop_val, target_off, override_int); + break; + default: + break; + } + + if (prop_buf) + { + /* Add/extend the property by setting it */ + err = fdt_setprop(dtb->fdt, node_off, prop_name, prop_buf, new_prop_len); + free(prop_buf); + } + + if (strcmp(prop_name, "reg") == 0 && target_off == 0) + { + const char *old_name = fdt_get_name(dtb->fdt, node_off, NULL); + const char *atpos = strchr(old_name, '@'); + if (atpos) + { + int name_len = (atpos - old_name); + char *new_name = malloc(name_len + 1 + 16 + 1); + if (!new_name) + return -FDT_ERR_NOSPACE; + sprintf(new_name, "%.*s@%x", name_len, old_name, (uint32_t)override_int); + err = dtoverlay_set_node_name(dtb, node_off, new_name); + free(new_name); + } + } + break; + + case DTOVERRIDE_BOOLEAN: + /* This is a boolean property (present->true, absent->false) */ + if (override_int) + err = fdt_setprop(dtb->fdt, node_off, prop_name, NULL, 0); + else + { + err = fdt_delprop(dtb->fdt, node_off, prop_name); + if (err == -FDT_ERR_NOTFOUND) + err = 0; + } + break; + + case DTOVERRIDE_OVERLAY: + /* Apply the overlay-wide override. The supported options are ( is a fragment number): + + Enable a fragment + - Disable a fragment + = Enable/disable the fragment according to the override value + ! Disable/enable the fragment according to the inverse of the override value */ + p = prop_name; + while (*p && !err) + { + char type = *p; + int frag_off; + switch (type) + { + case '+': + case '-': + case '=': + case '!': + frag_num = strtoul(p + 1, &end, 0); + if (end != p) + { + /* Change fragment@/__overlay__<->__dormant__ as necessary */ + const char *states[2] = { "__dormant__", "__overlay__" }; + char node_name[24]; + int active = (type == '+') || + ((type == '=') && (override_int != 0)) || + ((type == '!') && (override_int == 0)); + snprintf(node_name, sizeof(node_name), "/fragment@%u", frag_num); + frag_off = fdt_path_offset(dtb->fdt, node_name); + if (frag_off >= 0) + { + frag_off = fdt_subnode_offset(dtb->fdt, frag_off, states[!active]); + if (frag_off >= 0) + (void)dtoverlay_set_node_name(dtb, frag_off, states[active]); + } + else + { + dtoverlay_error(" fragment %u not found", frag_num); + err = NON_FATAL(frag_off); + } + p = end; + } + else + { + dtoverlay_error(" invalid overlay override '%s'", prop_name); + err = NON_FATAL(FDT_ERR_BADVALUE); + } + break; + } + } + break; + } + } + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +// After calling this, assume all node offsets are no longer valid +int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + override_callback_t callback, + void *callback_value) +{ + int err = 0; + int target_phandle = 0; + char *data; + + /* Short-circuit the degenerate case of an empty parameter, avoiding an + apparent memory allocation failure. */ + if (!data_len) + return 0; + + /* Copy the override data in case it moves */ + data = malloc(data_len); + if (!data) + { + dtoverlay_error(" out of memory"); + return NON_FATAL(FDT_ERR_NOSPACE); + } + + memcpy(data, override_data, data_len); + override_data = data; + + while (err == 0) + { + const char *target_prop = NULL; + char *prop_name = NULL; + int name_len = 0; + int target_off = 0; + int target_size = 0; + int override_type; + int node_off = 0; + + override_type = dtoverlay_extract_override(override_name, + &target_phandle, + &override_data, &data_len, + &target_prop, &name_len, + &target_off, &target_size); + + /* Pass DTOVERRIDE_END to the callback, in case it is interested */ + + if (target_phandle != 0) + { + node_off = fdt_node_offset_by_phandle(dtb->fdt, target_phandle); + if (node_off < 0) + { + dtoverlay_error(" phandle %d not found", target_phandle); + err = NON_FATAL(node_off); + break; + } + } + + if (target_prop) + { + /* Sadly there are no '_namelen' setprop variants, so a copy is required */ + prop_name = malloc(name_len + 1); + if (!prop_name) + { + dtoverlay_error(" out of memory"); + err = NON_FATAL(FDT_ERR_NOSPACE); + break; + } + memcpy(prop_name, target_prop, name_len); + prop_name[name_len] = '\0'; + } + + err = callback(override_type, dtb, node_off, prop_name, + target_phandle, target_off, target_size, + callback_value); + + if (prop_name) + free(prop_name); + + if (override_type == DTOVERRIDE_END) + break; + } + + free(data); + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + const char *override_value) +{ + return dtoverlay_foreach_override_target(dtb, override_name, + override_data, data_len, + dtoverlay_override_one_target, + (void *)override_value); +} + +/* Returns an override type (DTOVERRIDE_INTEGER, DTOVERRIDE_BOOLEAN, DTOVERRIDE_STRING, DTOVERRIDE_OVERLAY), + DTOVERRIDE_END (0) at the end, or an error code (< 0) */ +static int dtoverlay_extract_override(const char *override_name, + int *phandle_ptr, + const char **data_ptr, int *len_ptr, + const char **namep, int *namelenp, + int *offp, int *sizep) +{ + const char *data; + const char *prop_name, *override_end; + int len, override_len, name_len, phandle; + const char *offset_seps = ".;:#?"; + int type; + + data = *data_ptr; + len = *len_ptr; + if (len <= 0) + { + if (len < 0) + return len; + *phandle_ptr = 0; + *namep = NULL; + return DTOVERRIDE_END; + } + + // Check for space for a phandle, a terminating NUL and at least one char + if (len < (sizeof(fdt32_t) + 1 + 1)) + { + dtoverlay_error(" override %s: data is truncated or mangled", + override_name); + return -FDT_ERR_BADSTRUCTURE; + } + + phandle = dtoverlay_read_u32(data, 0); + *phandle_ptr = phandle; + + data += sizeof(fdt32_t); + len -= sizeof(fdt32_t); + + override_end = memchr(data, 0, len); + if (!override_end) + { + dtoverlay_error(" override %s: string is not NUL-terminated", + override_name); + return -FDT_ERR_BADSTRUCTURE; + } + + prop_name = data; + + override_len = override_end - prop_name; + *data_ptr = data + (override_len + 1); + *len_ptr = len - (override_len + 1); + + if (phandle <= 0) + { + if (phandle < 0) + return -FDT_ERR_BADPHANDLE; + /* This is an "overlay" override, signalled using <0> as the phandle. */ + *namep = prop_name; + *namelenp = override_len; + return DTOVERRIDE_OVERLAY; + } + + name_len = strcspn(prop_name, offset_seps); + + *namep = prop_name; + *namelenp = name_len; + if (name_len < override_len) + { + /* There is a separator specified */ + char sep = prop_name[name_len]; + if (sep == '?') + { + /* The target is a boolean parameter (present->true, absent->false) */ + *offp = 0; + *sizep = 0; + type = DTOVERRIDE_BOOLEAN; + dtoverlay_debug(" override %s: boolean target %.*s", + override_name, name_len, prop_name); + } + else + { + /* The target is a cell/integer */ + *offp = atoi(prop_name + name_len + 1); + *sizep = 1 << (strchr(offset_seps, sep) - offset_seps); + type = DTOVERRIDE_INTEGER; + dtoverlay_debug(" override %s: cell target %.*s @ offset %d (size %d)", + override_name, name_len, prop_name, *offp, *sizep); + } + } + else + { + *offp = -1; + *sizep = 0; + type = DTOVERRIDE_STRING; + dtoverlay_debug(" override %s: string target '%.*s'", + override_name, name_len, prop_name); + } + + return type; +} + +int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src) +{ + /* Add/update all aliases, symbols and overrides named dst + to be equivalent to those named src. + An absent src is ignored. + */ + int err; + + err = dtoverlay_dup_property(dtb, "/aliases", dst, src); + if (err == 0) + err = dtoverlay_dup_property(dtb, "/__symbols__", dst, src); + if (err == 0) + dtoverlay_dup_property(dtb, "/__overrides__", dst, src); + return err; +} + +int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, + const char *dst, const char *src) +{ + /* Find the node and src property */ + const DTBLOB_T *src_prop; + int node_off; + int prop_len = 0; + int err = 0; + + node_off = fdt_path_offset(dtb->fdt, node_name); + if (node_off < 0) + return 0; + + src_prop = fdt_getprop(dtb->fdt, node_off, src, &prop_len); + if (!src_prop) + return 0; + + err = fdt_setprop_inplace(dtb->fdt, node_off, dst, src_prop, prop_len); + if (err != 0) + { + void *prop_data; + /* Copy the src property, just in case things move */ + prop_data = malloc(prop_len); + memcpy(prop_data, src_prop, prop_len); + + err = fdt_setprop(dtb->fdt, node_off, dst, prop_data, prop_len); + + free(prop_data); + } + + if (err == 0) + dtoverlay_debug("%s:%s=%s", node_name, dst, src); + return err; +} + +DTBLOB_T *dtoverlay_create_dtb(int max_size) +{ + DTBLOB_T *dtb = NULL; + void *fdt = NULL; + + fdt = malloc(max_size); + if (!fdt) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + if (fdt_create_empty_tree(fdt, max_size) != 0) + { + dtoverlay_error("failed to create empty dtb"); + goto error_exit; + } + + dtb = calloc(1, sizeof(DTBLOB_T)); + if (!dtb) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + dtb->fdt = fdt; + dtb->max_phandle = 0; // Not a valid phandle + + return dtb; + +error_exit: + free(fdt); + if (dtb) + free(dtb->trailer); + free(dtb); + return NULL; + +} + +DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size) +{ + DTBLOB_T *dtb = NULL; + void *fdt = NULL; + + if (fp) + { + long len; + long bytes_read; + int dtb_len; + + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (max_size > 0) + { + if (max_size < len) + { + dtoverlay_error("file too large (%d bytes) for max_size", len); + goto error_exit; + } + } + else if (max_size < 0) + { + max_size = len - max_size; + } + else + { + max_size = len; + } + + fdt = malloc(max_size); + if (!fdt) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + bytes_read = fread(fdt, 1, len, fp); + fclose(fp); + + if (bytes_read != len) + { + dtoverlay_error("fread failed"); + goto error_exit; + } + + // Record the total size before any expansion + dtb_len = fdt_totalsize(fdt); + + dtb = dtoverlay_import_fdt(fdt, max_size); + if (!dtb) + goto error_exit; + + dtb->fdt_is_malloced = 1; + + if (len > dtb_len) + { + /* Load the trailer */ + dtb->trailer_len = len - dtb_len; + dtb->trailer = malloc(dtb->trailer_len); + if (!dtb->trailer) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + dtb->trailer_is_malloced = 1; + memcpy(dtb->trailer, (char *)fdt + dtb_len, dtb->trailer_len); + } + } + + return dtb; + +error_exit: + free(fdt); + if (dtb) + free(dtb->trailer); + free(dtb); + return NULL; +} + +DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp) + return dtoverlay_load_dtb_from_fp(fp, max_size); + dtoverlay_error("Failed to open '%s'", filename); + return NULL; +} + +DTBLOB_T *dtoverlay_import_fdt(void *fdt, int buf_size) +{ + DTBLOB_T *dtb = NULL; + int node_off; + int dtb_len; + int err; + + err = fdt_check_header(fdt); + if (err != 0) + { + dtoverlay_error("not a valid FDT - err %d", err); + goto error_exit; + } + + dtb_len = fdt_totalsize(fdt); + + if (buf_size < dtb_len) + { + dtoverlay_error("fdt is too large"); + err = -FDT_ERR_NOSPACE; + goto error_exit; + } + + if (buf_size > dtb_len) + fdt_set_totalsize(fdt, buf_size); + + dtb = calloc(1, sizeof(DTBLOB_T)); + if (!dtb) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + dtb->fdt = fdt; + dtb->max_phandle = 0; // Not a valid phandle + + // Find the minimum and maximum phandles, in case it is necessary to + // relocate existing ones or create new ones. + + for (node_off = 0; + node_off >= 0; + node_off = fdt_next_node(fdt, node_off, NULL)) + { + uint32_t phandle = fdt_get_phandle(fdt, node_off); + if (phandle > dtb->max_phandle) + dtb->max_phandle = phandle; + } + +error_exit: + return dtb; +} + +int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename) +{ + FILE *fp = fopen(filename, "wb"); + int err = 0; + + if (fp) + { + long len = fdt_totalsize(dtb->fdt); + if (len != fwrite(dtb->fdt, 1, len, fp)) + { + dtoverlay_error("fwrite failed"); + err = -2; + goto error_exit; + } + if (dtb->trailer_len && + (fwrite(dtb->trailer, 1, dtb->trailer_len, fp) != dtb->trailer_len)) + { + dtoverlay_error("fwrite failed"); + err = -2; + goto error_exit; + } + + dtoverlay_debug("Wrote %ld bytes to '%s'", len, filename); + fclose(fp); + } + else + { + dtoverlay_debug("Failed to create '%s'", filename); + err = -1; + } + +error_exit: + return err; +} + +int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size) +{ + int size = fdt_totalsize(dtb->fdt); + int err = 0; + + if (new_size < 0) + new_size = size - new_size; + + if (new_size > size) + { + void *fdt; + fdt = malloc(new_size); + if (fdt) + { + memcpy(fdt, dtb->fdt, size); + fdt_set_totalsize(fdt, new_size); + + if (dtb->fdt_is_malloced) + free(dtb->fdt); + + dtb->fdt = fdt; + dtb->fdt_is_malloced = 1; + } + else + { + err = -FDT_ERR_NOSPACE; + } + } + else if (new_size < size) + { + /* Can't shrink it */ + err = -FDT_ERR_NOSPACE; + } + + return err; +} + +int dtoverlay_dtb_totalsize(DTBLOB_T *dtb) +{ + return fdt_totalsize(dtb->fdt); +} + +void dtoverlay_pack_dtb(DTBLOB_T *dtb) +{ + fdt_pack(dtb->fdt); +} + +void dtoverlay_free_dtb(DTBLOB_T *dtb) +{ + if (dtb) + { + if (dtb->fdt_is_malloced) + free(dtb->fdt); + if (dtb->trailer_is_malloced) + free(dtb->trailer); + free(dtb); + } +} + +int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle) +{ + return fdt_node_offset_by_phandle(dtb->fdt, phandle); +} + +int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name) +{ + int symbols_off, path_len; + const char *node_path; + + node_path = dtoverlay_get_alias(dtb, symbol_name); + + if (node_path) + { + path_len = strlen(node_path); + } + else + { + symbols_off = fdt_path_offset(dtb->fdt, "/__symbols__"); + + if (symbols_off < 0) + { + dtoverlay_error("No symbols found"); + return -FDT_ERR_NOTFOUND; + } + + node_path = fdt_getprop(dtb->fdt, symbols_off, symbol_name, &path_len); + if (path_len < 0) + return -FDT_ERR_NOTFOUND; + } + return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); +} + +int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, + int pos) +{ + while (1) + { + const char *node_name; + pos = fdt_next_node(dtb->fdt, pos, NULL); + if (pos < 0) + break; + node_name = fdt_get_name(dtb->fdt, pos, NULL); + if (node_name) + { + int i; + for (i = 0; node_names[i]; i++) + { + const char *node = node_names[i]; + int matchlen = strlen(node); + if ((strncmp(node_name, node, matchlen) == 0) && + ((node[matchlen] == '\0') || + (node[matchlen] == '@'))) + return pos; + } + } + } + return -1; +} + +int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos) +{ + if (pos >= 0) + { + const void *prop = dtoverlay_get_property(dtb, pos, "status", NULL); + if (prop && + ((strcmp((const char *)prop, "okay") == 0) || + (strcmp((const char *)prop, "ok") == 0))) + return 1; + } + return 0; +} + +const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, const char *prop_name, int *prop_len) +{ + return fdt_getprop(dtb->fdt, pos, prop_name, prop_len); +} + +int dtoverlay_set_property(DTBLOB_T *dtb, int pos, + const char *prop_name, const void *prop, int prop_len) +{ + int err = fdt_setprop(dtb->fdt, pos, prop_name, prop, prop_len); + if (err < 0) + dtoverlay_error("Failed to set property '%s'", prop_name); + return err; +} + +const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name) +{ + int node_off; + int prop_len; + const char *alias; + + node_off = fdt_path_offset(dtb->fdt, "/aliases"); + + alias = fdt_getprop(dtb->fdt, node_off, alias_name, &prop_len); + if (alias && !prop_len) + alias = ""; + return alias; +} + +int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value) +{ + int node_off; + + node_off = fdt_path_offset(dtb->fdt, "/aliases"); + + return fdt_setprop_string(dtb->fdt, node_off, alias_name, value); +} + +void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func) +{ + dtoverlay_logging_func = func; +} + +void dtoverlay_enable_debug(int enable) +{ + dtoverlay_debug_enabled = enable; +} + +void dtoverlay_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + (*dtoverlay_logging_func)(DTOVERLAY_ERROR, fmt, args); + va_end(args); +} + +void dtoverlay_debug(const char *fmt, ...) +{ + va_list args; + if (dtoverlay_debug_enabled) + { + va_start(args, fmt); + (*dtoverlay_logging_func)(DTOVERLAY_DEBUG, fmt, args); + va_end(args); + } +} + +static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, + const char *fmt, va_list args) +{ + const char *type_str; + + switch (type) + { + case DTOVERLAY_ERROR: + type_str = "error"; + break; + + case DTOVERLAY_DEBUG: + type_str = "debug"; + break; + + default: + type_str = "?"; + } + + fprintf(stderr, "DTOVERLAY[%s]: ", type_str); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} diff --git a/helpers/dtoverlay/dtoverlay.h b/helpers/dtoverlay/dtoverlay.h new file mode 100755 index 0000000..4602114 --- /dev/null +++ b/helpers/dtoverlay/dtoverlay.h @@ -0,0 +1,197 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef DTOVERLAY_H +#define DTOVERLAY_H + +#define BE4(x) ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, ((x)>>0)&0xff +#define GETBE4(p, off) ((((unsigned char *)p)[off + 0]<<24) + (((unsigned char *)p)[off + 1]<<16) + \ + (((unsigned char *)p)[off + 2]<<8) + (((unsigned char *)p)[off + 3]<<0)) +#define SETBE4(p, off, x) do { \ + ((unsigned char *)p)[off + 0] = ((x>>24) & 0xff); \ + ((unsigned char *)p)[off + 1] = ((x>>16) & 0xff); \ + ((unsigned char *)p)[off + 2] = ((x>>8) & 0xff); \ + ((unsigned char *)p)[off + 3] = ((x>>0) & 0xff); \ +} while (0) + +#define NON_FATAL(err) (((err) < 0) ? -(err) : (err)) +#define IS_FATAL(err) ((err) < 0) +#define ONLY_FATAL(err) (IS_FATAL(err) ? (err) : 0) + +#define DTOVERLAY_PADDING(size) (-(size)) + +typedef enum +{ + DTOVERLAY_ERROR, + DTOVERLAY_DEBUG +} dtoverlay_logging_type_t; + +typedef struct dtoverlay_struct +{ + const char *param; + int len; + const char *b; +} DTOVERLAY_PARAM_T; + +typedef struct dtblob_struct +{ + void *fdt; + char fdt_is_malloced; + char trailer_is_malloced; + char fixups_applied; + int min_phandle; + int max_phandle; + void *trailer; + int trailer_len; +} DTBLOB_T; + + +typedef void DTOVERLAY_LOGGING_FUNC(dtoverlay_logging_type_t type, + const char *fmt, va_list args); + +typedef int (*override_callback_t)(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value); + +/* Return values: -ve = fatal error, positive = non-fatal error */ +int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_name, int path_len); + +int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_name, int path_len); + +int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len); + +int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, + DTOVERLAY_PARAM_T *properties, + unsigned int num_properties); + +int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, + const char *prop_name, const void *prop_data, + int prop_len); + +int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); + +int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); + +int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, + unsigned int num_params); + +const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, + int *data_len); + +int dtoverlay_override_one_target(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value); + +int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + override_callback_t callback, + void *callback_value); + +int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + const char *override_value); + +int dtoverlay_override_one_target(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value); + +int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src); + +int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, + const char *dst, const char *src); + +DTBLOB_T *dtoverlay_create_dtb(int max_size); + +DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size); + +DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size); + +DTBLOB_T *dtoverlay_import_fdt(void *fdt, int max_size); + +int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename); + +int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size); + +int dtoverlay_dtb_totalsize(DTBLOB_T *dtb); + +void dtoverlay_pack_dtb(DTBLOB_T *dtb); + +void dtoverlay_free_dtb(DTBLOB_T *dtb); + +static inline void *dtoverlay_dtb_trailer(DTBLOB_T *dtb) +{ + return dtb->trailer; +} + +static inline int dtoverlay_dtb_trailer_len(DTBLOB_T *dtb) +{ + return dtb->trailer_len; +} + +static inline void dtoverlay_dtb_set_trailer(DTBLOB_T *dtb, + void *trailer, + int trailer_len) +{ + dtb->trailer = trailer; + dtb->trailer_len = trailer_len; + dtb->trailer_is_malloced = 0; +} + +int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle); + +int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name); + +int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, + int pos); + +int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos); + +const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, + const char *prop_name, int *prop_len); + +int dtoverlay_set_property(DTBLOB_T *dtb, int pos, + const char *prop_name, const void *prop, int prop_len); + +const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name); + +int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value); + +void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func); + +void dtoverlay_enable_debug(int enable); + +void dtoverlay_error(const char *fmt, ...); + +void dtoverlay_debug(const char *fmt, ...); + +#endif diff --git a/helpers/v3d/v3d_common.h b/helpers/v3d/v3d_common.h new file mode 100755 index 0000000..61002b1 --- /dev/null +++ b/helpers/v3d/v3d_common.h @@ -0,0 +1,39 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef V3D_COMMON_H +#define V3D_COMMON_H + +#include "helpers/v3d/v3d_ver.h" +#include "interface/vcos/vcos_assert.h" +#include "interface/vcos/vcos_stdbool.h" +#include "interface/vcos/vcos_stdint.h" +#include "interface/vcos/vcos_types.h" /* for vcos_unused and VCOS_STATIC_INLINE */ + +typedef uint32_t V3D_ADDR_T; +typedef uint16_t float16_t; + +#endif diff --git a/helpers/v3d/v3d_ver.h b/helpers/v3d/v3d_ver.h new file mode 100755 index 0000000..967e55c --- /dev/null +++ b/helpers/v3d/v3d_ver.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef V3D_VER_H +#define V3D_VER_H + +/* if V3D_TECH_VERSION/V3D_REVISION aren't defined, try to figure them out from + * other defines... */ +#ifndef V3D_TECH_VERSION + #define V3D_TECH_VERSION 2 +#endif +#ifndef V3D_REVISION + #ifdef __BCM2708A0__ + #ifdef HERA + /* bcg a1, hera */ + #define V3D_REVISION 1 + #else + /* 2708 a0 */ + #define V3D_REVISION 0 + #endif + #elif defined(__BCM2708B0__) + /* 2708 b0 */ + #define V3D_REVISION 2 + #elif defined(__BCM2708C0__) || defined(__BCM2708C1__) + /* 2708 c0/1 */ + #define V3D_REVISION 3 + #else + /* capri */ + #define V3D_REVISION 6 + #endif +#endif + +#define V3D_MAKE_VER(TECH_VERSION, REVISION) (((TECH_VERSION) * 100) + (REVISION)) +#define V3D_VER (V3D_MAKE_VER(V3D_TECH_VERSION, V3D_REVISION)) +#define V3D_VER_AT_LEAST(TECH_VERSION, REVISION) (V3D_VER >= V3D_MAKE_VER(TECH_VERSION, REVISION)) + +/* TODO this is temporary */ +#if V3D_VER == V3D_MAKE_VER(9, 9) +#define V3D_VER_D3D +#endif + +#endif diff --git a/helpers/vc_image/metadata_fourcc.h b/helpers/vc_image/metadata_fourcc.h new file mode 100755 index 0000000..64bccf7 --- /dev/null +++ b/helpers/vc_image/metadata_fourcc.h @@ -0,0 +1,107 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _METADATA_FOURCC_H +#define _METADATA_FOURCC_H + +/****************************************************************************** +Definition of 4CCs assigned to metadata items. This list acts more as a +central repository of 4CCs for different metadata items so as to avoid clashes. +They are not otherwise necessary for library to function correctly. +******************************************************************************/ + +/* + Note: Multi-character constants are not valid C and although supported by + some compilers such as Metaware, they are ambiguous in how they should be + packed into a long due to endian-ness (and also due to size for 2 and 3 + character constants). +*/ + +typedef enum { + + METADATA_CDI = ('C'<<24)+('D'<<16)+('M'<<8)+('D'), // 'CDMD' struct CDI_METADATA_T in /middleware/camplus/cdi/cdi.h + METADATA_CAM_CAL = ('C'<<24)+('C'<<16)+('A'<<8)+('L'), // 'CCAL' struct CAMERA_CALIBRATION_METADATA_T in /middleware/camplus/cdi/cdi.h + METADATA_TIMING = ('T'<<24)+('I'<<16)+('M'<<8)+('E'), // 'TIME' struct TIMING_METADATA_T in /middleware/camplus/cdi/cdi.h + METADATA_CDI_FILE = ( 0 <<24)+('C'<<16)+('D'<<8)+('F'), // '\x00CDF' struct CDI_FILE_METADATA_T in /middleware/camplus/cdi/cdi_file.h + METADATA_CDI_RAW = ('C'<<24)+('D'<<16)+('R'<<8)+('W'), // 'CDRW' struct CDI_FILE_RAW_METADATA_T in /middleware/camplus/cdi/cdi_file_raw.h + METADATA_STABILISE = ('S'<<24)+('T'<<16)+('A'<<8)+('B'), // 'STAB' struct GLOBAL_MOTION_VECTOR_T in /middleware/camplus/sw/stabilise/stabilise.h + METADATA_LOCAL_MV1 = ('L'<<24)+('M'<<16)+('V'<<8)+('1'), // 'LMV1' struct ALL_LOCAL_MOTION_VECTORS_T in /middleware/camplus/sw/stabilise/stabilise.h + METADATA_LOCAL_MV2 = ('L'<<24)+('M'<<16)+('V'<<8)+('2'), // 'LMV2' struct ALL_LOCAL_MOTION_VECTORS_T in /middleware/camplus/sw/stabilise/stabilise.h + METADATA_TUNER_AGC = ( 0 <<24)+('A'<<16)+('G'<<8)+('C'), // '\x00AGC' struct ISP_AGC_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_AGC_DEBUG = ('A'<<24)+('G'<<16)+('C'<<8)+('D'), // 'AGCD' struct ISP_TUNER_BRCM_AGC_DEBUG_T in /middleware/ISP/tuner/isp_tuner_agc.h + METADATA_FOCUS_REGION = ('F'<<24)+('R'<<16)+('G'<<8)+('N'), // 'FRGN' struct ISP_TUNER_BRCM_AF_STATISTICS_PARAMS_T in /middleware/ISP/tuner/isp_tuner_brcm_common.h + METADATA_FOCUS_WOI = ('F'<<24)+('W'<<16)+('O'<<8)+('I'), // 'FWOI' struct ISP_WOI_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_FOCUS_CAF = ('F'<<24)+('C'<<16)+('A'<<8)+('F'), // 'FCAF' struct ISP_CAF_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_AUTOFOCUS = ( 0 <<24)+( 0 <<16)+('A'<<8)+('F'), // '\x00\x00AF' struct ISP_AF_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_EV = ('E'<<24)+('V'<<16)+('M'<<8)+('D'), // 'EVMD' struct ISP_TUNER_BRCM_EV_METADATA_T in /middleware/ISP/tuner/isp_tuner_brcm_common.h + METADATA_ISP = ('I'<<24)+('S'<<16)+('P'<<8)+('M'), // 'ISPM' struct ISP_ISP_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_FACETRACKING = ('F'<<24)+('A'<<16)+('C'<<8)+('E'), // 'FACE' struct FACE_METADATA_T defined in /middleware/camplus/sw/face_metadata.h + METADATA_ANTISHAKE = ('S'<<24)+('H'<<16)+('A'<<8)+('K'), // 'SHAK' struct SHAKE_METADATA_T defined in /middleware/camplus/sw/shake_metadata.h + METADATA_RER = ( 0 <<24)+('R'<<16)+('E'<<8)+('R'), // '\x00RER' struct RER_METADATA_T defined in /middleware/camplus/sw/rer_metadata.h + METADATA_SCENEDETECTION = ( 0 <<24)+('A'<<16)+('S'<<8)+('D'), // '\x00ASD' struct ASD_METADATA_T defined in /middleware/camplus/sw/asd_metadata.h + METADATA_TUNER_SYNC = ('S'<<24)+('Y'<<16)+('N'<<8)+('C'), // 'SYNC' NULL data, just adds the item header. + METADATA_DARK_FRAME_CORRECT = ('D'<<24)+('F'<<16)+('R'<<8)+('C'), // 'DFRC' 5 byte literal string "dfrc" + METADATA_DARK_FRAME_SUB = ('D'<<24)+('F'<<16)+('S'<<8)+('B'), // 'DFSB' 3 byte literal string "on" + METADATA_TUNER_DROP_FRAME = ('T'<<24)+('D'<<16)+('R'<<8)+('P'), + METADATA_ABL = ( 0 <<24)+('A'<<16)+('B'<<8)+('L'), // '\x00ABL' struct ISP_TUNER_BRCM_BLACK_LEVEL_ABL_T defined in /middleware/ISP/tuner/isp_tuner_brcm_black_level.h + METADATA_DRC = ( 0 <<24)+('D'<<16)+('R'<<8)+('C'), // 'DRC' struct DRC_METADATA_T defined in /middleware/camplus/sw/drc/drc.h + METADATA_REGISTRATION = ( 0 <<24)+('R'<<16)+('E'<<8)+('G'), // 'REG' struct REGISTRATION_OFFSETS_T defined in /middleware/camplus/sw/registration/registration.h + METADATA_RAW_CAPTURE = ('R'<<24)+('W'<<16)+('M'<<8)+('D'), // 'RWMD' struct RAW_CAPTURE_METADATA_T defined in /middleware/camplus/sw/raw_metadata.h + // structure definitions for IL metadata are + // in middleware/openmaxil/headers/ilmetadata.h + METADATA_IL_CAMERA_NAME = ('I'<<24)+('L'<<16)+('C'<<8)+('A'), // 'ILCA' + METADATA_IL_CROP_RECTANGLE = ('I'<<24)+('L'<<16)+('C'<<8)+('R'), // 'ILCR' + METADATA_IL_PIXEL_ASPECT_RATIO = ('I'<<24)+('L'<<16)+('P'<<8)+('A'), // 'ILPA' + + METADATA_TUNER_FLASH_MONITOR = ('F'<<24)+('L'<<16)+('M'<<8)+('N'), // 'FLMN' Flash monitor - type ISP_TUNER_BRCM_FLASH_MONITOR_T from isp_tuner_brcm.h + + + // VoWIFI video analysis + METADATA_SPATIAL_ACT_A = ( 'S'<<24)+('P'<<16)+('T'<<8)+('A'), // 'SPTA' : SPATIAL_ACTIVITY_METADATA_T defined in /middleware/camplus/sw/perceptual/spatial_activity.h + METADATA_TEMPORAL_ACT_A = ( 'T'<<24)+('M'<<16)+('P'<<8)+('A'), // 'TMPA' : TEMPORAL_ACTIVITY_METADATA_T defined in /middleware/camplus/sw/perceptual/temporal_activity.h + METADATA_FILMGRAIN_NOISE_A = ( 'F'<<24)+('G'<<16)+('N'<<8)+('A'), // 'FGNA' : FILMGRAIN_NOISE_METADATA_T defined in /middleware/camplus/sw/perceptual/filmgrain_noise.h + METADATA_COLORSPACE_A = ( 'C'<<24)+('L'<<16)+('R'<<8)+('A'), // 'CLRA' : COLORSPACE_METADATA_T defined in /middleware/camplus/sw/perceptual/colorspace.h + METADATA_FLAT_AREA_A = ( 'F'<<24)+('L'<<16)+('T'<<8)+('A'), // 'FLTA' : FLAT_AREA_METADATA_T defined in /middleware/camplus/sw/perceptual/flatarea.h + METADATA_STILL_AREA_A = ( 'S'<<24)+('T'<<16)+('L'<<8)+('A'), // 'STLA' : FLAT_AREA_METADATA_T defined in /middleware/camplus/sw/perceptual/stillarea.h + METADATA_DDITHER_A = ( 'D'<<24)+('D'<<16)+('T'<<8)+('A'), // 'DDTA' : DDITHER_METADATA_T defined in /middleware/camplus/sw/perceptual/... + + METADATA_LINKED_MULTICHANN = ( 'I'<<24)+('L'<<16)+('M'<<8)+('C'), // 'ILMC' : VC_IMAGE_LINKED_MULTICHANN_T defined in /helpers/vc_image/vc_image.h + + METADATA_HDR = ( 0 <<24)+( 'H'<<16)+('D'<<8)+('R'), // 'HDR' : HDR_METADATA_T defined in /middleware/camplus/sw/hdr/hdr_metadata.h + METADATA_FOCUS_STATS_PREPROC = ('F'<<24)+('S'<<16)+('P'<<8)+('M'), // 'FSPM' : FOCUS_STATS_PREPROC_METADATA defined in /middleware/camplus/sw/hdr/focus_stats_preproc/focus_stats_preproc.h + METADATA_ACUTE_AWB_LOG = ('A'<<24)+('E'<<16)+('L'<<8)+('C'), // 'AELC' : ISP_ACUTE_AWB_LOG + METADATA_DF = ( 0 <<24)+( 0<<16)+('D'<<8)+('F'), // '\x00\x00DF' : DF_METADATA_T defined in /middleware/camplus/sw/df/df_metadata.h + METADATA_MAGIC_MEASURE = ('S'<<24)+('S'<<16)+('M'<<8) + ('M'), // 'SSMM' : A statistic from the ISP used to determine the JPEG quality setting for a certain customer. + METADATA_SNAPSHOT_JPEG_QUANTISER = ('S'<<24)+('S'<<16)+('J'<<8) + ('S'), // 'SSJQ' : The size of the snapshot frame when JPEG-encoded. + + METADATA_SUPPLEMENTARY_INFO = ('S'<<24)+('U'<<16)+('P'<<8) + ('P'), // 'SUPP' : Supplimentary info defined in /codecs/video/hw/enc/venc_supplementary_info.h + + METADATA_UNKNOWN = ('U'<<24)+('N'<<16)+('K'<<8)+('N') // 'UNKN' + +} METADATA_CODE_T; + +#endif diff --git a/helpers/vc_image/vc_image.h b/helpers/vc_image/vc_image.h new file mode 100755 index 0000000..f566348 --- /dev/null +++ b/helpers/vc_image/vc_image.h @@ -0,0 +1,804 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file + * + * This file describes the public interfaces to the old vc_image library. + */ + +#ifndef VC_IMAGE_H +#define VC_IMAGE_H + +#if !defined(__VIDEOCORE__) && !defined(__vce__) && !defined(VIDEOCORE_CODE_IN_SIMULATION) +#error This code is only for use on VideoCore. If it is being included +#error on the host side, then you have done something wrong. Defining +#error __VIDEOCORE__ is *not* the right answer! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Definition for VC_IMAGE_TYPE_T live in here */ +#include "vcinclude/vc_image_types.h" +#include "helpers/vc_image/vc_image_helper.h" + +#if !defined __SYMBIAN32__ + +#if !defined( _MSC_VER) && !defined(__WIN32__) + #include "vcfw/rtos/rtos.h" +#endif +#include "vcfw/rtos/common/rtos_common_mem.h" + +#include "helpers/vc_image/vc_image_metadata.h" + +#if defined(__VIDEOCORE3__) && !defined(RGB888) +#ifdef __GNUC__ +#warning "RGB888 should be defined on all VideoCore3 platforms, please check your platform makefile." +#else +# warn "RGB888 should be defined on all VideoCore3 platforms, please check your platform makefile." +#endif +#endif + + /* Types of extra properties that can be passed to vc_image_configure() */ + + typedef enum + { + VC_IMAGE_PROP_END = 0, + VC_IMAGE_PROP_NONE = 0x57EAD400, + VC_IMAGE_PROP_ALIGN, + VC_IMAGE_PROP_STAGGER, + VC_IMAGE_PROP_PADDING, + VC_IMAGE_PROP_PRIORITY, + VC_IMAGE_PROP_MALLOCFN, + VC_IMAGE_PROP_CACHEALIAS, + VC_IMAGE_PROP_CLEARVALUE, + + /* insert only allocation-related properties above this line */ + + VC_IMAGE_PROP_REF_IMAGE = 0x57EAD47f, + VC_IMAGE_PROP_ROTATED, + VC_IMAGE_PROP_PITCH, + VC_IMAGE_PROP_DATA, + VC_IMAGE_PROP_SIZE, + VC_IMAGE_PROP_PALETTE, + VC_IMAGE_PROP_MIPMAPS, + VC_IMAGE_PROP_BAYER_ORDER, + VC_IMAGE_PROP_BAYER_FORMAT, + VC_IMAGE_PROP_BAYER_BLOCK_SIZE, + VC_IMAGE_PROP_CODEC_FOURCC, + VC_IMAGE_PROP_CODEC_MAXSIZE, + VC_IMAGE_PROP_CUBEMAP, + VC_IMAGE_PROP_OPENGL_FORMAT_AND_TYPE, + VC_IMAGE_PROP_INFO, + VC_IMAGE_PROP_METADATA, + VC_IMAGE_PROP_NEW_LIST, + /* Multi-channel properties */ + VC_IMAGE_PROP_NUM_CHANNELS, + VC_IMAGE_PROP_IS_TOP_BOTTOM, + VC_IMAGE_PROP_IS_DECIMATED, + VC_IMAGE_PROP_IS_PACKED, + VC_IMAGE_PROP_YUV_COLOURSPACE, + /* Linked-multichannel properties*/ + VC_IMAGE_PROP_LINKED_MULTICHANN, + VC_IMAGE_PROP_VPITCH + } VC_IMAGE_PROPERTY_T; + + /* A property key and value */ + typedef struct + { + VC_IMAGE_PROPERTY_T prop; + unsigned long value; + } VC_IMAGE_PROPLIST_T; +#define VC_IMAGE_PROPLIST_COUNT(x) (sizeof(x)/sizeof(x[0])) +#define VC_IMAGE_PROPLIST_NULL ((VC_IMAGE_PROPLIST_T*)0) + + + /* Useful bits that can be set, to check validity we'll assume that all other + bits should be zero and enforce this in vc_image functions to catch people + who aren't initialising the VC_IMAGE_T structure nicely; update when other + bits are added */ +#define VC_IMAGE_INFO_VALIDBITS 0x9F0F + /* Define the bits of info used to denote the colourspace */ +#define VC_IMAGE_INFO_COLOURSPACE 0x0F + +#endif //#if !defined __SYMBIAN32__ + +#define is_valid_vc_image_hndl(_img, _type) ((_img) != NULL \ + && (_img)->image_data == NULL \ + && ((_img)->info.info & ~VC_IMAGE_INFO_VALIDBITS) == 0 \ + && (_type) > VC_IMAGE_MIN && (_type) < VC_IMAGE_MAX \ + && (_img)->type == (_type) \ + ) + +#define is_valid_vc_image_buf(_img, _type) ((_img) != NULL \ + && (_img)->image_data != NULL \ + && ((_img)->info.info & ~VC_IMAGE_INFO_VALIDBITS) == 0 \ + && (_type) > VC_IMAGE_MIN && (_type) < VC_IMAGE_MAX \ + && (_img)->type == (_type) \ + ) + +#define is_within_rectangle(x, y, w, h, cont_x, cont_y, cont_w, cont_h) ( \ + (x) >= (cont_x) \ + && (y) >= (cont_y) \ + && ((x) + (w)) <= ((cont_x) + (cont_w)) \ + && ((y) + (h)) <= ((cont_y) + (cont_h)) \ + ) + + +#if !defined __SYMBIAN32__ + + + /** + * \brief storage for pointers to image headers of the previous and next channel + * + * + * When linked-multichannel structure is being used, this structure stores the pointers + * necessary to link the current image header in to make represent a multichannel image. + */ + struct VC_IMAGE_LINKED_MULTICHANN_T { + VC_IMAGE_T* prev; + VC_IMAGE_T* next; + }; + typedef struct VC_IMAGE_LINKED_MULTICHANN_T VC_IMAGE_LINKED_MULTICHANN_T; + + /* Point type for use in polygon drawing. */ + typedef struct vc_image_point_s { + int x, y; + } VC_IMAGE_POINT_T; + + /* Useful for rectangular crop regions in a vc_image */ + typedef struct vc_image_rect_s + { + unsigned int x, y; + unsigned int width; + unsigned int height; + } VC_IMAGE_RECT_T; + + /* CRC type for the return of CRC results */ + typedef struct VC_IMAGE_CRC_T_ { + unsigned int y; + unsigned int uv; + } VC_IMAGE_CRC_T; + + /* To make stack-based initialisation convenient: */ + +#define NULL_VC_IMAGE_T {0, 0, 0, 0, 0, 0} + + /* Convenient constants that can be passed more readably to vc_image_text. */ + +#define VC_IMAGE_COLOUR_TRANSPARENT -1 +#define VC_IMAGE_COLOUR_FADE -2 + +#define CLIP_DEST(x_offset, y_offset, width, height, dest_width, dest_height) \ + if (x_offset<0) \ + width-=-x_offset, x_offset=0; \ + if (y_offset<0) \ + height-=-y_offset, y_offset=0; \ + if (x_offset+width>dest_width) \ + width-=x_offset+width-dest_width; \ + if (y_offset+height>dest_height) \ + height-=y_offset+height-dest_height; \ + + + /* Publicly exported functions. */ + + /****************************************************************************** + General functions. + ******************************************************************************/ + +//routine to return a bitmask (bit referenced by VC_IMAGE_TYPE_T) of the supported image formats built in +//this particular version of the vc_image library + unsigned int vc_image_get_supported_image_formats( void ); + + /****************************************************************************** + Data member access. + ******************************************************************************/ + + /* Initialise all fields of a VC_IMAGE_T to zero. */ + + void vc_image_initialise(VC_IMAGE_T *image); + + /* Return specified channel of a multi-channel VC_IMAGE_T as a single-channel VC_IMAGE_T. */ + + void vc_image_extract_channel(VC_IMAGE_T *extract, VC_IMAGE_T *image, uint8_t chan_idx); + + /* Fill out all fields of a VC_IMAGE_T in the same way as vc_image_malloc(), but non-allocating. */ + +#if defined(va_start) /* can't publish this without including */ + int vc_image_vconfigure(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, va_list proplist); + int vc_image_vreconfigure(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, va_list proplist); +#endif + int vc_image_configure_unwrapped(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); + int vc_image_reconfigure_unwrapped(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); + /* remaining arguments are a VC_IMAGE_PROPERTY_T list terminated with VC_IMAGE_END */ + + int vc_image_configure_proplist(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPLIST_T *props, unsigned long n_props); + + void vc_image_lock( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src ); + + void vc_image_lock_extract( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src, uint8_t chan_idx ); + + void vc_image_lock_channel(VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *chann); + void vc_image_lock_channel_perma(VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *chann); //lightweight version of lock channel + + void vc_image_unlock( VC_IMAGE_BUF_T *img ); + + void vc_image_lock_perma( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src ); + + void vc_image_unlock_perma( VC_IMAGE_BUF_T *img ); + + /* Mipmap-related functions. Image must be brcm1. */ + + /* Take the base image of an image and scale it down into its mipmaps. */ + /* The filter argument type is likely to change, but 0 should always be a + * reasonable value (even if it becomes a pointer). */ + void vc_image_rebuild_mipmaps(VC_IMAGE_BUF_T *dst, int filter); + + /****************************************************************************** + Image/bitmap manipulation. + ******************************************************************************/ + + /* Fill a region of an image with solid colour. */ + + void vc_image_fill(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int value); + + /* Blt a region of an image to another. */ + + void vc_image_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Blt a region of an image to another, changing the format on the fly. */ + + void vc_image_convert_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Expose the special cases of above */ + void vc_image_yuv420_to_rgba32_part(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T const *src, int src_x_offset, int src_y_offset, int alpha); + + void vc_image_rgb565_to_rgba32_part(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T const *src, int src_x_offset, int src_y_offset, int alpha); + /* Blt a region of an image to another with a nominated transparent colour. */ + + void vc_image_transparent_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour); + + /* Blt a region of an image to another but only overwriting pixels of a given colour. */ + + void vc_image_overwrite_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int overwrite_colour); + + /* Blt a region of an image to another only where a mask bitmap has 1s. */ + + void vc_image_masked_blt (VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, + VC_IMAGE_BUF_T *mask, int mask_x_offset, int mask_y_offset, + int invert); + + /* Masked fill a region with a solid colour. */ + + void vc_image_masked_fill(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int value, + VC_IMAGE_BUF_T *mask, int mask_x_offset, int mask_y_offset, + int invert); + + /* Blt an image into 3 separate buffers, either YUV or RGB + Will always copy the entire image. In the case of RGB image: + Y = RGB, U = NULL, V = NULL */ + + void vc_image_fetch(VC_IMAGE_BUF_T *src, unsigned char* Y, unsigned char* U, unsigned char* V); + + /* Blt the data in Y U V (or RGB) buffers into a VC_IMAGE structure + Opposite to vc_image_put above */ + + void vc_image_put(VC_IMAGE_BUF_T *src, unsigned char* Y, unsigned char* U, unsigned char* V); + + /* NOT a region of an image. */ + + void vc_image_not(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height); + + /* Fade (or brighten) a region of an image. fade is an 8.8 value. */ + + void vc_image_fade(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int fade); + + /* OR two images together. */ + + void vc_image_or(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* XOR two images together. */ + + void vc_image_xor(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Copy an image in its entirety (works on 1bpp images). */ + + void vc_image_copy(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Transpose an image. */ + + void vc_image_transpose(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + int vc_image_transpose_ret(VC_IMAGE_T *dest, VC_IMAGE_T *src); + + /* Transpose an image and subsample it by some integer factor */ + void vc_image_transpose_and_subsample(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int factor); + + /* Vertically flip an image (turn "upside down"). */ + + void vc_image_vflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Horizontally flip an image ("mirror"). */ + + void vc_image_hflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Transpose an image in place. Relies on being able to preserve the pitch. */ + + void vc_image_transpose_in_place(VC_IMAGE_BUF_T *dest); + + /* Vertically flip an image (turn "upside down") in place. */ + + void vc_image_vflip_in_place(VC_IMAGE_BUF_T *dest); + + /* Horizontally flip an image ("mirror") in place. */ + + void vc_image_hflip_in_place(VC_IMAGE_BUF_T *dest); + + /* Add text string to an image. */ + + void vc_image_text(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_small_text(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_text_20(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_text_24(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_text_32(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + /* Experimental vector font thingy */ + +#define VC_IMAGE_ATEXT_FIXED (1<<16) // Add this to "size" if you want fixed-width + + int vc_image_atext_measure(int size, const char * str); + + int vc_image_atext_clip_length(int size, const char * str, int width); + + int vc_image_atext(VC_IMAGE_BUF_T * dest, int x, int y, int fg, int bg, int size, const char * str); + + /* Add optionally rotated text to an image */ + + void vc_image_textrotate(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int rotate); + + + /* Line drawing. */ + + void vc_image_line(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int x_end, int y_end, int fg_colour); + + /* Unfilled Rect. */ + + void vc_image_rect(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int width, int height, int fg_colour); + + /* Add frame . */ + + void vc_image_frame(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int width, int height); + + void vc_image_aa_line(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int x_end, int y_end, int thickness, int colour); + void vc_image_aa_ellipse(VC_IMAGE_BUF_T *dest, int cx, int cy, int a, int b, int thickness, int colour, int filled); + void vc_image_aa_polygon(VC_IMAGE_BUF_T *dest, VC_IMAGE_POINT_T *p, int n, int thickness, int colour, int filled); + + /* Copy and convert image to 48bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_convert_to_48bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Copy and convert image from 16bpp to 24bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_convert_to_24bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Convert and overlay image from 16bpp to 24bpp with nominated transparent colour. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_overlay_to_24bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour); + + /* Copy and convert image from 24bpp to 16bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_convert_to_16bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Copy all, or a portion of src to dest with resize. This function is specialized + for YUV images (other cases are not yet coded, and code could be rather large + to link all cases by default). All dimensions should be multiples of 2. + If smooth_flag nonzero, appropriate smoothing will be applied. */ + + void vc_image_resize_yuv(VC_IMAGE_BUF_T *dest, + int dest_x_offset, int dest_y_offset, + int dest_width, int dest_height, + VC_IMAGE_BUF_T *src, + int src_x_offset, int src_y_offset, + int src_width, int src_height, + int smooth_flag); + + /* RGB565 resize. Pitch must be 32-byte aligned, dimensions need not be. + XXX kept YUV and RGB565 version separate to avoid unnecessary linking. + However if we're going down the DLL route they ought to be combined. */ + + void vc_image_resize_rgb565(VC_IMAGE_BUF_T * dest, + int dest_x_offset, int dest_y_offset, + int dest_width, int dest_height, + VC_IMAGE_BUF_T *src, + int src_x_offset, int src_y_offset, + int src_width, int src_height, + int smooth_flag); + + void vc_image_resize(VC_IMAGE_BUF_T *dest, + int dest_x_offset, int dest_y_offset, + int dest_width, int dest_height, + VC_IMAGE_BUF_T *src, + int src_x_offset, int src_y_offset, + int src_width, int src_height, + int smooth_flag); + + /* Resize YUV in strips. XXX this function has rather bizarre arguments. */ + + void vc_image_resize_yuv_strip(VC_IMAGE_BUF_T * dest, VC_IMAGE_BUF_T * src, + int src_x_offset, int src_width, + int pos, int step, int smooth_flag, + void * tmp_buf, int tmp_buf_size); + + /* Blit from a YUV image to an RGB image with optional mirroring followed + by optional clockwise rotation */ + void vc_image_convert(VC_IMAGE_BUF_T *dest,VC_IMAGE_BUF_T *src,int mirror,int rotate); + + /* Convert bitmap from one type to another, doing vertical flip as part of the conversion */ + void vc_image_convert_vflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Convert from YUV to RGB, with optional downsample by 2 in each direction. */ + void vc_image_yuv2rgb(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int downsample); + + void vc_image_convert_yuv2rgb(unsigned char *datay, unsigned char *datau + , unsigned char *datav, int realwidth, int realheight + , unsigned short *buffer, int screenwidth, int screenheight); + + /* Frees up (using free_256bit) the source bytes and vc_image header */ + void vc_image_free(VC_IMAGE_T *img); + + /* Returns an image based on decompressing the given bytes (made by helpers/vc_image/runlength.m) */ + VC_IMAGE_BUF_T *vc_runlength_decode(unsigned char *data); + + /* Returns a 16bit image based on decompressing the given bytes (made by helpers/vc_image/covnertbmp2run.m) */ + VC_IMAGE_BUF_T *vc_run_decode(VC_IMAGE_BUF_T *img,unsigned char *data); + + /* Returns an image based on decompressing the given bytes (made by helpers/vc_image/runlength8.m) */ + VC_IMAGE_BUF_T *vc_runlength_decode8(unsigned char *data); + + /* Returns an image based on decompressing an image (made by helpers/vc_image/enc4by4.m) + Will place the decompressed data into an existing image if supplied */ + VC_IMAGE_BUF_T *vc_4by4_decode(VC_IMAGE_BUF_T *img,unsigned short *data); + + /* Deblocks a YUV image using a simple averaging scheme on 8 by 8 blocks */ + void vc_image_deblock(VC_IMAGE_BUF_T *img); + + /* Pack the bytes (i.e. remove the padding bytes) in a vc_image. */ + void vc_image_pack(VC_IMAGE_BUF_T *img, int x, int y, int w, int h); + + /* Pack bytes as above, but copy them to another memory block in the process. */ + void vc_image_copy_pack (unsigned char *dest, VC_IMAGE_BUF_T *img, int x, int y, int w, int h); + + /* Unpack bytes to have the correct padding. */ + void vc_image_unpack(VC_IMAGE_BUF_T *img, int w, int h); + + /* Unpack bytes as above, but also copy them to another memory block in the process. */ + void vc_image_copy_unpack(VC_IMAGE_BUF_T *img, int dest_x_off, int dest_y_off, unsigned char *src, int w, int h); + + /* swap red/blue */ + void vc_image_swap_red_blue(VC_IMAGE_BUF_T *img); + +#if defined(va_start) /* can't publish this without including */ + VC_IMAGE_BUF_T *vc_image_vparmalloc_unwrapped(VC_IMAGE_TYPE_T type, char const *description, long width, long height, va_list proplist); +#endif + VC_IMAGE_BUF_T *vc_image_parmalloc_unwrapped(VC_IMAGE_TYPE_T type, char const *description, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); + + void vc_image_parfree(VC_IMAGE_BUF_T *p); + + VC_IMAGE_BUF_T *vc_image_malloc( VC_IMAGE_TYPE_T type, long width, long height, int rotate ); + VC_IMAGE_BUF_T *vc_image_prioritymalloc( VC_IMAGE_TYPE_T type, long width, long height, int rotate, int priority, char const *name); +#define vc_image_priorityfree(p) vc_image_free(p) + + VC_IMAGE_T *vc_image_relocatable_alloc(VC_IMAGE_TYPE_T type, const char *description, long width, long height, MEM_FLAG_T flags, VC_IMAGE_PROPERTY_T prop, ...); + void vc_image_relocatable_free(VC_IMAGE_T *img); + + void vc_image_set_palette( short *colourmap ); + short *vc_image_get_palette( VC_IMAGE_T *src ); + + void vc_image_convert_in_place(VC_IMAGE_BUF_T *image, VC_IMAGE_TYPE_T new_type); + + /* Image transformations (flips and 90 degree rotations) */ + VC_IMAGE_TRANSFORM_T vc_image_inverse_transform(VC_IMAGE_TRANSFORM_T transform); + + VC_IMAGE_TRANSFORM_T vc_image_combine_transforms(VC_IMAGE_TRANSFORM_T transform1, VC_IMAGE_TRANSFORM_T transform2); + + void vc_image_transform_point(int *pX, int *pY, int w, int h, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_rect(int *pX, int *pY, int *pW, int *pH, int w, int h, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_dimensions(int *pW, int *pH, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_TRANSFORM_T transform); + int vc_image_transform_ret(VC_IMAGE_T *dest, VC_IMAGE_T *src, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_in_place(VC_IMAGE_BUF_T *image, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_brcm1s(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_TRANSFORM_T transform, int hdeci); + + const char *vc_image_transform_string(VC_IMAGE_TRANSFORM_T xfm); + + /* Blt a region of an image to another with a nominated transparent colour and alpha blending. + Alpha should be between 0 and 256 */ + void vc_image_transparent_alpha_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour, int alpha); + + /* Blt a region of an image to another with a nominated transparent colour and alpha blending. + Alpha should be between 0 and 256 */ + void vc_image_transparent_alpha_blt_rotated(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, + VC_IMAGE_BUF_T *src, int transparent_colour, int alpha, int scale, int sin_theta, int cos_theta); + + /* Blt a special 4.4 font image generated from a PNG file. + Only works with a 565 destination image. + Can choose a colour via red,green,blue arguments. Each component should be between 0 and 255. + The alpha value present in the font image is scaled by the alpha argument given to the routine + Alpha should be between 0 and 256 */ + void vc_image_font_alpha_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int red, int green, int blue, int alpha); + + /* Utilities for image editor */ + extern void im_split_components(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, + int width, int height, int pitch); + + extern void im_merge_components(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, + int width, int height, int pitch); + + extern void im_merge_components_adjacent(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, + int width, int height, int pitch, + int badjacent); + + /* Deflicker a YUV image using a simple low pass filter */ + void vc_image_deflicker(VC_IMAGE_BUF_T *img, unsigned char *context); + + /* Blend an Interlaced YUV image using a simple low pass filter which works on colour as well as luma*/ + void vc_image_blend_interlaced(VC_IMAGE_BUF_T *img, unsigned char *context); + + /* Stripe resizing. Resize a 16-high stripe horizontally to have the given width, + or a 16-wide column to have the given height. Work in place, uses bilinear filtering. */ + void vc_image_resize_stripe_h(VC_IMAGE_BUF_T *image, int d_width, int s_width); + void vc_image_resize_stripe_v(VC_IMAGE_BUF_T *image, int d_height, int s_height); + + /* Stripe resizing. Resize a horizontal stripe to 16-high, or a vertical stripe to 16 wide. + Work in place, uses bilinear filtering. */ + void vc_image_decimate_stripe_h(VC_IMAGE_BUF_T *src, int offset, int step); + void vc_image_decimate_stripe_v(VC_IMAGE_BUF_T *src, int offset, int step); + + + extern int vc_image_set_alpha(VC_IMAGE_BUF_T *image, + const int x, + const int y, + const int width, + const int height, + const int alpha ); + + /* Make a horizontal alpha gradient mask for used in alpha blending below */ + extern void vc_image_make_alpha_gradient(VC_IMAGE_BUF_T *pimage, + int start_alpha, int end_alpha); + + /* Alpha blend two images together using an alpha mask */ + extern void vc_image_alphablend(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_BUF_T *mask, + int dest_xoffset, int dest_yoffset, + int src_xoffset, int src_yoffset, + int width, int height); + + typedef struct vc_image_pool_s * VC_IMAGE_POOL_HANDLE_T; + typedef VC_IMAGE_POOL_HANDLE_T VC_IMAGE_POOL_HANDLE; /* legacy */ + + /* Blt a region of an image onto a particular mipmap of a brcm1 image */ + /* XXX do not use this function - the interface is likely to change as I decide + * what options are available here */ + void vc_image_XXX_mipmap_blt_XXX(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int miplvl, int cubeface, VC_IMAGE_TRANSFORM_T transform); + + /* Function to calculate the crc of the VC_IMAGE */ + VC_IMAGE_CRC_T vc_image_calc_crc_interlaced(VC_IMAGE_BUF_T *img, int cl, int cr, int ct, int cb, int field); + + /* make compatible with old function */ + #define vc_image_calc_crc(img,cl,cr,ct,cb) vc_image_calc_crc_interlaced((img),(cl),(cr),(ct),(cb),-1) + +#define vc_image_calc_crc_fast vc_image_calc_crc + +#if defined(NDEBUG) +# define vc_image_debugstr(x) NULL +#else +# define vc_image_debugstr(x) (x) +#endif + +#define vc_image_vparmalloc(type, desc, width, height, proplist) vc_image_vpmalloc(type, vc_image_debugstr(desc), width, height, proplist) +#if __STDC_VERSION__ >= 199901L || _MSC_VER >= 1400 +# define vc_image_parmalloc(type, desc, ...) vc_image_parmalloc_unwrapped(type, vc_image_debugstr(desc), __VA_ARGS__, VC_IMAGE_PROP_END) +# define vc_image_configure(...) vc_image_configure_unwrapped(__VA_ARGS__, VC_IMAGE_PROP_END) +# define vc_image_reconfigure(...) vc_image_reconfigure_unwrapped(__VA_ARGS__, VC_IMAGE_PROP_END) +#elif !defined(_MSC_VER) && !defined(VCMODS_LCC) +# define vc_image_parmalloc(type, desc, arglist...) vc_image_parmalloc_unwrapped(type, vc_image_debugstr(desc), arglist, VC_IMAGE_PROP_END) +# define vc_image_configure(image, arglist...) vc_image_configure_unwrapped(image, arglist, VC_IMAGE_PROP_END) +# define vc_image_reconfigure(image, arglist...) vc_image_reconfigure_unwrapped(image, arglist, VC_IMAGE_PROP_END) +#else + /*# warning "Your compiler does not support variadic macros. You'll have no vc_image_configure() or vc_image_parmalloc() functions."*/ +#endif + +// now included in vc_image.c as openmaxil uses it regardless of which formats are built for +extern void vc_subsample(VC_IMAGE_BUF_T *sub,VC_IMAGE_BUF_T *cur); + +#ifdef USE_YUV_UV + +extern void vc_save_YUV( VC_IMAGE_BUF_T *frame, + const char * const sz_filename); + +extern void vc_save_YUV_1file( VC_IMAGE_BUF_T *frame, const char * const sz_filename, int framenum ); + +extern void vc_load_YUVUV( VC_IMAGE_BUF_T *frame, + const char * const sz_filename,int yuvwidth,int yuvheight); + +extern void vc_load_YUV( VC_IMAGE_BUF_T *frame, + const char * const sz_filename,int framenum); + +extern void vc_endianreverse32(uint32_t *addr,int count); + +/* +** This is my preferred function to load a YUV_UV image. It works with either a collection of images +** e.g. foo%03u.yuv or one giant concatenated file e.g. foo.yuv. +** It will tile or truncate the output image to what you want which is why there are two heights & widths. +** Return 0 if successful; -1 if the params are unsuitable, -2 if we couldn't open the file. +*/ +extern int vc_load_image( const char * const sz_file_fmt, /* in */ + const unsigned iim, /* in */ + const int pitch, /* in */ + const int i_height, /* in */ /* (i)nput image: i.e. raw file */ + const int i_width, /* in */ + const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ + const int o_width, /* in */ + const VC_IMAGE_BUF_T * const frame /* in */ ); + +/* Modified version of vc_load_image (for maximum efficiency): + * - must be supplied with a buffer to read a whole YUV frame into + * - reads the next sequential image in an already-open file (no fopen/fseek/fclose). + * - no tiling: output size = input size. + */ +void vc_slurp_image (FILE * fid, /* in */ + const int pitch, /* in */ + const int height, /* in */ + const int width, /* in */ + const VC_IMAGE_T * const frame, /* in */ + unsigned char * frame_buf, /* in */ + int rewind); /* in */ + +extern int vc_load_image_vowifi( void * infile_fid, + const unsigned iim, /* in */ + const int pitch, /* in */ + const int i_height, /* in */ /* (i)nput image: i.e. raw file */ + const int i_width, /* in */ + const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ + const int o_width, /* in */ + const VC_IMAGE_BUF_T * const frame /* in */ ) ; + +extern int vc_load_image_vowifi_brcm1d( void * infile_fid, + const unsigned iim, /* in */ + const int pitch, /* in */ + const int i_height, /* in */ /* (i)nput image: i.e. raw file */ + const int i_width, /* in */ + const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ + const int o_width, /* in */ + const VC_IMAGE_BUF_T * const frame, /* in */ + const unsigned char *frame_buf) ; + +/* +** Vector routine to difference a pair of images ( d |-|= a ). +** Should probably dcache flush after this if using via uncached alias? +*/ +extern +void vc_image_difference( VC_IMAGE_BUF_T * const d, /* in/out */ + const VC_IMAGE_BUF_T * const a, /* in */ + float * const psnr_y, /* out */ + float * const psnr_uv, /* out */ + unsigned * const max_y, /* out */ + unsigned * const max_uv /* out */ ); + + +/* +** The h.263 Annex J deblocker. +** ("dest" & "src" may point to the same storage.) +*/ +extern +void vc_image_deblock_h263( VC_IMAGE_BUF_T * const dest, /*(in)out */ + const VC_IMAGE_BUF_T * const src, /* in */ + const int QUANT /* in */ ); + +/* + * Clone the top field of an interlaced image (lines 0,2,4,..) over the bottom field (lines 1,3,5,...) + * or vice versa +*/ +extern +void vc_image_clone_field(VC_IMAGE_T * image, int clone_top_field); + +#endif + + + /* RGBA32 colour macros */ +#define RGBA_ALPHA_TRANSPARENT 0 +#define RGBA_ALPHA_OPAQUE 255 + +#define RGBA32_TRANSPARENT_COLOUR 0x00000000 +#ifdef RGB888 +#define RGBA32_APIXEL(r, g, b, a) (((unsigned long)(a) << 24) | ((b) << 16) | ((g) << 8) | (r)) +#define RGBA32_SPIXEL(r, g, b, a) ((RGBA_ALPHA_OPAQUE << 24) | ((b) << 16) | ((g) << 8) | (r)) +#else +#define RGBA32_APIXEL(r, g, b, a) (((unsigned long)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#define RGBA32_SPIXEL(r, g, b, a) (((unsigned long)RGBA_ALPHA_OPAQUE << 24) | ((r) << 16) | ((g) << 8) | (b)) +#endif +#define RGBA32_PIXEL(r, g, b, a) ((a) >= 255 ? RGBA32_SPIXEL(r, g, b, a) : RGBA32_APIXEL(r, g, b, a)) +#define RGBA32_ALPHA(rgba) ((unsigned long)rgba >> 24) + + + /* RGBA16 colour macros */ +#define RGBA16_APIXEL(r, g, b, a) (((r) >> 4 << 12) | ((g) >> 4 << 8) | ((b) >> 4 << 4) | ((a) >> 4 << 0)) +#define RGBA16_SPIXEL(r, g, b) (((r) >> 4 << 12) | ((g) >> 4 << 8) | ((b) >> 4 << 4) | 0x000F) +#define RGBA16_PIXEL(r, g, b, a) RGBA16_APIXEL(r, g, b, a) + + /* RGBA565 colour macros */ +#define RGBA565_TRANSPARENTBITS 0x18DF +#define RGBA565_TRANSPARENTKEY 0xE700 +#define RGBA565_ALPHABITS 0x001C + +#define RGBA565_TRANSPARENT_COLOUR RGBA565_TRANSPARENTKEY + +#define RGBA565_APIXEL(r, g, b, a) (((r) >> 6 << 11) | ((g) >> 6 << 6) | (((a)+16) >> 5 << 2) | ((b) >> 6) | RGBA565_TRANSPARENTKEY) +#define RGBA565_SPIXEL(r, g, b) (((r) >> 3 << 11) | ((g) >> 2 << 5) | ((b) >> 3) | (((r) & (g) & 0xE0) == 0xE0 ? 0x20 : 0x00)) +#define RGBA565_PIXEL(r, g, b, a) ((a) >= 0xF0 ? RGBA565_SPIXEL(r, g, b) : RGBA565_APIXEL(r, g, b, a)) + +#define RGBA565_FROM_RGB565(rgb) ((((rgb) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? (rgb) ^ 0x0020 : (rgb)) +#define RGBA565_TO_RGB565(rgba) ((((rgba) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? ((rgba) & (RGBA565_TRANSPARENTBITS ^ RGBA565_ALPHABITS)) * 10 : (rgba)) +#define RGBA565_ALPHA(rgba) ((((rgba) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? ((rgba) & RGBA565_ALPHABITS) << 3 : 0x100) + +#endif //#if !defined __SYMBIAN32__ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/helpers/vc_image/vc_image_helper.h b/helpers/vc_image/vc_image_helper.h new file mode 100755 index 0000000..f11d945 --- /dev/null +++ b/helpers/vc_image/vc_image_helper.h @@ -0,0 +1,252 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_IMAGE_HELPER_H +#define VC_IMAGE_HELPER_H + +#include "interface/vctypes/vc_image_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Image buffer object, with image data locked in memory and ready for access. + * + * This data type is fully compatible with \c VC_IMAGE_T for backwards + * compatibility. New code should use this type where the object refers to + * a locked image. + */ +typedef VC_IMAGE_T VC_IMAGE_BUF_T; + +/* Macros to determine which format a vc_image is */ + +typedef struct +{ + unsigned bits_per_pixel : 8, + is_rgb : 1, + is_yuv : 1, + is_raster_order : 1, + is_tformat_order : 1, + has_alpha : 1; +} VC_IMAGE_TYPE_INFO_T; + +#define VC_IMAGE_COMPONENT_ORDER(red_lsb, green_lsb, blue_lsb, alpha_lsb) \ + ( (((red_lsb) & 0x1f) << 0) \ + | (((green_lsb) & 0x1f) << 6) \ + | (((blue_lsb) & 0x1f) << 12) \ + | (((alpha_lsb) & 0x1f) << 18) ) +#define VC_IMAGE_RED_OFFSET(component_order) (((component_order) >> 0) & 0x1f) +#define VC_IMAGE_GREEN_OFFSET(component_order) (((component_order) >> 6) & 0x1f) +#define VC_IMAGE_BLUE_OFFSET(component_order) (((component_order) >> 12) & 0x1f) +#define VC_IMAGE_ALPHA_OFFSET(component_order) (((component_order) >> 18) & 0x1f) + +extern const VC_IMAGE_TYPE_INFO_T vc_image_type_info[VC_IMAGE_MAX + 1]; +extern const unsigned int vc_image_rgb_component_order[VC_IMAGE_MAX + 1]; + +#define VC_IMAGE_IS_YUV(type) (vc_image_type_info[type].is_yuv) +#define VC_IMAGE_IS_RGB(type) (vc_image_type_info[type].is_rgb) +#define VC_IMAGE_IS_RASTER(type) (vc_image_type_info[type].is_raster_order) +#define VC_IMAGE_IS_TFORMAT(type) (vc_image_type_info[type].is_tformat_order) +#define VC_IMAGE_BITS_PER_PIXEL(type) (vc_image_type_info[type].bits_per_pixel) +#define VC_IMAGE_HAS_ALPHA(type) (vc_image_type_info[type].has_alpha) + +#define case_VC_IMAGE_ANY_YUV \ + case VC_IMAGE_YUV420: \ + case VC_IMAGE_YUV420SP: \ + case VC_IMAGE_YUV422: \ + case VC_IMAGE_YUV_UV: \ + case VC_IMAGE_YUV_UV32: \ + case VC_IMAGE_YUV422PLANAR: \ + case VC_IMAGE_YUV444PLANAR: \ + case VC_IMAGE_YUV420_16: \ + case VC_IMAGE_YUV_UV_16 + +#define case_VC_IMAGE_ANY_RGB \ + case VC_IMAGE_RGB565: \ + case VC_IMAGE_RGB2X9: \ + case VC_IMAGE_RGB666: \ + case VC_IMAGE_RGBA32: \ + case VC_IMAGE_RGBX32: \ + case VC_IMAGE_RGBA16: \ + case VC_IMAGE_RGBA565: \ + case VC_IMAGE_RGB888: \ + case VC_IMAGE_TF_RGBA32: \ + case VC_IMAGE_TF_RGBX32: \ + case VC_IMAGE_TF_RGBA16: \ + case VC_IMAGE_TF_RGBA5551: \ + case VC_IMAGE_TF_RGB565: \ + case VC_IMAGE_BGR888: \ + case VC_IMAGE_BGR888_NP: \ + case VC_IMAGE_ARGB8888: \ + case VC_IMAGE_XRGB8888 + +#define case_VC_IMAGE_ANY_RGB_NOT_TF \ + case VC_IMAGE_RGB565: \ + case VC_IMAGE_RGB2X9: \ + case VC_IMAGE_RGB666: \ + case VC_IMAGE_RGBA32: \ + case VC_IMAGE_RGBX32: \ + case VC_IMAGE_RGBA16: \ + case VC_IMAGE_RGBA565: \ + case VC_IMAGE_RGB888: \ + case VC_IMAGE_BGR888: \ + case VC_IMAGE_BGR888_NP: \ + case VC_IMAGE_ARGB8888: \ + case VC_IMAGE_XRGB8888: \ + case VC_IMAGE_RGBX8888: \ + case VC_IMAGE_BGRX8888 + +#define case_VC_IMAGE_ANY_TFORMAT \ + case VC_IMAGE_TF_RGBA32: \ + case VC_IMAGE_TF_RGBX32: \ + case VC_IMAGE_TF_FLOAT: \ + case VC_IMAGE_TF_RGBA16: \ + case VC_IMAGE_TF_RGBA5551: \ + case VC_IMAGE_TF_RGB565: \ + case VC_IMAGE_TF_YA88: \ + case VC_IMAGE_TF_BYTE: \ + case VC_IMAGE_TF_PAL8: \ + case VC_IMAGE_TF_PAL4: \ + case VC_IMAGE_TF_ETC1: \ + case VC_IMAGE_TF_Y8: \ + case VC_IMAGE_TF_A8: \ + case VC_IMAGE_TF_SHORT: \ + case VC_IMAGE_TF_1BPP + +/****************************************************************************** +General functions. +******************************************************************************/ + +int vc_image_bits_per_pixel(VC_IMAGE_TYPE_T type); + +int calculate_pitch(VC_IMAGE_TYPE_T type, int width, int height, uint8_t num_channels, VC_IMAGE_INFO_T *info, VC_IMAGE_EXTRA_T *extra); + +/* Check if an image will use an alternate memory layout, in order to cope with + * codec limitation. Applies to YUV_UV images taller than 1344 lines. */ +int vc_image_is_tall_yuv_uv(VC_IMAGE_TYPE_T type, int height); + +/****************************************************************************** +Data member access. +******************************************************************************/ + +/* Set the type of the VC_IMAGE_T. */ +void vc_image_set_type(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type); + +/* Set the image_data field, noting how big it is. */ +void vc_image_set_image_data(VC_IMAGE_BUF_T *image, int size, void *image_data); + +/* Set the image data with added u and v pointers */ +void vc_image_set_image_data_yuv(VC_IMAGE_BUF_T *image, int size, void *image_y, void *image_u, void *image_v); + +/* Set the dimensions of the image. */ +void vc_image_set_dimensions(VC_IMAGE_T *image, int width, int height); + +/* Check the integrity of a VC_IMAGE_T structure - checks structure values + data ptr */ +int vc_image_verify(const VC_IMAGE_T *image); + +/* Set the pitch (internal_width) of the image. */ +void vc_image_set_pitch(VC_IMAGE_T *image, int pitch); + +/* Specify the vertical pitch for YUV planar images */ +void vc_image_set_vpitch(VC_IMAGE_T *image, int vpitch); + +/* Specify that the YUV image is V/U interleaved. */ +void vc_image_set_is_vu(VC_IMAGE_T *image); + +/* Return 1 if the YUV image is V/U interleaved, else return 0. */ +int vc_image_get_is_vu(const VC_IMAGE_T *image); +int vc_image_info_get_is_vu(const VC_IMAGE_INFO_T *info); + +/* Reset the shape of an image */ +int vc_image_reshape(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, int width, int height); + +/* Return the space required (in bytes) for an image of this type and dimensions. */ +int vc_image_required_size(VC_IMAGE_T *image); + +/* Return the space required (in bytes) for an image of this type's palette. */ +int vc_image_palette_size (VC_IMAGE_T *image); + +/* Return 1 if image is high-definition, else return 0. */ +int vc_image_is_high_definition(const VC_IMAGE_T *image); + +/* Return true if palette is 32bpp */ +int vc_image_palette_is_32bit(VC_IMAGE_T *image); + +/* Retrieve Y, U and V pointers from a YUV image. Note that these are macros. */ + +#define vc_image_get_y(p) ((unsigned char *)((p)->image_data)) + +// replaced with functions to allow assert - revert to #define when fixed +//#define vc_image_get_u(p) ((unsigned char *)((p)->extra.uv.u)) +//#define vc_image_get_v(p) ((unsigned char *)((p)->extra.uv.v)) +unsigned char *vc_image_get_u(const VC_IMAGE_BUF_T *image); +unsigned char *vc_image_get_v(const VC_IMAGE_BUF_T *image); + +/* Calculate the address of the given pixel coordinate -- the result may point + * to a word containing data for several pixels (ie., for sub-8bpp and + * compressed formats). + */ +void *vc_image_pixel_addr(VC_IMAGE_BUF_T *image, int x, int y); +void *vc_image_pixel_addr_mm(VC_IMAGE_BUF_T *image, int x, int y, int miplevel); +void *vc_image_pixel_addr_u(VC_IMAGE_BUF_T *image, int x, int y); +void *vc_image_pixel_addr_v(VC_IMAGE_BUF_T *image, int x, int y); + +/* As above, but with (0,0) in the bottom-left corner */ +void *vc_image_pixel_addr_gl(VC_IMAGE_BUF_T *image, int x, int y, int miplevel); + +#define vc_image_get_y_422(p) vc_image_get_y(p) +#define vc_image_get_u_422(p) vc_image_get_u(p) +#define vc_image_get_v_422(p) vc_image_get_v(p) + +#define vc_image_get_y_422planar(p) vc_image_get_y(p) +#define vc_image_get_u_422planar(p) vc_image_get_u(p) +#define vc_image_get_v_422planar(p) vc_image_get_v(p) + +/* Mipmap-related functions. Image must be t-format. */ + +/* Return the pitch of the selected mipmap */ +unsigned int vc_image_get_mipmap_pitch(VC_IMAGE_T *image, int miplvl); + +/* Return the padded height of the selected mipmap (mipmaps must be padded to a + * power of 2) */ +unsigned int vc_image_get_mipmap_padded_height(VC_IMAGE_T *image, int miplvl); + +/* Return the offset, in bytes, of the selected mipmap. */ +int vc_image_get_mipmap_offset(VC_IMAGE_T *image, int miplvl); + +/* Return whether the selected mipmap is stored in t-format or linear microtile + * format. */ +#define VC_IMAGE_MIPMAP_TFORMAT 0 +#define VC_IMAGE_MIPMAP_LINEAR_TILE 1 +int vc_image_get_mipmap_type(VC_IMAGE_T const *image, int miplvl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/helpers/vc_image/vc_image_metadata.h b/helpers/vc_image/vc_image_metadata.h new file mode 100755 index 0000000..9521e49 --- /dev/null +++ b/helpers/vc_image/vc_image_metadata.h @@ -0,0 +1,99 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_IMAGE_METADATA_H +#define VC_IMAGE_METADATA_H + +#include "vcfw/rtos/rtos.h" +#include "helpers/vc_image/metadata_fourcc.h" + +#define VC_METADATA_ALIGN 3 + +/****************************************************************************** +Types of metadata. +******************************************************************************/ +typedef unsigned int VC_METADATA_TYPE_T; + +/****************************************************************************** +Metadata and item headers. +******************************************************************************/ +typedef struct vc_metadata_item_s { + VC_METADATA_TYPE_T type; + int len; +} VC_METADATA_ITEM_T; + + +typedef struct vc_metadata_header_s { + int size; +#ifdef VCMODS_LCC + unsigned char readonly; +#else + unsigned char readonly:1; +#endif + int offset_next; + RTOS_LATCH_T latch; +} VC_METADATA_HEADER_T; + + +/****************************************************************************** +Public declarations. +******************************************************************************/ +// Initialises a metadata header structure +int vc_metadata_initialise(VC_METADATA_HEADER_T *header, int size); +// Add a metadata entry to the buffer +int vc_metadata_add(VC_METADATA_HEADER_T *header, void *data, VC_METADATA_TYPE_T type, int len); +// Get a (pointer to a) particular metadata item from the buffer (optionally copy the data to dest), and return the length of the item +void *vc_metadata_get_with_length(void *dest, VC_METADATA_HEADER_T *header, VC_METADATA_TYPE_T type, int *retcode, int *len); +// Get a (pointer to a) particular metadata item from the buffer (optionally copy the data to dest) +void *vc_metadata_get(void *dest, VC_METADATA_HEADER_T *header, VC_METADATA_TYPE_T type, int *retcode); +// Clear the metadata buffer and reset the header (clears readonly flag) +int vc_metadata_clear(VC_METADATA_HEADER_T *header); +// Set or clear the read-only flag in the metadata buffer +int vc_metadata_set_readonly(VC_METADATA_HEADER_T *header, int value); +// Copies the contents of one metadata buffer to the other (appends the destination buffer) +int vc_metadata_copy(VC_METADATA_HEADER_T *dest, VC_METADATA_HEADER_T *src); +// Overwrites an item in the metadata buffer (caller must make sure the sizes match!) +int vc_metadata_overwrite(VC_METADATA_HEADER_T *header, void *data, VC_METADATA_TYPE_T type); +// Flush the metadata buffer out of the cache +int vc_metadata_cache_flush(VC_METADATA_HEADER_T *header); +// Locks the vc_metadata library semaphore +int vc_metadata_lock(VC_METADATA_HEADER_T *header); +// Unlocks the vc_metadata library semaphore +int vc_metadata_unlock(VC_METADATA_HEADER_T *header); + +/****************************************************************************** +Wrappers for the above functions using VC_IMAGEs. +******************************************************************************/ +#define vc_image_metadata_initialise(i, s) vc_metadata_initialise((i)->metadata, s) +#define vc_image_metadata_add(i, d, t, l) vc_metadata_add((i)->metadata, d, t, l) +#define vc_image_metadata_get(d, i, t, r) vc_metadata_get(d, (i)->metadata, t, r) +#define vc_image_metadata_clear(i) vc_metadata_clear((i)->metadata) +#define vc_image_metadata_set_readonly(i, v) vc_metadata_set_readonly((i)->metadata, v) +#define vc_image_metadata_copy(d, s) vc_metadata_copy((d)->metadata, (s)->metadata) +#define vc_image_metadata_overwrite(i, d, t) vc_metadata_overwrite((i)->metadata, d, t) +#define vc_image_metadata_cache_flush(i) vc_metadata_cache_flush((i)->metadata) +#endif diff --git a/host_applications/android/apps/vidtex/CMakeLists.txt b/host_applications/android/apps/vidtex/CMakeLists.txt new file mode 100755 index 0000000..1f705ef --- /dev/null +++ b/host_applications/android/apps/vidtex/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8) + +SET(COMPILE_DEFINITIONS -Werror -Wall) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) + +set (VIDTEX_SOURCES + main.cpp + launcher_rpi.c + svp.c + vidtex.c) +add_executable(vidtex ${VIDTEX_SOURCES}) +target_link_libraries(vidtex GLESv2 EGL m bcm_host mmal_core mmal_components mmal_util mmal_vc_client vcos) diff --git a/host_applications/android/apps/vidtex/applog.h b/host_applications/android/apps/vidtex/applog.h new file mode 100755 index 0000000..94cce06 --- /dev/null +++ b/host_applications/android/apps/vidtex/applog.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef APPLOG_H +#define APPLOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file Application support for VCOS logging. + * This header file is used to abstract the logging for files that are designed to be included + * in different applications. It should be included before any other VMCS header files. + */ + +#define VCOS_LOG_CATEGORY (&app_log_category) +#include "interface/vcos/vcos.h" +extern VCOS_LOG_CAT_T app_log_category; + +#ifdef __cplusplus +} +#endif + +#endif /* APPLOG_H */ diff --git a/host_applications/android/apps/vidtex/launcher.h b/host_applications/android/apps/vidtex/launcher.h new file mode 100755 index 0000000..7169fa6 --- /dev/null +++ b/host_applications/android/apps/vidtex/launcher.h @@ -0,0 +1,87 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef LAUNCHER_H +#define LAUNCHER_H + +#include +#include +#include +#include +#include + +/** + * Android-specific launcher for native graphical application. + * See runApp(). + */ +class Launcher : public android::Thread +{ +public: + /** Entry point function for application-specific code. + * @param params Application-specific parameters. + * @param win Native window/surface. + * @return 0 on success; -1 on failure. + */ + typedef int (*RUN_APP_FN_T)(const void *params, EGLNativeWindowType win); + + /** Run a function in a separate thread. + * A separate thread is launched to run the function, and a native surface created for + * that application. + * @param name Application name. + * @param run_app_fn Entry point function for application. + * @param params Application-specific parameters. This will be bitwise copyable, and + * between C and C++, so don't use pointers, bool or anything else liable + * for problems. + * @param param_size Size, in bytes, of the application parameters. + * @return 0 on success; -1 on failure. + */ + static int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, + size_t param_size); + + Launcher(const char *name, RUN_APP_FN_T run_fn, const void *params, size_t param_size); + virtual ~Launcher(); + + android::sp session() const; + +protected: + virtual android::status_t readyToRun(); + virtual bool threadLoop(); + +private: + virtual void onFirstRef(); + + android::sp m_session; + android::sp m_sf_control; + android::sp m_sf_surface; + + android::String8 m_name; + RUN_APP_FN_T m_run_fn; + android::Vector m_params; + int m_result; +}; + +#endif diff --git a/host_applications/android/apps/vidtex/launcher_rpi.c b/host_applications/android/apps/vidtex/launcher_rpi.c new file mode 100755 index 0000000..f349836 --- /dev/null +++ b/host_applications/android/apps/vidtex/launcher_rpi.c @@ -0,0 +1,87 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "launcher_rpi.h" + +#include +#include +#include +#include "bcm_host.h" + +static int create_native_window(EGL_DISPMANX_WINDOW_T *native_win) +{ + int32_t status = 0; + DISPMANX_DISPLAY_HANDLE_T disp; + DISPMANX_ELEMENT_HANDLE_T elem; + DISPMANX_UPDATE_HANDLE_T update; + VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; + VC_RECT_T src_rect = {0}; + VC_RECT_T dest_rect = {0}; + uint32_t display_width, display_height; + uint32_t disp_num = 0; // Primary + uint32_t layer_num = 0; + + status = graphics_get_display_size(0, &display_width, &display_height); + if (status != 0) + return status; + + /* Fullscreen */ + display_width = 1280; + display_height = 720; + dest_rect.width = display_width; + dest_rect.height = display_height; + src_rect.width = display_width << 16; + src_rect.height = display_height << 16; + + disp = vc_dispmanx_display_open(disp_num); + update = vc_dispmanx_update_start(0); + elem = vc_dispmanx_element_add(update, disp, layer_num, &dest_rect, 0, + &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, DISPMANX_NO_ROTATE); + + native_win->element = elem; + native_win->width = display_width; + native_win->height = display_height; + vc_dispmanx_update_submit_sync(update); + + return 0; +} + +int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size) +{ + EGL_DISPMANX_WINDOW_T win; + (void) param_size; + + + vcos_log_trace("Initialsing BCM HOST"); + bcm_host_init(); + + vcos_log_trace("Starting '%s'", name); + if (create_native_window(&win) != 0) + return -1; + + return run_app_fn(params, (EGLNativeWindowType *) &win); +} + diff --git a/host_applications/android/apps/vidtex/launcher_rpi.h b/host_applications/android/apps/vidtex/launcher_rpi.h new file mode 100755 index 0000000..335fdc4 --- /dev/null +++ b/host_applications/android/apps/vidtex/launcher_rpi.h @@ -0,0 +1,61 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LAUNCHER_RPI_H +#define LAUNCHER_RPI_H + +#include +#include +#include "applog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Entry point function for application-specific code. + * @param params Application-specific parameters. + * @param win Native window/surface. + * @return 0 on success; -1 on failure. + */ +typedef int (*RUN_APP_FN_T)(const void *params, EGLNativeWindowType win); + +/** Create native window and runs the function. + * + * No need to run in a separate thread on Linux / RPI. + * + * @param name Application name. + * @param run_app_fn Entry point function for application. + * @param params Application-specific parameters. + * @param param_size Size, in bytes, of the application parameters. + * @return 0 on success; -1 on failure. + */ +int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/host_applications/android/apps/vidtex/main.cpp b/host_applications/android/apps/vidtex/main.cpp new file mode 100755 index 0000000..701574d --- /dev/null +++ b/host_applications/android/apps/vidtex/main.cpp @@ -0,0 +1,115 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "applog.h" +#include "vidtex.h" + +#ifdef __ANDROID__ +#include "launcher.h" +#else +#include "launcher_rpi.h" +#endif + +VCOS_LOG_CAT_T app_log_category; + +static int launch_vidtex(const void *params, EGLNativeWindowType win) +{ + return vidtex_run((const VIDTEX_PARAMS_T *)params, win); +} + +static void usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s [-d duration-ms] [-i] [uri]\n", argv0); +} + +int main(int argc, char **argv) +{ + // Parse command line options/arguments. + VIDTEX_PARAMS_T params = {0}; + int rc; + int status = 0; + + opterr = 0; + + while ((rc = getopt(argc, argv, "d:iuvy")) != -1) + { + switch (rc) + { + case 'd': + params.duration_ms = atoi(optarg); + break; + + case 'i': + params.opts |= VIDTEX_OPT_IMG_PER_FRAME; + break; + + case 'y': + params.opts |= VIDTEX_OPT_Y_TEXTURE; + break; + + case 'u': + params.opts |= VIDTEX_OPT_U_TEXTURE; + break; + + case 'v': + params.opts |= VIDTEX_OPT_V_TEXTURE; + break; + + default: + usage(argv[0]); + return 2; + } + } + + if (optind < argc - 1) + { + usage(argv[0]); + return 2; + } + + if (optind < argc) + { + strncpy(params.uri, argv[optind], sizeof(params.uri) - 1); + params.uri[sizeof(params.uri) - 1] = '\0'; + } + + // Init vcos logging. + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_INFO); + vcos_log_register("vidtex", VCOS_LOG_CATEGORY); + + // Run video-on-texture application in a separate thread. +#ifdef __ANDROID__ + status = Launcher::runApp("vidtex", launch_vidtex, ¶ms, sizeof(params)); +#else + status = runApp("vidtex", launch_vidtex, ¶ms, sizeof(params)); +#endif + return (status == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/host_applications/android/apps/vidtex/svp.c b/host_applications/android/apps/vidtex/svp.c new file mode 100755 index 0000000..89b576a --- /dev/null +++ b/host_applications/android/apps/vidtex/svp.c @@ -0,0 +1,637 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "applog.h" +#include "interface/vcos/vcos_stdbool.h" +#include "interface/vcos/vcos_inttypes.h" +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/util/mmal_connection.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "svp.h" + +#define CHECK_STATUS(s, m) \ + if ((s) != MMAL_SUCCESS) { \ + LOG_ERROR("%s: %s", (m), mmal_status_to_string((s))); \ + goto error; \ + } + +/* Flags specifying fields of SVP_T struct */ +#define SVP_CREATED_SEM (1 << 0) +#define SVP_CREATED_THREAD (1 << 1) +#define SVP_CREATED_MUTEX (1 << 2) +#define SVP_CREATED_TIMER (1 << 3) +#define SVP_CREATED_WD_TIMER (1 << 4) + +/* Hard-coded camera parameters */ +#if 0 +#define SVP_CAMERA_WIDTH 1920 +#define SVP_CAMERA_HEIGHT 1080 +#else +#define SVP_CAMERA_WIDTH 1280 +#define SVP_CAMERA_HEIGHT 720 +#endif +#define SVP_CAMERA_FRAMERATE 30 +#define SVP_CAMERA_DURATION_MS 10000 + +/* Watchdog timeout - elapsed time to allow for no video frames received */ +#define SVP_WATCHDOG_TIMEOUT_MS 5000 + +/** Simple video player instance */ +struct SVP_T +{ + /* Player options */ + SVP_OPTS_T opts; + + /* Bitmask of SVP_CREATED_XXX values indicating which fields have been created. + * Only used for those fields which can't be (portably) determined from their + * own value. + */ + uint32_t created; + + /* Semaphore used for synchronising buffer handling for decoded frames */ + VCOS_SEMAPHORE_T sema; + + /* User supplied callbacks */ + SVP_CALLBACKS_T callbacks; + + /* Container reader component */ + MMAL_COMPONENT_T *reader; + + /* Video decoder component */ + MMAL_COMPONENT_T *video_decode; + + /* Camera component */ + MMAL_COMPONENT_T *camera; + + /* Connection: container reader -> video decoder */ + MMAL_CONNECTION_T *connection; + + /* Output port from video decoder or camera */ + MMAL_PORT_T *video_output; + + /* Pool of buffers for video decoder output */ + MMAL_POOL_T *out_pool; + + /* Queue to hold decoded video frames */ + MMAL_QUEUE_T *queue; + + /* Worker thread */ + VCOS_THREAD_T thread; + + /* Timer to trigger stop in camera preview case */ + VCOS_TIMER_T timer; + + /* Watchdog timer */ + VCOS_TIMER_T wd_timer; + + /* Mutex to synchronise access to all following fields */ + VCOS_MUTEX_T mutex; + + /* Stop control: 0 to process stream; bitmask of SVP_STOP_XXX values to stop */ + uint32_t stop; + + /* Player stats */ + SVP_STATS_T stats; +}; + +/* Local function prototypes */ +static MMAL_STATUS_T svp_setup_reader(MMAL_COMPONENT_T *reader, const char *uri, + MMAL_PORT_T **video_port); +static void svp_timer_cb(void *ctx); +static void svp_watchdog_cb(void *ctx); +static MMAL_STATUS_T svp_port_enable(SVP_T *svp, MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb); +static void *svp_worker(void *arg); +static void svp_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +static void svp_bh_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +static void svp_reset_stop(SVP_T *svp); +static void svp_set_stop(SVP_T *svp, uint32_t stop_flags); +static uint32_t svp_get_stop(SVP_T *svp); + +/* Create Simple Video Player instance. */ +SVP_T *svp_create(const char *uri, SVP_CALLBACKS_T *callbacks, const SVP_OPTS_T *opts) +{ + SVP_T *svp; + MMAL_STATUS_T st; + VCOS_STATUS_T vst; + MMAL_PORT_T *reader_output = NULL; + MMAL_COMPONENT_T *video_decode = NULL; + MMAL_PORT_T *video_output = NULL; + + LOG_TRACE("Creating player for %s", (uri ? uri : "camera preview")); + + vcos_assert(callbacks->video_frame_cb); + vcos_assert(callbacks->stop_cb); + + svp = vcos_calloc(1, sizeof(*svp), "svp"); + CHECK_STATUS((svp ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to allocate context"); + + svp->opts = *opts; + svp->callbacks = *callbacks; + + /* Semaphore used for synchronising buffer handling for decoded frames */ + vst = vcos_semaphore_create(&svp->sema, "svp-sem", 0); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create semaphore"); + svp->created |= SVP_CREATED_SEM; + + vst = vcos_mutex_create(&svp->mutex, "svp-mutex"); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create mutex"); + svp->created |= SVP_CREATED_MUTEX; + + vst = vcos_timer_create(&svp->timer, "svp-timer", svp_timer_cb, svp); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create timer"); + svp->created |= SVP_CREATED_TIMER; + + vst = vcos_timer_create(&svp->wd_timer, "svp-wd-timer", svp_watchdog_cb, svp); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create timer"); + svp->created |= SVP_CREATED_WD_TIMER; + + /* Create components */ + svp->reader = NULL; + svp->video_decode = NULL; + svp->camera = NULL; + svp->connection = NULL; + + if (uri) + { + /* Video from URI: setup container_reader -> video_decode */ + + /* Create and set up container reader */ + st = mmal_component_create(MMAL_COMPONENT_DEFAULT_CONTAINER_READER, &svp->reader); + CHECK_STATUS(st, "Failed to create container reader"); + + st = svp_setup_reader(svp->reader, uri, &reader_output); + if (st != MMAL_SUCCESS) + goto error; + + st = mmal_component_enable(svp->reader); + CHECK_STATUS(st, "Failed to enable container reader"); + + st = svp_port_enable(svp, svp->reader->control, svp_bh_control_cb); + CHECK_STATUS(st, "Failed to enable container reader control port"); + + /* Create and set up video decoder */ + st = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &svp->video_decode); + CHECK_STATUS(st, "Failed to create video decoder"); + + video_decode = svp->video_decode; + video_output = video_decode->output[0]; + + st = mmal_component_enable(video_decode); + CHECK_STATUS(st, "Failed to enable video decoder"); + + st = svp_port_enable(svp, video_decode->control, svp_bh_control_cb); + CHECK_STATUS(st, "Failed to enable video decoder control port"); + } + else + { + /* Camera preview */ + st = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &svp->camera); + CHECK_STATUS(st, "Failed to create camera"); + + st = mmal_component_enable(svp->camera); + CHECK_STATUS(st, "Failed to enable camera"); + + st = svp_port_enable(svp, svp->camera->control, svp_bh_control_cb); + CHECK_STATUS(st, "Failed to enable camera control port"); + + video_output = svp->camera->output[0]; /* Preview port */ + } + + st = mmal_port_parameter_set_boolean(video_output, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + CHECK_STATUS((st == MMAL_ENOSYS ? MMAL_SUCCESS : st), "Failed to enable zero copy"); + + if (uri) + { + /* Create connection: container_reader -> video_decoder */ + st = mmal_connection_create(&svp->connection, reader_output, video_decode->input[0], + MMAL_CONNECTION_FLAG_TUNNELLING); + CHECK_STATUS(st, "Failed to create connection"); + } + + /* Set video output port format. + * Opaque encoding ensures we get buffer data as handles to relocatable heap. */ + video_output->format->encoding = MMAL_ENCODING_OPAQUE; + + if (!uri) + { + /* Set video format for camera preview */ + MMAL_VIDEO_FORMAT_T *vfmt = &video_output->format->es->video; + + CHECK_STATUS((video_output->format->type == MMAL_ES_TYPE_VIDEO) ? MMAL_SUCCESS : MMAL_EINVAL, + "Output port isn't video format"); + + vfmt->width = SVP_CAMERA_WIDTH; + vfmt->height = SVP_CAMERA_HEIGHT; + vfmt->crop.x = 0; + vfmt->crop.y = 0; + vfmt->crop.width = vfmt->width; + vfmt->crop.height = vfmt->height; + vfmt->frame_rate.num = SVP_CAMERA_FRAMERATE; + vfmt->frame_rate.den = 1; + } + + st = mmal_port_format_commit(video_output); + CHECK_STATUS(st, "Failed to set output port format"); + + /* Finally, set buffer num/size. N.B. For container_reader/video_decode, must be after + * connection created, in order for port format to propagate. + * Don't enable video output port until want to produce frames. */ + video_output->buffer_num = video_output->buffer_num_recommended; + video_output->buffer_size = video_output->buffer_size_recommended; + + /* Pool + queue to hold decoded video frames */ + svp->out_pool = mmal_port_pool_create(video_output, video_output->buffer_num, + video_output->buffer_size); + CHECK_STATUS((svp->out_pool ? MMAL_SUCCESS : MMAL_ENOMEM), "Error allocating pool"); + svp->queue = mmal_queue_create(); + CHECK_STATUS((svp ? MMAL_SUCCESS : MMAL_ENOMEM), "Error allocating queue"); + + svp->video_output = video_output; + + return svp; + +error: + svp_destroy(svp); + return NULL; +} + +/** + * Setup container reader component. + * Sets URI, to initialize processing, and finds a video track. + * @param reader Container reader component. + * @param uri Media URI. + * @param video_port On success, the output port for the first video track is returned here. + * @return MMAL_SUCCESS if the container reader was successfully set up and a video track located. + */ +static MMAL_STATUS_T svp_setup_reader(MMAL_COMPONENT_T *reader, const char *uri, + MMAL_PORT_T **video_port) +{ + MMAL_STATUS_T st; + uint32_t track; + + st = mmal_util_port_set_uri(reader->control, uri); + if (st != MMAL_SUCCESS) + { + LOG_ERROR("%s: couldn't open uri %s", reader->name, uri); + return st; + } + + /* Find a video track */ + for (track = 0; track < reader->output_num; track++) + { + if (reader->output[track]->format->type == MMAL_ES_TYPE_VIDEO) + { + break; + } + } + + if (track == reader->output_num) + { + LOG_ERROR("%s: no video track", uri); + return MMAL_EINVAL; + } + + *video_port = reader->output[track]; + return MMAL_SUCCESS; +} + +/* Destroy SVP instance. svp may be NULL. */ +void svp_destroy(SVP_T *svp) +{ + if (svp) + { + MMAL_COMPONENT_T *components[] = { svp->reader, svp->video_decode, svp->camera }; + MMAL_COMPONENT_T **comp; + + /* Stop thread, disable connection and components */ + svp_stop(svp); + + for (comp = components; comp < components + vcos_countof(components); comp++) + { + mmal_component_disable(*comp); + } + + /* Destroy connection + components */ + if (svp->connection) + { + mmal_connection_destroy(svp->connection); + } + + for (comp = components; comp < components + vcos_countof(components); comp++) + { + mmal_component_destroy(*comp); + } + + /* Free remaining resources */ + if (svp->out_pool) + { + mmal_pool_destroy(svp->out_pool); + } + + if (svp->queue) + { + mmal_queue_destroy(svp->queue); + } + + if (svp->created & SVP_CREATED_WD_TIMER) + { + vcos_timer_delete(&svp->wd_timer); + } + + if (svp->created & SVP_CREATED_TIMER) + { + vcos_timer_delete(&svp->timer); + } + + if (svp->created & SVP_CREATED_MUTEX) + { + vcos_mutex_delete(&svp->mutex); + } + + if (svp->created & SVP_CREATED_SEM) + { + vcos_semaphore_delete(&svp->sema); + } + + vcos_free(svp); + } +} + +/* Start SVP. Enables MMAL connection + creates worker thread. */ +int svp_start(SVP_T *svp) +{ + MMAL_STATUS_T st; + VCOS_STATUS_T vst; + + /* Ensure SVP is stopped first */ + svp_stop(svp); + + /* Reset the worker thread stop status, before enabling ports that might trigger a stop */ + svp_reset_stop(svp); + + if (svp->connection) + { + /* Enable reader->decoder connection */ + st = mmal_connection_enable(svp->connection); + CHECK_STATUS(st, "Failed to create connection"); + } + + /* Enable video output port */ + st = svp_port_enable(svp, svp->video_output, svp_bh_output_cb); + CHECK_STATUS(st, "Failed to enable output port"); + + /* Reset stats */ + svp->stats.video_frame_count = 0; + + /* Create worker thread */ + vst = vcos_thread_create(&svp->thread, "svp-worker", NULL, svp_worker, svp); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create connection"); + + svp->created |= SVP_CREATED_THREAD; + + /* Set timer */ + if (svp->camera) + { + unsigned ms = svp->opts.duration_ms; + vcos_timer_set(&svp->timer, ((ms == 0) ? SVP_CAMERA_DURATION_MS : ms)); + } + + /* Start watchdog timer */ + vcos_timer_set(&svp->wd_timer, SVP_WATCHDOG_TIMEOUT_MS); + + return 0; + +error: + return -1; +} + +/* Stop SVP. Stops worker thread + disables MMAL connection. */ +void svp_stop(SVP_T *svp) +{ + vcos_timer_cancel(&svp->wd_timer); + vcos_timer_cancel(&svp->timer); + + /* Stop worker thread */ + if (svp->created & SVP_CREATED_THREAD) + { + svp_set_stop(svp, SVP_STOP_USER); + vcos_semaphore_post(&svp->sema); + vcos_thread_join(&svp->thread, NULL); + svp->created &= ~SVP_CREATED_THREAD; + } + + if (svp->connection) + { + mmal_connection_disable(svp->connection); + } + + mmal_port_disable(svp->video_output); +} + +/* Get stats since last call to svp_start() */ +void svp_get_stats(SVP_T *svp, SVP_STATS_T *stats) +{ + vcos_mutex_lock(&svp->mutex); + *stats = svp->stats; + vcos_mutex_unlock(&svp->mutex); +} + +/** Timer callback - stops playback */ +static void svp_timer_cb(void *ctx) +{ + SVP_T *svp = ctx; + svp_set_stop(svp, SVP_STOP_TIMEUP); + vcos_semaphore_post(&svp->sema); +} + +/** Watchdog timer callback - stops playback */ +static void svp_watchdog_cb(void *ctx) +{ + SVP_T *svp = ctx; + LOG_ERROR("%s: no frames received for %d ms, aborting", svp->video_output->name, + SVP_WATCHDOG_TIMEOUT_MS); + svp_set_stop(svp, SVP_STOP_ERROR); + vcos_semaphore_post(&svp->sema); +} + +/** Enable MMAL port, setting SVP instance as port userdata. */ +static MMAL_STATUS_T svp_port_enable(SVP_T *svp, MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) +{ + port->userdata = (struct MMAL_PORT_USERDATA_T *)svp; + return mmal_port_enable(port, cb); +} + +/** Process decoded buffers queued by video decoder output callback */ +static void svp_process_returned_bufs(SVP_T *svp) +{ + SVP_CALLBACKS_T *callbacks = &svp->callbacks; + MMAL_BUFFER_HEADER_T *buf; + + while ((buf = mmal_queue_get(svp->queue)) != NULL) + { + if ((svp_get_stop(svp) & SVP_STOP_ERROR) == 0) + { + callbacks->video_frame_cb(callbacks->ctx, buf->data); + } + + svp->stats.video_frame_count++; + mmal_buffer_header_release(buf); + } +} + +/** Worker thread. Ensures video decoder output is supplied with buffers and sends decoded frames + * via user-supplied callback. + * @param arg Pointer to SVP instance. + * @return NULL always. + */ +static void *svp_worker(void *arg) +{ + SVP_T *svp = arg; + MMAL_PORT_T *video_output = svp->video_output; + SVP_CALLBACKS_T *callbacks = &svp->callbacks; + MMAL_BUFFER_HEADER_T *buf; + MMAL_STATUS_T st; + uint32_t stop; + + while (svp_get_stop(svp) == 0) + { + /* Send empty buffers to video decoder output port */ + while ((buf = mmal_queue_get(svp->out_pool->queue)) != NULL) + { + st = mmal_port_send_buffer(video_output, buf); + if (st != MMAL_SUCCESS) + { + LOG_ERROR("Failed to send buffer to %s", video_output->name); + } + } + + /* Process returned buffers */ + svp_process_returned_bufs(svp); + + /* Block for buffer release */ + vcos_semaphore_wait(&svp->sema); + } + + /* Might have the last few buffers queued */ + svp_process_returned_bufs(svp); + + /* Notify caller if we stopped unexpectedly */ + stop = svp_get_stop(svp); + LOG_TRACE("Worker thread exiting: stop=0x%x", (unsigned)stop); + callbacks->stop_cb(callbacks->ctx, stop); + + return NULL; +} + +/** Callback from a control port. */ +static void svp_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + SVP_T *svp = (SVP_T *)port->userdata; + + switch (buf->cmd) + { + case MMAL_EVENT_EOS: + LOG_TRACE("%s: EOS", port->name); + svp_set_stop(svp, SVP_STOP_EOS); + break; + + case MMAL_EVENT_ERROR: + LOG_ERROR("%s: MMAL error: %s", port->name, + mmal_status_to_string(*(MMAL_STATUS_T *)buf->data)); + svp_set_stop(svp, SVP_STOP_ERROR); + break; + + default: + LOG_TRACE("%s: buf %p, event %4.4s", port->name, buf, (char *)&buf->cmd); + break; + } + + mmal_buffer_header_release(buf); + + vcos_semaphore_post(&svp->sema); +} + +/** Callback from video decode output port. */ +static void svp_bh_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + SVP_T *svp = (SVP_T *)port->userdata; + + if (buf->length == 0) + { + LOG_TRACE("%s: zero-length buffer => EOS", port->name); + svp_set_stop(svp, SVP_STOP_EOS); // This shouldn't be necessary, but it is ... + mmal_buffer_header_release(buf); + } + else if (buf->data == NULL) + { + LOG_ERROR("%s: zero buffer handle", port->name); + mmal_buffer_header_release(buf); + } + else + { + /* Reset watchdog timer */ + vcos_timer_set(&svp->wd_timer, SVP_WATCHDOG_TIMEOUT_MS); + + /* Enqueue the decoded frame so we can return quickly to MMAL core */ + mmal_queue_put(svp->queue, buf); + } + + /* Notify worker */ + vcos_semaphore_post(&svp->sema); +} + +/** Reset svp->stop to 0, with locking. */ +static void svp_reset_stop(SVP_T *svp) +{ + vcos_mutex_lock(&svp->mutex); + svp->stop = 0; + vcos_mutex_unlock(&svp->mutex); +} + +/** Set additional flags in svp->stop, with locking. */ +static void svp_set_stop(SVP_T *svp, uint32_t stop_flags) +{ + vcos_mutex_lock(&svp->mutex); + svp->stop |= stop_flags; + vcos_mutex_unlock(&svp->mutex); +} + +/** Get value of svp->stop, with locking. */ +static uint32_t svp_get_stop(SVP_T *svp) +{ + uint32_t stop; + + vcos_mutex_lock(&svp->mutex); + stop = svp->stop; + vcos_mutex_unlock(&svp->mutex); + + return stop; +} diff --git a/host_applications/android/apps/vidtex/svp.h b/host_applications/android/apps/vidtex/svp.h new file mode 100755 index 0000000..80c2151 --- /dev/null +++ b/host_applications/android/apps/vidtex/svp.h @@ -0,0 +1,146 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SVP_H +#define SVP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + * Simple video player using MMAL. + * Uses MMAL container reader plus video decode component, and provides callback to retrieve + * buffers of decoded video frames. + * + * Thread-safety: The public API functions must be called from the same thread for a given SVP_T + * instance. The user is notified of decoded frames and other events from a separate + * thread, started by svp_start(). + */ + +/** Flags indicating reason for processing stop */ +/** Stop on to user request */ +#define SVP_STOP_USER (1 << 0) +/** Stop on end-of-stream */ +#define SVP_STOP_EOS (1 << 1) +/** Stop on error */ +#define SVP_STOP_ERROR (1 << 2) +/** Stop on timer */ +#define SVP_STOP_TIMEUP (1 << 3) + +/** + * Callback functions and context for player to communicate asynchronous data + * and events to user. + */ +typedef struct SVP_CALLBACKS_T +{ + /** Private pointer specified by caller, passed to callback functions. + * Not examined by svp_ functions, so may have any value, including NULL. + */ + void *ctx; + + /** Callback for decoded video frames. + * The buffer is released when this function returns, so the callback must either have processed + * the buffer or acquired a reference before returning. + * @param ctx Caller's private context, as specified by SVP_CALLBACKS_T::ctx. + * @param ob MMAL opaque buffer handle. + */ + void (*video_frame_cb)(void *ctx, void *buf); + + /** Callback for end of processing. + * @param ctx Caller's private context, as specified by SVP_CALLBACKS_T::ctx. + * @param reason Bitmask of SVP_STOP_XXX values giving reason for stopping. + */ + void (*stop_cb)(void *ctx, uint32_t stop_reason); +} SVP_CALLBACKS_T; + +/** Options to SVP playback */ +typedef struct SVP_OPTS_T +{ + /** Duration of playback, in milliseconds. 0 = default, which is full duration for media and + * a limited duration for camera. + */ + unsigned duration_ms; +} SVP_OPTS_T; + +/** Playback stats */ +typedef struct SVP_STATS_T +{ + /** Total number of video frames processed since the last call to svp_start(). + * 0 if svp_start() has never been called. */ + unsigned video_frame_count; +} SVP_STATS_T; + +/** Simple Video Player instance. Opaque structure. */ +typedef struct SVP_T SVP_T; + +/** + * Create a simple video player instance. + * @param uri URI to media, or NULL to use camera preview. + * @param callbacks Callbacks for caller to receive notification of events, + * such as decoded buffers. + * @param opts Player options. + * @return Newly created simple video player instance, or NULL on error. + */ +SVP_T *svp_create(const char *uri, SVP_CALLBACKS_T *callbacks, const SVP_OPTS_T *opts); + +/** + * Destroy a simple video player instance. + * @param svp Simple video player instance. May be NULL, in which case this + * function does nothing. + */ +void svp_destroy(SVP_T *svp); + +/** + * Start a simple video player instance. + * Decoded frames are returned to the SVP_CALLBACKS_T::video_frame_cb function + * passed to svp_create(). + * @param svp Simple video player instance. + * @return 0 on success; -1 on failure. + */ +int svp_start(SVP_T *svp); + +/** + * Stop a simple video player instance. + * If the player is not running, then this function does nothing. + * @param svp Simple video player instance. + */ +void svp_stop(SVP_T *svp); + +/** + * Get player stats. + * @param svp Simple video player instance. + * @param stats Buffer in which stats are returned. + */ +void svp_get_stats(SVP_T *svp, SVP_STATS_T *stats); + + +#ifdef __cplusplus +} +#endif + +#endif /* SVP_H */ diff --git a/host_applications/android/apps/vidtex/vidtex.c b/host_applications/android/apps/vidtex/vidtex.c new file mode 100755 index 0000000..c72a78c --- /dev/null +++ b/host_applications/android/apps/vidtex/vidtex.c @@ -0,0 +1,555 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include "applog.h" +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_stdbool.h" +#include "interface/khronos/include/EGL/eglext_brcm.h" +#include "vidtex.h" + +/** Max number of simultaneous EGL images supported = max number of distinct video decoder + * buffers. + */ +#define VT_MAX_IMAGES 32 + +/** Maximum permitted difference between the number of EGL buffer swaps and number of video + * frames. + */ +#define VT_MAX_FRAME_DISPARITY 2 + +/** Mapping of MMAL opaque buffer handle to EGL image */ +typedef struct VIDTEX_IMAGE_SLOT_T +{ + /* Decoded video frame, as MMAL opaque buffer handle. NULL => unused slot. */ + void *video_frame; + + /* Corresponding EGL image */ + EGLImageKHR image; +} VIDTEX_IMAGE_SLOT_T; + +/** + * Video Texture. Displays video from a URI or camera onto an EGL surface. + */ +typedef struct VIDTEX_T +{ + /** Test options; bitmask of VIDTEX_OPT_XXX values */ + uint32_t opts; + + /* Semaphore to synchronise use of decoded frame (video_frame). */ + VCOS_SEMAPHORE_T sem_decoded; + + /* Semaphore to synchronise drawing of video frame. */ + VCOS_SEMAPHORE_T sem_drawn; + + /* Mutex to guard access to quit field. */ + VCOS_MUTEX_T mutex; + + /* Signal thread to quit. */ + bool quit; + + /* Reason for quitting */ + uint32_t stop_reason; + + /* EGL display/surface/context on which to render the video */ + EGLDisplay display; + EGLSurface surface; + EGLContext context; + + /* EGL texture name */ + GLuint texture; + + /* Table of EGL images corresponding to MMAL opaque buffer handles */ + VIDTEX_IMAGE_SLOT_T slots[VT_MAX_IMAGES]; + + /* Current decoded video frame, as MMAL opaque buffer handle. + * NULL if no buffer currently available. */ + void *video_frame; + + /* Number of EGL buffer swaps */ + unsigned num_swaps; +} VIDTEX_T; + +/* Vertex co-ordinates: + * + * v0----v1 + * | | + * | | + * | | + * v3----v2 + */ + +static const GLfloat vt_vertices[] = +{ +#define VT_V0 -0.8, 0.8, 0.8, +#define VT_V1 0.8, 0.8, 0.8, +#define VT_V2 0.8, -0.8, 0.8, +#define VT_V3 -0.8, -0.8, 0.8, + VT_V0 VT_V3 VT_V2 VT_V2 VT_V1 VT_V0 +}; + +/* Texture co-ordinates: + * + * (0,0) b--c + * | | + * a--d + * + * b,a,d d,c,b + */ +static const GLfloat vt_tex_coords[] = +{ + 0, 0, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0 +}; + +/* Local function prototypes */ +static VIDTEX_T *vidtex_create(EGLNativeWindowType win); +static void vidtex_destroy(VIDTEX_T *vt); +static int vidtex_gl_init(VIDTEX_T *vt, EGLNativeWindowType win); +static void vidtex_gl_term(VIDTEX_T *vt); +static void vidtex_destroy_images(VIDTEX_T *vt); +static int vidtex_play(VIDTEX_T *vt, const VIDTEX_PARAMS_T *params); +static void vidtex_check_gl(VIDTEX_T *vt, uint32_t line); +static void vidtex_draw(VIDTEX_T *vt, void *video_frame); +static void vidtex_flush_gl(VIDTEX_T *vt); +static bool vidtex_set_quit(VIDTEX_T *vt, bool quit); +static void vidtex_video_frame_cb(void *ctx, void *ob); +static void vidtex_stop_cb(void *ctx, uint32_t stop_reason); + +#define VIDTEX_CHECK_GL(VT) vidtex_check_gl(VT, __LINE__) + +/** Create a new vidtex instance */ +static VIDTEX_T *vidtex_create(EGLNativeWindowType win) +{ + VIDTEX_T *vt; + VCOS_STATUS_T st; + + vt = vcos_calloc(1, sizeof(*vt), "vidtex"); + if (vt == NULL) + { + vcos_log_trace("Memory allocation failure"); + return NULL; + } + + st = vcos_semaphore_create(&vt->sem_decoded, "vidtex-dec", 0); + if (st != VCOS_SUCCESS) + { + vcos_log_trace("Error creating semaphore"); + goto error_ctx; + } + + st = vcos_semaphore_create(&vt->sem_drawn, "vidtex-drw", 0); + if (st != VCOS_SUCCESS) + { + vcos_log_trace("Error creating semaphore"); + goto error_sem1; + } + + st = vcos_mutex_create(&vt->mutex, "vidtex"); + if (st != VCOS_SUCCESS) + { + vcos_log_trace("Error creating semaphore"); + goto error_sem2; + } + + if (vidtex_gl_init(vt, win) != 0) + { + vcos_log_trace("Error initialising EGL"); + goto error_mutex; + } + + vt->quit = false; + vt->stop_reason = 0; + + return vt; + +error_mutex: + vcos_mutex_delete(&vt->mutex); +error_sem2: + vcos_semaphore_delete(&vt->sem_drawn); +error_sem1: + vcos_semaphore_delete(&vt->sem_decoded); +error_ctx: + vcos_free(vt); + return NULL; +} + +/** Destroy a vidtex instance */ +static void vidtex_destroy(VIDTEX_T *vt) +{ + vidtex_gl_term(vt); + vcos_mutex_delete(&vt->mutex); + vcos_semaphore_delete(&vt->sem_drawn); + vcos_semaphore_delete(&vt->sem_decoded); + vcos_free(vt); +} + +/** Init GL using a native window */ +static int vidtex_gl_init(VIDTEX_T *vt, EGLNativeWindowType win) +{ + const EGLint attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_NONE + }; + EGLConfig config; + EGLint num_configs; + + vt->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(vt->display, 0, 0); + + eglChooseConfig(vt->display, attribs, &config, 1, &num_configs); + + vt->surface = eglCreateWindowSurface(vt->display, config, win, NULL); + vt->context = eglCreateContext(vt->display, config, NULL, NULL); + + if (!eglMakeCurrent(vt->display, vt->surface, vt->surface, vt->context)) + { + vidtex_gl_term(vt); + return -1; + } + + glGenTextures(1, &vt->texture); + + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glEnable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); + + return 0; +} + +/** Terminate GL */ +static void vidtex_gl_term(VIDTEX_T *vt) +{ + vidtex_destroy_images(vt); + + /* Delete texture name */ + glDeleteTextures(1, &vt->texture); + + /* Terminate EGL */ + eglMakeCurrent(vt->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(vt->display, vt->context); + eglDestroySurface(vt->display, vt->surface); + eglTerminate(vt->display); +} + +/** Destroy all EGL images */ +static void vidtex_destroy_images(VIDTEX_T *vt) +{ + VIDTEX_IMAGE_SLOT_T *slot; + + for (slot = vt->slots; slot < vt->slots + vcos_countof(vt->slots); slot++) + { + slot->video_frame = NULL; + + if (slot->image) + { + vcos_log_trace("Destroying EGL image %p", slot->image); + eglDestroyImageKHR(vt->display, slot->image); + slot->image = NULL; + } + } +} + +/** Play video - from URI or camera - on EGL surface. */ +static int vidtex_play(VIDTEX_T *vt, const VIDTEX_PARAMS_T *params) +{ + const char *uri; + SVP_CALLBACKS_T callbacks; + SVP_T *svp; + SVP_OPTS_T opts; + SVP_STATS_T stats; + int rv = -1; + + uri = (params->uri[0] == '\0') ? NULL : params->uri; + vt->opts = params->opts; + callbacks.ctx = vt; + callbacks.video_frame_cb = vidtex_video_frame_cb; + callbacks.stop_cb = vidtex_stop_cb; + opts.duration_ms = params->duration_ms; + + svp = svp_create(uri, &callbacks, &opts); + if (svp) + { + /* Reset stats */ + vt->num_swaps = 0; + + /* Run video player until receive quit notification */ + if (svp_start(svp) == 0) + { + while (!vidtex_set_quit(vt, false)) + { + vcos_semaphore_wait(&vt->sem_decoded); + + if (vt->video_frame) + { + vidtex_draw(vt, vt->video_frame); + vcos_semaphore_post(&vt->sem_drawn); + } + } + + vcos_semaphore_post(&vt->sem_drawn); + + /* Dump stats */ + svp_get_stats(svp, &stats); + vcos_log_info("video frames decoded: %6u", stats.video_frame_count); + vcos_log_info("EGL buffer swaps: %6u", vt->num_swaps); + + /* Determine status of operation and log errors */ + if (vt->stop_reason & SVP_STOP_ERROR) + { + vcos_log_error("vidtex exiting on error"); + } + else if (vt->num_swaps == 0) + { + vcos_log_error("vidtex completed with no EGL buffer swaps"); + } + else if (abs((int)vt->num_swaps - (int)stats.video_frame_count) > VT_MAX_FRAME_DISPARITY) + { + vcos_log_error("vidtex completed with %u EGL buffer swaps, but %u video frames", + vt->num_swaps, (int)stats.video_frame_count); + } + else + { + rv = 0; + } + } + + svp_destroy(svp); + } + + vidtex_flush_gl(vt); + + return rv; +} + +/** Check for OpenGL errors - logs any errors and sets quit flag */ +static void vidtex_check_gl(VIDTEX_T *vt, uint32_t line) +{ + GLenum error = glGetError(); + int abort = 0; + while (error != GL_NO_ERROR) + { + vcos_log_error("GL error: line %d error 0x%04x", line, error); + abort = 1; + error = glGetError(); + } + if (abort) + vidtex_stop_cb(vt, SVP_STOP_ERROR); +} + +/* Draw one video frame onto EGL surface. + * @param vt vidtex instance. + * @param video_frame MMAL opaque buffer handle for decoded video frame. Can't be NULL. + */ +static void vidtex_draw(VIDTEX_T *vt, void *video_frame) +{ + EGLImageKHR image; + VIDTEX_IMAGE_SLOT_T *slot; + static uint32_t frame_num = 0; + + vcos_assert(video_frame); + + glClearColor(0, 0, 0, 0); + glClearDepthf(1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, vt->texture); + VIDTEX_CHECK_GL(vt); + + /* Lookup or create EGL image corresponding to supplied buffer handle. + * N.B. Slot array is filled in sequentially, with the images all destroyed together on + * vidtex termination; it never has holes. */ + image = EGL_NO_IMAGE_KHR; + + for (slot = vt->slots; slot < vt->slots + vcos_countof(vt->slots); slot++) + { + if (slot->video_frame == video_frame) + { + vcos_assert(slot->image); + image = slot->image; + break; + } + + if (slot->video_frame == NULL) + { + EGLenum target; + vcos_assert(slot->image == NULL); + + if (vt->opts & VIDTEX_OPT_Y_TEXTURE) + target = EGL_IMAGE_BRCM_MULTIMEDIA_Y; + else if (vt->opts & VIDTEX_OPT_U_TEXTURE) + target = EGL_IMAGE_BRCM_MULTIMEDIA_U; + else if (vt->opts & VIDTEX_OPT_V_TEXTURE) + target = EGL_IMAGE_BRCM_MULTIMEDIA_V; + else + target = EGL_IMAGE_BRCM_MULTIMEDIA; + + image = eglCreateImageKHR(vt->display, EGL_NO_CONTEXT, target, + (EGLClientBuffer)video_frame, NULL); + if (image == EGL_NO_IMAGE_KHR) + { + vcos_log_error("EGL image conversion error"); + } + else + { + vcos_log_trace("Created EGL image %p for buf %p", image, video_frame); + slot->video_frame = video_frame; + slot->image = image; + } + VIDTEX_CHECK_GL(vt); + + break; + } + } + + if (slot == vt->slots + vcos_countof(vt->slots)) + { + vcos_log_error("Exceeded configured max number of EGL images"); + } + + /* Draw the EGL image */ + if (image != EGL_NO_IMAGE_KHR) + { + /* Assume 30fps */ + int frames_per_rev = 30 * 15; + GLfloat angle = (frame_num * 360) / (GLfloat) frames_per_rev; + frame_num = (frame_num + 1) % frames_per_rev; + + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); + VIDTEX_CHECK_GL(vt); + + glRotatef(angle, 0.0, 0.0, 1.0); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vt_vertices); + glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, vt_tex_coords); + + glDrawArrays(GL_TRIANGLES, 0, vcos_countof(vt_tex_coords) / 2); + + eglSwapBuffers(vt->display, vt->surface); + + if (vt->opts & VIDTEX_OPT_IMG_PER_FRAME) + { + vidtex_destroy_images(vt); + } + + vt->num_swaps++; + } + + VIDTEX_CHECK_GL(vt); +} + +/** Do some GL stuff in order to ensure that any multimedia-related GL buffers have been released + * if they are going to be released. + */ +static void vidtex_flush_gl(VIDTEX_T *vt) +{ + int i; + + glFlush(); + glClearColor(0, 0, 0, 0); + + for (i = 0; i < 10; i++) + { + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(vt->display, vt->surface); + VIDTEX_CHECK_GL(vt); + } + + glFlush(); + VIDTEX_CHECK_GL(vt); +} + +/** Set quit flag, with locking. + * @param quit New value of the quit flag: true - command thread to quit; false - command thread + * to continue. + * @return Old value of the quit flag. + */ +static bool vidtex_set_quit(VIDTEX_T *vt, bool quit) +{ + vcos_mutex_lock(&vt->mutex); + bool old_quit = vt->quit; + vt->quit = quit; + vcos_mutex_unlock(&vt->mutex); + + return old_quit; +} + +/** Callback to receive decoded video frame */ +static void vidtex_video_frame_cb(void *ctx, void *ob) +{ + if (ob) + { + VIDTEX_T *vt = ctx; + /* coverity[missing_lock] Coverity gets confused by the semaphore locking scheme */ + vt->video_frame = ob; + vcos_semaphore_post(&vt->sem_decoded); + vcos_semaphore_wait(&vt->sem_drawn); + vt->video_frame = NULL; + } +} + +/** Callback to receive stop notification. Sets quit flag and posts semaphore. + * @param ctx VIDTEX_T instance. Declared as void * in order to use as SVP callback. + * @param stop_reason SVP stop reason. + */ +static void vidtex_stop_cb(void *ctx, uint32_t stop_reason) +{ + VIDTEX_T *vt = ctx; + vt->stop_reason = stop_reason; + vidtex_set_quit(vt, true); + vcos_semaphore_post(&vt->sem_decoded); +} + +/* Convenience function to create/play/destroy */ +int vidtex_run(const VIDTEX_PARAMS_T *params, EGLNativeWindowType win) +{ + VIDTEX_T *vt; + int rv = -1; + + vt = vidtex_create(win); + if (vt) + { + rv = vidtex_play(vt, params); + vidtex_destroy(vt); + } + + return rv; +} diff --git a/host_applications/android/apps/vidtex/vidtex.h b/host_applications/android/apps/vidtex/vidtex.h new file mode 100755 index 0000000..4a55846 --- /dev/null +++ b/host_applications/android/apps/vidtex/vidtex.h @@ -0,0 +1,89 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VIDTEX_H +#define VIDTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file Video-on-texture display. + * Displays video decoded from a media URI or from camera preview onto an EGL surface. + */ +#include +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_stdint.h" +#include "svp.h" + +/** Max length of a vidtex URI */ +#define VIDTEX_MAX_URI 256 + +/** Test option - create + destroy EGL image every frame */ +#define VIDTEX_OPT_IMG_PER_FRAME (1 << 0) + +/** Test option - display the Y' plane as greyscale texture */ +#define VIDTEX_OPT_Y_TEXTURE (1 << 1) + +/** Test option - display the U plane as greyscale texture */ +#define VIDTEX_OPT_U_TEXTURE (1 << 2) + +/** Test option - display the V plane as greyscale texture */ +#define VIDTEX_OPT_V_TEXTURE (1 << 3) + +/** Parameters to vidtex run. + * N.B. Types need to be bitwise copyable, and between C and C++, so don't use pointers, bool + * or anything else liable for problems. + */ +typedef struct VIDTEX_PARAMS_T +{ + /** Duration of playback, in milliseconds. 0 = default, which is full duration for media and + * a limited duration for camera. + */ + uint32_t duration_ms; + + /** Media URI, or empty string to use camera preview */ + char uri[VIDTEX_MAX_URI]; + + /** Test options; bitmask of VIDTEX_OPT_XXX values */ + uint32_t opts; +} VIDTEX_PARAMS_T; + +/** Run video-on-texture. + * @param params Parameters to video-on-texture display. + * @param win Native window handle. + * @return 0 on success; -1 on failure. + * It is considered successful if the video decode completed without error and at least + * one EGL image was shown. + */ +int vidtex_run(const VIDTEX_PARAMS_T *params, EGLNativeWindowType win); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/host_applications/framework/common/host_ilcore.h b/host_applications/framework/common/host_ilcore.h new file mode 100755 index 0000000..d91a240 --- /dev/null +++ b/host_applications/framework/common/host_ilcore.h @@ -0,0 +1,79 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef HOST_ILCORE_H +#define HOST_ILCORE_H + +#ifdef WANT_OMX_NAME_MANGLE +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_Init(void); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_Deinit(void); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput); +OMX_API OMX_ERRORTYPE host_OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames); +OMX_API OMX_ERRORTYPE host_OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles); +OMX_ERRORTYPE host_OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen); + +#define OMX_Init host_OMX_Init +#define OMX_Deinit host_OMX_Deinit +#define OMX_ComponentNameEnum host_OMX_ComponentNameEnum +#define OMX_GetHandle host_OMX_GetHandle +#define OMX_FreeHandle host_OMX_FreeHandle +#define OMX_SetupTunnel host_OMX_SetupTunnel +#define OMX_GetComponentsOfRole host_OMX_GetComponentsOfRole +#define OMX_GetRolesOfComponent host_OMX_GetRolesOfComponent +#define OMX_GetDebugInformation host_OMX_GetDebugInformation +#else +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen); +#endif + +#endif // HOST_ILCORE_H + + diff --git a/host_applications/framework/common/ilcore.c b/host_applications/framework/common/ilcore.c new file mode 100755 index 0000000..ebd13cc --- /dev/null +++ b/host_applications/framework/common/ilcore.c @@ -0,0 +1,353 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +//includes +#include +#include +#include +#include + +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vchost.h" +#include "interface/vmcs_host/vcilcs_common.h" + +#include "host_ilcore.h" + + +#ifdef WANT_OMX_NAME_MANGLE +#define OMX_Deinit host_OMX_Deinit +#define OMX_Init host_OMX_Init +#define OMX_SetupTunnel host_OMX_SetupTunnel +#define OMX_FreeHandle host_OMX_FreeHandle +#define OMX_GetHandle host_OMX_GetHandle +#define OMX_GetRolesOfComponent host_OMX_GetRolesOfComponent +#define OMX_GetComponentsOfRole host_OMX_GetComponentsOfRole +#define OMX_ComponentNameEnum host_OMX_ComponentNameEnum +#define OMX_GetDebugInformation host_OMX_GetDebugInformation +#endif + +#ifdef WANT_LOCAL_OMX + // xxx: we need to link to local OpenMAX IL core as well + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_Init(void); + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_Deinit(void); + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks); + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent); +#endif + +static int coreInit = 0; +static int nActiveHandles = 0; +static ILCS_SERVICE_T *ilcs_service = NULL; +static VCOS_MUTEX_T lock; +static VCOS_ONCE_T once = VCOS_ONCE_INIT; + +/* Atomic creation of lock protecting shared state */ +static void initOnce(void) +{ + VCOS_STATUS_T status; + status = vcos_mutex_create(&lock, VCOS_FUNCTION); + vcos_demand(status == VCOS_SUCCESS); +} + +/* OMX_Init */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) +{ + VCOS_STATUS_T status; + OMX_ERRORTYPE err = OMX_ErrorNone; + + status = vcos_once(&once, initOnce); + vcos_demand(status == VCOS_SUCCESS); + + vcos_mutex_lock(&lock); + +#ifdef WANT_LOCAL_OMX + vc_OMX_Init(); // initialise local core first +#endif + + if(coreInit == 0) + { + // we need to connect via an ILCS connection to VideoCore + VCHI_INSTANCE_T initialise_instance; + VCHI_CONNECTION_T *connection; + ILCS_CONFIG_T config; + + vc_host_get_vchi_state(&initialise_instance, &connection); + + vcilcs_config(&config); +#ifdef USE_VCHIQ_ARM + ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); +#else + ilcs_service = ilcs_init((VCHIQ_STATE_T *) initialise_instance, (void **) &connection, &config, 0); +#endif + + if(ilcs_service == NULL) + { + err = OMX_ErrorHardware; + goto end; + } + + coreInit = 1; + } +#ifdef USE_VCHIQ_ARM + else + coreInit++; +#endif + +end: + vcos_mutex_unlock(&lock); + return err; +} + +/* OMX_Deinit */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) +{ + if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) + return OMX_ErrorNotReady; + + vcos_mutex_lock(&lock); + +#ifdef USE_VCHIQ_ARM + coreInit--; +#endif + + if(coreInit == 0) + { + // we need to teardown the ILCS connection to VideoCore + ilcs_deinit(ilcs_service); + ilcs_service = NULL; + } + +#ifdef WANT_LOCAL_OMX + vc_OMX_Deinit(); // deinitialise local core as well +#endif + + vcos_mutex_unlock(&lock); + + return OMX_ErrorNone; +} + + +/* OMX_ComponentNameEnum */ +OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); +} + + +/* OMX_GetHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE eError; + OMX_COMPONENTTYPE *pComp; + OMX_HANDLETYPE hHandle = 0; + + if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) + { + if(pHandle) + *pHandle = NULL; + return OMX_ErrorBadParameter; + } + +#if defined(WANT_LOCAL_OMX) && 0 + if ((eError = vc_OMX_GetHandle(pHandle, cComponentName, pAppData, pCallBacks)) != OMX_ErrorNone) +#endif + { + pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); + if (!pComp) + { + vcos_assert(0); + return OMX_ErrorInsufficientResources; + } + memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); + hHandle = (OMX_HANDLETYPE)pComp; + pComp->nSize = sizeof(OMX_COMPONENTTYPE); + pComp->nVersion.nVersion = OMX_VERSION; + eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); + + if (eError == OMX_ErrorNone) { + // Check that all function pointers have been filled in. + // All fields should be non-zero. + int i; + uint32_t *p = (uint32_t *) pComp; + for(i=0; i>2; i++) + if(*p++ == 0) + eError = OMX_ErrorInvalidComponent; + + if(eError != OMX_ErrorNone && pComp->ComponentDeInit) + pComp->ComponentDeInit(hHandle); + } + + if (eError == OMX_ErrorNone) { + eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); + if (eError != OMX_ErrorNone) + pComp->ComponentDeInit(hHandle); + } + if (eError == OMX_ErrorNone) { + *pHandle = hHandle; + } + else { + *pHandle = NULL; + free(pComp); + } + } + + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + nActiveHandles++; + vcos_mutex_unlock(&lock); + } + + return eError; +} + +/* OMX_FreeHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pComp; + + if (hComponent == NULL || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + pComp = (OMX_COMPONENTTYPE*)hComponent; + +#ifdef WANT_LOCAL_OMX + // xxx: a bit of a bodge, we rely on knowing that + // the local core doesn't make use of this field but + // ILCS does... + if (pComp->pApplicationPrivate == NULL) + return vc_OMX_FreeHandle(hComponent); +#endif + + if (ilcs_service == NULL) + return OMX_ErrorBadParameter; + + eError = (pComp->ComponentDeInit)(hComponent); + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + --nActiveHandles; + vcos_mutex_unlock(&lock); + free(pComp); + } + + vcos_assert(nActiveHandles >= 0); + + return eError; +} + +/* OMX_SetupTunnel */ +OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pCompIn, *pCompOut; + OMX_TUNNELSETUPTYPE oTunnelSetup; + + if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + oTunnelSetup.nTunnelFlags = 0; + oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; + + pCompOut = (OMX_COMPONENTTYPE*)hOutput; + + if (hOutput){ + eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); + } + + if (eError == OMX_ErrorNone && hInput) { + pCompIn = (OMX_COMPONENTTYPE*)hInput; + eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); + + if (eError != OMX_ErrorNone && hOutput) { + /* cancel tunnel request on output port since input port failed */ + pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); + } + } + return eError; +} + +/* OMX_GetComponentsOfRole */ +OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumComps = 0; + return eError; +} + +/* OMX_GetRolesOfComponent */ +OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumRoles = 0; + return eError; +} + +/* OMX_GetDebugInformation */ +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); +} + + + +/* File EOF */ + diff --git a/host_applications/linux/CMakeLists.txt b/host_applications/linux/CMakeLists.txt new file mode 100755 index 0000000..80a1908 --- /dev/null +++ b/host_applications/linux/CMakeLists.txt @@ -0,0 +1,25 @@ +# linux apps + +if(NOT ARM64) + add_subdirectory(libs/bcm_host) + add_subdirectory(apps/hello_pi) +endif() +add_subdirectory(apps/gencmd) +add_subdirectory(apps/tvservice) +add_subdirectory(apps/vcmailbox) +if(NOT ARM64) + add_subdirectory(apps/raspicam) + add_subdirectory(libs/sm) + add_subdirectory(apps/smem) +endif() +add_subdirectory(libs/debug_sym) +add_subdirectory(apps/dtoverlay) +add_subdirectory(apps/dtmerge) + +if(ALL_APPS) + add_subdirectory(apps/vcdbg) + add_subdirectory(libs/elftoolchain) + # add_subdirectory(apps/smct) + add_subdirectory(apps/edid_parser) + add_subdirectory(apps/hello_pi) +endif() diff --git a/host_applications/linux/apps/dtmerge/CMakeLists.txt b/host_applications/linux/apps/dtmerge/CMakeLists.txt new file mode 100755 index 0000000..d173697 --- /dev/null +++ b/host_applications/linux/apps/dtmerge/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +get_filename_component (VIDEOCORE_ROOT ../../../.. ABSOLUTE) +include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) + +if (NOT WIN32) + add_definitions(-Wall -Werror) +endif () + +include_directories ( + ${VIDEOCORE_HEADERS_BUILD_DIR} + ${VIDEOCORE_ROOT} + ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt + ${VIDEOCORE_ROOT}/helpers/dtoverlay +) + +add_executable(dtmerge dtmerge.c) +target_link_libraries(dtmerge dtovl) + +install(TARGETS dtmerge RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/dtmerge/dtmerge.c b/host_applications/linux/apps/dtmerge/dtmerge.c new file mode 100755 index 0000000..9243da1 --- /dev/null +++ b/host_applications/linux/apps/dtmerge/dtmerge.c @@ -0,0 +1,172 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "dtoverlay.h" + +static void usage(void) +{ + printf("Usage:\n"); + printf(" dtmerge [ - [param=value] ...\n"); + printf(" to apply a parameter to the base dtb (like dtparam)\n"); + printf(" dtmerge [ [param=value] ...\n"); + printf(" to apply an overlay with parameters (like dtoverlay)\n"); + printf(" where is any of:\n"); + printf(" -d Enable debug output\n"); + printf(" -h Show this help message\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + const char *base_file; + const char *merged_file; + const char *overlay_file; + DTBLOB_T *base_dtb; + DTBLOB_T *overlay_dtb; + int err; + int argn = 1; + int max_dtb_size = 100000; + + while ((argn < argc) && (argv[argn][0] == '-')) + { + const char *arg = argv[argn++]; + if ((strcmp(arg, "-d") == 0) || + (strcmp(arg, "--debug") == 0)) + dtoverlay_enable_debug(1); + else if ((strcmp(arg, "-h") == 0) || + (strcmp(arg, "--help") == 0)) + usage(); + else + { + printf("* Unknown option '%s'\n", arg); + usage(); + } + } + + if (argc < (argn + 3)) + { + usage(); + } + + base_file = argv[argn++]; + merged_file = argv[argn++]; + overlay_file = argv[argn++]; + + base_dtb = dtoverlay_load_dtb(base_file, max_dtb_size); + if (!base_dtb) + { + printf("* failed to load '%s'\n", base_file); + return -1; + } + + err = dtoverlay_set_synonym(base_dtb, "i2c", "i2c0"); + err = dtoverlay_set_synonym(base_dtb, "i2c_arm", "i2c0"); + err = dtoverlay_set_synonym(base_dtb, "i2c_vc", "i2c1"); + err = dtoverlay_set_synonym(base_dtb, "i2c_baudrate", "i2c0_baudrate"); + err = dtoverlay_set_synonym(base_dtb, "i2c_arm_baudrate", "i2c0_baudrate"); + err = dtoverlay_set_synonym(base_dtb, "i2c_vc_baudrate", "i2c1_baudrate"); + + if (strcmp(overlay_file, "-") == 0) + { + overlay_dtb = base_dtb; + } + else + { + overlay_dtb = dtoverlay_load_dtb(overlay_file, max_dtb_size); + if (overlay_dtb) + err = dtoverlay_fixup_overlay(base_dtb, overlay_dtb); + else + err = -1; + } + + while (!err && (argn < argc)) + { + char *param_name = argv[argn++]; + char *param_value = param_name + strcspn(param_name, "="); + const void *override_data; + int data_len; + + if (*param_value == '=') + { + *(param_value++) = '\0'; + } + else + { + /* This isn't a well-formed parameter assignment, but it can be + treated as an assignment of true. */ + param_value = "true"; + } + + override_data = dtoverlay_find_override(overlay_dtb, param_name, + &data_len); + if (override_data) + { + err = dtoverlay_apply_override(overlay_dtb, param_name, + override_data, data_len, + param_value); + } + else + { + override_data = dtoverlay_find_override(base_dtb, param_name, &data_len); + if (override_data) + { + err = dtoverlay_apply_override(base_dtb, param_name, + override_data, data_len, + param_value); + } + else + { + printf("* unknown param '%s'\n", param_name); + err = data_len; + } + } + } + + if (!err && (overlay_dtb != base_dtb)) + { + err = dtoverlay_merge_overlay(base_dtb, overlay_dtb); + + dtoverlay_free_dtb(overlay_dtb); + } + + if (!err) + { + dtoverlay_pack_dtb(base_dtb); + err = dtoverlay_save_dtb(base_dtb, merged_file); + } + + dtoverlay_free_dtb(base_dtb); + + if (err != 0) + printf("* Exiting with error code %d\n", err); + + return err; +} diff --git a/host_applications/linux/apps/dtoverlay/CMakeLists.txt b/host_applications/linux/apps/dtoverlay/CMakeLists.txt new file mode 100755 index 0000000..9009200 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 2.8) + +get_filename_component (VIDEOCORE_ROOT ../../../.. ABSOLUTE) +include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) + +if (NOT WIN32) + add_definitions(-Wall -Werror) +endif () + +include_directories ( + ${VIDEOCORE_HEADERS_BUILD_DIR} + ${VIDEOCORE_ROOT} + ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt + ${VIDEOCORE_ROOT}/helpers/dtoverlay +) + +add_executable(dtoverlay dtoverlay_main.c utils.c) +target_link_libraries(dtoverlay dtovl) +install(TARGETS dtoverlay RUNTIME DESTINATION bin) + +add_custom_command(TARGET dtoverlay POST_BUILD COMMAND ln;-sf;dtoverlay;dtparam) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dtparam DESTINATION bin) + +set(DTOVERLAY_SCRIPTS dtoverlay-pre dtoverlay-post) +foreach(_script ${DTOVERLAY_SCRIPTS}) + add_custom_command( + TARGET dtoverlay + COMMAND ${CMAKE_COMMAND} + -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${_script} + ${CMAKE_BINARY_DIR}/../../bin/${_script} + ) +endforeach() +install(PROGRAMS ${DTOVERLAY_SCRIPTS} DESTINATION bin) diff --git a/host_applications/linux/apps/dtoverlay/dtoverlay-post b/host_applications/linux/apps/dtoverlay/dtoverlay-post new file mode 100755 index 0000000..6d8c66d --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/dtoverlay-post @@ -0,0 +1,10 @@ +#!/bin/bash +if [ "$DISPLAY" == "" ]; then + export DISPLAY=":0.0" +fi +CMD="which lxpanelctl >/dev/null 2>&1 && lxpanelctl alsastart >/dev/null" +if [ $EUID -eq 0 ]; then + exec su pi -c "$CMD" +else + exec $CMD +fi diff --git a/host_applications/linux/apps/dtoverlay/dtoverlay-pre b/host_applications/linux/apps/dtoverlay/dtoverlay-pre new file mode 100755 index 0000000..5065fc5 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/dtoverlay-pre @@ -0,0 +1,10 @@ +#!/bin/bash +if [ "$DISPLAY" == "" ]; then + export DISPLAY=":0.0" +fi +CMD="which lxpanelctl >/dev/null 2>&1 && lxpanelctl alsastop >/dev/null" +if [ $EUID -eq 0 ]; then + exec su pi -c "$CMD" +else + exec $CMD +fi diff --git a/host_applications/linux/apps/dtoverlay/dtoverlay_main.c b/host_applications/linux/apps/dtoverlay/dtoverlay_main.c new file mode 100755 index 0000000..5f87e66 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/dtoverlay_main.c @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dtoverlay.h" +#include "utils.h" + + +#define CFG_DIR_1 "/sys/kernel/config" +#define CFG_DIR_2 "/config" +#define DT_SUBDIR "/device-tree" +#define WORK_DIR "/tmp/.dtoverlays" +#define OVERLAY_SRC_SUBDIR "overlays" +#define README_FILE "README" +#define DT_OVERLAYS_SUBDIR "overlays" +#define DTOVERLAY_PATH_MAX 128 +#define DIR_MODE 0755 + + +enum { + OPT_ADD, + OPT_REMOVE, + OPT_REMOVE_FROM, + OPT_LIST, + OPT_LIST_ALL, + OPT_HELP +}; + +static const char *boot_dirs[] = +{ +#ifdef FORCE_BOOT_DIR + FORCE_BOOT_DIR, +#else + "/boot", + "/flash", +#ifdef OTHER_BOOT_DIR + OTHER_BOOT_DIR, +#endif +#endif + NULL /* Terminator */ +}; + +typedef struct state_struct +{ + int count; + struct dirent **namelist; +} STATE_T; + +static int dtoverlay_add(STATE_T *state, const char *overlay, + int argc, const char **argv); +static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later); +static int dtoverlay_list(STATE_T *state); +static int dtoverlay_list_all(STATE_T *state); +static void usage(void); +static void root_check(void); + +static void overlay_help(const char *overlay, const char **params); + +static int apply_overlay(const char *overlay_file, const char *overlay); +static int overlay_applied(const char *overlay_dir); + +static STATE_T *read_state(const char *dir); +static void free_state(STATE_T *state); + +const char *cmd_name; + +const char *work_dir = WORK_DIR; +const char *overlay_src_dir; +const char *dt_overlays_dir; +const char *error_file = NULL; +int dry_run = 0; + +int main(int argc, const char **argv) +{ + int argn = 1; + int opt = OPT_ADD; + int is_dtparam; + const char *overlay = NULL; + const char **params = NULL; + int ret = 0; + STATE_T *state = NULL; + const char *cfg_dir; + + cmd_name = argv[0]; + if (strrchr(cmd_name, '/')) + cmd_name = strrchr(cmd_name, '/') + 1; + is_dtparam = (strcmp(cmd_name, "dtparam") == 0); + + while ((argn < argc) && (argv[argn][0] == '-')) + { + const char *arg = argv[argn++]; + if (strcmp(arg, "-r") == 0) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_REMOVE; + } + else if (strcmp(arg, "-R") == 0) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_REMOVE_FROM; + } + else if (strcmp(arg, "-D") == 0) + { + if (opt != OPT_ADD) + usage(); + dry_run = 1; + work_dir = "."; + } + else if ((strcmp(arg, "-l") == 0) || + (strcmp(arg, "--list") == 0)) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_LIST; + } + else if ((strcmp(arg, "-a") == 0) || + (strcmp(arg, "--listall") == 0) || + (strcmp(arg, "--all") == 0)) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_LIST_ALL; + } + else if (strcmp(arg, "-d") == 0) + { + if (argn == argc) + usage(); + overlay_src_dir = argv[argn++]; + } + else if (strcmp(arg, "-v") == 0) + { + opt_verbose = 1; + } + else if (strcmp(arg, "-h") == 0) + { + opt = OPT_HELP; + } + else + { + fprintf(stderr, "* unknown option '%s'\n", arg); + usage(); + } + } + + if ((opt == OPT_ADD) || (opt == OPT_REMOVE) || + (opt == OPT_REMOVE_FROM) || (opt == OPT_HELP)) + { + if ((argn == argc) && + ((!is_dtparam && + ((opt == OPT_ADD) || (opt == OPT_HELP))) || + (is_dtparam && (opt == OPT_HELP)))) + usage(); + if (is_dtparam && (opt == OPT_ADD) && (argn == argc)) + opt = OPT_HELP; + if (is_dtparam && + ((opt == OPT_ADD) || (opt == OPT_HELP))) + overlay = "dtparam"; + else if (argn < argc) + overlay = argv[argn++]; + } + + if ((opt == OPT_HELP) && (argn < argc)) + { + params = &argv[argn]; + argn = argc; + } + + if ((opt != OPT_ADD) && (argn != argc)) + usage(); + + dtoverlay_enable_debug(opt_verbose); + + if (!overlay_src_dir) + { + /* Find the overlays and README */ + int i; + + for (i = 0; boot_dirs[i]; i++) + { + overlay_src_dir = sprintf_dup("%s/" OVERLAY_SRC_SUBDIR, + boot_dirs[i]); + if (dir_exists(overlay_src_dir)) + break; + free_string(overlay_src_dir); + overlay_src_dir = NULL; + } + + if (!overlay_src_dir) + fatal_error("Failed to find overlays directory"); + } + + if (opt == OPT_HELP) + { + overlay_help(overlay, params); + goto orderly_exit; + } + + if (!dir_exists(work_dir)) + { + if (mkdir(work_dir, DIR_MODE) != 0) + fatal_error("Failed to create '%s' - %d", work_dir, errno); + } + + error_file = sprintf_dup("%s/%s", work_dir, "error.dtb"); + + cfg_dir = CFG_DIR_1 DT_SUBDIR; + if (!dry_run && !dir_exists(cfg_dir)) + { + root_check(); + + cfg_dir = CFG_DIR_2; + if (!dir_exists(cfg_dir)) + { + if (mkdir(cfg_dir, DIR_MODE) != 0) + fatal_error("Failed to create '%s' - %d", cfg_dir, errno); + } + + cfg_dir = CFG_DIR_2 DT_SUBDIR; + if (!dir_exists(cfg_dir) && + (run_cmd("mount -t configfs none '%s'", cfg_dir) != 0)) + fatal_error("Failed to mount configfs - %d", errno); + } + + dt_overlays_dir = sprintf_dup("%s/%s", cfg_dir, DT_OVERLAYS_SUBDIR); + if (!dir_exists(dt_overlays_dir)) + fatal_error("configfs overlays folder not found - incompatible kernel"); + + if (!dry_run) + { + state = read_state(work_dir); + if (!state) + fatal_error("Failed to read state"); + } + + switch (opt) + { + case OPT_ADD: + case OPT_REMOVE: + case OPT_REMOVE_FROM: + if (!dry_run) + { + root_check(); + run_cmd("which dtoverlay-pre >/dev/null 2>&1 && dtoverlay-pre"); + } + break; + default: + break; + } + + switch (opt) + { + case OPT_ADD: + ret = dtoverlay_add(state, overlay, argc - argn, argv + argn); + break; + case OPT_REMOVE: + ret = dtoverlay_remove(state, overlay, 0); + break; + case OPT_REMOVE_FROM: + ret = dtoverlay_remove(state, overlay, 1); + break; + case OPT_LIST: + ret = dtoverlay_list(state); + break; + case OPT_LIST_ALL: + ret = dtoverlay_list_all(state); + break; + default: + ret = 1; + break; + } + + switch (opt) + { + case OPT_ADD: + case OPT_REMOVE: + case OPT_REMOVE_FROM: + if (!dry_run) + run_cmd("which dtoverlay-post >/dev/null 2>&1 && dtoverlay-post"); + break; + default: + break; + } + +orderly_exit: + if (state) + free_state(state); + free_strings(); + + if ((ret == 0) && error_file) + unlink(error_file); + + return ret; +} + +struct dtparam_state +{ + STRING_VEC_T *used_props; + const char *override_value; +}; + +int dtparam_callback(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value) +{ + struct dtparam_state *state = callback_value; + char prop_id[80]; + int err; + + err = dtoverlay_override_one_target(override_type, + dtb, node_off, + prop_name, target_phandle, + target_off, target_size, + (void *)state->override_value); + + if ((err == 0) && (target_phandle != 0)) + { + if (snprintf(prop_id, sizeof(prop_id), "%08x%s", target_phandle, + prop_name) < 0) + err = FDT_ERR_INTERNAL; + else if (string_vec_find(state->used_props, prop_id, 0) < 0) + string_vec_add(state->used_props, prop_id, 0); + } + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtparam_apply(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + const char *override_value, STRING_VEC_T *used_props) +{ + struct dtparam_state state; + void *data; + int err; + + state.used_props = used_props; + state.override_value = override_value; + + /* Copy the override data in case it moves */ + data = malloc(data_len); + if (data) + { + memcpy(data, override_data, data_len); + err = dtoverlay_foreach_override_target(dtb, override_name, + data, data_len, + dtparam_callback, + (void *)&state); + free(data); + } + else + { + dtoverlay_error("out of memory"); + err = NON_FATAL(FDT_ERR_NOSPACE); + } + + return err; +} + +static int dtoverlay_add(STATE_T *state, const char *overlay, + int argc, const char **argv) +{ + const char *overlay_name; + const char *overlay_file; + char *param_string = NULL; + int is_dtparam; + DTBLOB_T *base_dtb = NULL; + DTBLOB_T *overlay_dtb; + STRING_VEC_T used_props; + int err; + int len; + int i; + + len = strlen(overlay) - 5; + is_dtparam = (strcmp(overlay, "dtparam") == 0); + if (is_dtparam) + { + /* Convert /proc/device-tree to a .dtb and load it */ + overlay_file = sprintf_dup("%s/%s", work_dir, "base.dtb"); + if (run_cmd("dtc -I fs -O dtb -o '%s' /proc/device-tree 1>/dev/null 2>&1", + overlay_file) != 0) + return error("Failed to read active DTB"); + } + else if ((len > 0) && (strcmp(overlay + len, ".dtbo") == 0)) + { + const char *p; + overlay_file = overlay; + p = strrchr(overlay, '/'); + if (p) + { + overlay = p + 1; + len = strlen(overlay) - 5; + } + + overlay = sprintf_dup("%.*s", len, overlay); + } + else + { + overlay_file = sprintf_dup("%s/%s.dtbo", overlay_src_dir, overlay); + } + + if (dry_run) + overlay_name = "dry_run"; + else + overlay_name = sprintf_dup("%d_%s", state->count, overlay); + overlay_dtb = dtoverlay_load_dtb(overlay_file, DTOVERLAY_PADDING(4096)); + if (!overlay_dtb) + return error("Failed to read '%s'", overlay_file); + + if (is_dtparam) + { + base_dtb = overlay_dtb; + string_vec_init(&used_props); + } + + /* Apply any parameters next */ + for (i = 0; i < argc; i++) + { + const char *arg = argv[i]; + const char *param_val = strchr(arg, '='); + const char *param, *override; + char *p = NULL; + int override_len; + if (param_val) + { + int len = (param_val - arg); + p = sprintf_dup("%.*s", len, arg); + param = p; + param_val++; + } + else + { + /* Use the default parameter value - true */ + param = arg; + param_val = "true"; + } + + override = dtoverlay_find_override(overlay_dtb, param, &override_len); + + if (!override) + return error("Unknown parameter '%s'", param); + + if (is_dtparam) + err = dtparam_apply(overlay_dtb, param, + override, override_len, + param_val, &used_props); + else + err = dtoverlay_apply_override(overlay_dtb, param, + override, override_len, + param_val); + if (err != 0) + return error("Failed to set %s=%s", param, param_val); + + param_string = sprintf_dup("%s %s=%s", + param_string ? param_string : "", + param, param_val); + + free_string(p); + } + + if (is_dtparam) + { + /* Build an overlay DTB */ + overlay_dtb = dtoverlay_create_dtb(2048 + 256 * used_props.num_strings); + + for (i = 0; i < used_props.num_strings; i++) + { + int phandle, node_off, prop_len; + const char *str, *prop_name; + const void *prop_data; + + str = used_props.strings[i]; + sscanf(str, "%8x", &phandle); + prop_name = str + 8; + node_off = dtoverlay_find_phandle(base_dtb, phandle); + + prop_data = dtoverlay_get_property(base_dtb, node_off, + prop_name, &prop_len); + err = dtoverlay_create_prop_fragment(overlay_dtb, i, phandle, + prop_name, prop_data, prop_len); + } + + dtoverlay_free_dtb(base_dtb); + } + + if (param_string) + dtoverlay_dtb_set_trailer(overlay_dtb, param_string, + strlen(param_string) + 1); + + /* Create a filename with the sequence number */ + overlay_file = sprintf_dup("%s/%s.dtbo", work_dir, overlay_name); + + /* then write the overlay to the file */ + dtoverlay_pack_dtb(overlay_dtb); + dtoverlay_save_dtb(overlay_dtb, overlay_file); + dtoverlay_free_dtb(overlay_dtb); + + if (!dry_run && !apply_overlay(overlay_file, overlay_name)) + { + if (error_file) + { + rename(overlay_file, error_file); + free_string(error_file); + } + return 1; + } + + return 0; +} + +static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later) +{ + const char *overlay_dir; + const char *dir_name = NULL; + char *end; + int overlay_len; + int count = state->count; + int rmpos; + int i; + + if (chdir(work_dir) != 0) + fatal_error("Failed to chdir to '%s'", work_dir); + + if (overlay) + { + overlay_len = strlen(overlay); + + rmpos = strtoul(overlay, &end, 10); + if (end && (*end == '\0')) + { + if (rmpos >= count) + return error("Overlay index (%d) too large", rmpos); + dir_name = state->namelist[rmpos]->d_name; + } + /* Locate the most recent reference to the overlay */ + else for (rmpos = count - 1; rmpos >= 0; rmpos--) + { + const char *left, *right; + dir_name = state->namelist[rmpos]->d_name; + left = strchr(dir_name, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + if (((right - left) == overlay_len) && + (memcmp(overlay, left, overlay_len) == 0)) + break; + dir_name = NULL; + } + + if (rmpos < 0) + return error("Overlay '%s' is not loaded", overlay); + } + else + { + if (!count) + return error("No overlays loaded"); + rmpos = and_later ? 0 : (count - 1); + dir_name = state->namelist[rmpos]->d_name; + } + + if (rmpos < count) + { + /* Unload it and all subsequent overlays in reverse order */ + for (i = count - 1; i >= rmpos; i--) + { + const char *left, *right; + left = state->namelist[i]->d_name; + right = strrchr(left, '.'); + if (!right) + return error("Internal error"); + + overlay_dir = sprintf_dup("%s/%.*s", dt_overlays_dir, + right - left, left); + if (rmdir(overlay_dir) != 0) + return error("Failed to remove directory '%s'", overlay_dir); + + free_string(overlay_dir); + } + + /* Replay the sequence, deleting files for the specified overlay, + and renumbering and reloading all other overlays. */ + for (i = rmpos, state->count = rmpos; i < count; i++) + { + const char *left, *right; + const char *filename = state->namelist[i]->d_name; + + left = strchr(filename, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + + if (and_later || (i == rmpos)) + { + /* This one is being deleted */ + unlink(filename); + } + else + { + /* Keep this one - renumber and reload */ + int len = right - left; + char *new_name = sprintf_dup("%d_%.*s", state->count, + len, left); + char *new_file = sprintf_dup("%s.dtbo", new_name); + int ret = 0; + + if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) + { + /* Regenerate the overlay in case multiple overlays target + different parts of the same property. */ + + DTBLOB_T *dtb; + char *params; + const char **paramv; + int paramc; + int j; + char *p; + + /* Extract the parameters */ + dtb = dtoverlay_load_dtb(filename, 0); + unlink(filename); + + if (!dtb) + { + error("Failed to re-apply dtparam"); + continue; + } + + params = (char *)dtoverlay_dtb_trailer(dtb); + if (!params) + { + error("Failed to re-apply dtparam"); + dtoverlay_free_dtb(dtb); + continue; + } + + /* Count and NUL-separate the params */ + p = params; + paramc = 0; + while (*p) + { + int paramlen; + *(p++) = '\0'; + paramlen = strcspn(p, " "); + paramc++; + p += paramlen; + } + + paramv = malloc((paramc + 1) * sizeof(const char *)); + if (!paramv) + { + error("out of memory re-applying dtparam"); + dtoverlay_free_dtb(dtb); + continue; + } + + for (j = 0, p = params + 1; j < paramc; j++) + { + paramv[j] = p; + p += strlen(p) + 1; + } + paramv[j] = NULL; + + /* Create the new overlay */ + ret = dtoverlay_add(state, "dtparam", paramc, paramv); + free(paramv); + dtoverlay_free_dtb(dtb); + } + else + { + rename(filename, new_file); + ret = !apply_overlay(new_file, new_name); + } + if (ret != 0) + { + error("Failed to re-apply dtparam"); + continue; + } + state->count++; + } + } + } + + return 0; +} + +static int dtoverlay_list(STATE_T *state) +{ + if (state->count == 0) + { + printf("No overlays loaded\n"); + } + else + { + int i; + printf("Overlays (in load order):\n"); + for (i = 0; i < state->count; i++) + { + const char *name, *left, *right; + const char *saved_overlay; + DTBLOB_T *dtb; + name = state->namelist[i]->d_name; + left = strchr(name, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + + saved_overlay = sprintf_dup("%s/%s", work_dir, name); + dtb = dtoverlay_load_dtb(saved_overlay, 0); + + if (dtoverlay_dtb_trailer(dtb)) + printf("%d: %.*s %.*s\n", i, (int)(right - left), left, + dtoverlay_dtb_trailer_len(dtb), + (char *)dtoverlay_dtb_trailer(dtb)); + else + printf("%d: %.*s\n", i, (int)(right - left), left); + + dtoverlay_free_dtb(dtb); + } + } + + return 0; +} + +static int dtoverlay_list_all(STATE_T *state) +{ + int i; + DIR *dh; + struct dirent *de; + STRING_VEC_T strings; + + string_vec_init(&strings); + + /* Enumerate .dtbo files in the /boot/overlays directory */ + dh = opendir(overlay_src_dir); + while ((de = readdir(dh)) != NULL) + { + int len = strlen(de->d_name) - 5; + if ((len >= 0) && strcmp(de->d_name + len, ".dtbo") == 0) + { + char *str = string_vec_add(&strings, de->d_name, len + 2); + str[len] = '\0'; + str[len + 1] = ' '; + } + } + closedir(dh); + + /* Merge in active overlays, marking them */ + for (i = 0; i < state->count; i++) + { + const char *left, *right; + char *str; + int len, idx; + + left = strchr(state->namelist[i]->d_name, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + + len = right - left; + if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) + continue; + idx = string_vec_find(&strings, left, len); + if (idx >= 0) + { + str = strings.strings[idx]; + len = strlen(str); + } + else + { + str = string_vec_add(&strings, left, len + 2); + str[len] = '\0'; + } + str[len + 1] = '*'; + } + + if (strings.num_strings == 0) + { + printf("No overlays found\n"); + } + else + { + /* Sort */ + string_vec_sort(&strings); + + /* Display */ + printf("All overlays (* = loaded):\n"); + + for (i = 0; i < strings.num_strings; i++) + { + const char *str = strings.strings[i]; + printf("%c %s\n", str[strlen(str)+1], str); + } + } + + string_vec_uninit(&strings); + + return 0; +} + +static void usage(void) +{ + printf("Usage:\n"); + if (strcmp(cmd_name, "dtparam") == 0) + { + printf(" %s Display help on all parameters\n", cmd_name); + printf(" %s =...\n", cmd_name); + printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); + printf(" %s -D [] Dry-run (prepare overlay, but don't apply -\n", + cmd_name); + printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); + printf(" %s -r [] Remove an overlay (by index, or the last)\n", cmd_name); + printf(" %s -R [] Remove from an overlay (by index, or all)\n", + cmd_name); + printf(" %s -l List active overlays/dtparams\n", cmd_name); + printf(" %s -a List all overlays/dtparams (marking the active)\n", cmd_name); + printf(" %s -h Show this usage message\n", cmd_name); + printf(" %s -h ... Display help on the listed parameters\n", cmd_name); + } + else + { + printf(" %s [=...]\n", cmd_name); + printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); + printf(" %s -D [] Dry-run (prepare overlay, but don't apply -\n", + cmd_name); + printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); + printf(" %s -r [] Remove an overlay (by name, index or the last)\n", cmd_name); + printf(" %s -R [] Remove from an overlay (by name, index or all)\n", + cmd_name); + printf(" %s -l List active overlays/params\n", cmd_name); + printf(" %s -a List all overlays (marking the active)\n", cmd_name); + printf(" %s -h Show this usage message\n", cmd_name); + printf(" %s -h Display help on an overlay\n", cmd_name); + printf(" %s -h .. Or its parameters\n", cmd_name); + printf(" where is the name of an overlay or 'dtparam' for dtparams\n"); + } + printf("Options applicable to most variants:\n"); + printf(" -d Specify an alternate location for the overlays\n"); + printf(" (defaults to /boot/overlays or /flash/overlays)\n"); + printf(" -v Verbose operation\n"); + printf("\n"); + printf("Adding or removing overlays and parameters requires root privileges.\n"); + + exit(1); +} + +static void root_check(void) +{ + if (getuid() != 0) + fatal_error("Must be run as root - try 'sudo %s ...'", cmd_name); +} + +static void overlay_help(const char *overlay, const char **params) +{ + OVERLAY_HELP_STATE_T *state; + const char *readme_path = sprintf_dup("%s/%s", overlay_src_dir, + README_FILE); + + state = overlay_help_open(readme_path); + free_string(readme_path); + + if (state) + { + if (strcmp(overlay, "dtparam") == 0) + overlay = ""; + + if (overlay_help_find(state, overlay)) + { + if (params && overlay_help_find_field(state, "Params")) + { + int in_param = 0; + + while (1) + { + const char *line = overlay_help_field_data(state); + if (!line) + break; + if (line[0] == '\0') + continue; + if (line[0] != ' ') + { + /* This is a parameter name */ + int param_len = strcspn(line, " "); + const char **p = params; + in_param = 0; + while (*p) + { + if ((param_len == strlen(*p)) && + (memcmp(line, *p, param_len) == 0)) + { + in_param = 1; + break; + } + p++; + } + } + if (in_param) + printf("%s\n", line); + } + } + else + { + printf("Name: %s\n\n", overlay); + overlay_help_print_field(state, "Info", "Info:", 8, 0); + overlay_help_print_field(state, "Load", "Usage:", 8, 0); + overlay_help_print_field(state, "Params", "Params:", 8, 0); + } + } + else + { + fatal_error("No help found for overlay '%s'", overlay); + } + overlay_help_close(state); + } + else + { + fatal_error("Help file not found"); + } +} + +static int apply_overlay(const char *overlay_file, const char *overlay) +{ + const char *overlay_dir = sprintf_dup("%s/%s", dt_overlays_dir, overlay); + int ret = 0; + if (dir_exists(overlay_dir)) + { + error("Overlay '%s' is already loaded", overlay); + } + else if (mkdir(overlay_dir, DIR_MODE) == 0) + { + DTBLOB_T *dtb = dtoverlay_load_dtb(overlay_file, 0); + if (!dtb) + { + error("Failed to apply overlay '%s' (load)", overlay); + } + else + { + const char *dest_file = sprintf_dup("%s/dtbo", overlay_dir); + + /* then write the overlay to the file */ + if (dtoverlay_save_dtb(dtb, dest_file) != 0) + error("Failed to apply overlay '%s' (save)", overlay); + else if (!overlay_applied(overlay_dir)) + error("Failed to apply overlay '%s' (kernel)", overlay); + else + ret = 1; + + free_string(dest_file); + dtoverlay_free_dtb(dtb); + } + + if (!ret) + rmdir(overlay_dir); + } + else + { + error("Failed to create overlay directory"); + } + + return ret; +} + +static int overlay_applied(const char *overlay_dir) +{ + char status[7] = { '\0' }; + const char *status_path = sprintf_dup("%s/status", overlay_dir); + FILE *fp = fopen(status_path, "r"); + int bytes = 0; + if (fp) + { + bytes = fread(status, 1, sizeof(status), fp); + fclose(fp); + } + free_string(status_path); + return (bytes == sizeof(status)) && + (memcmp(status, "applied", sizeof(status)) == 0); +} + +int seq_filter(const struct dirent *de) +{ + int num; + return (sscanf(de->d_name, "%d_", &num) == 1); +} + +int seq_compare(const struct dirent **de1, const struct dirent **de2) +{ + int num1 = atoi((*de1)->d_name); + int num2 = atoi((*de2)->d_name); + if (num1 < num2) + return -1; + else if (num1 == num2) + return 0; + else + return 1; +} + +static STATE_T *read_state(const char *dir) +{ + STATE_T *state = malloc(sizeof(STATE_T)); + int i; + + if (state) + { + state->count = scandir(dir, &state->namelist, seq_filter, seq_compare); + + for (i = 0; i < state->count; i++) + { + int num = atoi(state->namelist[i]->d_name); + if (i != num) + error("Overlay sequence error"); + } + } + return state; +} + +static void free_state(STATE_T *state) +{ + int i; + for (i = 0; i < state->count; i++) + { + free(state->namelist[i]); + } + free(state->namelist); + free(state); +} diff --git a/host_applications/linux/apps/dtoverlay/utils.c b/host_applications/linux/apps/dtoverlay/utils.c new file mode 100755 index 0000000..511efd9 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/utils.c @@ -0,0 +1,463 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define OVERLAY_HELP_INDENT 8 + +int opt_verbose; +int opt_dry_run; +static STRING_T *allocated_strings; + +struct overlay_help_state_struct +{ + FILE *fp; + long rec_pos; + int line_len; + int line_pos; + int blank_count; + int end_of_field; + char line_buf[82]; +}; + +static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state); + +OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile) +{ + OVERLAY_HELP_STATE_T *state = NULL; + FILE *fp = fopen(helpfile, "r"); + if (fp) + { + state = calloc(1, sizeof(OVERLAY_HELP_STATE_T)); + if (!state) + fatal_error("Out of memory"); + state->fp = fp; + state->line_pos = -1; + state->rec_pos = -1; + } + + return state; +} + +void overlay_help_close(OVERLAY_HELP_STATE_T *state) +{ + fclose(state->fp); + free(state); +} + +int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name) +{ + state->line_pos = -1; + state->rec_pos = -1; + state->blank_count = 0; + + fseek(state->fp, 0, SEEK_SET); + + while (overlay_help_find_field(state, "Name")) + { + const char *overlay = overlay_help_field_data(state); + if (overlay && (strcmp(overlay, name) == 0)) + { + state->rec_pos = (long)ftell(state->fp); + return 1; + } + } + + return 0; +} + +int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field) +{ + int field_len = strlen(field); + int found = 0; + + if (state->rec_pos >= 0) + fseek(state->fp, state->rec_pos, SEEK_SET); + + while (!found) + { + int line_len = overlay_help_get_line(state); + if (line_len < 0) + break; + + /* Check for the ":" prefix */ + if ((line_len >= (field_len + 1)) && + (state->line_buf[field_len] == ':') && + (memcmp(state->line_buf, field, field_len) == 0)) + { + /* Found it + If this initial line has no content then skip it */ + if (line_len > OVERLAY_HELP_INDENT) + state->line_pos = OVERLAY_HELP_INDENT; + else + state->line_pos = -1; + state->end_of_field = 0; + found = 1; + } + else + { + state->line_pos = -1; + } + } + + return found; +} + +const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state) +{ + int line_len, pos; + + if (state->end_of_field) + return NULL; + + line_len = state->line_len; + + if ((state->line_pos < 0) || + (state->line_pos >= line_len)) + { + line_len = overlay_help_get_line(state); + + /* Fields end at the start of the next field or the end of the record */ + if ((line_len < 0) || (state->line_buf[0] != ' ')) + { + state->end_of_field = 1; + return NULL; + } + + if (line_len == 0) + return ""; + } + + /* Return field data starting at OVERLAY_HELP_INDENT, if there is any */ + pos = line_len; + if (pos > OVERLAY_HELP_INDENT) + pos = OVERLAY_HELP_INDENT; + + state->line_pos = -1; + return &state->line_buf[pos]; +} + +void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, + const char *field, const char *label, + int indent, int strip_blanks) +{ + if (!overlay_help_find_field(state, field)) + return; + + while (1) + { + const char *line = overlay_help_field_data(state); + if (!line) + break; + + if (label) + { + int spaces = indent - strlen(label); + if (spaces < 0) + spaces = 0; + + printf("%s%*s%s\n", label, spaces, "", line); + label = NULL; + } + else if (line[0]) + { + printf("%*s%s\n", indent, "", line); + } + else if (!strip_blanks) + { + printf("\n"); + } + } + + if (!strip_blanks) + printf("\n"); +} + +/* Returns the length of the line, or -1 on end of file or record */ +static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state) +{ + int line_len; + + if (state->line_pos >= 0) + return state->line_len; + +get_next_line: + state->line_buf[sizeof(state->line_buf) - 1] = ' '; + line_len = -1; + if (fgets(state->line_buf, sizeof(state->line_buf), state->fp)) + { + // Check for overflow + + // Strip the newline + line_len = strlen(state->line_buf); + if (line_len && (state->line_buf[line_len - 1] == '\n')) + { + line_len--; + state->line_buf[line_len] = '\0'; + } + } + + if (state->rec_pos >= 0) + { + if (line_len == 0) + { + state->blank_count++; + if (state->blank_count >= 2) + return -1; + state->line_pos = 0; + goto get_next_line; + } + else if (state->blank_count) + { + /* Return a single blank line now - the non-empty line will be + returned next time */ + state->blank_count = 0; + return 0; + } + } + + state->line_len = line_len; + state->line_pos = (line_len >= 0) ? 0 : -1; + return line_len; +} + +int run_cmd(const char *fmt, ...) +{ + va_list ap; + char *cmd; + int ret; + va_start(ap, fmt); + cmd = vsprintf_dup(fmt, ap); + va_end(ap); + + if (opt_dry_run || opt_verbose) + fprintf(stderr, "run_cmd: %s\n", cmd); + + ret = opt_dry_run ? 0 : system(cmd); + + free_string(cmd); + + return ret; +} + + +/* Not thread safe */ +void free_string(const char *string) +{ + STRING_T *str; + + if (!string) + return; + + str = (STRING_T *)(string - sizeof(STRING_T)); + if (str == allocated_strings) + { + allocated_strings = str->next; + if (allocated_strings == str) + allocated_strings = NULL; + } + str->prev->next = str->next; + str->next->prev = str->prev; + free(str); +} + +/* Not thread safe */ +void free_strings(void) +{ + if (allocated_strings) + { + STRING_T *str = allocated_strings; + do + { + STRING_T *t = str; + str = t->next; + free(t); + } while (str != allocated_strings); + allocated_strings = NULL; + } +} + +/* Not thread safe */ +char *sprintf_dup(const char *fmt, ...) +{ + va_list ap; + char *str; + va_start(ap, fmt); + str = vsprintf_dup(fmt, ap); + va_end(ap); + return str; +} + +/* Not thread safe */ +char *vsprintf_dup(const char *fmt, va_list ap) +{ + char scratch[512]; + int len; + STRING_T *str; + len = vsnprintf(scratch, sizeof(scratch), fmt, ap) + 1; + + if (len > sizeof(scratch)) + fatal_error("Maximum string length exceeded"); + + str = malloc(sizeof(STRING_T) + len); + if (!str) + fatal_error("Out of memory"); + + memcpy(str->data, scratch, len); + if (allocated_strings) + { + str->next = allocated_strings; + str->prev = allocated_strings->prev; + str->next->prev = str; + str->prev->next = str; + } + else + { + str->next = str; + str->prev = str; + allocated_strings = str; + } + + return str->data; +} + +int dir_exists(const char *dirname) +{ + struct stat finfo; + return (stat(dirname, &finfo) == 0) && S_ISDIR(finfo.st_mode); +} + +int file_exists(const char *dirname) +{ + struct stat finfo; + return (stat(dirname, &finfo) == 0) && S_ISREG(finfo.st_mode); +} + +void string_vec_init(STRING_VEC_T *vec) +{ + vec->num_strings = 0; + vec->max_strings = 0; + vec->strings = NULL; +} + +char *string_vec_add(STRING_VEC_T *vec, const char *str, int len) +{ + char *copy; + if (vec->num_strings == vec->max_strings) + { + if (vec->max_strings) + vec->max_strings *= 2; + else + vec->max_strings = 16; + vec->strings = realloc(vec->strings, vec->max_strings * sizeof(const char *)); + if (!vec->strings) + fatal_error("Out of memory"); + } + + if (len) + { + copy = malloc(len + 1); + strncpy(copy, str, len); + copy[len] = '\0'; + } + else + copy = strdup(str); + + if (!copy) + fatal_error("Out of memory"); + + vec->strings[vec->num_strings++] = copy; + + return copy; +} + +int string_vec_find(STRING_VEC_T *vec, const char *str, int len) +{ + int i; + + for (i = 0; i < vec->num_strings; i++) + { + if (len) + { + if ((strncmp(vec->strings[i], str, len) == 0) && + (vec->strings[i][len] == '\0')) + return i; + } + else if (strcmp(vec->strings[i], str) == 0) + return i; + } + + return -1; +} + +int string_vec_compare(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +void string_vec_sort(STRING_VEC_T *vec) +{ + qsort(vec->strings, vec->num_strings, sizeof(char *), &string_vec_compare); +} + +void string_vec_uninit(STRING_VEC_T *vec) +{ + int i; + for (i = 0; i < vec->num_strings; i++) + free(vec->strings[i]); + free(vec->strings); +} + +int error(const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "* "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + return 1; +} + +void fatal_error(const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "* "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} diff --git a/host_applications/linux/apps/dtoverlay/utils.h b/host_applications/linux/apps/dtoverlay/utils.h new file mode 100755 index 0000000..3d3b59f --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/utils.h @@ -0,0 +1,74 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTILS_H +#define UTILS_H + +typedef struct string_struct +{ + struct string_struct *prev; + struct string_struct *next; + char data[0]; +} STRING_T; + +typedef struct string_vec_struct +{ + int num_strings; + int max_strings; + char **strings; +} STRING_VEC_T; + +typedef struct overlay_help_state_struct OVERLAY_HELP_STATE_T; + +extern int opt_verbose; +extern int opt_dry_run; + +OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile); +void overlay_help_close(OVERLAY_HELP_STATE_T *state); +int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name); +int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field); +const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state); +void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, + const char *field, const char *label, + int indent, int strip_blanks); + +int run_cmd(const char *fmt, ...); +void free_string(const char *string); /* Not thread safe */ +void free_strings(void); /* Not thread safe */ +char *sprintf_dup(const char *fmt, ...); /* Not thread safe */ +char *vsprintf_dup(const char *fmt, va_list ap); /* Not thread safe */ +int dir_exists(const char *dirname); +int file_exists(const char *dirname); +void string_vec_init(STRING_VEC_T *vec); +char *string_vec_add(STRING_VEC_T *vec, const char *str, int len); +int string_vec_find(STRING_VEC_T *vec, const char *str, int len); +void string_vec_sort(STRING_VEC_T *vec); +void string_vec_uninit(STRING_VEC_T *vec); +int error(const char *fmt, ...); +void fatal_error(const char *fmt, ...); + +#endif diff --git a/host_applications/linux/apps/gencmd/CMakeLists.txt b/host_applications/linux/apps/gencmd/CMakeLists.txt new file mode 100755 index 0000000..f95d1a1 --- /dev/null +++ b/host_applications/linux/apps/gencmd/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +if (WIN32) + set(VCOS_PLATFORM win32) +else () + set(VCOS_PLATFORM pthreads) + add_definitions(-Wall -Werror) +endif () + +include_directories( ../../../.. + ../../../../interface/vcos + ../../../../interface/vcos/${VCOS_PLATFORM} ) + +#add_subdirectory( ../../../../interface/vcos/${VCOS_PLATFORM} vcos) +#add_subdirectory( ../../bin/gencmd) + +add_executable(vcgencmd gencmd.c) +target_link_libraries(vcgencmd vcos vchiq_arm vchostif) +install(TARGETS vcgencmd RUNTIME DESTINATION bin) + diff --git a/host_applications/linux/apps/gencmd/gencmd.c b/host_applications/linux/apps/gencmd/gencmd.c new file mode 100755 index 0000000..9b48cf9 --- /dev/null +++ b/host_applications/linux/apps/gencmd/gencmd.c @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* ---- Include Files ---------------------------------------------------- */ + +#include +#include +#include +#include +#include + +#include "interface/vmcs_host/vc_vchi_gencmd.h" + +int main( int argc, char **argv ) +{ + int instNum = 0; + VCHI_INSTANCE_T vchi_instance; + VCHI_CONNECTION_T *vchi_connection = NULL; + + if ( argc > 1 ) + { + if (( strcmp( argv[1], "0" ) == 0 ) || ( strcmp( argv[1], "1" ) == 0 )) + { + instNum = atoi( argv[1] ); + argv++; + argc--; + } + } + + vcos_init(); + + if ( vchi_initialise( &vchi_instance ) != 0) + { + printf( "VCHI initialization failed\n" ); + return -1; + } + + //create a vchi connection + if ( vchi_connect( NULL, 0, vchi_instance ) != 0) + { + printf( "VCHI connection failed\n" ); + return -1; + } + + vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1 ); + + if (argc > 1) + { + int i = 1; + char buffer[ 1024 ]; + size_t buffer_offset = 0; + clock_t before=0, after=0; + double time_diff; + uint32_t show_time = 0; + int ret; + + //reset the string + buffer[0] = '\0'; + + //first, strip out a potential leading -t + if( strcmp( argv[1], "-t" ) == 0 ) + { + show_time = 1; + i++; + } + + for (; i <= argc-1; i++) + { + buffer_offset = vcos_safe_strcpy( buffer, argv[i], sizeof(buffer), buffer_offset ); + buffer_offset = vcos_safe_strcpy( buffer, " ", sizeof(buffer), buffer_offset ); + } + + if( show_time ) + before = clock(); + + //send the gencmd for the argument + if (( ret = vc_gencmd_send( "%s", buffer )) != 0 ) + { + printf( "vc_gencmd_send returned %d\n", ret ); + } + + //get + print out the response! + if (( ret = vc_gencmd_read_response( buffer, sizeof( buffer ) )) != 0 ) + { + printf( "vc_gencmd_read_response returned %d\n", ret ); + } + + if( show_time ) + after = clock(); + + if( show_time ) + { + time_diff = ((double) (after - before)) / CLOCKS_PER_SEC; + + printf( "Time took %f seconds (%f msecs) (%f usecs)\n", time_diff, time_diff * 1000, time_diff * 1000000 ); + } + + if ( buffer[0] != '\0' ) + { + if ( buffer[ strlen( buffer) - 1] == '\n' ) + { + fputs( buffer, stdout ); + } + else + { + printf("%s\n", buffer ); + } + } + } + + vc_gencmd_stop(); + + //close the vchi connection + if ( vchi_disconnect( vchi_instance ) != 0) + { + printf( "VCHI disconnect failed\n" ); + return -1; + } + + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/CMakeLists.txt b/host_applications/linux/apps/hello_pi/CMakeLists.txt new file mode 100755 index 0000000..21b29f3 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/CMakeLists.txt @@ -0,0 +1,31 @@ +set(BUILD_FONT FALSE) + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/ilclient) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/vgfont) + +set(ILCLIENT_SRCS libs/ilclient/ilclient.c libs/ilclient/ilcore.c) +add_library(ilclient ${ILCLIENT_SRCS}) + +set(HELLO_PI_LIBS ilclient openmaxil bcm_host vcos vchiq_arm) + +add_subdirectory(hello_world) +add_subdirectory(hello_video) +add_subdirectory(hello_audio) +#add_subdirectory(hello_triangle) +#add_subdirectory(hello_triangle2) +#add_subdirectory(hello_dispmanx) +#add_subdirectory(hello_tiger) +#add_subdirectory(hello_encode) +#add_subdirectory(hello_jpeg) +#add_subdirectory(hello_videocube) +#add_subdirectory(hello_teapot) + +if(BUILD_FONT) +set(VGFONT_SRCS libs/vgfont/font.c libs/vgfont/vgft.c libs/vgfont/graphics.c) +set_source_files_properties(${VGFONT_SRCS} PROPERTIES COMPILE_DEFINITIONS) +add_library(vgfont ${VGFONT_SRCS}) + +add_subdirectory(hello_font) +endif(BUILD_FONT) diff --git a/host_applications/linux/apps/hello_pi/Makefile.include b/host_applications/linux/apps/hello_pi/Makefile.include new file mode 100755 index 0000000..fecf555 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/Makefile.include @@ -0,0 +1,28 @@ + +CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi + +LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/ilclient -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont + +INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux -I./ -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/ilclient -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont + +all: $(BIN) $(LIB) + +%.o: %.c + @rm -f $@ + $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.o: %.cpp + @rm -f $@ + $(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.bin: $(OBJS) + $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic + +%.a: $(OBJS) + $(AR) r $@ $^ + +clean: + for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done + @rm -f $(BIN) $(LIB) + + diff --git a/host_applications/linux/apps/hello_pi/README b/host_applications/linux/apps/hello_pi/README new file mode 100755 index 0000000..7a91127 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/README @@ -0,0 +1,21 @@ +Building on Pi +++++++++++++++ + +To build the test apps on the pi, first build the libs: +make -C libs/ilclient +make -C libs/vgfont + +then by entering each test app directory and run make. E.g. + cd hello_world + make + ./hello_world.bin + +Running ./rebuild.sh will rebuild the all libs and and apps. + + +Building on a different PC +++++++++++++++++++++++++++ + +If you want to build the samples on a different machine (cross-compile) then set: +SDKSTAGE= and CC= +before running make. diff --git a/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt new file mode 100755 index 0000000..03207c5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_audio.bin) +set(SRCS audio.c sinewave.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_audio/Makefile b/host_applications/linux/apps/hello_pi/hello_audio/Makefile new file mode 100755 index 0000000..3c3c139 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/Makefile @@ -0,0 +1,6 @@ +OBJS=audio.o sinewave.o +BIN=hello_audio.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_audio/audio.c b/host_applications/linux/apps/hello_pi/hello_audio/audio.c new file mode 100755 index 0000000..ebbcbce --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/audio.c @@ -0,0 +1,426 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Audio output demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +#define N_WAVE 1024 /* dimension of Sinewave[] */ +#define PI (1<<16>>1) +#define SIN(x) Sinewave[((x)>>6) & (N_WAVE-1)] +#define COS(x) SIN((x)+(PI>>1)) +#define OUT_CHANNELS(num_channels) ((num_channels) > 4 ? 8: (num_channels) > 2 ? 4: (num_channels)) +extern short Sinewave[]; + +#ifndef countof + #define countof(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + +#define BUFFER_SIZE_SAMPLES 1024 + +typedef int int32_t; + +typedef struct { + sem_t sema; + ILCLIENT_T *client; + COMPONENT_T *audio_render; + COMPONENT_T *list[2]; + OMX_BUFFERHEADERTYPE *user_buffer_list; // buffers owned by the client + uint32_t num_buffers; + uint32_t bytes_per_sample; +} AUDIOPLAY_STATE_T; + +static void input_buffer_callback(void *data, COMPONENT_T *comp) +{ + // do nothing - could add a callback to the user + // to indicate more buffers may be available. +} + +int32_t audioplay_create(AUDIOPLAY_STATE_T **handle, + uint32_t sample_rate, + uint32_t num_channels, + uint32_t bit_depth, + uint32_t num_buffers, + uint32_t buffer_size) +{ + uint32_t bytes_per_sample = (bit_depth * OUT_CHANNELS(num_channels)) >> 3; + int32_t ret = -1; + + *handle = NULL; + + // basic sanity check on arguments + if(sample_rate >= 8000 && sample_rate <= 192000 && + (num_channels >= 1 && num_channels <= 8) && + (bit_depth == 16 || bit_depth == 32) && + num_buffers > 0 && + buffer_size >= bytes_per_sample) + { + // buffer lengths must be 16 byte aligned for VCHI + int size = (buffer_size + 15) & ~15; + AUDIOPLAY_STATE_T *st; + + // buffer offsets must also be 16 byte aligned for VCHI + st = calloc(1, sizeof(AUDIOPLAY_STATE_T)); + + if(st) + { + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE param; + OMX_AUDIO_PARAM_PCMMODETYPE pcm; + int32_t s; + + ret = 0; + *handle = st; + + // create and start up everything + s = sem_init(&st->sema, 0, 1); + assert(s == 0); + + st->bytes_per_sample = bytes_per_sample; + st->num_buffers = num_buffers; + + st->client = ilclient_init(); + assert(st->client != NULL); + + ilclient_set_empty_buffer_done_callback(st->client, input_buffer_callback, st); + + error = OMX_Init(); + assert(error == OMX_ErrorNone); + + ilclient_create_component(st->client, &st->audio_render, "audio_render", ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_DISABLE_ALL_PORTS); + assert(st->audio_render != NULL); + + st->list[0] = st->audio_render; + + // set up the number/size of buffers + memset(¶m, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + param.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = 100; + + error = OMX_GetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamPortDefinition, ¶m); + assert(error == OMX_ErrorNone); + + param.nBufferSize = size; + param.nBufferCountActual = num_buffers; + + error = OMX_SetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamPortDefinition, ¶m); + assert(error == OMX_ErrorNone); + + // set the pcm parameters + memset(&pcm, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + pcm.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE); + pcm.nVersion.nVersion = OMX_VERSION; + pcm.nPortIndex = 100; + pcm.nChannels = OUT_CHANNELS(num_channels); + pcm.eNumData = OMX_NumericalDataSigned; + pcm.eEndian = OMX_EndianLittle; + pcm.nSamplingRate = sample_rate; + pcm.bInterleaved = OMX_TRUE; + pcm.nBitPerSample = bit_depth; + pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; + + switch(num_channels) { + case 1: + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; + case 3: + pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; + case 8: + pcm.eChannelMapping[7] = OMX_AUDIO_ChannelRS; + case 7: + pcm.eChannelMapping[6] = OMX_AUDIO_ChannelLS; + case 6: + pcm.eChannelMapping[5] = OMX_AUDIO_ChannelRR; + case 5: + pcm.eChannelMapping[4] = OMX_AUDIO_ChannelLR; + case 4: + pcm.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + case 2: + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; + } + + error = OMX_SetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamAudioPcm, &pcm); + assert(error == OMX_ErrorNone); + + ilclient_change_component_state(st->audio_render, OMX_StateIdle); + if(ilclient_enable_port_buffers(st->audio_render, 100, NULL, NULL, NULL) < 0) + { + // error + ilclient_change_component_state(st->audio_render, OMX_StateLoaded); + ilclient_cleanup_components(st->list); + + error = OMX_Deinit(); + assert(error == OMX_ErrorNone); + + ilclient_destroy(st->client); + + sem_destroy(&st->sema); + free(st); + *handle = NULL; + return -1; + } + + ilclient_change_component_state(st->audio_render, OMX_StateExecuting); + } + } + + return ret; +} + +int32_t audioplay_delete(AUDIOPLAY_STATE_T *st) +{ + OMX_ERRORTYPE error; + + ilclient_change_component_state(st->audio_render, OMX_StateIdle); + + error = OMX_SendCommand(ILC_GET_HANDLE(st->audio_render), OMX_CommandStateSet, OMX_StateLoaded, NULL); + assert(error == OMX_ErrorNone); + + ilclient_disable_port_buffers(st->audio_render, 100, st->user_buffer_list, NULL, NULL); + ilclient_change_component_state(st->audio_render, OMX_StateLoaded); + ilclient_cleanup_components(st->list); + + error = OMX_Deinit(); + assert(error == OMX_ErrorNone); + + ilclient_destroy(st->client); + + sem_destroy(&st->sema); + free(st); + + return 0; +} + +uint8_t *audioplay_get_buffer(AUDIOPLAY_STATE_T *st) +{ + OMX_BUFFERHEADERTYPE *hdr = NULL; + + hdr = ilclient_get_input_buffer(st->audio_render, 100, 0); + + if(hdr) + { + // put on the user list + sem_wait(&st->sema); + + hdr->pAppPrivate = st->user_buffer_list; + st->user_buffer_list = hdr; + + sem_post(&st->sema); + } + + return hdr ? hdr->pBuffer : NULL; +} + +int32_t audioplay_play_buffer(AUDIOPLAY_STATE_T *st, + uint8_t *buffer, + uint32_t length) +{ + OMX_BUFFERHEADERTYPE *hdr = NULL, *prev = NULL; + int32_t ret = -1; + + if(length % st->bytes_per_sample) + return ret; + + sem_wait(&st->sema); + + // search through user list for the right buffer header + hdr = st->user_buffer_list; + while(hdr != NULL && hdr->pBuffer != buffer && hdr->nAllocLen < length) + { + prev = hdr; + hdr = hdr->pAppPrivate; + } + + if(hdr) // we found it, remove from list + { + ret = 0; + if(prev) + prev->pAppPrivate = hdr->pAppPrivate; + else + st->user_buffer_list = hdr->pAppPrivate; + } + + sem_post(&st->sema); + + if(hdr) + { + OMX_ERRORTYPE error; + + hdr->pAppPrivate = NULL; + hdr->nOffset = 0; + hdr->nFilledLen = length; + + error = OMX_EmptyThisBuffer(ILC_GET_HANDLE(st->audio_render), hdr); + assert(error == OMX_ErrorNone); + } + + return ret; +} + +int32_t audioplay_set_dest(AUDIOPLAY_STATE_T *st, const char *name) +{ + int32_t success = -1; + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest; + + if (name && strlen(name) < sizeof(ar_dest.sName)) + { + OMX_ERRORTYPE error; + memset(&ar_dest, 0, sizeof(ar_dest)); + ar_dest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); + ar_dest.nVersion.nVersion = OMX_VERSION; + strcpy((char *)ar_dest.sName, name); + + error = OMX_SetConfig(ILC_GET_HANDLE(st->audio_render), OMX_IndexConfigBrcmAudioDestination, &ar_dest); + assert(error == OMX_ErrorNone); + success = 0; + } + + return success; +} + + +uint32_t audioplay_get_latency(AUDIOPLAY_STATE_T *st) +{ + OMX_PARAM_U32TYPE param; + OMX_ERRORTYPE error; + + memset(¶m, 0, sizeof(OMX_PARAM_U32TYPE)); + param.nSize = sizeof(OMX_PARAM_U32TYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = 100; + + error = OMX_GetConfig(ILC_GET_HANDLE(st->audio_render), OMX_IndexConfigAudioRenderingLatency, ¶m); + assert(error == OMX_ErrorNone); + + return param.nU32; +} + +#define CTTW_SLEEP_TIME 10 +#define MIN_LATENCY_TIME 20 + +static const char *audio_dest[] = {"local", "hdmi"}; +void play_api_test(int samplerate, int bitdepth, int nchannels, int dest) +{ + AUDIOPLAY_STATE_T *st; + int32_t ret; + unsigned int i, j, n; + int phase = 0; + int inc = 256<<16; + int dinc = 0; + int buffer_size = (BUFFER_SIZE_SAMPLES * bitdepth * OUT_CHANNELS(nchannels))>>3; + + assert(dest == 0 || dest == 1); + + ret = audioplay_create(&st, samplerate, nchannels, bitdepth, 10, buffer_size); + assert(ret == 0); + + ret = audioplay_set_dest(st, audio_dest[dest]); + assert(ret == 0); + + // iterate for 5 seconds worth of packets + for (n=0; n<((samplerate * 1000)/ BUFFER_SIZE_SAMPLES); n++) + { + uint8_t *buf; + int16_t *p; + uint32_t latency; + + while((buf = audioplay_get_buffer(st)) == NULL) + usleep(10*1000); + + p = (int16_t *) buf; + + // fill the buffer + for (i=0; i>16; + inc += dinc; + if (inc>>16 < 512) + dinc++; + else + dinc--; + + for(j=0; j (samplerate * (MIN_LATENCY_TIME + CTTW_SLEEP_TIME) / 1000)) + usleep(CTTW_SLEEP_TIME*1000); + + ret = audioplay_play_buffer(st, buf, buffer_size); + assert(ret == 0); + } + + audioplay_delete(st); +} + +int main (int argc, char **argv) +{ + // 0=headphones, 1=hdmi + int audio_dest = 0; + // audio sample rate in Hz + int samplerate = 48000; + // numnber of audio channels + int channels = 2; + // number of bits per sample + int bitdepth = 16; + bcm_host_init(); + + if (argc > 1) + audio_dest = atoi(argv[1]); + if (argc > 2) + channels = atoi(argv[2]); + if (argc > 3) + samplerate = atoi(argv[3]); + + printf("Outputting audio to %s\n", audio_dest==0 ? "analogue":"hdmi"); + + play_api_test(samplerate, bitdepth, channels, audio_dest); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h b/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h new file mode 100755 index 0000000..9c96938 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h @@ -0,0 +1,159 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// API for host applications to deliver raw PCM samples to rendered on VideoCore + +#ifndef AUDIOPLAY_H +#define AUDIOPLAY_H + +/** + * \file + * + * \brief This API allows the host to provide PCM samples to be + * rendered via audio_render. + * + * This file describes a simple API for host applications to play sound + * using VideoCore. It includes the functionality to: + * + * \li open/close + * \li set pcm parameters + * \li set buffer size parameters + * \li retrieve empty buffer available to use + * \li send full buffer to be played + * \li retrieve current buffering level + * + * This API has no thread context of it's own, so the caller must be + * aware that the IL API will be used in context. This has + * implications on executing calls inside callback contexts, and on + * the minimum size of stack that the caller requires. See the + * ilclient_stack_size() function for assistance. + * + * This API will use a single audio_render IL component, and + * supply buffers to the input port using the OpenMAX IL base profile mode. + ********************************************************************************/ + +struct AUDIOPLAY_STATE_T; + +/** + * The AUDIOPLAY_STATE_T is an opaque type that represents the + * audioplus engine handle. + *******************************************************************************/ +typedef struct AUDIOPLAY_STATE_T AUDIOPLAY_STATE_T; + +/** + * The audioplay_create() function creates the audioplay object. + * + * @param handle On success, this is filled in with a handle to use in other + * API functions. + * + * @param sample_rate The sample rate, in samples per second, for the PCM data. + * This shall be between 8000 and 96000. + * + * @param num_channels The number of channels for the PCM data. Must be 1, 2, 4, or 8. + * Channels must be sent interleaved. + * + * @param bit_depth The bitdepth per channel per sample. Must be 16 or 32. + * + * @param num_buffers The number of buffers that will be created to write PCM + * samples into. + * + * @param buffer_size The size in bytes of each buffer that will be used to write + * PCM samples into. Note that small buffers of less than a few + * Kb in size may be faster than larger buffers, although this is + * platform dependent. + * + * @return 0 on success, -1 on failure. + *********************************************************************************/ +VCHPRE_ int32_t VCHPOST_ audioplay_create(AUDIOPLAY_STATE_T **handle, + uint32_t sample_rate, + uint32_t num_channels, + uint32_t bit_depth, + uint32_t num_buffers, + uint32_t buffer_size); + +/** + * The audioplay_delete() function deletes the audioplay object. + * + * @param handle Must be a handle previously created by + * audioplay_create(). After calling this + * function that handle is no longer valid. Any + * buffers provided by audioplay_get_buffer() + * are also no longer valid and must not be referenced. + * + * @return 0 on success, -1 on failure. + ********************************************************************************/ +VCHPRE_ int32_t VCHPOST_ audioplay_delete(AUDIOPLAY_STATE_T *handle); + +/** + * The audioplay_get_buffer() function requests an empty + * buffer. Any buffer returned will have the valid size indicated in + * the call to audioplay_create(). + * + * @param handle Must be a handle previously created by + * audioplay_create(). + * + * @return A pointer to an available buffer. If no buffers are + * available, then NULL will be returned. + *********************************************************************************/ +VCHPRE_ uint8_t * VCHPOST_ audioplay_get_buffer(AUDIOPLAY_STATE_T *handle); + + +/** + * The audioplay_play_buffer() sends a buffer containing + * raw samples to be rendered. + * + * @param handle Must be a handle previously created by + * audioplay_create(). + * + * @param buffer Must be a pointer previously returned by + * audioplay_get_buffer(). After calling this function + * the buffer pointer must not be referenced until returned + * again by another call to audioplay_get_buffer(). + * + * @param length Length in bytes of valid data. Must be a whole number of + * samples, ie a multiple of (num_channels*bit_depth/8). + * + * @return 0 on success, -1 on failure + ********************************************************************************/ +VCHPRE_ int32_t VCHPOST_ audioplay_play_buffer(AUDIOPLAY_STATE_T *handle, + uint8_t *buffer, + uint32_t length); + +/** + * The audioplay_get_latency() requests the current audio + * playout buffer size in samples, which is the latency until the next + * sample supplied is to be rendered. + * + * @param handle Must be a handle previously created by + * audioplay_create(). + * + * @return Number of samples currently buffered. + *********************************************************************************/ +VCHPRE_ uint32_t VCHPOST_ audioplay_get_latency(AUDIOPLAY_STATE_T *handle); + + +#endif /* AUDIOPLAY_H */ diff --git a/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c b/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c new file mode 100755 index 0000000..10668b5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c @@ -0,0 +1,160 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Lookup table for audio output demo + +short Sinewave[] = { + 0, 201, 402, 603, 804, 1005, 1206, 1406, + 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, + 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, + 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, + 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, + 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, + 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, + 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, + 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, + 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, + 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, + 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, + 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, + 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, + 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, + 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, + 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, + 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, + 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, + 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, + 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, + 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, + 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, + 30851, 30918, 30984, 31049, + 31113, 31175, 31236, 31297, + 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, + 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, + 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, + 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, + 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, + 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, + 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, + 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, + 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, + 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, + 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, + 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, + 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, + 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, + 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, + 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, + 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, + 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, + 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, + 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, + 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, + 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, + 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, + 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, + 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, + 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, + 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, + 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, + 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, + 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, + 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, + 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, + 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, + 0, -201, -402, -603, -804, -1005, -1206, -1406, + -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, + -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, + -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, + -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, + -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, + -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, + -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, + -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, + -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, + -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, + -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, + -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, + -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, + -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, + -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, + -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, + -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, + -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, + -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, + -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, + -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, + -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, + -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, + -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, + -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, + -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, + -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, + -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, + -32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736, + -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628, + -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, + -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, + -32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, + -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413, + -31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, + -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349, + -30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, + -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992, + -28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, + -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, + -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, + -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456, + -25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, + -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311, + -23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, + -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942, + -20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, + -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371, + -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, + -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, + -15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, + -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724, + -12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, + -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703, + -9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, + -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589, + -6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, + -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, + -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, + -1607, -1406, -1206, -1005, -804, -603, -402, -201, +}; diff --git a/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt new file mode 100755 index 0000000..0471a1d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_dispmanx.bin) +set(SRCS dispmanx.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile b/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile new file mode 100755 index 0000000..98dc5e9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile @@ -0,0 +1,5 @@ +OBJS=dispmanx.o +BIN=hello_dispmanx.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c b/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c new file mode 100755 index 0000000..1f23b32 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c @@ -0,0 +1,163 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A simple demo using dispmanx to display an overlay + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#define WIDTH 200 +#define HEIGHT 200 + +#ifndef ALIGN_UP +#define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1)) +#endif + +typedef struct +{ + DISPMANX_DISPLAY_HANDLE_T display; + DISPMANX_MODEINFO_T info; + void *image; + DISPMANX_UPDATE_HANDLE_T update; + DISPMANX_RESOURCE_HANDLE_T resource; + DISPMANX_ELEMENT_HANDLE_T element; + uint32_t vc_image_ptr; + +} RECT_VARS_T; + +static RECT_VARS_T gRectVars; + +static void FillRect( VC_IMAGE_TYPE_T type, void *image, int pitch, int aligned_height, int x, int y, int w, int h, int val ) +{ + int row; + int col; + + uint16_t *line = (uint16_t *)image + y * (pitch>>1) + x; + + for ( row = 0; row < h; row++ ) + { + for ( col = 0; col < w; col++ ) + { + line[col] = val; + } + line += (pitch>>1); + } +} + +int main(void) +{ + RECT_VARS_T *vars; + uint32_t screen = 0; + int ret; + VC_RECT_T src_rect; + VC_RECT_T dst_rect; + VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565; + int width = WIDTH, height = HEIGHT; + int pitch = ALIGN_UP(width*2, 32); + int aligned_height = ALIGN_UP(height, 16); + VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, + 120, /*alpha 0->255*/ + 0 }; + + vars = &gRectVars; + + bcm_host_init(); + + printf("Open display[%i]...\n", screen ); + vars->display = vc_dispmanx_display_open( screen ); + + ret = vc_dispmanx_display_get_info( vars->display, &vars->info); + assert(ret == 0); + printf( "Display is %d x %d\n", vars->info.width, vars->info.height ); + + vars->image = calloc( 1, pitch * height ); + assert(vars->image); + + FillRect( type, vars->image, pitch, aligned_height, 0, 0, width, height, 0xFFFF ); + FillRect( type, vars->image, pitch, aligned_height, 0, 0, width, height, 0xF800 ); + FillRect( type, vars->image, pitch, aligned_height, 20, 20, width - 40, height - 40, 0x07E0 ); + FillRect( type, vars->image, pitch, aligned_height, 40, 40, width - 80, height - 80, 0x001F ); + + vars->resource = vc_dispmanx_resource_create( type, + width, + height, + &vars->vc_image_ptr ); + assert( vars->resource ); + vc_dispmanx_rect_set( &dst_rect, 0, 0, width, height); + ret = vc_dispmanx_resource_write_data( vars->resource, + type, + pitch, + vars->image, + &dst_rect ); + assert( ret == 0 ); + vars->update = vc_dispmanx_update_start( 10 ); + assert( vars->update ); + + vc_dispmanx_rect_set( &src_rect, 0, 0, width << 16, height << 16 ); + + vc_dispmanx_rect_set( &dst_rect, ( vars->info.width - width ) / 2, + ( vars->info.height - height ) / 2, + width, + height ); + + vars->element = vc_dispmanx_element_add( vars->update, + vars->display, + 2000, // layer + &dst_rect, + vars->resource, + &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha, + NULL, // clamp + VC_IMAGE_ROT0 ); + + ret = vc_dispmanx_update_submit_sync( vars->update ); + assert( ret == 0 ); + + printf( "Sleeping for 10 seconds...\n" ); + sleep( 10 ); + + vars->update = vc_dispmanx_update_start( 10 ); + assert( vars->update ); + ret = vc_dispmanx_element_remove( vars->update, vars->element ); + assert( ret == 0 ); + ret = vc_dispmanx_update_submit_sync( vars->update ); + assert( ret == 0 ); + ret = vc_dispmanx_resource_delete( vars->resource ); + assert( ret == 0 ); + ret = vc_dispmanx_display_close( vars->display ); + assert( ret == 0 ); + + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt new file mode 100755 index 0000000..147623b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_encode.bin) +set(SRCS encode.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_encode/Makefile b/host_applications/linux/apps/hello_pi/hello_encode/Makefile new file mode 100755 index 0000000..62d320e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_encode/Makefile @@ -0,0 +1,6 @@ +OBJS=encode.o +BIN=hello_encode.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_encode/encode.c b/host_applications/linux/apps/hello_pi/hello_encode/encode.c new file mode 100755 index 0000000..7acb54b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_encode/encode.c @@ -0,0 +1,320 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, Kalle Vahlman + Tuomas Kulve +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video encode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +#define NUMFRAMES 300 +#define WIDTH 640 +#define PITCH ((WIDTH+31)&~31) +#define HEIGHT ((WIDTH)*9/16) +#define HEIGHT16 ((HEIGHT+15)&~15) +#define SIZE ((WIDTH * HEIGHT16 * 3)/2) + +// generate an animated test card in YUV format +static int +generate_test_card(void *buf, OMX_U32 * filledLen, int frame) +{ + int i, j; + char *y = buf, *u = y + PITCH * HEIGHT16, *v = + u + (PITCH >> 1) * (HEIGHT16 >> 1); + + for (j = 0; j < HEIGHT / 2; j++) { + char *py = y + 2 * j * PITCH; + char *pu = u + j * (PITCH >> 1); + char *pv = v + j * (PITCH >> 1); + for (i = 0; i < WIDTH / 2; i++) { + int z = (((i + frame) >> 4) ^ ((j + frame) >> 4)) & 15; + py[0] = py[1] = py[PITCH] = py[PITCH + 1] = 0x80 + z * 0x8; + pu[0] = 0x00 + z * 0x10; + pv[0] = 0x80 + z * 0x30; + py += 2; + pu++; + pv++; + } + } + *filledLen = SIZE; + return 1; +} + +static void +print_def(OMX_PARAM_PORTDEFINITIONTYPE def) +{ + printf("Port %u: %s %u/%u %u %u %s,%s,%s %ux%u %ux%u @%u %u\n", + def.nPortIndex, + def.eDir == OMX_DirInput ? "in" : "out", + def.nBufferCountActual, + def.nBufferCountMin, + def.nBufferSize, + def.nBufferAlignment, + def.bEnabled ? "enabled" : "disabled", + def.bPopulated ? "populated" : "not pop.", + def.bBuffersContiguous ? "contig." : "not cont.", + def.format.video.nFrameWidth, + def.format.video.nFrameHeight, + def.format.video.nStride, + def.format.video.nSliceHeight, + def.format.video.xFramerate, def.format.video.eColorFormat); +} + +static int +video_encode_test(char *outputfilename) +{ + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_PARAM_PORTDEFINITIONTYPE def; + COMPONENT_T *video_encode = NULL; + COMPONENT_T *list[5]; + OMX_BUFFERHEADERTYPE *buf; + OMX_BUFFERHEADERTYPE *out; + OMX_ERRORTYPE r; + ILCLIENT_T *client; + int status = 0; + int framenumber = 0; + FILE *outf; + + memset(list, 0, sizeof(list)); + + if ((client = ilclient_init()) == NULL) { + return -3; + } + + if (OMX_Init() != OMX_ErrorNone) { + ilclient_destroy(client); + return -4; + } + + // create video_encode + r = ilclient_create_component(client, &video_encode, "video_encode", + ILCLIENT_DISABLE_ALL_PORTS | + ILCLIENT_ENABLE_INPUT_BUFFERS | + ILCLIENT_ENABLE_OUTPUT_BUFFERS); + if (r != 0) { + printf + ("ilclient_create_component() for video_encode failed with %x!\n", + r); + exit(1); + } + list[0] = video_encode; + + // get current settings of video_encode component from port 200 + memset(&def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + def.nVersion.nVersion = OMX_VERSION; + def.nPortIndex = 200; + + if (OMX_GetParameter + (ILC_GET_HANDLE(video_encode), OMX_IndexParamPortDefinition, + &def) != OMX_ErrorNone) { + printf("%s:%d: OMX_GetParameter() for video_encode port 200 failed!\n", + __FUNCTION__, __LINE__); + exit(1); + } + + print_def(def); + + // Port 200: in 1/1 115200 16 enabled,not pop.,not cont. 320x240 320x240 @1966080 20 + def.format.video.nFrameWidth = WIDTH; + def.format.video.nFrameHeight = HEIGHT; + def.format.video.xFramerate = 30 << 16; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar; + + print_def(def); + + r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), + OMX_IndexParamPortDefinition, &def); + if (r != OMX_ErrorNone) { + printf + ("%s:%d: OMX_SetParameter() for video_encode port 200 failed with %x!\n", + __FUNCTION__, __LINE__, r); + exit(1); + } + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 201; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + printf("OMX_SetParameter for video_encode:201...\n"); + r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), + OMX_IndexParamVideoPortFormat, &format); + if (r != OMX_ErrorNone) { + printf + ("%s:%d: OMX_SetParameter() for video_encode port 201 failed with %x!\n", + __FUNCTION__, __LINE__, r); + exit(1); + } + + OMX_VIDEO_PARAM_BITRATETYPE bitrateType; + // set current bitrate to 1Mbit + memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); + bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE); + bitrateType.nVersion.nVersion = OMX_VERSION; + bitrateType.eControlRate = OMX_Video_ControlRateVariable; + bitrateType.nTargetBitrate = 1000000; + bitrateType.nPortIndex = 201; + r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), + OMX_IndexParamVideoBitrate, &bitrateType); + if (r != OMX_ErrorNone) { + printf + ("%s:%d: OMX_SetParameter() for bitrate for video_encode port 201 failed with %x!\n", + __FUNCTION__, __LINE__, r); + exit(1); + } + + + // get current bitrate + memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); + bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE); + bitrateType.nVersion.nVersion = OMX_VERSION; + bitrateType.nPortIndex = 201; + + if (OMX_GetParameter + (ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, + &bitrateType) != OMX_ErrorNone) { + printf("%s:%d: OMX_GetParameter() for video_encode for bitrate port 201 failed!\n", + __FUNCTION__, __LINE__); + exit(1); + } + printf("Current Bitrate=%u\n",bitrateType.nTargetBitrate); + + + + printf("encode to idle...\n"); + if (ilclient_change_component_state(video_encode, OMX_StateIdle) == -1) { + printf + ("%s:%d: ilclient_change_component_state(video_encode, OMX_StateIdle) failed", + __FUNCTION__, __LINE__); + } + + printf("enabling port buffers for 200...\n"); + if (ilclient_enable_port_buffers(video_encode, 200, NULL, NULL, NULL) != 0) { + printf("enabling port buffers for 200 failed!\n"); + exit(1); + } + + printf("enabling port buffers for 201...\n"); + if (ilclient_enable_port_buffers(video_encode, 201, NULL, NULL, NULL) != 0) { + printf("enabling port buffers for 201 failed!\n"); + exit(1); + } + + printf("encode to executing...\n"); + ilclient_change_component_state(video_encode, OMX_StateExecuting); + + outf = fopen(outputfilename, "w"); + if (outf == NULL) { + printf("Failed to open '%s' for writing video\n", outputfilename); + exit(1); + } + + printf("looping for buffers...\n"); + do { + buf = ilclient_get_input_buffer(video_encode, 200, 1); + if (buf == NULL) { + printf("Doh, no buffers for me!\n"); + } + else { + /* fill it */ + generate_test_card(buf->pBuffer, &buf->nFilledLen, framenumber++); + + if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_encode), buf) != + OMX_ErrorNone) { + printf("Error emptying buffer!\n"); + } + + out = ilclient_get_output_buffer(video_encode, 201, 1); + + r = OMX_FillThisBuffer(ILC_GET_HANDLE(video_encode), out); + if (r != OMX_ErrorNone) { + printf("Error filling buffer: %x\n", r); + } + + if (out != NULL) { + if (out->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + int i; + for (i = 0; i < out->nFilledLen; i++) + printf("%x ", out->pBuffer[i]); + printf("\n"); + } + + r = fwrite(out->pBuffer, 1, out->nFilledLen, outf); + if (r != out->nFilledLen) { + printf("fwrite: Error emptying buffer: %d!\n", r); + } + else { + printf("Writing frame %d/%d\n", framenumber, NUMFRAMES); + } + out->nFilledLen = 0; + } + else { + printf("Not getting it :(\n"); + } + + } + } + while (framenumber < NUMFRAMES); + + fclose(outf); + + printf("Teardown.\n"); + + printf("disabling port buffers for 200 and 201...\n"); + ilclient_disable_port_buffers(video_encode, 200, NULL, NULL, NULL); + ilclient_disable_port_buffers(video_encode, 201, NULL, NULL, NULL); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return status; +} + +int +main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(1); + } + bcm_host_init(); + return video_encode_test(argv[1]); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c new file mode 100755 index 0000000..5bda67b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c @@ -0,0 +1,135 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft.h" + +#define GPU_FFT_BUSY_WAIT_LIMIT (5<<12) // ~1ms + +typedef struct GPU_FFT_COMPLEX COMPLEX; + +int gpu_fft_prepare( + int mb, // mailbox file_desc + int log2_N, // log2(FFT_length) = 8...22 + int direction, // GPU_FFT_FWD: fft(); GPU_FFT_REV: ifft() + int jobs, // number of transforms in batch + struct GPU_FFT **fft) { + + unsigned info_bytes, twid_bytes, data_bytes, code_bytes, unif_bytes, mail_bytes; + unsigned size, *uptr, vc_tw, vc_data; + int i, q, shared, unique, passes, ret; + + struct GPU_FFT_BASE *base; + struct GPU_FFT_PTR ptr; + struct GPU_FFT *info; + + if (gpu_fft_twiddle_size(log2_N, &shared, &unique, &passes)) return -2; + + info_bytes = 4096; + data_bytes = (1+((sizeof(COMPLEX)<x = 1<y = jobs; + + // Ping-pong buffers leave results in or out of place + info->in = info->out = ptr.arm.cptr; + info->step = data_bytes / sizeof(COMPLEX); + if (passes&1) info->out += info->step * jobs; // odd => out of place + vc_data = gpu_fft_ptr_inc(&ptr, data_bytes*jobs*2); + + // Shader code + memcpy(ptr.arm.vptr, gpu_fft_shader_code(log2_N), code_bytes); + base->vc_code = gpu_fft_ptr_inc(&ptr, code_bytes); + + // Twiddles + gpu_fft_twiddle_data(log2_N, direction, ptr.arm.fptr); + vc_tw = gpu_fft_ptr_inc(&ptr, twid_bytes); + + uptr = ptr.arm.uptr; + + // Uniforms + for (q=0; qvc_unifs[q] = gpu_fft_ptr_inc(&ptr, sizeof(int)*(5+jobs*2)); + } + + if ((jobs<vc_msg = 0; + } + else { + // Mailbox message + for (q=0; qvc_unifs[q]; + *uptr++ = base->vc_code; + } + + base->vc_msg = ptr.vc; + } + + *fft = info; + return 0; +} + +unsigned gpu_fft_execute(struct GPU_FFT *info) { + return gpu_fft_base_exec(&info->base, GPU_FFT_QPUS); +} + +void gpu_fft_release(struct GPU_FFT *info) { + gpu_fft_base_release(&info->base); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h new file mode 100755 index 0000000..8b1cb08 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h @@ -0,0 +1,101 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __GPU_FFT__ +#define __GPU_FFT__ + +#define GPU_FFT_QPUS 8 + +#define GPU_FFT_PI 3.14159265358979323846 + +#define GPU_FFT_FWD 0 // forward FFT +#define GPU_FFT_REV 1 // inverse FFT + +struct GPU_FFT_COMPLEX { + float re, im; +}; + +struct GPU_FFT_PTR { + unsigned vc; + union { struct GPU_FFT_COMPLEX *cptr; + void *vptr; + char *bptr; + float *fptr; + unsigned *uptr; } arm; +}; + +struct GPU_FFT_BASE { + int mb; + unsigned handle, size, vc_msg, vc_code, vc_unifs[GPU_FFT_QPUS], peri_size; + volatile unsigned *peri; +}; + +struct GPU_FFT { + struct GPU_FFT_BASE base; + struct GPU_FFT_COMPLEX *in, *out; + int x, y, step; +}; + +int gpu_fft_prepare( + int mb, // mailbox file_desc + int log2_N, // log2(FFT_length) = 8...22 + int direction, // GPU_FFT_FWD: fft(); GPU_FFT_REV: ifft() + int jobs, // number of transforms in batch + struct GPU_FFT **fft); + +unsigned gpu_fft_execute( + struct GPU_FFT *info); + +void gpu_fft_release( + struct GPU_FFT *info); + +// private +int gpu_fft_twiddle_size(int, int *, int *, int *); +void gpu_fft_twiddle_data(int, int, float *); +unsigned int gpu_fft_shader_size(int); +unsigned int *gpu_fft_shader_code(int); + +// gpu_fft_base: + +unsigned gpu_fft_base_exec ( + struct GPU_FFT_BASE *base, + int num_qpus); + +int gpu_fft_alloc ( + int mb, + unsigned size, + struct GPU_FFT_PTR *ptr); + +void gpu_fft_base_release( + struct GPU_FFT_BASE *base); + +unsigned gpu_fft_ptr_inc ( + struct GPU_FFT_PTR *ptr, + int bytes); + +#endif // __GPU_FFT__ diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt new file mode 100755 index 0000000..49d65bf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt @@ -0,0 +1,159 @@ +BCM2835 "GPU_FFT" release 3.0 by Andrew Holme, 2015. + +GPU_FFT is an FFT library for the Raspberry Pi which exploits the BCM2835 SoC +3D hardware to deliver ten times more data throughput than is possible on the +700 MHz ARM of the Pi 1. Kernels are provided for all power-of-2 FFT lengths +between 256 and 4,194,304 points inclusive. A transpose function, which also +uses the 3D hardware, is provided to support 2-dimensional transforms. + + +*** Accuracy *** + +GPU_FFT uses single-precision floats for data and twiddle factors. The output +is not scaled. The relative root-mean-square (rms) error in parts-per-million +(ppm) for different transform lengths (N) is typically: + +log2(N) | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 +ppm rms | 0.33 | 0.46 | 0.52 | 0.59 | 0.78 | 0.83 | 0.92 | 0.98 + +log2(N) | 16 | 17 | 18 | 19 | 20 | 21 | 22 +ppm rms | 1.0 | 1.3 | 1.3 | 1.4 | 1.5 | 1.5 | 1.5 + +Accuracy has improved significantly over previous releases at the expense of a +small (2%) performance hit; however, FFTW is still one order of magnitude more +accurate than GPU_FFT. + + +*** Throughput *** + +GPU_FFT 1.0 had to be invoked through a "mailbox" which added a 100us overhead +on every call. To mitigate this, batches of transforms could be submitted via +a single call. GPU_FFT now avoids this 100us overhead by poking GPU registers +directly from the ARM if total batch runtime will be short; but still uses the +mailbox for longer jobs to avoid busy waiting at 100% CPU for too long. + +Typical per-transform runtimes for batch sizes of 1 and 10; and comparative +figures for FFTW (FFTW_MEASURE mode) on a Pi 1 with L2 cache enabled are: + +log2(N) | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 + 1 | 0.033 | 0.049 | 0.070 | 0.12 | 0.25 | 0.61 | 1.2 | 3.5 + 10 | 0.017 | 0.029 | 0.049 | 0.11 | 0.27 | 0.66 | 1.2 | 3.3 + FFTW | 0.092 | 0.22 | 0.48 | 0.95 | 3.0 | 5.1 | 12 | 31 + +log2(N) | 16 | 17 | 18 | 19 | 20 | 21 | 22 All times in + 1 | 7.0 | 17 | 43 | 97 | 194 | 388 | 786 milliseconds + FFTW | 83 | 180 | 560 | 670 | 1600 | 3400 | 8800 2 sig. figs. + + +*** API functions *** + + gpu_fft_prepare() Call once to allocate memory and initialise data + structures. Returns 0 for success. + + gpu_fft_execute() Call one or more times to execute a previously + prepared FFT batch. Returns 0 for success. + + gpu_fft_release() Call once to release resources after use. + GPU memory is permanently lost if not freed. + + +*** Parameters *** + + int mb Mailbox file descriptor obtained by calling mbox_open() + + int log2_N log2(FFT length) = 8 to 22 + + int direction FFT direction: GPU_FFT_FWD for forward FFT + GPU_FFT_REV for inverse FFT + + int jobs Number of transforms in batch = 1 or more + + GPU_FFT ** Output parameter from prepare: control structure. + GPU_FFT * Input parameter to execute and release + + +*** Data format *** + +Complex data arrays are stored as alternate real and imaginary parts: + + struct GPU_FFT_COMPLEX { + float re, im; + }; + +The GPU_FFT struct created by gpu_fft_prepare() contains pointers to the input +and output arrays: + + struct GPU_FFT { + struct GPU_FFT_COMPLEX *in, *out; + +When executing a batch of transforms, buffer pointers are obtained as follows: + + struct GPU_FFT *fft = gpu_fft_prepare( ... , jobs); + for (int j=0; jin + j*fft->step; + struct GPU_FFT_COMPLEX *out = fft->out + j*fft->step; + +GPU_FFT.step is greater than FFT length because a guard space is left between +buffers for caching and alignment reasons. + +GPU_FFT performs multiple passes between ping-pong buffers. The final output +lands in the same buffer as input after an even number of passes. Transforms +where log2_N=12...16 use an odd number of passes and the final result is left +out-of-place. The input data is never preserved. + + +*** Example program *** + +The code that produced the above accuracy and performance figures is included +as a demo with the latest Raspbian distro. Build and run it as follows: + +cd /opt/vc/src/hello_pi/hello_fft +make +sudo ./hello_fft.bin 12 + +It accepts three optional command-line arguments: + +The special character device is required for the ioctl mailbox through which +the ARM communicates with the Videocore GPU. + + +*** With Open GL on Pi 1 *** + +GPU_FFT and Open GL will run concurrently on Pi 1 if GPU_FFT is configured not +to use VC4 L2 cache by zeroing a define in file gpu_fft_base.c as follows: + +#define GPU_FFT_USE_VC4_L2_CACHE 0 // Pi 1 only: cached=1; direct=0 + +Overall performance will probably be higher if GPU_FFT and Open GL take turns +at using the 3D hardware. Since eglSwapBuffers() returns immediately without +waiting for rendering, call glFlush() and glFinish() afterwards as follows: + + for (;;) { + .... + eglSwapBuffers(....); // non-blocking call returns immediately + glFlush(); + glFinish(); // wait until V3D hardware is idle + .... + gpu_fft_execute(....); // blocking call + .... + } + + +*** 2-dimensional FFT *** + +Please study the hello_fft_2d demo source, which is built and executed thus: + +make hello_fft_2d.bin +sudo ./hello_fft_2d.bin + +This generates a Windows BMP file: "hello_fft_2d.bmp" + +The demo uses a square 512x512 array; however, rectangular arrays are allowed. +The following lines in gpu_fft_trans.c will do what is safe: + + ptr.arm.uptr[6] = src->x < dst->y? src->x : dst->y; + ptr.arm.uptr[7] = src->y < dst->x? src->y : dst->x; + +One may transpose the output from the second FFT pass back into the first pass +input buffer, by preparing and executing a second transposition; however, this +is probably unnecessary. It depends on how the final output will be accessed. diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c new file mode 100755 index 0000000..76656b8 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c @@ -0,0 +1,190 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft.h" +#include "mailbox.h" + +#define BUS_TO_PHYS(x) ((x)&~0xC0000000) + +// V3D spec: http://www.broadcom.com/docs/support/videocore/VideoCoreIV-AG100-R.pdf +#define V3D_L2CACTL (0xC00020>>2) +#define V3D_SLCACTL (0xC00024>>2) +#define V3D_SRQPC (0xC00430>>2) +#define V3D_SRQUA (0xC00434>>2) +#define V3D_SRQCS (0xC0043c>>2) +#define V3D_DBCFG (0xC00e00>>2) +#define V3D_DBQITE (0xC00e2c>>2) +#define V3D_DBQITC (0xC00e30>>2) + +// Setting this define to zero on Pi 1 allows GPU_FFT and Open GL +// to co-exist and also improves performance of longer transforms: +#define GPU_FFT_USE_VC4_L2_CACHE 1 // Pi 1 only: cached=1; direct=0 + +#define GPU_FFT_NO_FLUSH 1 +#define GPU_FFT_TIMEOUT 2000 // ms + +struct GPU_FFT_HOST { + unsigned mem_flg, mem_map, peri_addr, peri_size; +}; + +int gpu_fft_get_host_info(struct GPU_FFT_HOST *info) { + void *handle; + unsigned (*bcm_host_get_sdram_address) (void); + unsigned (*bcm_host_get_peripheral_address)(void); + unsigned (*bcm_host_get_peripheral_size) (void); + + // Pi 1 defaults + info->peri_addr = 0x20000000; + info->peri_size = 0x01000000; + info->mem_flg = GPU_FFT_USE_VC4_L2_CACHE? 0xC : 0x4; + info->mem_map = GPU_FFT_USE_VC4_L2_CACHE? 0x0 : 0x20000000; // Pi 1 only + + handle = dlopen("libbcm_host.so", RTLD_LAZY); + if (!handle) return -1; + + *(void **) (&bcm_host_get_sdram_address) = dlsym(handle, "bcm_host_get_sdram_address"); + *(void **) (&bcm_host_get_peripheral_address) = dlsym(handle, "bcm_host_get_peripheral_address"); + *(void **) (&bcm_host_get_peripheral_size) = dlsym(handle, "bcm_host_get_peripheral_size"); + + if (bcm_host_get_sdram_address && bcm_host_get_sdram_address()!=0x40000000) { // Pi 2? + info->mem_flg = 0x4; // ARM cannot see VC4 L2 on Pi 2 + info->mem_map = 0x0; + } + + if (bcm_host_get_peripheral_address) info->peri_addr = bcm_host_get_peripheral_address(); + if (bcm_host_get_peripheral_size) info->peri_size = bcm_host_get_peripheral_size(); + + dlclose(handle); + return 0; +} + +unsigned gpu_fft_base_exec_direct ( + struct GPU_FFT_BASE *base, + int num_qpus) { + + unsigned q, t; + + base->peri[V3D_DBCFG] = 0; // Disallow IRQ + base->peri[V3D_DBQITE] = 0; // Disable IRQ + base->peri[V3D_DBQITC] = -1; // Resets IRQ flags + + base->peri[V3D_L2CACTL] = 1<<2; // Clear L2 cache + base->peri[V3D_SLCACTL] = -1; // Clear other caches + + base->peri[V3D_SRQCS] = (1<<7) | (1<<8) | (1<<16); // Reset error bit and counts + + for (q=0; qperi[V3D_SRQUA] = base->vc_unifs[q]; + base->peri[V3D_SRQPC] = base->vc_code; + } + + // Busy wait polling + for (;;) { + if (((base->peri[V3D_SRQCS]>>16) & 0xff) == num_qpus) break; // All done? + } + + return 0; +} + +unsigned gpu_fft_base_exec( + struct GPU_FFT_BASE *base, + int num_qpus) { + + if (base->vc_msg) { + // Use mailbox + // Returns: 0x0 for success; 0x80000000 for timeout + return execute_qpu(base->mb, num_qpus, base->vc_msg, GPU_FFT_NO_FLUSH, GPU_FFT_TIMEOUT); + } + else { + // Direct register poking + return gpu_fft_base_exec_direct(base, num_qpus); + } +} + +int gpu_fft_alloc ( + int mb, + unsigned size, + struct GPU_FFT_PTR *ptr) { + + struct GPU_FFT_HOST host; + struct GPU_FFT_BASE *base; + volatile unsigned *peri; + unsigned handle; + + if (gpu_fft_get_host_info(&host)) return -5; + + if (qpu_enable(mb, 1)) return -1; + + // Shared memory + handle = mem_alloc(mb, size, 4096, host.mem_flg); + if (!handle) { + qpu_enable(mb, 0); + return -3; + } + + peri = (volatile unsigned *) mapmem(host.peri_addr, host.peri_size); + if (!peri) { + mem_free(mb, handle); + qpu_enable(mb, 0); + return -4; + } + + ptr->vc = mem_lock(mb, handle); + ptr->arm.vptr = mapmem(BUS_TO_PHYS(ptr->vc+host.mem_map), size); + + base = (struct GPU_FFT_BASE *) ptr->arm.vptr; + base->peri = peri; + base->peri_size = host.peri_size; + base->mb = mb; + base->handle = handle; + base->size = size; + + return 0; +} + +void gpu_fft_base_release(struct GPU_FFT_BASE *base) { + int mb = base->mb; + unsigned handle = base->handle, size = base->size; + unmapmem((void*)base->peri, base->peri_size); + unmapmem((void*)base, size); + mem_unlock(mb, handle); + mem_free(mb, handle); + qpu_enable(mb, 0); +} + +unsigned gpu_fft_ptr_inc ( + struct GPU_FFT_PTR *ptr, + int bytes) { + + unsigned vc = ptr->vc; + ptr->vc += bytes; + ptr->arm.bptr += bytes; + return vc; +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c new file mode 100755 index 0000000..f8e3bfe --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c @@ -0,0 +1,102 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +static unsigned int shader_256[] = { + #include "hex/shader_256.hex" +}; +static unsigned int shader_512[] = { + #include "hex/shader_512.hex" +}; +static unsigned int shader_1k[] = { + #include "hex/shader_1k.hex" +}; +static unsigned int shader_2k[] = { + #include "hex/shader_2k.hex" +}; +static unsigned int shader_4k[] = { + #include "hex/shader_4k.hex" +}; +static unsigned int shader_8k[] = { + #include "hex/shader_8k.hex" +}; +static unsigned int shader_16k[] = { + #include "hex/shader_16k.hex" +}; +static unsigned int shader_32k[] = { + #include "hex/shader_32k.hex" +}; +static unsigned int shader_64k[] = { + #include "hex/shader_64k.hex" +}; +static unsigned int shader_128k[] = { + #include "hex/shader_128k.hex" +}; +static unsigned int shader_256k[] = { + #include "hex/shader_256k.hex" +}; +static unsigned int shader_512k[] = { + #include "hex/shader_512k.hex" +}; +static unsigned int shader_1024k[] = { + #include "hex/shader_1024k.hex" +}; +static unsigned int shader_2048k[] = { + #include "hex/shader_2048k.hex" +}; +static unsigned int shader_4096k[] = { + #include "hex/shader_4096k.hex" +}; + +static struct { + unsigned int size, *code; +} +shaders[] = { + {sizeof(shader_256), shader_256}, + {sizeof(shader_512), shader_512}, + {sizeof(shader_1k), shader_1k}, + {sizeof(shader_2k), shader_2k}, + {sizeof(shader_4k), shader_4k}, + {sizeof(shader_8k), shader_8k}, + {sizeof(shader_16k), shader_16k}, + {sizeof(shader_32k), shader_32k}, + {sizeof(shader_64k), shader_64k}, + {sizeof(shader_128k), shader_128k}, + {sizeof(shader_256k), shader_256k}, + {sizeof(shader_512k), shader_512k}, + {sizeof(shader_1024k), shader_1024k}, + {sizeof(shader_2048k), shader_2048k}, + {sizeof(shader_4096k), shader_4096k} +}; + +unsigned int gpu_fft_shader_size(int log2_N) { + return shaders[log2_N-8].size; +} + +unsigned int *gpu_fft_shader_code(int log2_N) { + return shaders[log2_N-8].code; +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c new file mode 100755 index 0000000..265c2b6 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c @@ -0,0 +1,95 @@ +/* +BCM2835 "GPU_FFT" release 2.0 +Copyright (c) 2014, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft_trans.h" + +static unsigned int shader_trans[1024] = { + #include "hex/shader_trans.hex" +}; + +int gpu_fft_trans_prepare( + int mb, + struct GPU_FFT *src, + struct GPU_FFT *dst, + struct GPU_FFT_TRANS **out) { + + unsigned size, info_bytes, code_bytes, unif_bytes, mail_bytes; + int ret; + + struct GPU_FFT_TRANS *info; + struct GPU_FFT_BASE *base; + struct GPU_FFT_PTR ptr; + + info_bytes = code_bytes = unif_bytes = mail_bytes = 4096; // 4k align + + size = info_bytes + // control + code_bytes + // shader, aligned + unif_bytes + // uniforms + mail_bytes; // mailbox message + + ret = gpu_fft_alloc(mb, size, &ptr); + if (ret) return ret; + + // Control header + info = (struct GPU_FFT_TRANS *) ptr.arm.vptr; + base = (struct GPU_FFT_BASE *) info; + gpu_fft_ptr_inc(&ptr, info_bytes); + + // Shader code + memcpy(ptr.arm.vptr, shader_trans, code_bytes); + base->vc_code = gpu_fft_ptr_inc(&ptr, code_bytes); + + // Uniforms + ptr.arm.uptr[0] = src->base.vc_msg; + ptr.arm.uptr[1] = ((char*)src->out) - ((char*)src->in); // output buffer offset + ptr.arm.uptr[2] = dst->base.vc_msg; + ptr.arm.uptr[3] = 0; + ptr.arm.uptr[4] = src->step * sizeof(struct GPU_FFT_COMPLEX); + ptr.arm.uptr[5] = dst->step * sizeof(struct GPU_FFT_COMPLEX); + ptr.arm.uptr[6] = src->x < dst->y? src->x : dst->y; + ptr.arm.uptr[7] = src->y < dst->x? src->y : dst->x; + base->vc_unifs[0] = gpu_fft_ptr_inc(&ptr, unif_bytes); + + // Mailbox message + ptr.arm.uptr[0] = base->vc_unifs[0]; + ptr.arm.uptr[1] = base->vc_code; + base->vc_msg = gpu_fft_ptr_inc(&ptr, mail_bytes); + + *out = info; + return 0; +} + +unsigned gpu_fft_trans_execute(struct GPU_FFT_TRANS *info) { + return gpu_fft_base_exec(&info->base, 1); +} + +void gpu_fft_trans_release(struct GPU_FFT_TRANS *info) { + gpu_fft_base_release(&info->base); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h new file mode 100755 index 0000000..682efc1 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h @@ -0,0 +1,45 @@ +/* +BCM2835 "GPU_FFT" release 2.0 +Copyright (c) 2014, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "gpu_fft.h" + +struct GPU_FFT_TRANS { + struct GPU_FFT_BASE base; +}; + +int gpu_fft_trans_prepare( + int mb, + struct GPU_FFT *src, + struct GPU_FFT *dst, + struct GPU_FFT_TRANS **out); + +unsigned gpu_fft_trans_execute( // src->out ==> T ==> dst->in + struct GPU_FFT_TRANS *info); + +void gpu_fft_trans_release( + struct GPU_FFT_TRANS *info); diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c new file mode 100755 index 0000000..5323650 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c @@ -0,0 +1,315 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft.h" + +#define ALPHA(dx) (2*pow(sin((dx)/2),2)) +#define BETA(dx) (sin(dx)) + +static double k[16] = {0,8,4,4,2,2,2,2,1,1,1,1,1,1,1,1}; +static double m[16] = {0,0,0,1,0,1,2,3,0,1,2,3,4,5,6,7}; + +/****************************************************************************/ + +static float *twiddles_base_16(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = cos(two_pi/16*k[i]*m[i] + theta*k[i]); + *out++ = sin(two_pi/16*k[i]*m[i] + theta*k[i]); + } + return out; +} + +static float *twiddles_base_32(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = cos(two_pi/32*i + theta); + *out++ = sin(two_pi/32*i + theta); + } + return twiddles_base_16(two_pi, out, 2*theta); +} + +static float *twiddles_base_64(double two_pi, float *out) { + int i; + for (i=0; i<32; i++) { + *out++ = cos(two_pi/64*i); + *out++ = sin(two_pi/64*i); + } + return twiddles_base_32(two_pi, out, 0); +} + +/****************************************************************************/ + +static float *twiddles_step_16(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = ALPHA(theta*k[i]); + *out++ = BETA(theta*k[i]); + } + return out; +} + +static float *twiddles_step_32(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = ALPHA(theta); + *out++ = BETA(theta); + } + return twiddles_step_16(two_pi, out, 2*theta); +} + +static float *twiddles_step_64(double two_pi, float *out, double theta) { + int i; + for (i=0; i<32; i++) { + *out++ = ALPHA(theta); + *out++ = BETA(theta); + } + return twiddles_step_32(two_pi, out, 2*theta); +} + +/****************************************************************************/ + +static void twiddles_256(double two_pi, float *out) { + double N=256; + int q; + + out = twiddles_base_16(two_pi, out, 0); + out = twiddles_step_16(two_pi, out, two_pi/N * GPU_FFT_QPUS); + + for (q=0; q22) return -1; + *shared = shaders[log2_N-8].shared; + *unique = shaders[log2_N-8].unique; + *passes = shaders[log2_N-8].passes; + return 0; +} + +void gpu_fft_twiddle_data(int log2_N, int direction, float *out) { + shaders[log2_N-8].twiddles((direction==GPU_FFT_FWD?-2:2)*GPU_FFT_PI, out); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c b/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c new file mode 100755 index 0000000..90317a4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c @@ -0,0 +1,109 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "mailbox.h" +#include "gpu_fft.h" + +char Usage[] = + "Usage: hello_fft.bin log2_N [jobs [loops]]\n" + "log2_N = log2(FFT_length), log2_N = 8...22\n" + "jobs = transforms per batch, jobs>0, default 1\n" + "loops = number of test repeats, loops>0, default 1\n"; + +unsigned Microseconds(void) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec*1000000 + ts.tv_nsec/1000; +} + +int main(int argc, char *argv[]) { + int i, j, k, ret, loops, freq, log2_N, jobs, N, mb = mbox_open(); + unsigned t[2]; + double tsq[2]; + + struct GPU_FFT_COMPLEX *base; + struct GPU_FFT *fft; + + log2_N = argc>1? atoi(argv[1]) : 12; // 8 <= log2_N <= 22 + jobs = argc>2? atoi(argv[2]) : 1; // transforms per batch + loops = argc>3? atoi(argv[3]) : 1; // test repetitions + + if (argc<2 || jobs<1 || loops<1) { + printf(Usage); + return -1; + } + + N = 1<in + j*fft->step; // input buffer + for (i=0; iout + j*fft->step; // output buffer + freq = j+1; + for (i=0; i +#include +#include + +#include "gpu_fft_trans.h" +#include "hello_fft_2d_bitmap.h" + +#define log2_N 9 +#define N (1<io+(fft)->step*(y)) + +unsigned Microseconds(void) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec*1000000 + ts.tv_nsec/1000; +} + +int main(int argc, char *argv[]) { + int x, y, ret, mb = mbox_open(); + unsigned t[4]; + + struct GPU_FFT_COMPLEX *row; + struct GPU_FFT_TRANS *trans; + struct GPU_FFT *fft_pass[2]; + + BITMAPFILEHEADER bfh; + BITMAPINFOHEADER bih; + + // Create Windows bitmap file + FILE *fp = fopen("hello_fft_2d.bmp", "wb"); + if (!fp) return -666; + + // Write bitmap header + memset(&bfh, 0, sizeof(bfh)); + bfh.bfType = 0x4D42; //"BM" + bfh.bfSize = N*N*3; + bfh.bfOffBits = sizeof(bfh) + sizeof(bih); + fwrite(&bfh, sizeof(bfh), 1, fp); + + // Write bitmap info + memset(&bih, 0, sizeof(bih)); + bih.biSize = sizeof(bih); + bih.biWidth = N; + bih.biHeight = N; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + fwrite(&bih, sizeof(bih), 1, fp); + + // Prepare 1st FFT pass + ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_REV, N, fft_pass+0); + if (ret) { + return ret; + } + // Prepare 2nd FFT pass + ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_REV, N, fft_pass+1); + if (ret) { + gpu_fft_release(fft_pass[0]); + return ret; + } + // Transpose from 1st pass output to 2nd pass input + ret = gpu_fft_trans_prepare(mb, fft_pass[0], fft_pass[1], &trans); + if (ret) { + gpu_fft_release(fft_pass[0]); + gpu_fft_release(fft_pass[1]); + return ret; + } + + // Clear input array + for (y=0; y FFT() ==> T() ==> FFT() ==> + usleep(1); /* yield to OS */ t[0] = Microseconds(); + gpu_fft_execute(fft_pass[0]); t[1] = Microseconds(); + gpu_fft_trans_execute(trans); t[2] = Microseconds(); + gpu_fft_execute(fft_pass[1]); t[3] = Microseconds(); + + // Write output to bmp file + for (y=0; y> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149c01c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149c01c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c91c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb50, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00001258, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149c01c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149c01c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c91c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff998, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff970, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9d0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00007fff, 0xe0020827, // mov r0, 0x7FFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff9a0, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff710, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff4a0, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff480, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff460, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff440, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff1b0, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xffffef40, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffecb0, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffed78, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex new file mode 100755 index 0000000..6b82f92 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex @@ -0,0 +1,735 @@ +0x00000011, 0xe0021227, // mov rb_STAGES, STAGES +0x00000010, 0xe00216a7, // mov rb_0x10, 0x10 +0x00000040, 0xe00216e7, // mov rb_0x40, 0x40 +0x00000080, 0xe0021727, // mov rb_0x80, 0x80 +0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217a7, // mov rb_0x100, 0x100 +0x00000fff, 0xe00217e7, // mov rb_0xFFF, 0xFFF +0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 +0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 +0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F +0x00ff00ff, 0xe0021627, // mov rx_0x00FF00FF, 0x00FF00FF +0x0000ffff, 0xe0021667, // mov rx_0x0000FFFF, 0x0000FFFF +0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc000ffc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0007fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000560, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cc1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000d00, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cc1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa08, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff9e0, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffaf0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x141dfdc0, 0x100229e7, // and.setf -, ra_points, rb_0xFFF +0xfffffac8, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff938, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff778, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff758, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff5c8, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000020, 0xe0020827, // mov r0, 4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff408, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff278, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff2d0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex new file mode 100755 index 0000000..160d783 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex @@ -0,0 +1,688 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0000fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x000005f0, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c11c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb10, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000b10, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c11c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9a0, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff978, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff988, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff968, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff6d8, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000020, 0xe0020827, // mov r0, 4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff5f8, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff468, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff4c0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex new file mode 100755 index 0000000..7de3279 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex @@ -0,0 +1,523 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 +0x00005555, 0xe00207a7, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00217a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00217e7, // mov rx_0x00FF, 0x00FF +0x90104000, 0xe0020767, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202a7, // mov rx_tw_shared, unif +0x15827d80, 0x100212a7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246e0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10024720, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15767d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000588, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c31c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x956c2ff6, 0x100246c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95707ff6, 0x10024707, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95741ff6, 0x10024741, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x956c2ff6, 0x100246c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95707ff6, 0x10024707, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95741ff6, 0x10024741, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000007a0, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c31c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0e1cadc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff9e8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9f8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024a7c80, 0x10020827, // fsub r0, a, b +0x024a7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024a7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014e7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024a7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204e7, // fadd a+1, r0, r1 +0x029d2ec0, 0x10020827, // fsub r0, a, b +0x029d21c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d2e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d33c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d2e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214e7, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02427c80, 0x10020827, // fsub r0, a, b +0x02427180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02427c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01467380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02427c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020467, // fadd a+1, r0, r1 +0x029d0ec0, 0x10020827, // fsub r0, a, b +0x029d01c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d0e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d13c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d0e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021467, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cadc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff768, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff830, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex new file mode 100755 index 0000000..c49cd94 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex @@ -0,0 +1,1353 @@ +0x00000010, 0xe0021227, // mov rb_0x10, 0x10 +0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000002e8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x153a7d80, 0x10020827, // mov r0, ra_vdw_32 +0x8c04ddf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00080000, 0xe00208e7, // mov r3, PASS32_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000520, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x80904000, 0xe0020827, // mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) +0x00000040, 0xe0020867, // mov r1, 0x40 +0x8c067c76, 0x10024061, // add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00040000, 0xe00208e7, // mov r3, PASS64_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000998, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd50, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffbe0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffff8c0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff7e0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff790, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00001378, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff4f8, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000015, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff4c8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x80904000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) +0x00000015, 0xe00212e7, // mov rb_STAGES, STAGES +0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 +0x00000040, 0xe0021367, // mov rb_0x40, 0x40 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff8b0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00007fff, 0xe0020827, // mov r0, 0x7FFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff880, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff5f0, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff380, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff360, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff340, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff320, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0x00000100, 0xe0020827, // mov r0, 0x100 +0xfffff088, 0xf00809e7, // brr.allz -, r:pass_3 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000060, 0xe0020827, // mov r0, (4-1)*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000009, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000008, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xffffee18, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffeb88, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffec58, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex new file mode 100755 index 0000000..bfd5b45 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex @@ -0,0 +1,359 @@ +0x00000040, 0xe00217a7, // mov rb_0x40, 0x40 +0x00000080, 0xe00217e7, // mov rb_0x80, 0x80 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x88104000, 0xe0020727, // mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88104800, 0xe0021727, // mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) +0x15827d80, 0x10020227, // mov rx_tw_shared, unif +0x15827d80, 0x10021227, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246e0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100256e0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100049e0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100009e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc0000040, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05edf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000248, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffd50, 0xf0f80027, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0xfffffd30, 0xf0f80027, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9c8e00, 0x10020e27, // add t0s, ptr, r0 +0x0c9c8e40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb50, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02367c80, 0x10020827, // fsub r0, a, b +0x02367180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02367c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x013a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02367c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100203a7, // fadd a+1, r0, r1 +0x029cdec0, 0x10020827, // fsub r0, a, b +0x029cd1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029cde40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029cde80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100213a7, // fadd a+1, r0, r1 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0xfffff9c8, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0xfffff9d0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex new file mode 100755 index 0000000..d51e651 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex @@ -0,0 +1,861 @@ +0x00000012, 0xe0021227, // mov rb_STAGES, STAGES +0x00000010, 0xe00216a7, // mov rb_0x10, 0x10 +0x00000040, 0xe00216e7, // mov rb_0x40, 0x40 +0x00000080, 0xe0021727, // mov rb_0x80, 0x80 +0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217a7, // mov rb_0x100, 0x100 +0x00001fff, 0xe00217e7, // mov rb_0x1FFF, 0x1FFF +0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 +0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 +0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F +0x00ff00ff, 0xe0021627, // mov rx_0x00FF00FF, 0x00FF00FF +0x0000ffff, 0xe0021667, // mov rx_0x0000FFFF, 0x0000FFFF +0x80904000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0( 1, 16, dma_h32( 0,0)) +0x80905000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0( 1, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000001d0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x156e7d80, 0x10020827, // mov r0, arg_vdw +0x8c05bdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00020000, 0xe00208e7, // mov r3, PASS16_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc000ffc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000640, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cb1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb38, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffae8, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000ef0, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cb1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff928, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff900, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x141dfdc0, 0x100229e7, // and.setf -, ra_points, rb_0x1FFF +0xfffff9e8, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff858, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff698, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff678, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff658, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff638, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff4a8, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff2a0, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff010, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff0e0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex new file mode 100755 index 0000000..bd30abb --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex @@ -0,0 +1,765 @@ +0x00000010, 0xe0021727, // mov rb_0x10, 0x10 +0x00000040, 0xe0021767, // mov rb_0x40, 0x40 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x000001d0, 0xe00217e7, // mov rb_0x1D0, 0x1D0 +0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 +0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15367d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc00001c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000f8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0xa0104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(64, 16, dma_h32(0,0)) +0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(PASS64_STRIDE-16*4) +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, step; mov vw_addr, ra_save_ptr +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000858, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffb20, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa00, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff920, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff8d0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9534dff6, 0x1002434d, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000870, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff688, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0e1cbdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff660, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x90104000, 0xe0020367, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021367, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff920, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff690, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff760, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex new file mode 100755 index 0000000..3b6fd77 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex @@ -0,0 +1,697 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202a7, // mov rx_tw_shared, unif +0x15827d80, 0x100212a7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246a0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246e0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000588, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c21c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000d00, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c21c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff9e8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9f8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff9d8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff9b8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff998, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024a7c80, 0x10020827, // fsub r0, a, b +0x024a7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024a7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014e7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024a7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204e7, // fadd a+1, r0, r1 +0x029d2ec0, 0x10020827, // fsub r0, a, b +0x029d21c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d2e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d33c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d2e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214e7, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02427c80, 0x10020827, // fsub r0, a, b +0x02427180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02427c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01467380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02427c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020467, // fadd a+1, r0, r1 +0x029d0ec0, 0x10020827, // fsub r0, a, b +0x029d01c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d0e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d13c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d0e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021467, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff708, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff498, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024a7c80, 0x10020827, // fsub r0, a, b +0x024a7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024a7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014e7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024a7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204e7, // fadd a+1, r0, r1 +0x029d2ec0, 0x10020827, // fsub r0, a, b +0x029d21c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d2e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d33c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d2e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214e7, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02427c80, 0x10020827, // fsub r0, a, b +0x02427180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02427c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01467380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02427c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020467, // fadd a+1, r0, r1 +0x029d0ec0, 0x10020827, // fsub r0, a, b +0x029d01c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d0e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d13c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d0e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021467, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff208, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff2d0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex new file mode 100755 index 0000000..f49df21 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex @@ -0,0 +1,1523 @@ +0x00000010, 0xe0021227, // mov rb_0x10, 0x10 +0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000002e8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x153a7d80, 0x10020827, // mov r0, ra_vdw_32 +0x8c04ddf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00100000, 0xe00208e7, // mov r3, PASS32_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000520, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x80904000, 0xe0020827, // mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) +0x00000040, 0xe0020867, // mov r1, 0x40 +0x8c067c76, 0x10024061, // add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00080000, 0xe00208e7, // mov r3, PASS64_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000ba8, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd50, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffbe0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffff8c0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225b19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206e701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209db017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216c97d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff7e0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff790, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff700, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff6b0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225b19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206e701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209db017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216c97d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff5d0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff580, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000016b8, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff2e8, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000016, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff2b8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002469a, // mov ra_tw_re+TW48+1, 0; mov rb_tw_im+TW48+1, 0 +0x00000000, 0xe002471c, // mov ra_tw_re+TW64+1, 0; mov rb_tw_im+TW64+1, 0 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020767, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021767, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100207a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100217a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff568, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0000ffff, 0xe0020827, // mov r0, 0xFFFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff538, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 +0x956dbdbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x207a7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209de017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209de01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x217a709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x026e7c80, 0x10020827, // fsub r0, a, b +0x026e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x026e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01727380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002589b, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x026e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020727, // fadd a+1, r0, r1 +0x029dbec0, 0x10020827, // fsub r0, a, b +0x029db1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029dbe40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019dc3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002489b, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029dbe80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021727, // fadd a+1, r0, r1 +0x95659dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20767016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209dd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209dd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2176709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02667c80, 0x10020827, // fsub r0, a, b +0x02667180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02667c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x016a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025899, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02667c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100206a7, // fadd a+1, r0, r1 +0x029d9ec0, 0x10020827, // fsub r0, a, b +0x029d91c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d9e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019da3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024899, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d9e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100216a7, // fadd a+1, r0, r1 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x00000016, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff0a0, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x80904000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) +0x00000016, 0xe00212e7, // mov rb_STAGES, STAGES +0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 +0x00000040, 0xe0021367, // mov rb_0x40, 0x40 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000009, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000008, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff008, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x000003ff, 0xe0020827, // mov r0, 0x3FF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xffffefd8, 0xf01809e7, // brr.allnz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffed48, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x0000000b, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x0000000a, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xffffead8, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffe848, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffe918, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex new file mode 100755 index 0000000..c37e50d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex @@ -0,0 +1,514 @@ +0x00000020, 0xe0021767, // mov rb_0x20, 0x20 +0x00000040, 0xe00217a7, // mov rb_0x40, 0x40 +0x00000080, 0xe00217e7, // mov rb_0x80, 0x80 +0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 +0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206e7, // mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88104800, 0xe00216e7, // mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) +0x15827d80, 0x10020227, // mov rx_tw_shared, unif +0x15827d80, 0x10021227, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246a0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100256a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100049e0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100009e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc00007c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05edf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x000003e8, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] +0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 +0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 +0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] +0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 +0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] +0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 +0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 +0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] +0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 +0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 +0xfffffd40, 0xf0f809e7, // brr -, r:fft_16 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffcf8, 0xf0f809e7, // brr -, r:fft_16 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000928, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c11c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] +0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 +0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 +0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] +0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 +0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbe0, 0xf0f80027, // brr ra_link_1, r:pass_1 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffffbb8, 0xf00809e7, // brr.allz -, r:pass_1 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb78, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffffb58, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x0d01ddc0, 0x10020027, // sub ra_link_1, ra_link_1, rb_0x20 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02367c80, 0x10020827, // fsub r0, a, b +0x02367180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02367c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x013a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02367c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100203a7, // fadd a+1, r0, r1 +0x029cdec0, 0x10020827, // fsub r0, a, b +0x029cd1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029cde40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029cde80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100213a7, // fadd a+1, r0, r1 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff9c0, 0xf00809e7, // brr.allz -, r:pass_2 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9c8e00, 0x10020e27, // add t0s, ptr, r0 +0x0c9c8e40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff808, 0xf0f80027, // brr ra_link_1, r:pass_3 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02367c80, 0x10020827, // fsub r0, a, b +0x02367180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02367c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x013a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02367c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100203a7, // fadd a+1, r0, r1 +0x029cdec0, 0x10020827, // fsub r0, a, b +0x029cd1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029cde40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029cde80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100213a7, // fadd a+1, r0, r1 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff678, 0xf00809e7, // brr.allz -, r:pass_3 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff6a8, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex new file mode 100755 index 0000000..505ab41 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex @@ -0,0 +1,494 @@ +0x00000010, 0xe0021727, // mov rb_0x10, 0x10 +0x00000040, 0xe0021767, // mov rb_0x40, 0x40 +0x00000080, 0xe00217a7, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217e7, // mov rb_0xF0, 0xF0 +0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 +0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206a7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe00206e7, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024620, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10024660, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156a7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0000040, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000510, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c41c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95602ff6, 0x10024602, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95647ff6, 0x10024647, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x956c1ff6, 0x100246c1, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95602ff6, 0x10024602, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95680ff6, 0x10024680, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000005e8, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c41c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa80, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0xfffffa60, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb20, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c9dc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff990, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff9e8, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex new file mode 100755 index 0000000..ebc84d8 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex @@ -0,0 +1,983 @@ +0x00000013, 0xe0021227, // mov rb_STAGES, STAGES +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 +0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 +0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F +0x00ff00ff, 0xe0021667, // mov rx_0x00FF00FF, 0x00FF00FF +0x0000ffff, 0xe00216a7, // mov rx_0x0000FFFF, 0x0000FFFF +0x80904000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(1, 16, dma_h32(32,0)) +0x80904000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000001d0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x156e7d80, 0x10020827, // mov r0, arg_vdw +0x8c05cdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00040000, 0xe00208e7, // mov r3, PASS16_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000002e8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x15727d80, 0x10020827, // mov r0, ra_vdw_32 +0x8c05cdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00020000, 0xe00208e7, // mov r3, PASS32_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000640, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9ca1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb38, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffae8, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000010a8, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9ca1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff928, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff900, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00003fff, 0xe0020827, // mov r0, 0x3FFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff9e0, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff850, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff648, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff628, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff608, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff5e8, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff358, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff0e8, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffee58, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffef28, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex new file mode 100755 index 0000000..5daa0a5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex @@ -0,0 +1,940 @@ +0x00000010, 0xe0021227, // mov rb_0x10, 0x10 +0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 +0x00005555, 0xe00207a7, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00217a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00217e7, // mov rx_0x00FF, 0x00FF +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x153a7d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0003fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c04ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000100, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xe0020827, // mov r0, 0x40 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0xa0104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(64, 16, dma_h32(0,0)) +0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(PASS64_STRIDE-16*4) +0x8c067c36, 0x10024072, // add ra_save_ptr, ra_save_ptr, step; mov vw_addr, ra_save_ptr +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000858, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffb20, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa00, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff920, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff8d0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000df0, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff688, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000010, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff658, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x90104000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x00000060, 0xe00212e7, // mov rb_3x4x8, 3*4*8 +0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 +0x00000040, 0xe0021367, // mov rb_0x40, 0x40 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff900, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff8e0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff8c0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff8a0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff610, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0d20bdc0, 0x10020227, // sub ra_link_1, ra_link_1, rb_3x4x8 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff3a0, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff110, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff1e0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex new file mode 100755 index 0000000..7e1f112 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex @@ -0,0 +1,603 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc0000fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc00007c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000500, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda8, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc90, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffc00, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000958, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa98, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffffa70, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb20, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffffb00, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff970, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000020, 0xe0020827, // mov r0, 4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff7b0, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff620, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff678, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex new file mode 100755 index 0000000..93af75e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex @@ -0,0 +1,126 @@ +0x15827d80, 0x10020e27, // mov t0s, unif +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c9cc9c0, 0xd0020e27, // add t0s, r4, 3*4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c827980, 0x100200a7, // add ra_src_base, r4, unif +0x15827d80, 0x10020e27, // mov t0s, unif +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c9cc9c0, 0xd0020e27, // add t0s, r4, 3*4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c827980, 0x100200e7, // add ra_dst_base, r4, unif +0x15827d80, 0x100214a7, // mov rb_Y_STRIDE_SRC, unif +0x15827d80, 0x100214e7, // mov rb_Y_STRIDE_DST, unif +0x15827d80, 0x10021527, // mov rb_NX, unif +0x15827d80, 0x10021567, // mov rb_NY, unif +0x00000008, 0xe0021467, // mov rb_X_STRIDE, 2*4 +0x00000010, 0xe0021427, // mov rb_0x10, 0x10 +0xc0000000, 0xe0020827, // mov r0, vdw_setup_1(0) +0x0c9d31c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_DST +0x00000040, 0xe0020867, // mov r1, 16*4 +0x0d9e7040, 0x100201a7, // sub ra_vdw_stride, r0, r1 +0x40991037, 0x100049e0, // nop; mul24 r0, elem_num, rb_X_STRIDE +0x159e7000, 0x10021027, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021227, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x10021067, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021267, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100210a7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00212a7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100210e7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00212e7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x10021127, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021327, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x10021167, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021367, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100211a7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00213a7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100211e7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00213e7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x00000000, 0xe0020067, // mov ra_y, 0 +0x00000000, 0xe0020027, // mov ra_x, 0 +0x40052037, 0x100049e1, // nop; mul24 r1, ra_y, rb_Y_STRIDE_SRC +0x40011037, 0x100049e0, // nop; mul24 r0, ra_x, rb_X_STRIDE +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c0a7c00, 0x10020127, // add ra_src_cell, ra_src_base, r0 +0x40013037, 0x100049e1, // nop; mul24 r1, ra_x, rb_Y_STRIDE_DST +0x40051037, 0x100049e0, // nop; mul24 r0, ra_y, rb_X_STRIDE +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c0e7c00, 0x10020167, // add ra_dst_cell, ra_dst_base, r0 +0x00001200, 0xe0021c67, // mov vw_setup, vpm_setup(16, 1, v32(0,0)) +0x0c100dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re +0x0c108dc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im +0x0c101dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c109dc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c102dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10adc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c103dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10bdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c104dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10cdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c105dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10ddc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c106dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10edc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c107dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10fdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x88104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(16, 16, dma_h32(0,0)) +0x151a7d80, 0x10021c67, // mov vw_setup, ra_vdw_stride +0x15167d80, 0x10021ca7, // mov vw_addr, ra_dst_cell +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x0c010dc0, 0x10020027, // add ra_x, ra_x, rb_0x10 +0x009e7000, 0x100009e7, // nop +0x0d014dc0, 0x100229e7, // sub.setf -, ra_x, rb_NX +0xfffffde0, 0xf01809e7, // brr.allnz -, r:inner +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c048dc0, 0xd0020067, // add ra_y, ra_y, 8 +0x009e7000, 0x100009e7, // nop +0x0d055dc0, 0x100229e7, // sub.setf -, ra_y, rb_NY +0xfffffda0, 0xf01809e7, // brr.allnz -, r:outer +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000001, 0xe00209a7, // mov interrupt, 1 +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c b/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c new file mode 100755 index 0000000..de44e81 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c @@ -0,0 +1,262 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mailbox.h" + +#define PAGE_SIZE (4*1024) + +void *mapmem(unsigned base, unsigned size) +{ + int mem_fd; + unsigned offset = base % PAGE_SIZE; + base = base - offset; + size = size + offset; + /* open /dev/mem */ + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { + printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n"); + exit (-1); + } + void *mem = mmap( + 0, + size, + PROT_READ|PROT_WRITE, + MAP_SHARED/*|MAP_FIXED*/, + mem_fd, + base); +#ifdef DEBUG + printf("base=0x%x, mem=%p\n", base, mem); +#endif + if (mem == MAP_FAILED) { + printf("mmap error %d\n", (int)mem); + exit (-1); + } + close(mem_fd); + return (char *)mem + offset; +} + +void unmapmem(void *addr, unsigned size) +{ + const intptr_t offset = (intptr_t)addr % PAGE_SIZE; + addr = (char *)addr - offset; + size = size + offset; + int s = munmap(addr, size); + if (s != 0) { + printf("munmap error %d\n", s); + exit (-1); + } +} + +/* + * use ioctl to send mbox property message + */ + +static int mbox_property(int file_desc, void *buf) +{ + int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); + + if (ret_val < 0) { + printf("ioctl_set_msg failed:%d\n", ret_val); + } + +#ifdef DEBUG + unsigned *p = buf; int i; unsigned size = *(unsigned *)buf; + for (i=0; i + +#define MAJOR_NUM 100 +#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) +#define DEVICE_FILE_NAME "/dev/vcio" + +int mbox_open(); +void mbox_close(int file_desc); + +unsigned get_version(int file_desc); +unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags); +unsigned mem_free(int file_desc, unsigned handle); +unsigned mem_lock(int file_desc, unsigned handle); +unsigned mem_unlock(int file_desc, unsigned handle); +void *mapmem(unsigned base, unsigned size); +void unmapmem(void *addr, unsigned size); + +unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5); +unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout); +unsigned qpu_enable(int file_desc, unsigned enable); diff --git a/host_applications/linux/apps/hello_pi/hello_fft/makefile b/host_applications/linux/apps/hello_pi/hello_fft/makefile new file mode 100755 index 0000000..c31fece --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/makefile @@ -0,0 +1,36 @@ +S = hex/shader_256.hex \ + hex/shader_512.hex \ + hex/shader_1k.hex \ + hex/shader_2k.hex \ + hex/shader_4k.hex \ + hex/shader_8k.hex \ + hex/shader_16k.hex \ + hex/shader_32k.hex \ + hex/shader_64k.hex \ + hex/shader_128k.hex \ + hex/shader_256k.hex \ + hex/shader_512k.hex \ + hex/shader_1024k.hex \ + hex/shader_2048k.hex \ + hex/shader_4096k.hex + +C = mailbox.c gpu_fft.c gpu_fft_base.c gpu_fft_twiddles.c gpu_fft_shaders.c + +C1D = $(C) hello_fft.c +C2D = $(C) hello_fft_2d.c gpu_fft_trans.c + +H1D = gpu_fft.h mailbox.h +H2D = gpu_fft.h mailbox.h gpu_fft_trans.h hello_fft_2d_bitmap.h + +F = -lrt -lm -ldl + +all: hello_fft.bin hello_fft_2d.bin + +hello_fft.bin: $(S) $(C1D) $(H1D) + gcc -o hello_fft.bin $(F) $(C1D) + +hello_fft_2d.bin: $(S) hex/shader_trans.hex $(C2D) $(H2D) + gcc -o hello_fft_2d.bin $(F) $(C2D) + +clean: + rm -f *.bin diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc new file mode 100755 index 0000000..1ff96c4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc @@ -0,0 +1,509 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################## +# Bit-rotated write + +.set PASS16_STRIDE, ((1<> (1<> (1<> 1 + mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +.endm + +############################################################################## + +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, rx_0x5555 # 16 SIMD + bit_rev 2, rx_0x3333 + bit_rev 4, rx_0x0F0F + bit_rev 8, rx_0x00FF # reversal creates left shift by 16-STAGES +.if STAGES>13 + shl r0, r0, STAGES-13 +.endif +.if STAGES<13 + shr r0, r0, 13-STAGES +.endif # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} + add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} + + interleave + swizzle + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm + +.macro load_rev, stride, call + read_rev stride + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + brr ra_link_0, call + interleave +.endm + +############################################################################## + +.macro read_lin, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + shl r0, r0, 3 + add r1, r0, 4 + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm + +.macro load_lin, stride, call + read_lin stride + brr ra_link_0, call + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 +.endm + +############################################################################## +# Unpack twiddles + +.macro unpack_twiddles + mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +.rep i, 4 + and.setf -, elem_num, (8>>i) + mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) + mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +.endr +.endm + +############################################################################## +# float-float enhanced-precision subtract (corrects rounding errors) + +.macro df64_sub32, a, b # df64_sub32(float2 &a, float b) + fsub r0, a, b # float2 s = twoSub(a.x, b); + fsub r1, r0, a + fadd r2, r1, b + fsub r1, r0, r1 + fsub r1, a, r1 + fsub r1, r1, r2 + + fadd r1, r1, a+1 # s.y += a.y; + + fadd r2, r0, r1 # a = twoSum(s.x, s,y); + fsub r2, r2, r0; mov a, r2 + fsub r1, r1, r2 + fsub r2, a, r2 + fsub r0, r0, r2 + fadd a+1, r0, r1 +.endm + +############################################################################## +# Rotate twiddles using enhanced-precision trig recurrence + +.macro rotate, base, step + mov r2, ra_tw_re+base; mov r3, rb_tw_im+base + nop; fmul r0, r2, ra_tw_re+step # a.cos + nop; fmul r1, r2, rb_tw_im+step # b.cos + nop; fmul r2, r3, rb_tw_im+step # b.sin + fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step # a.sin + fsub r3, r3, r1 + df64_sub32 ra_tw_re+base, r2 + df64_sub32 rb_tw_im+base, r3 +.endm + +.macro next_twiddles_32 + rotate TW32, TW32_STEP +.endm + +.macro next_twiddles_16 + rotate TW16+3, TW16_STEP + unpack_twiddles +.endm + +############################################################################## +# Alternate input/output buffers between stages + +.macro swap_buffers + mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +.endm + +############################################################################## +# Reset counters and twiddles + +.macro init_stage, m + mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +.ifset TW32 + mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +.endif + unpack_twiddles + mov r0, rb_inst + shl r0, r0, m + add ra_load_idx, r0, elem_num + mov ra_points, 0 + mov ra_save_ptr, rb_addr_y +.endm + +############################################################################## + +.set LOAD_STRAIGHT, 0 +.set LOAD_REVERSED, 1 + +.macro loader_16, stride, mode + .if mode==LOAD_REVERSED + load_rev stride, r:fft_16 + .else + load_lin stride, r:fft_16 + .endif +.endm + +.macro body_pass_16, mode + loader_16 rb_0x80, mode + bra -, ra_save_16 + nop + mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo + mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +.endm + +.macro body_pass_32, mode + loader_16 rb_0xF0, mode + mov ra_32_re, r0; mov rb_32_im, r1 + loader_16 rb_0x10, mode + fft_twiddles_32 + bra -, ra_save_32 + mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo + mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi + mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +.endm + +.macro body_pass_64, mode, step + loader_16 rb_0x10, mode + mov ra_32_re, r0; mov rb_32_im, r1 + loader_16 rb_0x10, mode + fft_twiddles_32 + + fadd ra_64+0, ra_32_re, r0 + fadd ra_64+1, rb_32_im, r1 + fsub ra_64+2, ra_32_re, r0 + fsub ra_64+3, rb_32_im, r1 + + loader_16 step, mode + mov ra_32_re, r0; mov rb_32_im, r1 + loader_16 rb_0x10, mode + fft_twiddles_32 + + fsub r3, rb_32_im, r1 + fsub r2, ra_32_re, r0 + fadd r1, rb_32_im, r1 + fadd r0, ra_32_re, r0 + + nop; fmul rb_32_im, r1, ra_tw_re+TW48 # ir + nop; fmul ra_32_re, r1, rb_tw_im+TW48 # ii + nop; fmul r1, r0, rb_tw_im+TW48 # ri + fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 # rr + fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 # ii + nop; fmul rb_32_im, r3, ra_tw_re+TW64 # ir + bra -, ra_save_64 + nop; fmul r3, r2, rb_tw_im+TW64 # ri + fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 # rr + fsub rb_64+2, r2, ra_32_re +.endm + +############################################################################## + +.macro exit, flag + mov interrupt, flag + nop; nop; thrend + nop + nop +.endm + +############################################################################## diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm new file mode 100755 index 0000000..c3c5205 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm @@ -0,0 +1,319 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 20 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 +.set TW32_P3_STEP, 4 +.set TW16_P3_STEP, 5 +.set TW32_P4_STEP, 6 +.set TW16_P4_STEP, 7 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +# rx_0x00FF00FF rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +# spare ra4 +# spare rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra26 +.set ra_vpm_hi, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb0 +.set rx_0x0000FFFF, rb26 + +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + + mov r0, 0x7FFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm new file mode 100755 index 0000000..a1f06ed --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm @@ -0,0 +1,319 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 17 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW16_P3_STEP, 3 +.set TW16_P4_STEP, 4 + +.set TW16_P4_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb24 +.set rx_0x0000FFFF, rb25 + +.set rb_0x10, rb26 +.set rb_0x40, rb27 +.set rb_0x80, rb28 +.set rb_0xF0, rb29 +.set rb_0x100, rb30 +.set rb_0xFFF, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 +mov rb_0xFFF, 0xFFF + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 +:pass_4 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + and.setf -, ra_points, rb_0xFFF + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 2 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + .endr + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm new file mode 100755 index 0000000..177890b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm @@ -0,0 +1,282 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 14 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 +.set TW16_P3_STEP, 4 + +.set TW16_P3_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rx_0x00FF, rb26 +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_32 LOAD_STRAIGHT + +:pass_3 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 2 + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + mov r0, 4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm new file mode 100755 index 0000000..31314ae --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm @@ -0,0 +1,231 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 10 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_P1_BASE, 0 # rx_tw_shared +.set TW16_P1_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 + +.set TW32_P2_BASE, 0 # rx_tw_unique +.set TW16_P2_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +# rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 + +.set rx_tw_shared, ra10 +.set rx_tw_unique, rb10 + +.set ra_tw_re, ra11 # 9 +.set rb_tw_im, rb11 # 9 + +.set ra_vpm_lo, ra27 +.set ra_vpm_hi, ra28 +.set ra_vdw_32, ra29 + +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0xF0, rb29 + +.set rx_0x5555, ra30 +.set rx_0x3333, rb30 +.set rx_0x0F0F, ra31 +.set rx_0x00FF, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0xF0, 0xF0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + load_tw rx_tw_shared, TW32+0, TW32_P1_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_unique, TW32+0, TW32_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm new file mode 100755 index 0000000..bc904f2 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm @@ -0,0 +1,336 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 21 + +.include "gpu_fft_2048k.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_BASE0, 0 # rx_tw_shared +.set TW64_BASE1, 1 +.set TW32_BASE, 2 +.set TW16_BASE, 3 +.set TW32_P2_STEP, 4 +.set TW16_P2_STEP, 5 +.set TW32_P3_STEP, 6 +.set TW16_P3_STEP, 7 +.set TW32_P4_STEP, 8 +.set TW16_P4_STEP, 9 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 1 +.set TW64, 10 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +.set rb_0x10, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 11 +.set rb_tw_im, rb16 # 11 + +############################################################################## +# Dual-use registers + +.set rb_STAGES, rb_64+0 +.set rb_0xF0, rb_64+1 +.set rb_0x40, rb_64+2 + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+3 +.set rb_vdw_32, rb_64+3 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov r5rep, 0x1D0 + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f +body_ra_save_64 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, r5 + +:pass_2 +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48, TW64_BASE0 + load_tw rx_tw_shared, TW64, TW64_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + + mov rb_STAGES, STAGES + mov rb_0xF0, 0xF0 + mov rb_0x40, 0x40 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + mov r0, 0x7FFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + mov r0, 0x100 + add.ifnz ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + mov r0, 0x100 + brr.allz -, r:pass_3 + add ra_points, ra_points, r0 + mov r0, (4-1)*4*8 + sub ra_link_1, ra_link_1, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc new file mode 100755 index 0000000..680f7c4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc @@ -0,0 +1,91 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################## +# Macro baseline + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Redefining some macros + +.macro body_ra_save_64 + mov -, vw_wait + + .rep i, 7 + mov -, srel(i+1) # Master releases slaves + .endr + + write_vpm_64 + + .rep i, 7 + mov -, sacq(i+9) # Master waits for slaves + .endr + + mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) + mov r1, 0x40 + add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr + mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) + mov r3, PASS64_STRIDE + + .rep i, 64 + add r0, r0, r2; mov vw_setup, r0 + add r1, r1, r3; mov vw_addr, r1 + .endr + + bra -, ra_link_1 + nop + nop + nop +.endm + +.macro bit_rev, shift, mask + mov r2, mask + and r1, r0, r2 + shr r0, r0, shift + and r0, r0, r2 + shl r1, r1, shift + or r0, r0, r1 +.endm + +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, 0x55555555 # 16 SIMD + bit_rev 2, 0x33333333 + bit_rev 4, 0x0F0F0F0F + bit_rev 8, 0x00FF00FF + bit_rev rb_0x10, 0x0000FFFF + + shr r0, r0, 32-STAGES-3 # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} + add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} + + interleave + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm new file mode 100755 index 0000000..c6328aa --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm @@ -0,0 +1,233 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 8 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW16_P1_BASE, 0 # rx_tw_shared +.set TW16_P2_STEP, 1 + +.set TW16_P2_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW16, 1 # 5 + +############################################################################## +# Registers + +.set ra_link_1, ra0 +# rb0 +.set ra_save_ptr, ra1 +# rb1 +.set ra_temp, ra2 +# rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +# rb7 + +.set rx_tw_shared, ra8 +.set rx_tw_unique, rb8 + +.set ra_tw_re, ra9 # 6 +.set rb_tw_im, rb9 # 6 + +.set ra_vpm, ra27 +.set rb_vpm, rb27 +.set ra_vdw, ra28 +.set rb_vdw, rb28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rb_0x40, rb30 +.set rb_0x80, rb31 + +############################################################################## +# Register alias + +.set ra_link_0, ra_save_16 + +############################################################################## +# Constants + +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F + +mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm, rb_vpm, -, - + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm, ra_vdw +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Redefining this macro + +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, rx_0x5555 # 16 SIMD + bit_rev 2, rx_0x3333 + bit_rev 4, rx_0x0F0F + + shl r0, r0, 3 # {idx[0:7], 1'b0, 2'b0} + add r1, r0, 4 # {idx[0:7], 1'b1, 2'b0} + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 +:pass_2 + brr -, r:fft_16 + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + init_stage 4 + read_rev rb_0x80 + read_rev rb_0x80 + +.rep i, 2 + brr ra_link_1, r:pass_1 + nop + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +.endr + + bra ra_link_1, ra_sync + nop + nop + nop + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw + + next_twiddles_16 + + brr ra_link_1, r:pass_2 + nop + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw + + bra ra_link_1, ra_sync + nop + nop + nop + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm new file mode 100755 index 0000000..759eb69 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm @@ -0,0 +1,326 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 18 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW16_P3_STEP, 3 +.set TW32_P4_STEP, 4 +.set TW16_P4_STEP, 5 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb24 +.set rx_0x0000FFFF, rb25 + +.set rb_0x10, rb26 +.set rb_0x40, rb27 +.set rb_0x80, rb28 +.set rb_0xF0, rb29 +.set rb_0x100, rb30 +.set rb_0x1FFF, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 +mov rb_0x1FFF, 0x1FFF + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_16, vdw_setup_0( 1, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0( 1, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 + body_pass_16 LOAD_STRAIGHT + +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + and.setf -, ra_points, rb_0x1FFF + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + .endr + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm new file mode 100755 index 0000000..304b144 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm @@ -0,0 +1,265 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 11 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_P1_BASE0, 0 # rx_tw_shared +.set TW64_P1_BASE1, 1 +.set TW32_P1_BASE, 2 +.set TW16_P1_BASE, 3 +.set TW32_P2_STEP, 4 +.set TW16_P2_STEP, 5 + +.set TW32_P2_BASE, 0 # rx_tw_unique +.set TW16_P2_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 1 +.set TW64, 10 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 11 +.set rb_tw_im, rb16 # 11 + +.set rx_0x5555, ra28 +.set rx_0x3333, ra29 +.set rx_0x0F0F, ra30 +.set rx_0x00FF, ra31 + +.set rb_0x10, rb28 +.set rb_0x40, rb29 +.set rb_0xF0, rb30 +.set rb_0x1D0, rb31 + +############################################################################## +# Dual-use registers + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+2 +.set rb_vdw_32, rb_64+2 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0xF0, 0xF0 +mov rb_0x1D0, 0x1D0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f +body_ra_save_64 rb_0x40 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, rb_0x1D0 + +:pass_2 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + load_tw rx_tw_shared, TW32+0, TW32_P1_BASE + load_tw rx_tw_shared, TW48, TW64_P1_BASE0 + load_tw rx_tw_shared, TW64, TW64_P1_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_unique, TW32+0, TW32_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm new file mode 100755 index 0000000..d0baf9c --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm @@ -0,0 +1,271 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 15 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 +.set TW32_P3_STEP, 4 +.set TW16_P3_STEP, 5 + +.set TW32_P3_BASE, 0 # rx_tw_unique +.set TW16_P3_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +# rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 + +.set rx_tw_shared, ra10 +.set rx_tw_unique, rb10 + +.set ra_tw_re, ra11 # 9 +.set rb_tw_im, rb11 # 9 + +.set ra_vpm_lo, ra26 +.set ra_vpm_hi, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rx_0x00FF, rb26 +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_unique, TW32+0, TW32_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm new file mode 100755 index 0000000..831f5d0 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm @@ -0,0 +1,356 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 22 + +.include "gpu_fft_2048k.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_BASE0, 0 # rx_tw_shared +.set TW64_BASE1, 1 +.set TW32_BASE, 2 +.set TW16_BASE, 3 + +.set TW48_P2_STEP, 4 +.set TW64_P2_STEP, 5 + +.set TW32_P2_STEP, 6 +.set TW16_P2_STEP, 7 +.set TW32_P3_STEP, 8 +.set TW16_P3_STEP, 9 +.set TW32_P4_STEP, 10 +.set TW16_P4_STEP, 11 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 2 +.set TW64, 11 # 2 +.set TW48_STEP, 13 # 1 +.set TW64_STEP, 14 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +.set rb_0x10, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 15 +.set rb_tw_im, rb16 # 15 + +############################################################################## +# Dual-use registers + +.set rb_STAGES, rb_64+0 +.set rb_0xF0, rb_64+1 +.set rb_0x40, rb_64+2 + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+3 +.set rb_vdw_32, rb_64+3 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov r5rep, 0x1D0 + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f +body_ra_save_64 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, r5 +:pass_2 + body_pass_64 LOAD_STRAIGHT, r5 +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48+0, TW64_BASE0 + load_tw rx_tw_shared, TW64+0, TW64_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48+0, TW64_BASE0 + load_tw rx_tw_shared, TW64+0, TW64_BASE1 + mov ra_tw_re+TW48+1, 0; mov rb_tw_im+TW48+1, 0 + mov ra_tw_re+TW64+1, 0; mov rb_tw_im+TW64+1, 0 + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + load_tw rx_tw_shared, TW48_STEP, TW48_P2_STEP + load_tw rx_tw_shared, TW64_STEP, TW64_P2_STEP + init_stage 6 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r0, 0xFFFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + mov r0, 0x200 + add.ifnz ra_points, ra_points, r0 + + rotate TW64, TW64_STEP + rotate TW48, TW48_STEP + next_twiddles_32 + next_twiddles_16 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_2 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + + mov rb_STAGES, STAGES + mov rb_0xF0, 0xF0 + mov rb_0x40, 0x40 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + mov r0, 0x3FF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_3 + nop + mov r0, 0x100 + add.ifnz ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm new file mode 100755 index 0000000..0b2b558 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm @@ -0,0 +1,276 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 12 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW16_BASE, 0 # rx_tw_shared +.set TW16_P2_STEP, 1 +.set TW16_P3_STEP, 2 + +.set TW16_P3_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW16, 1 # 5 + +############################################################################## +# Registers + +.set ra_link_1, ra0 +# rb0 +.set ra_save_ptr, ra1 +# rb1 +.set ra_temp, ra2 +# rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +# rb7 + +.set rx_tw_shared, ra8 +.set rx_tw_unique, rb8 + +.set ra_tw_re, ra9 # 6 +.set rb_tw_im, rb9 # 6 + +.set ra_vpm, ra26 +.set rb_vpm, rb26 +.set ra_vdw, ra27 +.set rb_vdw, rb27 + +.set rx_0x5555, ra28 +.set rx_0x3333, ra29 +.set rx_0x0F0F, ra30 +.set rx_0x00FF, ra31 + +.set rb_0x20, rb29 +.set rb_0x40, rb30 +.set rb_0x80, rb31 + +############################################################################## +# Register alias + +.set ra_link_0, ra_save_16 + +############################################################################## +# Constants + +mov rb_0x20, 0x20 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm, rb_vpm, -, - + +############################################################################## +# Macros + +.macro swap_vpm_vdw + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +.endm + +.macro swizzle + mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] + mov r2, r0; mov.ifnz r0, r0 << 6 + mov r3, r1; mov.ifnz r1, r1 << 6 + mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] + nop; mov.ifnz r0, r2 >> 6 + nop; mov.ifnz r1, r3 >> 6 +.endm + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm, ra_vdw +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + read_rev rb_0x80 + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + swizzle + brr -, r:fft_16 + interleave + +:pass_2 +:pass_3 + read_lin rb_0x80 + brr -, r:fft_16 + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + init_stage 4 + read_rev rb_0x80 + + brr ra_link_1, r:pass_1 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 2 + brr ra_link_1, r:pass_2 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + .endr + + sub ra_link_1, ra_link_1, rb_0x20 + next_twiddles_16 + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_3 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm new file mode 100755 index 0000000..13e4071 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm @@ -0,0 +1,240 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 9 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_P1_BASE, 0 # rx_tw_shared +.set TW16_P1_BASE, 1 +.set TW16_P2_STEP, 2 + +.set TW16_P2_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra24 +.set ra_vpm_hi, ra25 +.set ra_vdw_16, ra26 +.set ra_vdw_32, ra27 + +.set rx_0x5555, ra28 +.set rx_0x3333, ra29 +.set rx_0x0F0F, ra30 +.set rx_0x00FF, ra31 + +.set rb_0x10, rb28 +.set rb_0x40, rb29 +.set rb_0x80, rb30 +.set rb_0xF0, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + load_tw rx_tw_shared, TW32+0, TW32_P1_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + nop + + brr ra_link_1, r:pass_1 + nop + nop + nop + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm new file mode 100755 index 0000000..3722639 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm @@ -0,0 +1,329 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 19 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW32_P3_STEP, 3 +.set TW16_P3_STEP, 4 +.set TW32_P4_STEP, 5 +.set TW16_P4_STEP, 6 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb25 +.set rx_0x0000FFFF, rb26 + +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_16, vdw_setup_0(1, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(1, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_16 LOAD_STRAIGHT + +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + mov r0, 0x3FFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm new file mode 100755 index 0000000..1a94548 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm @@ -0,0 +1,306 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 16 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_BASE0, 0 # rx_tw_shared +.set TW64_BASE1, 1 +.set TW32_BASE, 2 +.set TW16_BASE, 3 +.set TW32_P2_STEP, 4 +.set TW16_P2_STEP, 5 +.set TW32_P3_STEP, 6 +.set TW16_P3_STEP, 7 + +.set TW32_P3_BASE, 0 # rx_tw_unique +.set TW16_P3_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 1 +.set TW64, 10 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +.set rb_0x10, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 11 +.set rb_tw_im, rb16 # 11 + +.set rx_0x5555, ra30 +.set rx_0x3333, rb30 +.set rx_0x0F0F, ra31 +.set rx_0x00FF, rb31 + +############################################################################## +# Dual-use registers + +.set rb_STAGES, rb_0x10 + +.set rb_3x4x8, rb_64+0 +.set rb_0xF0, rb_64+1 +.set rb_0x40, rb_64+2 + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+3 +.set rb_vdw_32, rb_64+3 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov r5rep, 0x1D0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f + mov r0, 0x40 +body_ra_save_64 r0 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, r5 + +:pass_2 +:pass_3 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48, TW64_BASE0 + load_tw rx_tw_shared, TW64, TW64_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + + mov rb_3x4x8, 3*4*8 + mov rb_0xF0, 0xF0 + mov rb_0x40, 0x40 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + mov r0, 0x100 + add ra_points, ra_points, r0 + sub ra_link_1, ra_link_1, rb_3x4x8 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_unique, TW32+0, TW32_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm new file mode 100755 index 0000000..31acb77 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm @@ -0,0 +1,276 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 13 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW16_P3_STEP, 3 + +.set TW16_P3_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rx_0x00FF, rb26 +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 2 + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + .endr + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + mov r0, 4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc new file mode 100755 index 0000000..12acdb1 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc @@ -0,0 +1,112 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################## +# Macro baseline + +.include "gpu_fft.qinc" + +############################################################################## +# Redefining some macros + +.if STAGES>16 +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, rx_0x55555555 # 16 SIMD + bit_rev 2, rx_0x33333333 + bit_rev 4, rx_0x0F0F0F0F + bit_rev 8, rx_0x00FF00FF + bit_rev rb_0x10, rx_0x0000FFFF + + shr r0, r0, 32-STAGES-3 # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} + add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} + + interleave + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm +.endif + +.if STAGES>17 +.macro body_ra_save_16, arg_vpm, arg_vdw + write_vpm_16 arg_vpm + + mov -, vw_wait + + .rep i, 7 + mov -, sacq(i+9) # Master waits for slave + mov -, srel(i+1) # Master releases slave + .endr + + mov r0, arg_vdw + add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr + + mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) + mov r3, PASS16_STRIDE + + .rep i, 16 + add r0, r0, r2; mov vw_setup, r0 + add r1, r1, r3; mov vw_addr, r1 + .endr + + bra -, ra_link_1 + nop + nop + nop +.endm +.endif + +.if STAGES>18 +.macro body_ra_save_32 + write_vpm_32 + + mov -, vw_wait + + .rep i, 7 + mov -, sacq(i+9) # Master waits for slave + mov -, srel(i+1) # Master releases slave + .endr + + mov r0, ra_vdw_32 + add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr + + mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) + mov r3, PASS32_STRIDE + + .rep i, 32 + add r0, r0, r2; mov vw_setup, r0 + add r1, r1, r3; mov vw_addr, r1 + .endr + + bra -, ra_link_1 + nop + nop + nop +.endm +.endif diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm new file mode 100755 index 0000000..fdfa869 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm @@ -0,0 +1,133 @@ +# BCM2835 "GPU_FFT" release 2.0 +# +# Copyright (c) 2014, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set rb_offsets_re, rb0 # 8 +.set rb_offsets_im, rb8 # 8 +.set rb_0x10, rb16 +.set rb_X_STRIDE, rb17 +.set rb_Y_STRIDE_SRC, rb18 +.set rb_Y_STRIDE_DST, rb19 +.set rb_NX, rb20 +.set rb_NY, rb21 + +.set ra_x, ra0 +.set ra_y, ra1 +.set ra_src_base, ra2 +.set ra_dst_base, ra3 +.set ra_src_cell, ra4 +.set ra_dst_cell, ra5 +.set ra_vdw_stride, ra6 + + mov t0s, unif # src->vc_msg + ldtmu0 # r4 = vc_unifs + add t0s, r4, 3*4 # 3rd unif + ldtmu0 # r4 = src->in + add ra_src_base, r4, unif # optional offset + + mov t0s, unif # dst->vc_msg + ldtmu0 # r4 = vc_unifs + add t0s, r4, 3*4 # 3rd unif + ldtmu0 # r4 = src->in + add ra_dst_base, r4, unif # optional offset + + mov rb_Y_STRIDE_SRC, unif + mov rb_Y_STRIDE_DST, unif + mov rb_NX, unif + mov rb_NY, unif + + mov rb_X_STRIDE, 2*4 # sizeof complex + mov rb_0x10, 0x10 + + mov r0, vdw_setup_1(0) + add r0, r0, rb_Y_STRIDE_DST + mov r1, 16*4 + sub ra_vdw_stride, r0, r1 + + nop; mul24 r0, elem_num, rb_X_STRIDE +.rep i, 8 + mov rb_offsets_re+i, r0 + add rb_offsets_im+i, r0, 4 + add r0, r0, rb_Y_STRIDE_SRC +.endr + + mov ra_y, 0 +:outer + mov ra_x, 0 +:inner + + nop; mul24 r1, ra_y, rb_Y_STRIDE_SRC + nop; mul24 r0, ra_x, rb_X_STRIDE + add r0, r0, r1 + add ra_src_cell, ra_src_base, r0 + + nop; mul24 r1, ra_x, rb_Y_STRIDE_DST + nop; mul24 r0, ra_y, rb_X_STRIDE + add r0, r0, r1 + add ra_dst_cell, ra_dst_base, r0 + + mov vw_setup, vpm_setup(16, 1, v32(0,0)) + + add t0s, ra_src_cell, rb_offsets_re + add t1s, ra_src_cell, rb_offsets_im + .rep i, 7 + add t0s, ra_src_cell, rb_offsets_re+1+i + add t1s, ra_src_cell, rb_offsets_im+1+i + ldtmu0 + mov vpm, r4 + ldtmu1 + mov vpm, r4 + .endr + ldtmu0 + mov vpm, r4 + ldtmu1 + mov vpm, r4 + + mov vw_setup, vdw_setup_0(16, 16, dma_h32(0,0)) + mov vw_setup, ra_vdw_stride + mov vw_addr, ra_dst_cell + mov -, vw_wait + + add ra_x, ra_x, rb_0x10 + nop + sub.setf -, ra_x, rb_NX + brr.allnz -, r:inner + nop + nop + nop + + add ra_y, ra_y, 8 + nop + sub.setf -, ra_y, rb_NY + brr.allnz -, r:outer + nop + nop + nop + + mov interrupt, 1 + nop; nop; thrend + nop + nop diff --git a/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt new file mode 100755 index 0000000..448d2cf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt @@ -0,0 +1,9 @@ +set(EXEC hello_font.bin) +set(SRCS main.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) +target_link_libraries(${EXEC} vgfont freetype z) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_font/Makefile b/host_applications/linux/apps/hello_pi/hello_font/Makefile new file mode 100755 index 0000000..7ec49f2 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_font/Makefile @@ -0,0 +1,7 @@ +OBJS=main.o +BIN=hello_font.bin + +LDFLAGS+=-lvgfont -lfreetype -lz + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf b/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf new file mode 100755 index 0000000000000000000000000000000000000000..58cd6b5e61eff273e920942e28041f8ddcf1e1b5 GIT binary patch literal 65932 zcmdSC33yaR)<0Zz>)zY@nsoN1vlF(2gndgBNFXdBLRb|{$O1t~ViMNKut@^41ca~) zQ2_xF5g81KJAw$z=m0v5IF5?TyfVl*%#1>E`Ty$P?kuP?@AEzX?|Ht@raQN5JzJe~ z>eQ*0P(p|UA0n}j9-EYM?BUx5gnU@tBt8%AS5MEcEGIg=C>@6H=IOH*6q~CC z{T}r<2zlZ+GYV(V?|v)FN(jCZ*RUBy`Guc8{>%qxpNoQ?Gf-g9(3fHUk@y}vV|LYi z!V;FBa=Wf%KNM%$>as^vz}P>v%JqH5@cBJ zeYP0Whxb`W@{IrV zKI=(XNTv7LM3Tdv_C8yj@y6=GW#tPhN~X`Ka(5_5bf+XIr@E&taHp44RaR9L<K-&}mU|3uRp}m6R9RFpx2UjdOB?t2qKbU?*!u4R!KooDG==toyl87Ct|QdcYbAM zSwTrY=5rU870j7kR9cl^#o;L~nN?Kj?!ZS>JGjS|6<5v6uPBO6R3U-jR+JUaDJW8h zDJ%g?N~X=JDpFzKGqiN*>@F!Sm^G)6Lo%lgcXGl||q^T9*J+FZ%aQ&2hxApcy9gl1`my-i)%@ zKZljGp?FS3DJBF((6O-0U0K%IT{&mk%%XxSUZT->)~vF59HD};(!vr>u*$xip}9aN ze_GkxA{7Tsc2y8s1fjI73XA}QIAEMFDrlMvXm#$&8TmkKT9KD-0HmbU&5K$wEh~j& zRJdoCRj3leVQPoCyJ|ssQE@&d>goflef{kG1$>6tWrZchC0y9@XH`M`@PJ|S3ky~3 zRXX#@%kwJ$^_*Gx6)O6LMU^GfOI4CX!Isa!Q-vy}`2`rHlK1dIRO!BNCQa%JHKOIu za{uB0-abA!T1NwTrLz{eOWKJ#Xi!naHLc1q{!r-#DLHR^OQZ;LSEKZScfz1C8SbpH?wm2B$7c=67~+l|G#1~ZJG&=jR8|K1Wx7XYj2S!(BM(Z?8kvs{unTbIMxpM}M$;}!(Zsedb z?woOBaz>BMz!*a?Y<5<5<`~S9F)9N{V4%UHb0&?+8agbuGdks>u(LaN%%C9|qXvx` z(V0UyI(Jyc7`NJ_E1<*}?u_xg^Vng7Mvio+XXTE~9g{I=6mN^B?xESEM{ydB%N{Z) zH*0jZJ3Rxa3`!r#3jrIbFnHvktWllaLk5i+G?b&`n}j#>qSHza-eG7)cE*@NBRjjt z=41@c;t!x>)|iaJfEF!5dr$(U7-{h6?6DaSj6(t1`KACvhGnRD0D(dHH&}&CML!$p z@^NxUj{!lvpiIabo6*@lXiU~v&XLS9qX91GCwg!k$AO+`nw9N^m-C31@w)cXfmXb? zmx@C&293mk5R&Ylw^ijUV}3zVIaXYyZ;@+CQdOv$7KM?*%G8trq0}0}B5u-w6p%#xO@Wh{Oj7YQ4K3Ux z9c`*eCEgXJh~$&mq%%shNGaNP#nT`%3okbr(=t}2`mG3kiqK~+J`2(E=i|7^c(p}7 z+Ktqvb5&t94rQsz|8jM-O79G17_|y@C8*`^>1sZC9~M;@lh07B_T%!yM=Vg=&4%o0qx(kStu@$Z;co$Ya#`U0JCJCS*)m47Dxth@ zp*kMNy$tP3FrJ2=8#TOS4(Q59;jmVrUZYPjp18blXgZ)=gRyl6E{B{8Rb(Feae3!6 zw$g-`l%u>1v&>Q9)ab;aDa6>?Dk%Yt=3opCzi$p74nLoPkIv~(0LbR3qi9r}hf?0V zOdZRO+7jTz%i3b(8^3iWbKEoz&QWQ|$M>L95A`hM^l&=1^)<*NV|Rl^(M(&wro6w;GCp zVFl>Rxx@L*d8N(BC52;Brs7?xQeq}r6rkSM#y1a_V~%ebB*Q1Q9CI#-oF|%uRbrd( zTcNq?Y@BY>(2i@tRz9?H%STr}A77{KH9{$R^0E1f;8bX(m~XwbQmw5XXxoot$k(^V zt!XM8ZRJg)2ruE||2j`Ot{exA|FhM<+IOzCe02JCj`KDPRK6Bt9u1?eKcm)v>d$pP zw@4Ze90E>zzNUSejl<8^9bc!KuG669bmf%w@xE1_wYA6Pjjwl&)^jil|JI5X@5{C9 zbkLwx%BQ0p$7qJPjQ8;AQjVbp32(1a_kJ4jn*WSbE5|hqS|yER>IOXjTL{|Eb3Z*= zG4;{EQe6|A=X?f^L0c~K)xdSDCX<}nZk6Vxpc~gOK03S6N-N^46lzQPd8(Whsxw9Zf^CdOPmRYu>iT-Pp}T#)Lp1z?w(C-}H6t-&TU*2Bimz#o zfd(&^1Wsq)x|@sIk~Y}+<}4!fRc>>vcJLE{S7 z_HK0rbC@`c+^%uSX)ph+P-@uyk{;)LnSpc$xqYbBtP-g)%pMyD_L44E8LW(Tn52+mFIK*9&Pb%3Eh`4;3F-n~y^_ z3g5OTH47t*Lofb~myW~V9JCvY zUK$*nejM6tw9UpCW7NMxQO_aJIHA#MFk0ncZr)-j;L25@;4^XTcuNjdF6sw?BD_DJ zb%a`~LB?sqxy)f{9fj|b_}m&Coc`mz<8c|__>aVk)0We5tU5ymN=Kng8&@0E4X8LK z9Bz#ovY4EY$mj&p_6b7V_Pjc%GOaGnlAi%}}%yg$c;Q>0ZI+G64xtvz>s zNjiMe#>e7(b`BTv`e5&*h3s{$OCxDsh_Jb9(#QYEteoQlS+KKGp=46RrHvIKUy~a=~Zx(X5sGd`=Ft4<0VfT*`cWXr&5Ye_Y1+Ok4{1 zH$DSjBV5Kfmw26TeQI;~_&84O>l>B#YcKs=%J@3+we$7+Pr5^+k#BB3b}Q~&S~)E> z2sxKEYW(+cTeW=#Y#g_io z-?r^qOF3ovZiw5j);$n!>$A^4-#c?mwMYeT*VYsEc_W%PsqK}xebnIR9uoK2HJ_0C zewvq}`5N3S*LK-_H=ylQeY+UGJLI;x{r;~KFmgYDL!r&(v;VDQ@x2$1WpK}d&&DaN zLBnU$sQI64?fpAOzEkDhYm;ziO+tQ0Sbd156^WzR_CrG0q!Vebk~a*jljM*10uc#{2< zrLt4v5Yb9LV;9*$@)c$gG5&c{NA{3vz~WEK$YP;d7=x0t(nYczuQJqMq`T-PKzEWZ zCs)W;CJMvIE_wxcohSby%UQ0l80Yn=LNVY!i?J@E|8`O-66p#x5=H2QGC+^Hrm3Id ztc!F-ecd99F>@~2BR9(ax){vDDYlQkLvP3%NdvjW9%7HOPv{CUM%*tBBXt@DSRSdv z*xPv@xtJ~h?)+8FM;GRadGsLptC**ohOyt}7-8mP!WdvwOitlFPqqW6esl#}1xR^q zIJu}BE+(NrM$jz+)`XO?9%Lq-s>xw;lyqU6NgYN~@s)c?|3c55;^)A*j;ld`H$iLSPa1_Ko_lu{cE_Ln6vuu{VgKID{$*wVRM>5W{UeV3U}b;b%x=Z8@1GbX zeXp>ao7vwsvm1BVcX!zTDD1C&*|+KJ8-;zH!oIpbR{Cl)yN-s}$FeWKNRqz1!@fvj zpDXMy3i~XD{n?*=x|v;5*e6c*r$y}QtL%>o`v}cHTEwng9x7c~#4ZnIm;MkcT~gQ| zLfMB3`#@p8SJ>|qc5ySia6Ur1ps@21?EMsWPGM(OIHWUS?A-u%T4C=f>}`d;rLZ>@ z_J+bsdldHUGgj%@6!wgjJzdBe(4=8A+pVx&Pno4%3VX`TcJ2t4b{4W7+wIbhV7A@P zwi(%0g>Bhvk+vvovxU{8Q~hSPX`@xz)PZfZvM2Ab4eMW(HYjX;-4tp4t8D!ev2Iu1l(pV+$3wKw|v66+F1`1>mI>UEi9#*NlH;zHxo-vGD*o6mSkdGyBMUdcGktfI;XHs z9pj`wX0$d3Fq6WJc4knR9?kR$)A=*Gkcp@iAptIiQl>Bg--RxW+8I$8 zZKQ=O*3wS@fB295e;UZ}zOV50lINl{%o-}lvR*SU|7oFkS6 z?#6rfawdwQ(xf9&*bx?|KO)A(eEw^dpLgjzB4?ueNOQ&z@2DAhLr^w$A|}8;UX0l? zhIE1HA;rpOu~^!Jye1t9@tDQCM7~S)(qcg*NvAL0=tk_9Z(P2S?B|Gb#6>xxibc{? z$wHgHQa0r+=iLPN7jO%0#35QdyJ>k9f!UsqY?9eo=Uf zfy$@3G;YWY8e7sZo%U9q9zzEzJ7zRYS3a5k^bF-)nwP7*PD_f}3gsxPRr2X>C4ake zbel4b?&9xlGq490k;GDHwE^;eI49Mx_;yIIW@ozf2^>2>A zJ}rO5zfFn;GYwpBl2tz90N2Y$l$*h1d+viHj@VRAqXv@2k9a+*WYHMbl_vCvpn;CA zv`6=zy?Ug&@Wq8fM+9~G%R1(;;%`8pV<76|g=2-Zz7+@CHKPB}bw?28Y5 z`O%jj6;>^L^z+3_tCdT%i_oRZG0z}M--|u8`Poy}@4giyLtpIJRaC~s9NT%|9UIaU zw_9dT9G`bZ8SN;YJQ1mr5_$CAm%2ph7BL~?F@_|-Tdw!?jJ3tZ$Hm(cViVHIljevg zyRHp-GFE=lyf)ssrbFz8?g>$$aRz2_Sq&Cjl%TYj3edG2G`^|sdT(X zb?VjKyHA`HHf(x)S$+Mo<@JlNz541WpS*hN6CuBT+2flwJ-&4F;-CH@TRwU9wLg7w z>f|-P?v~#BQc^%M14*VAJ)14mYOZlO9i|$i$?0?$YKXxV;L=f9UlS1E5-6iJ;Su4a z#y}z>!rhTVRD{FmXT-8(LH-UuqfRf#28W-YQJ?}NT9pvwLJeyDjOk93fyu-e!8*9C za)$)DKB!ZD!lu{_L2Imj#;zu-fpm4c608xdt1}_W>abx|Iz#Q<>`jp8%Qx(2G+scS zxk&Tne&+hWzJ`q3&u}S+hzEK_9GsCf32*nO-50z5XX}8Mw3JSYK59#$bc*Mw&Ll+} z62nLsjT8b+9Z5$T@9ayuJBOI2l1X&3ah!8<$mGaL$rES7^#S$K z+qy&=Oa`;wVNNi22ogdK!KPqyup`Vr%oPwGnUX*fXrdv;+0n0~e+O4mNb&xLl>AAIyRDxbc;|g?bPkm@78ZO z>@aONuTN=6Ig-+63YkLHB?lSnWuOCTuT)vk(U=4)jfp0FjjAg(H6?&A(->9k=noH$ zyWH^bzAUAhHuX!FPnu^;p@B_xGp;ZHyYjo5n&gx}H;&yqZo;l1CCmGnQB$iJx2OC zS&E&YAd28DHzqgQn-Wo7F4)ofOo_!K-XRhH{^7lLeQ* zGcYDz=+WKTOQ^0{wtPjy=K4)rWarn)z;C`$`hE2sJ@c2(=;<4PV-MgcQ{jk&mF95h zC^0!jKcrgQul2v(3Wr~6fYaqK=wf<0dvq7}V95H-4J(!}mz_71{-6Ct>HFPR^xbd1 zp>Jc<0m5+h4%VoHWP3W>EhZwG4LT9Vm~E3B=50o5-Qd)ljm#iB7-a(Sw}~c$zeRT1 zFZaKmat&{;{JD9w-@XjHefkCp@I9GIk}eJgSxShD>m|V_h{NV?8=c-)IZ~k<=}V_8 z+xpU+3YsH+_Vzo|&MUQa!TD+Lyj^gfE>LRE1G1}7x}QiQ^lgmCK@4=Kj!A+`B!NcR zr8nEJHNh5hdvqCpPbX6cOfB~TdPF(cVWCU&rTxv9;0ue*mk#oWgNS)hvg@9czC#pf z^I(se?IO!%c+SBjNCx{ZU(mSNE7b*)ee2SmrDK#s%A1sXI)(HzVX?3rHrH{S>=Z;w zMEf<~o;z2VxKIdf{z_QBhs(<+_&AI?(DoIwT;RiNqL_3e8DqzMa_N$ypdGoFE*w>* zwu{G~gixrp5Jp(Kup0s_5XzEHtAYgqRxN9bL4fWS^aq=NgpB?)o9o%ydtZumKFj3s zlN+3*!Mwq_Cdd$Gi(p}{&>*09n=gjz-0CFLXu)B3rl!Ez5fV~}!%nbn@hPm{`P5VR z_taB&sX_Vo-Mh-asX@w7E-DxBzDQH?>P}M|luD&WsZ}cJTDpKPq-#0WpW_C@WME?? zBRsBj)*uQE(o!91Fz6%YFgRY+1X`WuD>CUu%5CnH0x8uoP?v^DT^c4ZTQmE|Y|JJK zQ+h=?q#kjpoVN-c4)G~^pAK)@b5N`t);R3Wm4kfd&6s&Oun!}9Jqf`fp)4rO0kLsN zl9+CP+Of&f;J-mc1dP~WIgDX}b|#0z0AIfG=9{YRRpDtvWL1x=kh$QR1b9s@mT$Pa ztiwsTPjjS<6UR&AbqmFX(%jJ6U>%f7uowbQKdg$(mFI+1hE|0wBQ?RxLY9Rt3)@fj zhdQ7;JdeD2&LD%y$t|2wJ$$@=*Rxy4zFtvzZqnD(ypF|1o?idy4{>qtbW7P>_jvujdF7SW zvGK>;?hlVX_B^D%5PaVQi4&li*LcFIg;@w=mUO~Qx(4iCmKvzpNWx^jXoh~g+#i}r zHS5>8nrd-Z&%w(&r*hi_6g7|Pe*Nv~Xd)ePTr&xw*Lma#q6?s%NIdPtdeUq<+C17a zo)*(NbRk2n?e}7KY839HC0C{q#+~nE7f3pA@*_ zkmUAkicr}UK_c$6LHLeYQSM!6TzkQDPE8>$Y$Vz;j`QnN7Tny>d1B`~G*-E+d_VP_ z8I#|9l_zaB<>vqVUHPZmeZE`r@tr%5$HsGwR0pg!s~RbmO!UP1 z$;47)CJg~{Ls-CGdxLpZ^oFoCapq`4Sa5`27>kMwjf0AU3|?22)b*z8e0QOtAVbj9E}jBV5il_p{1&)Aut~%F>bEVqEZ5cJu7$bUWqp~jNCEuy-T)! zM<4l|O3JM-lxF27&7q+qcd&jZpLzP#SD$|7q_ChdHeUHb`F_F_<@@ixR{lp-antDD z2+phhkhmG(l}rjeL6SpY0&|GaG7|X2Bt~HtWF0n(r&W(2sf|wYdGZ0=4bZ8q!9_CP z3UW>qsLVp7KGHC0Iy*v+$U2A-I74G-)PDA6^B0$>(wr(?8GmP~gdHs-t3lt@Dt%+H z^Be4m3j%c$7f40FYX*$mMCFaoxyQ0(Ne?Klk!0K)o~y85jT zgr^NL#0sb4;NpQ{m#hB<=l=%6!6%Y+!_4>Vg*RS8VSJ}I4!@WO$rfgXH}-+P8_SiWrI#%0Sl2=8vMt=+z(rgr;y_t7OUfAGP}OOCpu&(vN0_S>siVb8{KxBh`L%^CiU07I@Uj&Jc4zs8Ng9YHT zYF{h=^vO%W>EO3R-VA*+?9K4EBTh%^4mwXc|LSCrm|m(@a{754Rg$VnNpw6_cS}GE zJEzY_?i>L*>3ek6UzEGl{ss0W4&^1~tC2hDK(8!CLQ1HGI>$dmZQp%O15|^!TX`@- z*y58Uj?*m&%{yWY_@yIZ9;>`u+y{q14Xgwq*a0=fts%sOy9Hcf+`5GS6h(|t&|CFY z)ZPXX=kbI0q1z=cC;PAwl4!7q`)}$Hs@rnCiQ9EQZ5Y*ixy1b!4Agwp=fhkjQ>9M; zfsDvYM`0%u8QqC%c>Iq*C0QanWhq?}5!{m4e)%~a6-cZY19?XL2dnb-4e$Pk@9=$l z8NRnS2rk-#N}t^QQPkg2B!S&hHYgj9(+~I24>=XC(md%C_KcSb7PwFHP7x@GB!&~= zG>G7hQb85*7Y?BKICm8G%>G*kvF=(SAMNQR?<8>An6wj+`{8rlQ12=$|;UN}-BpM^AB`ib?17}Hmh+mxj8XO&LDfuendq=** zPrCUp<@QbcMHF%8nD6DG3gT2%5J%#?s^Itn!$RXiw-!h9i@};p!~O~z`4;2J*Q5>G zFCBJZwD$b@ci-qed2*lB<+Db=oImxg>5ZQan>;ZoK`+aSLN{zLS~h-CkEz`zm1Yh; z)u;E{yGO1XKR&5Pu&aM}&Y4MB9oRP~I7YZVBu#EvcDopp~@uU)@zL7foQf5-Gg zAOG?B={x(?J-Ii{Gefy@r231zr(UX@T|)hzTKdzB$%~Y$TTdvBOP18E{LNB2=C#Z8 zk?IknmA92|h2Xkp_pDp9caJh`RMt=Ly?1BC$mPxMfX`lf$T%__@c$Nha0ASU9J42d?0iB+xe|r)q^pTlb%8R-ZIRIz&%&$ zFft=?2=Hi(I=HhkFEluqQO_&jSwr5cbnNJeD}^QIt-?08Sq#+t9c&C@7^0lQDdnaRr&NC>^!dZe=7(2ak*v+Z?C_mVbg{A& zE9o38=nY`3$9~fdyA=~m>Wzka=Tcg4d?C_d(hGjUkrJ_n1xUeRT@576DMoPx#FrCy zPx(UPZi4-0pX8&qXuytrpQgK89^zp2x#3b>(U>T@kq&wGsi&S*PSH-AHf-3Wm;~{g zJ4+s`->clZ+x)F?uKCm2)oWG=#md04ibu=$z4_9rXZ+pgx4!o$Xr4+$uo9pHf=N$L zh~;VPVPn06K1~jbSpJSRA-Z4-N%psga1gzQh{N`;o5{y)p^>2iz~g?2*B9y8%LNhk zIVMs<@i)uv5#<)OQ?l%v;+cPYTzNrRNNecWn!icYt~@+dIjj6pxvHF<`tYS;!{}}b zKG5AmAvd6+bi_-=t{xYuH-LV2yo_vbv++F2BRBDqQ~hSU3>xNLLC|=j}NVxO<266HdEVyW6rV3&E-N)^O5)Yn8OY> z_u_sV=OXu(!bu;Gn@FLwo`u%yoliRsyXvhQ^lKsn66WYGrUnI@>~OGeG+l4P6nwJ` zZYq~m6&9yP7NA{6YC$oQAp7Po-;TkH5ZNcmYQvMufM+ zq}~Qx@Yl!+^npC0E(kXr%~7d}-7%so>gmV1_k};d|9*2cuy5We6yE8?Daw#d$H5dvapi9M`O5nGZaEi_gq?M7hC50jjGA4A{iMCiTEO0hbk z3EqUCNg%p<=?GbBmh^HTAF$U|9}}(#Hvzs`%<3#={DOd2-CL3^9!riT&r)aEZBb{j z%icZXx%V%AIV!ED6jN?gez<*b^V?orq?y3QNWS-U&^zF{=o~VPKX=7d-I=b36T--g z1{qFY(o>^pv{mhYFd} zVEs5@x-eImCoLCNN_F~8!Vdj6f(zPGGRUDUSSLX@>w;JZsgvAM*Hi2%^^|+)lFfsd zN6e5svPb7JPh)x5LrmArlgiDj*=lK>T&JruZ)Z=*Pw9@c-|F6F@9I8gAL+hje-*!# z{zv{d`%(Hy?mXpDGUZWlfJR|=iL)+ndKVR&Ls^LOujW+F?^VLQ=3z}=3cqje=B1Lz zsU*R7H1j1Y(lFMSh&-^f2g+(26QGawNtu zlQ%rwnM0@72@Wdg`5z`2j0PAfqaod>6PO<4)|+6Ba5gF#gp1)c~UkfwqIUPd}l1)`En zbwZffQwJQmMp7l5>- z&!iCft9O!mE%Fy^OJ%_>JCFRSVQ^pMk8g{y*~e#srpeS#mT*mJrtI1^N|k%pXkR*C zS*e^+-sMqQX{6Gqe5HJ?G}2)-goe^#dz1&2T?+O)bPt_|*IvygiEBYIJ^!5$PY~=8 zH%m^tQIE4|Sfw-vH%tBi2dYaG2{j7nG1**^t~A%ft`}VrH|O495v({uVqz!oi*8ib zZr{FE=}q6e%i+7Lye}m+|NhC^nkV;t`N^kWH1Fq>P=54MBAkrzbVOv+M$Hzpm0B$3 zbX$a3B~1{5qLv6ts12TOaHvWkRo`&s zoYKHeU4We2PN3EvIW)lf^F(kdO<(9oB_dG?4xmnS5f}9r0$8Ak{Rxc|;#qLMYxhye!r@l#vQJ4ck8J7X&d1#xM&?BjHbv? z9f=MNwsz44`$u=c<_s(1IyPl0U0~(C=dNd3)KlB@YY@iEO_6)x zWqBmM{W9WYP&>D^YzZSb;y+82@FRvuVuu2W)Y*|TQEu36FihcT37j{w_uBE0@5Xa}j)oR?G#DgK6 z#Od44M*7wH?e=5bx@bE&Xf%Z7uxO5+Km5+yhtDgYL9u+Ldce0_=&z<5R`H2!HF+mp<0_9 zJ(u=rgmq*?#i7zlzYrpZNF5R4jTaKdL@7>o>w6QNehB@={!%X) zS14$PkR@i}*O(@e@p7?HB9=%C$y{ub7KjU^Ir0)c&gbMrtcEC>YQXMD7~Xv561__Q z^oQoN(BXmNU%3~BYXL;J57ai(YEPCFB1^EUVu;beLXgNI;7ka495Oe&SoxCI@WOYZ z4*dL7x)E-U40~kKn@vW8Udvc9>4?RC*_*F|B$Zz_xh*?E%@RY%iE4p=kOf&1kk>t5atqH`e3u&>K3CUx9rxr^)ZH6W1PutbzA!jeOV7NRZ7

B-* zC^QzD=7A5@!hAMQtdbVU3v~1J<@)*N#pcD<8ljf06jwpfN)(KwZpZzF^0u2a0p-|!ObcW!}m_*E{=TZbfN8XRDk9()43 z^bP|YgmykD6|i~dJ`>KjIO|O5Cb*~wU%^FHpFlKXG(&K&oz_fa8y~g3ucYqeTf%SN z{1Bc(gOdw2D+D^=XD)Ul1RKt5us+a~pieM$7kcY^nnvg+N)PIbg-7)Bgn6bKVTn*H zt=6wFZ4%ZCTcoG-n@yqcQkY(+GawWI=Qhw_x5U#9LL!ToI_MG%i6*zD2jN~o=NqTVAwyT6x1cLziBqm2}Qk#f_k%@{ls=PlC&v=#|>^ zqfp(vf`vn4HbG;4gEgfmn>-!7yMh)DKqff{^y%D@L)L=mk)TU;2341;ak^hu8^p-f zMt@207kUWELNcT^Q}75L$)kTjctCnUUnD#(Y!vJPG=xPO<7p!6MSC-k5&L#FpOqVT z8~N!FQzZ@BSG<(Jc``@lvoS78Pzkp`_uJ29xQTQkS-A(w&s@((;Fma(i2kv z3(?z6Nv0mGk3P*blnvL9HjQJG^u?@1UuK%e=Ia-mcAEk?XK+3NJJN$jRf_dZIqdA+ z0qjWAbm_|WyJZKriyJs5Ja=LuGSqZrtj8uEkdF!n$V=GFv%y4<6Z{K2_R9kmEdfy^ z_@NuP#Uk}!7=FXv9km8sKhZKgGE_QSnPj|$9;S#*su^0-{f}qemoF9!2Y?1 zP^L`${(IT~$3NG}B8T-V+m9>9kL6!KYIHDj2H!2_{UBOk>`|Q z%CK_+groTqU9HSPQUfIZh7vCND~GVVxBZqJfK?RjJo<7OWCedj|GR%w4%O9hYz~UI zgjI4eT6Xgo=rQuL$c9j)GH@io1#g@d$z?#{{&)aifwYWjcN16f+R&pRvK4EpZYa&mEorr04tO+!eKo(>%=uMGK@1GG5dR@2-+oXvu zeC{U1Sy-f$cxB}%yZ{Ol}D6Emb=TNmP9OxT;g65 z71Z`DaRBWFHnoJBquRyZh1Wkjw6tv7iN?mXQ!5XhZ@x=~=eFb>&nS<}K77x+HXc zXhSI9ytTN-JPyx;o$9U$@mTgv_ER}8pE>h#&QsZ=_D*SrgV%-1fs|Bi({63DFBf9ZpjQrxyHQ9u(84 zb-Eq3cwkIrrk3y$(DpomJ=56O_oc_q-@AAIv6q_9f^7TugLLe;F!iS!`wR2w5UR&( zNWS9ol84cfS8g#Js!WJsBZ5Z5d%I zh^N(4AWl5(X#2K$Wba8#3oj3E2>&4bR=AW#(rB8H=1L2dI_r}3NrukGGEzp%gfdrI zsA0;ZoWN0PT19Ih89P!PBFs1f5f?WdHD7#X=GkclA3UPmR?gDIrZ1?jQP{h3`w6Qs zb@J_SdZhAmXW_>q1*2$^^5KaiM-IOx`)|vcQBc>E#6GOce)V~k z2g-PHGI(G@w##swA(+Dr&Kkdf6E=1tKBh6@l;MQ!wUF@mV4^n-IxB7~zS_RQY;O?&rls^8nFD0lJ?J@CM; zF~2?5=jdanv=1?6LYoCr+flJm;-5!k*@bgk8ILy}qZpR`ze+RaE#rr{7y(`U1?$&!szI zSNXd55;=u)X}w4?Th65sx5fJAdqyqI9_yP&fcY`?TaEZn%)8ql`~MZ=-TOotua0LT zHZsH$W)gJ7`np+HE4@ZenP0N&?UFp&LiJ{nX;+V|uS3a0k$?~UjFdA06FEGN97mp` z+@Ve6?+XHJ6F&Rf%x)zk)mhhk^ybd|ZE}adLZUbYcLEb5tWV;v$AV9hExur|o@BNU z24DB>kocK!yI`rx3ev}gX}r!xb9uuN4kHrTkPNBEir^gc6neI zZXpj&o;)GMeb;Z%=vT-VfdZSBIKIbX_r~kX zrCSJ5s_X)*WdEP=d&DZObm3Sv(PXkGUUnLSY(x&%xy-fUZq^ujD%h?g4x3&t=Q#AX zoUkC6q8Mndl%^)c>r~IUfB);Z)i5p>L62W@Y)))>?E2USyxxfYEcRZk0Wzsdp{uQA zwu-1r6Vb$sHl+eR?MsSYg*QJKlJ< zxmL_OJbl_@UJS%SVBm+-xOVI1)Gx0WZa&rZaxBmFdnB0Ow_?2D{OXFq#C*YMI)9F; zZvvrj{Nxi(a>Crm^DCXU2bj~9abJF=CnhbpnpDe+b&K_jvDaB_sx~jSEVeGTEw(Rq zR684jZv{I5O`DXPc4?TEn+`o+zwywajkl;%xq0jF%JR!NLdJLL> z@s|i31n`{QRFyP5Jru4*JC~#K#EBNqLg?*tH}*FlmW>D7_!jg#pUDLETC}wao6qlQ zw5h%nT|I@~n`(T6X(+;+_=KDUKj4*MM&x8w=Eq1+cV`Gc=(|ov%Q7=6B z)4#kj#fF1&4wCHgmk~X2;JT%?(QryP>kVR# zMKseJ#DovFO7yRBtqS5kSR8yXUlempsNSm6`$uPV;80y|7sZ5Ah772G-sFo@-3e+@ zO!bq;cM|yKb#|CB%oJws3fH2usk6DCp`Wpzsh`>8CTb@WT}PjYn(=n&B% zGSQtF6`N3FtTEM?Yb;IzdI^GTlugXcEX>Mm%+7*Y2n%IlxK5Rjl$e(IaN^>`C5h`3 z8xn6N24R!EnLb|&DiSf{gYR%nzkwJ^xl8}aq>H}iqGUPTT}GB z=lQLF`CaibG3{`N4!OCWtSD>8ZL4-3kBND`M~_JljL3md zcoYiWXdc3CPEW%9u?`uz1=iaodL%ppG!Q^5Ueb>{rB!F8#- z!=EKFt{aBHAP))hP|^lrkD%xC8<0uD4-!IHh!~H6Y9dP%-TEG+2kp!HiU^<}%$LQo z#7t?J?9q=W&Bp;PJ9ca(?jhKjBw-PoFD?Sp7t0HEixD|oU|4LZ zHqJFIGS~7Gc^q!>6=H1qPWFOrl>|xJ~&r1j71G?w+d(1Cd ze=EGiUK8=#0fslMr-gUe1@V1pfhs7WG!_47jETmKZ~XeJt6zWBsC;tu?>}6H$ZTda z`TK4I+uSr0#O{YRhhKm|D0i|aQ{utfK#8aPI%<^^8cgAuMz7J_a?nJC?P`-n)}1Q5HNqf2SdgMV8ZEv zPgJ%VMbQ`{x{UG00b)1fIB|k*qOsUGmo60N>Z*)u#bw5A;%;$^?n&c%<34&od{Nx1 zd)C-s3`3ww!cm0@L4C<(2r==HaGaqd0>X%zvtCkn9S`FtTe4WDlwlZd@>p<8LMI86 z*aT_3JV`fRKi)9Olw&Eg%%_VjJLo3e^K_5yh~@W|&n)*WNnnXV;1ORnEH4%+kI;ix zm6OWJtMp~1;wnv~iDF*!XU%WXMrD{VTnJDer97540GEgXk6jfGjY_V z2B%u0tgS~%>S+qjiBr^j1e_;&l@oS#`P$)?wJcwi6Zj5jQSRf!E!=EIDpwZvtZw|4B*b+!A zOt@QgONmH^h%?5TV$BJbj@FJgx1$&IEkf2}veety)6~=4+tSC{$Cm6EL_8D$Y^0}n zyvsG+kYOBZ$+BkIJdRxQ0DV9h$8y9RaBUp8Ho-6fOLm-jl68_T$5Bj+g&D>YYl$t- zQLUeEoo`!3o-nL1tuU{$tg^1MZ8OxH>do7&+iiPHd(6*UpSK-x{NC}I5)v$PqHwFKf!s7g%2QJcLMcf}2$4XJ}@8MQEX5*(|-W;WL zu2kcNp+c5UGU;umAQr0cq<5QoB1oQW;xx=qX*gIv0ip7TO?fm=C}w$Lo-_^N@+GDh zO`%-Pv;@o_Wiy*c3dfoj3CEg?#Jv4YpKRREkOM}EauheT{gH9J%+o#C<}%4~h7h|e z+$6c97%?3%AiVpg!F9mzr8u*}D8&W@lW?QtC-@V0@L;1&io>lu9-)DA15cH2t@#^! zZCrBYn{7CU{KmGgvL)<}{9|AY{qDv1C`|PfiMv1p;QniT!c$MxEke9TO|QhCfK)MX z;7#wn7JNY`L2Zc(L53drIm$S=}GE%efc z(?xZG?$6Igxf;*jV~Ot{FGEtZeeQHJNEYJvVFJz=7*# zJ@-@E>*MQw+_^3^c->P!uA5M|@zY!Nm338HzW;O+_;QtALI!;|w1TXq5EU9aG02VBL<69@0+~m^5(I*rTH}`m2v4$-R5fR>)P>WeZR<;0lhd zDI=%o9Pm)9nT=?il|+&Ao?NrTVh#-pwK~E=Bk&G)goTA#98tC?v%_k(*`nMITT~?f zo^B4cSq$tgmm#9wVp!)6iwF-3az{p4oU#?$!ca0kD9k30cZNkpa|?MR#eVrF4h`_~ z2{8{t_W$~$o2cNpw;uTWPEEZ59sJQsuoH6Q7-NdZ9b&FD?=bU>v(TKFVoQm2j-}eV zAZ$VST=(3lB{60!*tR=ghO|4L+TptvqvboZ+(~Jk2@})OCT&%22~o<#0RwkeRy>{7 zU+~xRpXJGElO_yGn>bPV2NI#P6DzYS8=kJnoSS%OwVDzQ%2q0Kc#bhBi-ZqOS@J2x zu?}i@F6?UEBdF=1)j+g&(K%X;l&YJGnr_}2i70A~nh~b*DaBjEXo6a!W_GAGy?r(0 zrdp$(;vkD5f#)OOKOI?%p9tg-{JduHuhx9rt_C+tTSi;guBKO;nm@L!K^A{&pKIQl zN0mAJbOJS*Uf4dxFJW=m)JVJv^{^JGSN}@QVDf7 zQAYz;@E_+7e)Y>sgZ4Fpf3@c0b~PLV-)QUF)o=)WHGlNhsQX(L0@z?L1o#~@K=AXL z!TcA_ezE4`b~PLV-)QT24K!V!d;J*lW1veCkOM8AFycofn?mt4ylnqCe4gBW-l!vz6eO8>Z4Fo6eusLjinkN}T+#ZMg zj_Wje$GjobFxmMan;aCXUSxq9y^YMKc30u>0~&$+1{@DtVDSqir?fODr?hOeXKtsi zT~E~19&41!%5p}}o;`YW`Ori18U&^Va!5IgT=uOv+l?X*cslt7_!FC% znshiYGTCcvE6peT1578vBf}a4)9q16xb31!EQvCt~gnb+L>=Eq4R}P_>tA-6)HLCdU z{6_cRi)q%XmirWhpe# zG|(@U&;>V*%Z9NZf>v=i@~G|GNZ~^94OVm%{33iwJ z<1&z%NDmXLUVREvT@L*2h1cac#&Ze7 z5V|x)-Z*>qqi+Xnk&YctOx$t#<2ohj;6eIf-AyX}Ba+kqp?d@H`-D6@b|Bf{>7SI` z5&yTk@Z_GNCE%{*vX?KfqgE#khOi{ zgiU>mAN@4=qa{-w?APzTeOcSs{;rd|j$BdO<-x8aRtg*UBqZbvom^?t&)Z%!c})+$DV)wu|w|s1V zs(uI_a}wF^N$!#mWfoZ(w z&(kv_eQ;XJxnarY`V1fZzPZo)&dL_?J_sOqu%7 z)Gr_3N_Dem&zd!Rw(`@~t;$c@Gu17st}dN0vG~a0lDwe7T~{4i+AphT`VOgh>eQ)U zEnE8K)Ts|YJax(!%U66kW$M)FrRaTU`&Q-d?AfJwrqb5!RK~M1O}Q~}#K^Si^A?OR zcj!lDefD8qsxO@$=rE_yOk!_PsFZ{n&2jle=FS`hL(k@?PvYbFcg%1Cpn9 zG{{4y;^wGxI5K+Fi;D z1)ob_mR|qd^E*5X(+980{Nvrbf6Q7bUHmnYO#dYU{&Q)R`^BerAC8P(93FQ2gAacQ zgWjbHY@?is^=`(A|3FU^#ie+o=(HlZc+LWYj+6;$8Z%5YSqf~^{0bZ{HTmu`bgP-F7Q!R*Z%lE^L{@wc|+a_353Li5CTC)L<^{hC8O==)e2QFYBg{zy;UlYO#a{Xotf~^d++alKL3GPIdjh5 zXYak%+H0-7_TFn(NRTxz_{h&mXT*~OALL_}P8G?u)bN#lTw=YGu4layW!L|<-bws{y0zix&Yxqs(?g<9 z-ZNgUFGeg^nKyr2R%5?wP=B^))0J^Lg4Z3v5DYR5)=7mdq!Kq!ES$upYqF^U&#&0L z=7kh`Bfhg_Ok8`{ypR@qNacYEsf5eOI}JXDX;}EWNL!>^WL#vj+^S(}UgGcRroR1l zbotwFn>=s5^_IxU4^$C$ejml`#O1+Uc)0Ysy$0;|cI^>YuBo z`=@8l?XyBH_1}|O-^UJJWW}xp*~fz5aH4J$rkzsE*euOx87b8%W{788W0uZbWO${k z^75x|!>*vBqqjUW%P{fW*5H-0MQG8h zuLiG_JwuCL8?kYwX4x$JTduoi*Q7URMNe_x&^6cWnh3ldRY#3`^{Xf( zu)@!kpK1uWi*f=P?woQ5e)&u#zTV|AC%No55W@q;rJ#cTEO2JTWAc~-mVh;26OU=SC(E1V%krlur3ccJ^L6?9e0Wc@ zu{1s3l8=cE@tu}<%DiR1G6Ya7!K7%Ft_sW4vGCVaZmzOaS*vV=U4z|&J;S`ie8c>M zlLiL{Ctn3$;8k&d>Q$Dj;7=Xx8toqKx!!xd?|T2}q|t%V$rH9^y^#Gv&I`FO6uwaO zLZ5BL=)AITCZ+<#X%n*qI3ozNp+~;MT7;C34M+4px$ME4W~{tt(zplqT=u~DnN7HQ zu=(m=PJL)6A_x6^uY2jjho}?fzEn{e3sN-THtJ5r4fi{aAaS>6~dZ&M@oKNHYkPiWBTEGIS9u7b+Ga6kn0- z-!rSh$qWwF(l}I0!wS*3KfKR_?q>IM?#=F(-Nqu!G8DOrJ$<}=eATWR&uYg*zUQ33 zcC~t1ye@H~$;v)ximDy#&sA3M}7#@w@5s6OIHs2K8rdgtIyskB9%XdZpi0hYc z!bbFPv_=azRQ{p?duK-IUh8_L;TM&Hp%7+yKEo^kj%W!MAY6bx*`&8R^qS9YTAi6J zQ|{{bIcZj(OuJ{vygMTZVD*L=_gGniNSi0P&w@*XUdhUxmb*6>%l|H#f@jZ*EnzKW zT*Ja5Z|K#BS3mLOt9b?1?9Ad(c~^~dSFEd>`B+JGg2~o3a@`ZpKd*cA+%vT`cE=Mb z$z#S|fBl#-UGE8h&F=oYez&m{{@Y?z7fe${Io1qQQNV{I-X4U<7 z^~<`vF8US%SG*X#`u$(MscE--d{<*My7#UIxFkW7wCKIq4YM1P{MKNS&EU`(&Dav| zupwA7Vj`Ikt}hmv!h6-Ln z)HGP{vOlJK3^!MrM0rUF0qo>OFS2UYhR$Y-Jf)yG_)d8e;z#Bm)UiJtlpR%{Hr@XS$&ZEzWZ>hqjyGnT55_Z--lp~ zIzwJ^z?hrmbL9DE8}qXAVW-HZOHfeMcp_3F_V3G@Wq1KELmUL^L7pq^e z7(kzrlpOG4K$V%AndZ&wBb_o2YfZ@m1FI29CdMe$j8iPyCl6{S$FTa9Ic|4ZwYPsk z@7~qv_bdS%h(}UF|gB&KSXP(Po@cw?!^)p5c z%_(==Y|%5i7w)Xl>9yBxx?!?S4Qg9TzYClT-Ti~eeD%F|gLGUZBkJmKMaA#`n zJZA}-fRSBA1;YsnslioWe=1uV_I&l97~;vy1Xuus7VgFLR#ne{4a=R;y!aFi7H!CY zS!{r+wYnF&#_B>(_G`X%HF%&&HkZoY=iFAXmHW6 z5vv!ke%Nr!ExEZ(nVBz~yz=_sbdHq&&+wEJo zuGLCj#gf&BqxVMN{$uogM6%S&oQHWK*65iK;rVh+AH1@tv|y;qsRpzZBtIB<$fsId zgMB+P)A~PHy0b*T!_{uS%=T(l+9L(S22>ZC+^V2D(_H8dD2sDwp~YQVZOOfA7{tsw zhtAa^0w&rMpTHuc>=AXe=hJft-wJktNbq21g?JdH;pM?q<$cThm6w+HFE1-E5B3fA z3zi1^2g`!xWBZQnH@0+a|FLCb%OB|bK>5mGb8vI;h2S58`+^_i-^pMvcp6Q^oWoqh z+{VN^68RH(vAw{w(7DjH&^@i+w9;w)riZp*?(nOFK#=aHENCp z*Mno0hU!JiQF`XZTV?bKh8e1vUeSKN=I1+HBSs(k(SK+bY*Tn=`|LkWpT2MIZ@#^5 z)ccS9{=kJBX?}e8AF)j~x+i3Rf>u6xYV_!t$-DkkRfXLP%kN#bto`}(J8PyzQ{%gC zK)I3K&lolsUW<>zJ`L9P?N^x9EB!m;upNcY9qF%rXB>u6STD0L?}lQJFXbv3hk@lP z;$sXUM_e(3QeRy(4vWpFmj@U1(T0^yN}7;4zSo58xq+EEIBUkxWNf-%9dMJQ!C4<@ zNN>t$%53V@)VnFGDZ3%3DYq%FDZi3VX`zhY#`l`sXO5cbpMf!4 zy}FBIxHY|>OkJPxGg&U;OF%yVn;NA3r#9 zLI0m!*Kx0gmBy6=p1=O3>)u=@tB(g%K0gMw4I(}2e+PRt8*1ypU|DuLHny756sI>- z&i#3gC;gA)ttv3(rX^dAno7?_r~)lFGp7)N36l{i?ZhF*c49{dj$<|&P#pa;dIE3` z`yRHB2ab@$;y5haxODnGXuk`aeeW{eWxglVD1MMwjI_9d<3=P=9uiAU!mc8)TBY{& z>(!Gd53am_{+Mmkrv72ps~?Y=G_kx8;k5R=^_F48h8aJ+dE)m*P8+DX5S{O$QxocV zYJzT+!GaL5dbBbYnU4!hzy2RiO+drqI|dWy99+8cq~}}(O%|f&dHt1sox-^afoGBo z??n;c=P_+Y^cSLOKhUzUMqnz&zbQeRVS^4q@={=WA@C{v^m|04iy@BD$Ma{O(@(|X zsV61h(C+t)X{JVu!%Bjw*hP+m4`9aV6n2#J3X(*|t?L?sw`WBsAeclFtfz;AK@23_4sTlTG}*0g zFnfFVP8*))Kw$UYTDq;p;(yrpd2)+edsuyLXvz7RJJXWiyBCZrhaI)DDIbifSS|Kc zAjF*X!c*demVSwEVg!>?A-gzWSe+lfTxwUvXPC@9oIz)j&Jl}NTMfp@@pFi6)D@2H zH*HB;EvCTUXd3+5&cV~m2HakD`~2KB-)bqt^56Vf6?E&fy)x^66pcLA^+5F4!9enJ zIXP>d)3rUOjo$u-PsWuvgylp1*RcDCU~gSk|E!u4RhsLU6&$@wHe6P-As5Ry92@+# zy;Z5Z7Q?ijScu|Fqz{NbGaFZjOlZ+9 zd}+*8L*N)R4ZZdzxisoBH$7Eihnv}$Kg@F&>YmV-B(W&|oUH|x4 zcjcM&*nPjp=sq(HZ{DaLH54E&CSuA$;*8RNA#tC+i0$zH0#(JTW6TVhY+vAD4(k^d zt3&?StWLAj@`c B$Rw4SzSQ=Ui5YQD@exg+`lsp<{syr;k8E%leX-35St!f~_w0 zE+oQ7#)2sDcnf$J5l7M=`(r4OGbW;a^J0GtdAqP3@9SOKGvl;pdM;&LxEn1QdA=o% zFL3gR&1(MwQI(uuV8y5dO~9H_;}?j@pw}6`z#eAP7wA<+G+EQsa0lW|u_X?RW>l7i zHnX-+uNI*twdXLfj~h=sP9@P2S+scGFOg_LqD2UZDecg-g4mzk+TmzlH020fO#yvM<9Eh0h*iLr84cnyC5N%zvrpX`>Y%TJrOgttP z<>IVPeDyy)bV%27`0$yw!-u2%$Qpv!+9Fv2lUQ|Rl2u1NPha(E z1*}vu$_g0Z8*3&k4(KWmJQ+iRS@1N&&#YFZikI1%idaPfxR<@~G6&L}4C)65GdKd> zFSb{J27$y+Pk4(1ITHRy<)q}r{#KgLB%<1#eJ^@_^a^d4_TsEn(OnWoUb_M=Qzhg(TzmI;0r9HPG>(w;> zBLhq-IrlQF8zDBXxy)z1p|3CVssS)rWGgjo%$Opjafx+P3DXj+T^=kLR&`s|qN+_* zd#mZQ=es>BY}1@jlo zPwZ`4c;~!D(W9#qd!IyODeX&x(k%zZJg*>}z?YbQD0`D5`Ev2$5)vrQVG^Lrbup7)PrwzOV&qegm zyE3=~^Oi#)c4E$hDCR`5rXF~dAC)+(S$I0R+~A^q+#YH4KwMm zf!^^P`Ramq^8^C)3`FwP)cJQC4>OkU?-CJx@ouaPlzOKI?P{Zb6$0a;wov-#7!?>J zrdTy&6^vI+Ftb%3)!s|w#5o9(Q(G+NLhKj>$r&r&AjKOGKNa1r4U^H)2kNJoOInC4 z>E*@2B-N=ibsBV*4F;P77Tyx9zC@FFD1+d7&pAXVmp+5OE?YUpd87O26h2*N#2sr` zcq;1qMt6mHg-y{s{Z}SgQ;2{~K`%tP1@Zf0@l6p?1wNaE8N!v=RB zyol1GYr2o={>+hc-=H6>$r1exM*``G>mE_-44@k7fu?=>X~O0Ziv#9{%omn!J~w5v z@#N`$iF}``#u;8SY=!kxrtXKPvStJfrM*>ArY@(K!&jPQx9RB)uu+h6lkOhy&!aS z+_Jn_arP+69u0Y+UKHA6PrYF@EZ+ch%`bv|>`AF+>_+pfcBQ_a{G#h;R`r@u!+fV9 z86IkPlEGq0Q8v@H2(kbpP*!lu@(Peubmiw2UtvW`+}7>!7N*l%n6A>}-a-r}xHK8R z(PZ3D%oL}4l07L11ej(h&lsDr(!J8N65$&5W&9(KZY@KMa8W2CgYkkeBGb)=! zZ_#Y9WIUSBb#1aVG1kKK4V3m>6i6VMtxHzPm$VMQQ6TOoTIVfK8Jn`ww$i>bWpm2D zlz*gPDvbl3OT!9apNQHK-F`l@fb5GlHe4KA;QR_inJdrsnRY0TWrq#=#$|J?()}^X zttNS$Sc_Xe7y&n7Cs#LUyk$kIPW2IAK2UnvFFXvhvTagWrZTw zDryTqicZm0FdMH^K4Aw-4+=IPu_)UBz~R^v{JWEKcD%i@u-#VcTgXhshcj=N;2UV ze^KmpI_kRUOi>Q8m&TP6Kf$?RQGjexN_{*A_&QeLwjN| zG-UWkvB!BGm^uiH3tJ~PEWAWE%N{6+h;++^>+&v$nRPAs3g_G>=1?xMXf8TL?E;@@ z4g|jcf^=9g8FN}+;xDy)ee?9`7ap#5`mXqKYK8(Hu9i4zZEO7SwN3HE*mU1>+SJrO^j?Y#9o{{kD?9Ji5UE6e3cpSbQb$5)nr*Wxx@lt=Vs zB9H$3YV=Fisl$Y#mHrGwVHQ`g_luWf=q{5d-BCQ5W|kTxo|t1#Fs@U&iPX!9C;Ir4 zArR&}FM69P{v>Ae%Qzun^Bdx;-eXUsoWv9JMNclwV~RhCt(2E1iIwt(_)7WM6M0+W ziTR=@7v?d^6ZFyDsP%HpSL#*vcC|~VT@Cbb)@mxf{Wl1WwL zY)_-CU&7H`cVi$OTP_LrWKV4U*cwwa#pVTDb}RjPML6gBl_$PgyKUQAS;+LwH+b_a z#$Ni-rfSR!+!4%IKC{bi(0pdM-Qarz#~8g4u>WT!4jEA9z=Y&`vG41iOs&0Lqa#U?dLDzvM!DwFz>hT5N*wYd8gBFNNdr(i7ZJSdRxUw)^tZvxEGV z7q&KhkNx(WCroqRW81iH-A2>MYpxl6?PJZ&SgE$&I6^m*Ys>ltt-5#BpAA@77MJ>o zR-poy2IZnBZv8v&m^|-@)$x_#6TJ_SEM-N_Z9y-#*8G=sxBH)|YJy`KHqxX*FMKU} zL?W9fFcqU&Dmf<=>kfMMD#{AB#Icho`a3BuuoSBk3*=!z>YkbyyyE7YuLw@8-?Vh; zCixn_9yal2+?*I(x_PtQ1MamvgfBziz?!M7pv!8qIsb(t^^~VbZ^a6H=?10wJ-k^qu8+l~{y`ukPH|Z&$Bs{Rm;y zq7O-&WNFK_J#EPjy6rBT`CqQD!Cp2)krzgS1df0qcP`eLO0J8Q9?-LLQ+xH^(!SLL zvoFso9MQXeY1Pn)S^fLF-4EDoH{V)52QRuv66n=7gZ2_m!@C>7PUDgQ3-m6o{&ysS zzA~k{Qm?&2LGX>?D{a{~-=OkZ_kY1sy&GdeCw!E>5lK|seaZyChz0h5-Gw75v`mM2 zFhXIwWaulVTSDt1iZ~p4<=e{LW8Dadfu))SDH=^3j4KYlt<{W;-iv_ZQ=ho*6GW_g z>cfT6uMp*5H)QUDjneycVA+wdk?m?~5J6961)>RIQ&FrHYyIjGkjPda+w<}1x!O;A z3Y9fKoD%?3erHp&-xCifztm7~Tjc!MdD3Z>iecyjdkobIzuI_h+{~a;`$ieQF~VDBH0iL66t)(6s5r{F7t)VvV?QKz zHayj3)15LXfzsEpt=YH}baP;YF)Ntuv9{QqpqlSVm)&gE(qM)=lhX_pHm@_&qL!!A zZ6TXLV`qfCSrNCBf_<_xnlfI<&~LJCvTd?&!tVJ^?oFOe-d)yRwq5pJj$N)@?p>Z; zUJut_(9u~lbzb90BMcA z-6n_6cZ127Dxtt2`=H5sR#yVq@y)RqN6Rod7Ci+A!&%~Am;C)tvnw7TiU+3*E0G1I z_aqne&FYg|mRFWvTu@qAT2#_!V8PV|6SF7gOhcr>nYnjo-<@-JuD>7;!)UC2FX}Te z9|`iWDwv!-Ij1hW4s(P}T9d9`-@rIQO&7!oYVtMto01v=^%?b?E0leS%W^lDoGdAl zaa&@#FScgTFN^ABS{#$(;+8AIv71GeDFrD;{nZr{i+{5t^!U2aKm)oU2JxKqcw1yAA^HVAvQurU@I3c2N_=<+M#Mvd8LlU-&L_dg ztLy={6&>D}hPi5+hJgAQAHp}>6C++`2N49wosyL@EakS8*hvLEE|Ia}v1luH{7d95 z9M%;J4*wRy#sB=r;JI% z`~EmdCt`7uE{#RRI7tk;_J4|#WPZKtnePph1bO%Y&MW;;_a5ZJ`BO$?yL)2`GPrV3 z_nxd1#E@_pj=<`G?0g`2ooz!b!o&v578r1{7lKh3H(&#XVM8n;#RiE;fy2I(Z381x z47bU#L70}YAn2=AqDPx$r4|-gG8hF`efKQ?PV@)yeo9%+Q}Nzy7=@;)(XenE5i>uf(PT0V$JD0ls9PP?{o8)j?OT_o zT$cN^fD5Z65lk21!nk@zBE)lSnHWW4R*^0~n2%Kh5l!L3Wjlc+&4k&I=et^ShiMaM zj~`G!^126V)`g-k57N7qEXW$9T{d<24S9JDnVCPjb8Ym~a@4L)_b5G#ebmTck(}0f z)S7iP+kZ6RJZk;c^zY20+27`^D^B*Sq_q1AJ@?5uoyDjiW+P;i1dVX`_+%_BixFfL zT&{iBNXmndj`fb7HAWbq>Ks#My#8WMkAtg?CPvI`#JHxAmEM!>ED#4k?~<7kLAD3^ReWKYrn4{ljP(>@TysV(5juz-Jy~* zcQ`$dB)@9>nO&FVK(ug+#b)|Jn$LfgoRx`HL+4UzvGuxZW|JkdhU=PT7#+PZC z!NJ+SgSno*=7ZL>r_)2pPxjJy{8rhzUXK)8EBfZ<6z3IU=1z}YB9?yHg?_Ww0)r<_ z6_(_b)gIbYagNbS;|}te&S&@8Q-L*@J&OpA3hrQ2_MB2j*AOG?U~|qjFQjr4P6fwE z74Rg)7iiYwEN{)OvtM>o(j3Q~t_ALeo`v3pzJ>ldN%OK6W-rWHn7c4U4<24-d1!~}?K8a3ybb7EUbaSK+Fg$ik z%gkTCm94v?y6bbD?D2hlQ1s#Kw|+UZ<(5f1ru}F1?LG&q*J|1yt2gw~2A-(ffpf^_ zO#V}QLu&uL?Ea|@?QczG^lrB z%qF%*$^K5UX~m6*A+rku%h+i7d$vZ&Lt_8G-4*3UitpbQg?Fd&6busTd=&BEcXvyn zgP>=~>~mtfl<@;Btbu05o-y4?dKa272ZebbOeE@uE8Q7P{b23~+fGCgy%Sdr@$-XG zGMCCW!z5xA`aI|_^q>XWTCDA$Ex5U72O2gTy2&Pv$<|93rA&_sWJ@*(W8bK zZdUL3^ykJ?(QlzOUIC6r8^6SQlDmUB$sV`f@4yl8dP|Dq4TJxU49l6`9?gx@i6^9* zCs+{}B(5x|(rWRe0@f`Ty(emW>0!W$+Fp8i@HUS?(t;wb~-AHP1Eo70Mia&dMz=% zCWteZaDInGX;<3+znI3RZU3(V?c7HHeQWKB=}7@Gh~bA0?zfoxgI#z6X!U( zMhoxx`R9Uj`06yZ7k-4xjNifwcP~~}$uW{}!pUNmJu$@Y;mDiRzjV-@z~`mG@)C13 zm!=Q;g#{c2VS-**bY7;F8m#E(aWCF8uN_?b+;eM<-$qYu-Fi{Y9*o6~KLe)#4?u}o zF){C;M2w^38)pH~p#yfjwBY}HMbRhpm@ig7y~mWTa`o9Jsc78E@C@sD5Kac$)~!*F zR)@hJ`xz83#Kn))uhq+{cWl*C?9}f77LiqHN270RRmsn- zUfmq6GtP|Os>|&9bpFnr%f&VgJ=N{YU$C@`8za3apJ?P$sdejb{NSAJ=@x$0CM5JEA zxeA$=a5ox1{4;GY9a#&iM-#E?T@~ z>0NiWBCL=z#}UKeXoIwU0ddt(Z7B4u`=i`N1F$$YzY2zb)V|i%$irr!bvt?-7;9A5O+ z%C#%BaR_C(Olw5mH+AnbuOE49m{*tUX5MMYt6_!(TVT4s!S{W9H+N$c-hE~F>~4ho zSL)oUa~~@8@lQW~;NuA&&6PWUTl}}%l=|yGjJrrYncYC78Z4!e4_5W@b0+p%>!GNY zt#K|$8y-*bJM}Aci3im0)lb#m)q_}Tu~z+3wWw!7^oOx_~?@Cy7)J*3`% z6@C({0b13+P}(=@8Px_qL0E-Uow`FctLf@(HADSLy`}c5chsNNyXp%yQyozI)PA)^ z%~JnR>(pU&P#sdURVUV;txS@&k>a0|&)N%Enh``f@Inmgs8>@r-{tHtB9!*zYQv)n;ir*41=>Xm}=eZGKzrl>3Iyn0}|E(~rjQzG|U9PgUbt z$nYBQwN_2Q-yw2ss8kb;STL>IiYOzpa2n;a-O_fnTiTpxlhj}8^u1ryQR;W$7ximK z{lfVE4d1U5b(8vC_?3EH_(j`m@O=aH`JK=R*Ha9yYL&R&XvB%pFitY!-y(y8-Kx}k zQg5{10^H9uwW^~DTCzPUY8>*0uo7mrak@&wzN`&~SGqp|Udr?xYAL?cOuf_?^M`2L zXKEzq|CsO|^QNhx$eYFM$=zr0d?UWQ!5=4ZUnOuQG`Bp4ZyDMK9>#NJC_tI`f+yv> zRo;PB(I(;@wAUs?*Wevf^_5t|R;hNQZDsOB{u=Tz@=1Q%YoMplLuirsi)--TGvL1{ z{+jR|B6Uc7$!o%I_zIuko$`GJ-^2KZfw%Se`xXA?;qOWOy%v9W8=AWROLw)>fp=C5 z-w1ySj|hK&?`@drR2N8a8k?J{k{Sy@$T4Zc(HtuyadYvSKJ=`X^I(qDSs3*I7M zC;rOwc>fi=qAu^nX^T3;G*qxh@x{A`;VenmH>vUP!`zIxV3X8jbql=6x59sXo0_Je)S(lm zs~M1uvmo#1K-SNLoSzR#zYtP>G3ESSkn|$wmqEtYs|LvUM#%RjB@+IJ&_1G@9)x^; z2)g27=%z=YiGB>MHA9m;4&4Ol3(fU2$nBp)V*f&IfcDy`HbIv>4ej(R^{o1}dQSaD zJrAAMs(!0}2aOeiE_y+|s6;1-77&f_s@ef<(yp)-S-q}ysXt&9z#pOS_CQCx2@Urr z=()Gl+i|V7ml{p<*kS0L_n=`uh-;gp&@~@H_k5!MgL>#Q^*QwF7tmv!&{!v+v%Z2B zJV~AUFKE!O)hXyL(OPGrt3{i2YcS~~DlxoQMpzPN9BE^PFU<~@rPI1}=3TRwFPc4L zfosu>C36-|zhlONyJjqzG2L+0-Afi4?-)3ssz%_za>C09D+ntI2NG5j4kD}}+vS2= zccRdbR+^Roi$T2K1JZ0Esc+2oNL~FRp_(PM?bf4)H^R?#f=5v-Amc5p* ztjX4K)}_{vEz96vkn&M#M(UNRD^tHsOHR8t z?Ij%XI4=F^^!GDXXY4@~vS0SviF2gx&H5tymh2_j|H7iJ;W_`2^N*Z!xz^ldOgNS2 z4$U2%dvose+{L-~<*vOey(Q$(McW`yT8!qTgfv zUMwA3y1Bor|G56GWvk1tDF6Ls#>;Xp8++N_ijfud6(3bzUAYMV8Y>^J{8{Da1L_An zHsDtS_7CW;%B-rXnpL%~YGc)JtM(4OW#CT+{<-?f>Xz!`gVdnRL8EKjHJLTVH3Ms| ztQlK#Yt7u6dux7Fv#w@i&2MX7t?8&aQ1g$$nS&b!Zyx;F5X+FMLw-DD&ycTcb8By_ z{Y~w*+TFG9)_zzU9eT&*LoOeA`S{D9y8Q2#cMZFJ*rUUa1P2GN2~G*l57q})2cHN& z9SjBAgMSJh4*tg#!7Cm-)!&`^{`^wv{+5r8$_4*?GDZUY?By&kZ(+X*OhwI0YIff8-e1L;!;IG)#&x+A!r%n^~2#Fah>=tY`*)r&A6@5xt%ye=WEARIs_KJk2D>~=u0 z{E@o55pZyKGay!70oL;A)lB~b!jXidK(%}|j@LI4-b^@=xh4Vs`D!xopO0vKcLUb3 zCexW_7U68BT*&K1go_E65H2NLPFPRaK)8aik?=mgp^0!M;VQxw)(z8TD7TH}h6uMX z*Eaan^3}`Th&O{Ma#FW;wy^`8l@A#$7>f|@WBUDs2M7=Horeey6JoT5^qqXealY*o zQ=aDiv%Ee>*hPqAwxLV0+De-LgA1? z)Isjo@w$cAQhFh9D^Ej&+X!FA??T{Hp311G5cm`rArxE{0$1`>@KeOHu#*ROiokmU z2lKwPx(K~b?nsG6N=ht3ZxR?H+y-th0v#iO(&{4Bfxcg)!h9Mbl$I8=lw#$LZpU>m zP^uUujsO&F7lZ!=j^Leb7yq?JWQ<$cXa1P;I!g+*u63!=FK)8sx z785QZTuQi{Z>}e7AY4J%NO&L9G!d>OTt(Od%oM}=5ZK1|hX@4^#jro*{!ZTM!1|tI z;6d6LAr!h7tK+;DikA>4C8Tl*Ft8nW1oI`J^?E>|bqO(FLd=(t)+MBM3Ha?Gp1w@j z&h#CiLJ4VILRy!A%IhJ!g_rtLBK3tuDc919zHCKbyk|SEUj{$-1?{#&%6kZfA4*C6 zQr4lAbtonEOG*7w<}GF3Qsiw$dZBwM+Is|g!qZ(0DDUZyUe*j)K`64WzZ!(v_Gka< zkM?fI^$4aMi8}WO-33l0oC50iXHEK}56XLH5zgit<}lY>!g+*u63!=FK)8tSSxmTu za4F$(!g|66!WD##g!d6P5w0X$Mc4xQ+@IL%Pi*ujHu|eAtkpJPvOjUsADkdHZ)fYI zC-x_P`p5CJkLmXl9w0o(79AoyOxVf1$N8SKOw$Ee2HYN@q=cIqSltd-9sL||5NcS4 zUYrFu7@St7YNICsN1*gF?9rE8;|M1bN^dFyr^$O}@jbKohB-_x-l;NJ`U3AHoKJ{J ze%uj#fsrQR5<<}zWlHo#nG$_bhIkGFMPHOD(HCXvKEfu#m4vGZg(u61)iUt8)F(u^ zg|*riy&b<^?vgf2|0+}MY?olQ3_LICBZT{yem~&>!h>wlA;QCioy;peu>!rL2G9#x zT|s?aL5_uGgIZOTpays8IhbiY0&Lg~&a6aJz z!bQxxm~aWpw|dMFYN7V&?^ftA6lat^m+(Ttc7Yd zBH9Lcb@W-lk%ZTRvejxFug6CZ;(7wpOpHcwJ(<^2(C@0zN91kO`L;Q{KbLSG;hlu@ z2^SD9 zau+F7LuV`Q2x}=Zm*%;UPHUR29{d{(pe5TL%L(+X1Jb^ufT4q@Tm|a|!1W-bpy0 zZ~@^WzF{%p62hf~%L(fV8wghrHWJ>)vYH5260RZ?i7}W~++g5Du0w>|SSztz2Ll^Y zL*cibP7Wb=4MES6r?dD}`j`dUhlT3VsCv_fkkJwC_%k$ieB@KX!9At@(Hc3 zIZQv7a30~Eg!2g(5H8{y785QZTuQi{u%57ia0Ou_;e9NziEt(1D#8}Xy;{&qS|swJ zmhz#Nc4jT`(~LV}Z`6`rwUiIFln=GQk31FIvKHJcP^`yVj4qque-Z0(C^hC#;(sXK zCU@j*LxJrh@DbkvI0`A70llchC~*6BK=Ck)f>%MH@bf6pQSOT-8ik&91)%WKDDu)M zye)uh;h!1O0a3!JmdB&6a zGSg2c2Tlggi!qNOezRM^b-Mw_5sJU-7VwhX zSxmTua4F$(!g|66!WD##gy)!7JV;a6kETG*$aODp$Q0IO3TrZjHJQSiOhL^LLNbcw zUq_y)BlYVbOQ9(tmyQ6I5Q>JaQv(Ra)~bVamH8JL9l(DF{;7kF+6*YZ!8%x~@>JSb zM=q*EOXXU8gLP=HTno3(!y($sHN%)p4v>$FW`=$9i=f>(z0rSBKdjd4upw9eJh>GeL4K5~GehQwPZ} z<%(~xj@r3SiEpqD6xRcaZ?F#IDQTnl2J4^&Bqc2j>gYP^=sN1?I;=;K_sgiEj-!S; z%-qPe_y+4Bh2(AGbE~7xSqG^o*Fw`edfw`kP{pI zbZV~Y)Lhd^@#&y=A<~HdZ94sL)2X?pQ*%vceWug@Hl1~tg|c8Lfh*boMJCLGwh|~K z&{-UT&O+(UxE5dBES5ft*qa5tAa}$UHw)6{AfU+bSsa1R0{@S|wfJOaK`#iD5$J5T zYc_4d*|Z5~qxA~+#g{mnZJEtF&t{!x(6ha@yKzS(=W^y+&RolpYdh|Ut8x(tAV*1m}>=dtzfPd%(a5KRxsBJ z=32pAE0}8qb2TzoBXcz}S0i&ZGFKyWH8NKtb2T#88s=KVG;5e<4bn7YhEy=N25H(b zhanhlW}0TEX+|2k|1$8|j5P90tg;r&;~W7LdD5ba2#a}NWJ?P?e3GV=*D{aOf*vJM z#&<0kVGEQ|UJFK(0;~CE85g%e=ExgF=Cptt=K&7owP>prXhnhJ2*(pnVtTO?T4*P< zkjGjmVOqe6l71QCa>9DT2ErADjf86mn=vokLjG(af3{%WSKc7@Y74ks>LXt67IJJ0 z=5*y+#+xlzvm{XFaS*VGu#@S3+fkgo4jj;b^GWzBcNb^GWzBcNb^GWzBcNRmy04A8 zuZ_B|jk>Rmy04A8uZ_B|jk>Rmy04A8uZ_B|jk>Rmy04A8uMK#T_lxe6H4Fj;|83NL zZRpo}T#N2&11+SDqWjvY`(%}#q!)^`QTMe`_q9>?wNdxAQTMe`*R)ahwNdxAkR7|+yp|b_ zZE6`)E@!TK!Un<>gpGu2Sd-=`G_-2t8$yIK`>+l471+-Dgn2(gD0275)wv?IL} z^$^cSJ3Sli^lY?)7R|UL^K0$&Y_!v}(GE}4cH9xqMms$l?euK4qX$V!nMZ4#k0`?NhNdMqP07SlMe8wTo)4xe|D(8gr(@G9jZUC%LoN~9jcPo z19)Asl7tclq_*38{w6_Bi3_co2FnPat z&O3Yx?WLH_JeolGzO{|@k{ykGdUgZS(qKEu=nVd{b~B}|yQ0A--P zVQTF#bwQZAAWU5lrY;Cm7htpoT@a=&2vZk?sSCo?1!3xfFm*wgeLPHE5T-5&Qx}A( z3&PX|Vd{b~bwQZAAWU5lrY;Cm7lf$`!qf#}>VhzJL72KAOkEJBE(lW>gsBU{)CFPc zf-rSKn7SZLT@a=&2vc%~sS9MCJp9Gf1z~FKFm*wgx*$wl5C$$~Zc;|9Vd{b~bwQZA zAWU5lrY;Cm7lf$`!qf#}>VhzJL72KAOkEJBE(lW>gsBU{)CE{G1sNV8r6QzMgp>jw zfKpf&Kq&TAgp`VqQkWG%&x(*zuqr?)c(}n25mG8bN<~Pi2q_gIr6QzMgp`VqQV~)r zLQ27x1L++hr6QzMgp`VqQV~)rLP|wQsR$_*A*CXuRD_g@kWvv+Dnd#{NT~=Z6(OY} zq*R2IijYzfQYu19MM$X#DHS25BBWG=l!}m25mG8bO3CgAc>nQFpx9RtQYu19MM$X# zDHS25BBWG=l!}m25mG8bN<~Pi2q_gIr6Q!%K5FfK)Y|(3_|QETs`*4{_0 zy^k7UA6vAKT6-V0_C9LueUy{?sI~V|Ywx4h-bbyyk6L>_OWe;A_p`+PED@{R(8m2N zaX(Ak&l2~u#QiLBKTF)t68E#j{VZ`mOWe;A_p`+PEO9?eJirnUu*3r_@c>K2iaL~d zfF&Mai3eEX0hV}xB_3dj2Uy|(mUw_A9$<+FSmFVecz`7yV2QHkPJu!)&vlSCgLvBH zT6`J@!2<$igebj4plIqt%yo#l4l&mu<~qb&hnVXSa~)!?L(FxUxehbeVdgr_T!)$K zFmoMduEWfAn7NK2S0Ok?=2eez{CJGx?qeJw9%FwzhWALC=}aSIKN( z5ea_`Zx$%y++!T)9^*Lo7=FnenO8kVK0L;G)nlAj{fzd;XBg=!XaX67dyV9WSTFL=2_ekkHnWqvmNW7#SZDD9nwiVq?2|?Cv|Qo?T}8|A)T~C zI%$V=(hljQ9nwiVq?3BElX|d|c1S1fkWShmowP$bX@_*u4(X&F(n&j{lXgfa?T}8| zA)T~CI%$V=(hljQ9nwiVq?2|?C+(0<+993bx-7Ip?2t~{A)T~CI;mAV(duU07tD0h z4(X&F(n&j{le)E&y0w$KwUc_YlX~+w@qCLh%%xCdZy8$DSs~ zo+ihhrdQ`QIrcO;_B1*6G&%M(IrcO;_B1*6G&%M(IrcQlmp7j#JWJRGc$O_a%a)#H ziD%KqBe*YZJj*toWgE}3jc3`$v&8UOdScI_jgnqg3Y}#e&$5kYS?*c1RGvz?T`a4M zWp%NvE|%5BvbtDS7t88mSzRovi)D4OtS*+-#j?6sRu{|aVp&}*tBYlEnrkiQe>Fg%G-eS<{3@eq|;!)Y7C7*nT&$(+U%F8~B^C6;1+IsVRP2>j3 zCdEV=#0+ZQa2r;tcQ-2)Lxa$v=vapmO-FXJZ(FYzF@ zKQ<`qabTzw2TmJ|7W~kgdOoO=#bUIX%!rf&fJa6PF7eMO(1>U_28+?i^vI^y8}(NC zfujZxh#C*Dd%=R7u@_wM+6xPj{PMM0twy88YDSUBgPpHbf8is+@bc#XwiFrXkaZmXB!+8-sAL0YQY&IM4VH0%V6h%bhlZqm?i4oMoTaW-~wb*S&Btyl3u=DtE z#;^cR7 zNOJ@eF??9fpdtQQ&1OjsstL=OkqxNCes+9}_$B0&zetAEIJ<-&;S{7x&{2LdKknP@ zcJP25)C2wSM!~1iVh1UuBqWqqBTADA$xy7|!z`5*CUMJ)_+Dg2Z7<+)lO9zCPFYE- z)EEU>&A4j;$>a?&%7B6jQRuM`9h_nV-oaxgo8TjcVu1z&aA|Vl3hl941s^t)Cp>~< z7J(RW85lxX#)52kuD44*^cD0#v*aSZjxyp=C(iW@JX+kno3~1D$ug37<;j0ACf=$%4hx^cri4QsA2Co5c8Bq{u zi>k;!G&hzOoU4@6Yau@D7Ka`9Fx$cD7vTfAG&@lM>H`kKOYEqu4Ryr<9ylN#_=xqU z9()M$h!2t!_z;4l9Im6K9Eq-CFm%>&FOc~ie1L#PkOk!k3z|@n-KqzXgs?_~bVYoCUxkqERweSxX>-}Z zV-}}~AYzz+xU}FHLLdP>7udHr?GCF0C%f2fZUD$fFobj=w4C^GOU2MJtrif$0^#RG zZmXPO!_W8NL%0}06s$M`54^j#j0Uts!2ad=&|2fas zk~oeJo5uqw;PePOfB*~dj#@fAc(dRGRQI4O;lObVlA&T~o!|p0fscS494Oo>odGDh z03TKZDr+^eHa6k|1>uNO5DEXFzAnUvH%0Jawz%vbCpwVT1=VCi1L7a@memUuMt$U6 z4yzlLbs`j>)9!QH9N;o=G3o@dYeV0(8NI@;5LCiHXq3tB0y6DvA{#G2>MhB@cKDzZ zpk8*n*Xu=}@HoNF$YX^h5t#veAnY^VVzYUX*y;0Hk<4gxp-53?U_PVKn!*Z;U=(aR zofq_Z8^jNUvaqSmCSB4Dd^l|;x$6*h9_L*2CZ&9-cANuiwz%zHCsdc!4IO+|{0Cvf-zKDY}>0(`iDUj)F$TaW;Baryl?LKt$xjXo&&KuUC)R0og_Jxl_k zuuJgaw3;B7PzI!r*$l+HP%(?s>a;?gUx*KXy5IvxA~<|5;KSw-e274ceM~rM+hg+s z;i!*W@L~730W(lm-ZwS z02FxK;6}UG?QwWqPKVnCe7K-Ly=afq?sOm<@MTIu&72TbNQw4BIeL-XiNkVZ7$yeD z*ZeKTKscc1Ih{#KN$3+v9+1x~oze}NWp(=jLQln#Qi`ZfIgArMUg%*hsy&gW%DFC?G~RCZ$Sbi_a-OXk<4uN0gDFWLpn`n z4?ciQ!3VjIzyTs6RB=pB`+UF$5HI+U&`S=OHzM%y0jDXI+*|NrwfS5DFH~0yA0TM_ zgJOL?M=}cVx`123hoAUxIX&)VoRbWlF8Bba;0Sf}C#NYHHFH65yIeqo!)12)go8ca zSbe@99}r2%l#Hg@eF5SFcidjlDc%4y6W-(lK9eC@ys61{Btyj@#SD<;z$Ng}8z(kP z`QRI`7c6oSKJYGw8Pq||L_}GEc&`frMeqTNp6|&@rDP#GKTIFLJJ|<(IQ(M5q5<&_ zdCQT40#F~H*KBq>K|hzz<92y*7>vsUo$iM$krSkmjrfr63kBzP`GF4@3x45XuTQ!S zKI90gw>wpxLBpBo*>1Lf|vNKcXv>%*Fk#rs(X0ywb3QD;# z3URxW+(47XorK(OpFdWg7(T=t{0=_csi~=u5h;EYiL4F}x(M)*;&R&XGYQR21wQ=g zsSYGV#ehWvR60_^O3CiQ2apMrm}6}eNPIXge&Sw4lnsdY3qH{C!QqxTJ|NYVO3TI3 za2A^*;7Lt_Dt871A0TM_W3hm@oM}L~-vitNAFgD7k~_)kar?Y!Ubh!gC;(XkPH|g3 z9^k_)Sn|M}^bjA2M;Q((^pTN3u7K_(+za2pSOokhfgvC;;?HO0rnIf)7s;LOc6?={}DS zhG{a|gExDuUew2JP6Io55$fOL4xsfgo|2Ip_>gXck4pgYAs75Wd4Nt{PkMSfWJFpL z*f|+@d`WH>_$A%rvf*bknwt*Ml9ZX@L^6vd87MSBx5e<0hq_Rg3PXcMF2IM|?1d=9 z;RzrVq%rUT#3vyfD(?DFLD63FAwDwlJqlvfk>X7cz_f9t$S@#*4Y3!&TdoXr>3|oS z4yxOok`(X+{2-Js!|(OOo=Abv2d8*#qRTv%bjb~Y?!|Fa9>GV7&*%3h1(ITKMMCO4 zOYEmW$q#|+&B(}rj7UcaxRey!0SQE>WWaX9&lE_74B#WsE7OH!s2HdKV;EK~ez@{| zkWivag`t5G2=~Otjh6deR zx* +#include +#include +#include +#include + +#include "bcm_host.h" +#include "vgfont.h" + +static const char *strnchr(const char *str, size_t len, char c) +{ + const char *e = str + len; + do { + if (*str == c) { + return str; + } + } while (++str < e); + return NULL; +} + +int32_t render_subtitle(GRAPHICS_RESOURCE_HANDLE img, const char *text, const int skip, const uint32_t text_size, const uint32_t y_offset) +{ + uint32_t text_length = strlen(text)-skip; + uint32_t width=0, height=0; + const char *split = text; + int32_t s=0; + int len = 0; // length of pre-subtitle + uint32_t img_w, img_h; + + graphics_get_resource_size(img, &img_w, &img_h); + + if (text_length==0) + return 0; + while (split[0]) { + s = graphics_resource_text_dimensions_ext(img, split, text_length-(split-text), &width, &height, text_size); + if (s != 0) return s; + if (width > img_w) { + const char *space = strnchr(split, text_length-(split-text), ' '); + if (!space) { + len = split+1-text; + split = split+1; + } else { + len = space-text; + split = space+1; + } + } else { + break; + } + } + // split now points to last line of text. split-text = length of initial text. text_length-(split-text) is length of last line + if (width) { + s = graphics_resource_render_text_ext(img, (img_w - width)>>1, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ + GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ + split, text_length-(split-text), text_size); + if (s!=0) return s; + } + return render_subtitle(img, text, skip+text_length-len, text_size, y_offset - height); +} + +int main(void) +{ + GRAPHICS_RESOURCE_HANDLE img; + uint32_t width, height; + int LAYER=1; + bcm_host_init(); + int s; + + s = gx_graphics_init("."); + assert(s == 0); + + s = graphics_get_display_size(0, &width, &height); + assert(s == 0); + + s = gx_create_window(0, width, height, GRAPHICS_RESOURCE_RGBA32, &img); + assert(s == 0); + + // transparent before display to avoid screen flash + graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); + + graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 1); + + uint32_t text_size = 10; + while (1) { + const char *text = "The quick brown fox jumps over the lazy dog"; + uint32_t y_offset = height-60+text_size/2; + graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); + // blue, at the top (y=40) + graphics_resource_fill(img, 0, 40, width, 1, GRAPHICS_RGBA32(0,0,0xff,0xff)); + + // green, at the bottom (y=height-40) + graphics_resource_fill(img, 0, height-40, width, 1, GRAPHICS_RGBA32(0,0xff,0,0xff)); + + // draw the subtitle text + render_subtitle(img, text, 0, text_size, y_offset); + graphics_update_displayed_resource(img, 0, 0, 0, 0); + text_size += 1; + if (text_size > 50) + text_size = 10; + } + + graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 0); + graphics_delete_resource(img); + + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt new file mode 100755 index 0000000..a56dda5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_jpeg.bin) +set(SRCS jpeg.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile b/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile new file mode 100755 index 0000000..04fd84f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile @@ -0,0 +1,6 @@ +OBJS=jpeg.o +BIN=hello_jpeg.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c new file mode 100755 index 0000000..233b20e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c @@ -0,0 +1,696 @@ +/* +Copyright (c) 2012, Matt Ownby + Anthong Sale + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include "jpeg.h" + +#define TIMEOUT_MS 2000 + +typedef struct _COMPONENT_DETAILS { + COMPONENT_T *component; + OMX_HANDLETYPE handle; + int inPort; + int outPort; +} COMPONENT_DETAILS; + +struct _OPENMAX_JPEG_DECODER { + ILCLIENT_T *client; + COMPONENT_DETAILS *imageDecoder; + COMPONENT_DETAILS *imageResizer; + OMX_BUFFERHEADERTYPE **ppInputBufferHeader; + int inputBufferHeaderCount; + OMX_BUFFERHEADERTYPE *pOutputBufferHeader; +}; + +int bufferIndex = 0; // index to buffer array + +int +portSettingsChanged(OPENMAX_JPEG_DECODER * decoder) +{ + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + // need to setup the input for the resizer with the output of the + // decoder + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageDecoder->outPort; + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamPortDefinition, &portdef); + + unsigned int uWidth = + (unsigned int) portdef.format.image.nFrameWidth; + unsigned int uHeight = + (unsigned int) portdef.format.image.nFrameHeight; + + // tell resizer input what the decoder output will be providing + portdef.nPortIndex = decoder->imageResizer->inPort; + OMX_SetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // establish tunnel between decoder output and resizer input + OMX_SetupTunnel(decoder->imageDecoder->handle, + decoder->imageDecoder->outPort, + decoder->imageResizer->handle, + decoder->imageResizer->inPort); + + // enable ports + OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandPortEnable, + decoder->imageDecoder->outPort, NULL); + OMX_SendCommand(decoder->imageResizer->handle, + OMX_CommandPortEnable, + decoder->imageResizer->inPort, NULL); + + // put resizer in idle state (this allows the outport of the decoder + // to become enabled) + OMX_SendCommand(decoder->imageResizer->handle, + OMX_CommandStateSet, OMX_StateIdle, NULL); + + // wait for state change complete + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, + OMX_CommandStateSet, 1, + OMX_StateIdle, 1, 0, TIMEOUT_MS); + + // once the state changes, both ports should become enabled and the + // resizer + // output should generate a settings changed event + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, + OMX_CommandPortEnable, 1, + decoder->imageDecoder->outPort, 1, 0, + TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandPortEnable, 1, + decoder->imageResizer->inPort, 1, 0, + TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventPortSettingsChanged, + decoder->imageResizer->outPort, 1, 0, 1, 0, + TIMEOUT_MS); + + ilclient_disable_port(decoder->imageResizer->component, + decoder->imageResizer->outPort); + + // query output buffer requirements for resizer + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageResizer->outPort; + OMX_GetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // change output color format and dimensions to match input + portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; + portdef.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888; + portdef.format.image.nFrameWidth = uWidth; + portdef.format.image.nFrameHeight = uHeight; + portdef.format.image.nStride = 0; + portdef.format.image.nSliceHeight = 0; + portdef.format.image.bFlagErrorConcealment = OMX_FALSE; + + OMX_SetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // grab output requirements again to get actual buffer size + // requirement (and buffer count requirement!) + OMX_GetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // move resizer into executing state + ilclient_change_component_state(decoder->imageResizer->component, + OMX_StateExecuting); + + // show some logging so user knows it's working + printf + ("Width: %u Height: %u Output Color Format: 0x%x Buffer Size: %u\n", + (unsigned int) portdef.format.image.nFrameWidth, + (unsigned int) portdef.format.image.nFrameHeight, + (unsigned int) portdef.format.image.eColorFormat, + (unsigned int) portdef.nBufferSize); + fflush(stdout); + + // enable output port of resizer + OMX_SendCommand(decoder->imageResizer->handle, + OMX_CommandPortEnable, + decoder->imageResizer->outPort, NULL); + + // allocate the buffer + // void* outputBuffer = 0; + // if (posix_memalign(&outputBuffer, portdef.nBufferAlignment, + // portdef.nBufferSize) != 0) + // { + // perror("Allocating output buffer"); + // return OMXJPEG_ERROR_MEMORY; + // } + + // set the buffer + // int ret = OMX_UseBuffer(decoder->imageResizer->handle, + // &decoder->pOutputBufferHeader, + // decoder->imageResizer->outPort, NULL, + // portdef.nBufferSize, + // (OMX_U8 *) outputBuffer); + int ret = OMX_AllocateBuffer(decoder->imageResizer->handle, + &decoder->pOutputBufferHeader, + decoder->imageResizer-> + outPort, + NULL, + portdef.nBufferSize); + if (ret != OMX_ErrorNone) { + perror("Eror allocating buffer"); + fprintf(stderr, "OMX_AllocateBuffer returned 0x%x allocating buffer size 0x%x\n", ret, portdef.nBufferSize); + return OMXJPEG_ERROR_MEMORY; + } + + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, + OMX_CommandPortEnable, 1, + decoder->imageResizer->outPort, 1, 0, + TIMEOUT_MS); + + return OMXJPEG_OK; +} + +int +portSettingsChangedAgain(OPENMAX_JPEG_DECODER * decoder) +{ + ilclient_disable_port(decoder->imageDecoder->component, + decoder->imageDecoder->outPort); + ilclient_disable_port(decoder->imageResizer->component, + decoder->imageResizer->inPort); + + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + // need to setup the input for the resizer with the output of the + // decoder + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageDecoder->outPort; + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamPortDefinition, &portdef); + + // tell resizer input what the decoder output will be providing + portdef.nPortIndex = decoder->imageResizer->inPort; + OMX_SetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // enable output of decoder and input of resizer (ie enable tunnel) + ilclient_enable_port(decoder->imageDecoder->component, + decoder->imageDecoder->outPort); + ilclient_enable_port(decoder->imageResizer->component, + decoder->imageResizer->inPort); + + // need to wait for this event + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventPortSettingsChanged, + decoder->imageResizer->outPort, 1, + 0, 0, 0, TIMEOUT_MS); + + return OMXJPEG_OK; +} + +int +prepareResizer(OPENMAX_JPEG_DECODER * decoder) +{ + decoder->imageResizer = malloc(sizeof(COMPONENT_DETAILS)); + if (decoder->imageResizer == NULL) { + perror("malloc image resizer"); + return OMXJPEG_ERROR_MEMORY; + } + + int ret = ilclient_create_component(decoder->client, + &decoder-> + imageResizer-> + component, + "resize", + ILCLIENT_DISABLE_ALL_PORTS + | + ILCLIENT_ENABLE_INPUT_BUFFERS + | + ILCLIENT_ENABLE_OUTPUT_BUFFERS); + if (ret != 0) { + perror("image resizer"); + return OMXJPEG_ERROR_CREATING_COMP; + } + // grab the handle for later use + decoder->imageResizer->handle = + ILC_GET_HANDLE(decoder->imageResizer->component); + + // get and store the ports + OMX_PORT_PARAM_TYPE port; + port.nSize = sizeof(OMX_PORT_PARAM_TYPE); + port.nVersion.nVersion = OMX_VERSION; + + OMX_GetParameter(ILC_GET_HANDLE(decoder->imageResizer->component), + OMX_IndexParamImageInit, &port); + if (port.nPorts != 2) { + return OMXJPEG_ERROR_WRONG_NO_PORTS; + } + decoder->imageResizer->inPort = port.nStartPortNumber; + decoder->imageResizer->outPort = port.nStartPortNumber + 1; + + decoder->pOutputBufferHeader = NULL; + + return OMXJPEG_OK; +} + +int +prepareImageDecoder(OPENMAX_JPEG_DECODER * decoder) +{ + decoder->imageDecoder = malloc(sizeof(COMPONENT_DETAILS)); + if (decoder->imageDecoder == NULL) { + perror("malloc image decoder"); + return OMXJPEG_ERROR_MEMORY; + } + + int ret = ilclient_create_component(decoder->client, + &decoder-> + imageDecoder-> + component, + "image_decode", + ILCLIENT_DISABLE_ALL_PORTS + | + ILCLIENT_ENABLE_INPUT_BUFFERS); + + if (ret != 0) { + perror("image decode"); + return OMXJPEG_ERROR_CREATING_COMP; + } + // grab the handle for later use in OMX calls directly + decoder->imageDecoder->handle = + ILC_GET_HANDLE(decoder->imageDecoder->component); + + // get and store the ports + OMX_PORT_PARAM_TYPE port; + port.nSize = sizeof(OMX_PORT_PARAM_TYPE); + port.nVersion.nVersion = OMX_VERSION; + + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamImageInit, &port); + if (port.nPorts != 2) { + return OMXJPEG_ERROR_WRONG_NO_PORTS; + } + decoder->imageDecoder->inPort = port.nStartPortNumber; + decoder->imageDecoder->outPort = port.nStartPortNumber + 1; + + return OMXJPEG_OK; +} + +int +startupImageDecoder(OPENMAX_JPEG_DECODER * decoder) +{ + // move to idle + ilclient_change_component_state(decoder->imageDecoder->component, + OMX_StateIdle); + + // set input image format + OMX_IMAGE_PARAM_PORTFORMATTYPE imagePortFormat; + memset(&imagePortFormat, 0, sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE)); + imagePortFormat.nSize = sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE); + imagePortFormat.nVersion.nVersion = OMX_VERSION; + imagePortFormat.nPortIndex = decoder->imageDecoder->inPort; + imagePortFormat.eCompressionFormat = OMX_IMAGE_CodingJPEG; + OMX_SetParameter(decoder->imageDecoder->handle, + OMX_IndexParamImagePortFormat, &imagePortFormat); + + // get buffer requirements + OMX_PARAM_PORTDEFINITIONTYPE portdef; + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageDecoder->inPort; + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamPortDefinition, &portdef); + + // enable the port and setup the buffers + OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandPortEnable, + decoder->imageDecoder->inPort, NULL); + decoder->inputBufferHeaderCount = portdef.nBufferCountActual; + // allocate pointer array + decoder->ppInputBufferHeader = + (OMX_BUFFERHEADERTYPE **) malloc(sizeof(void) * + decoder->inputBufferHeaderCount); + // allocate each buffer + int i; + for (i = 0; i < decoder->inputBufferHeaderCount; i++) { + if (OMX_AllocateBuffer(decoder->imageDecoder->handle, + &decoder->ppInputBufferHeader[i], + decoder->imageDecoder->inPort, + (void *) i, + portdef.nBufferSize) != OMX_ErrorNone) { + perror("Allocate decode buffer"); + return OMXJPEG_ERROR_MEMORY; + } + } + // wait for port enable to complete - which it should once buffers are + // assigned + int ret = + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, + OMX_CommandPortEnable, 0, + decoder->imageDecoder->inPort, 0, + 0, TIMEOUT_MS); + if (ret != 0) { + fprintf(stderr, "Did not get port enable %d\n", ret); + return OMXJPEG_ERROR_EXECUTING; + } + // start executing the decoder + ret = OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandStateSet, OMX_StateExecuting, NULL); + if (ret != 0) { + fprintf(stderr, "Error starting image decoder %x\n", ret); + return OMXJPEG_ERROR_EXECUTING; + } + ret = ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, + OMX_StateExecuting, 0, 0, 1, 0, + TIMEOUT_MS); + if (ret != 0) { + fprintf(stderr, "Did not receive executing stat %d\n", ret); + // return OMXJPEG_ERROR_EXECUTING; + } + + return OMXJPEG_OK; +} + +// this function run the boilerplate to setup the openmax components; +int +setupOpenMaxJpegDecoder(OPENMAX_JPEG_DECODER ** pDecoder) +{ + *pDecoder = malloc(sizeof(OPENMAX_JPEG_DECODER)); + if (pDecoder[0] == NULL) { + perror("malloc decoder"); + return OMXJPEG_ERROR_MEMORY; + } + memset(*pDecoder, 0, sizeof(OPENMAX_JPEG_DECODER)); + + if ((pDecoder[0]->client = ilclient_init()) == NULL) { + perror("ilclient_init"); + return OMXJPEG_ERROR_ILCLIENT_INIT; + } + + if (OMX_Init() != OMX_ErrorNone) { + ilclient_destroy(pDecoder[0]->client); + perror("OMX_Init"); + return OMXJPEG_ERROR_OMX_INIT; + } + // prepare the image decoder + int ret = prepareImageDecoder(pDecoder[0]); + if (ret != OMXJPEG_OK) + return ret; + + ret = prepareResizer(pDecoder[0]); + if (ret != OMXJPEG_OK) + return ret; + + ret = startupImageDecoder(pDecoder[0]); + if (ret != OMXJPEG_OK) + return ret; + + return OMXJPEG_OK; +} + +// this function passed the jpeg image buffer in, and returns the decoded +// image +int +decodeImage(OPENMAX_JPEG_DECODER * decoder, char *sourceImage, + size_t imageSize) +{ + char *sourceOffset = sourceImage; // we store a separate + // buffer ot image so we + // can offset it + size_t toread = 0; // bytes left to read from buffer + toread += imageSize; + int bFilled = 0; // have we filled our output + // buffer + bufferIndex = 0; + + while (toread > 0) { + // get next buffer from array + OMX_BUFFERHEADERTYPE *pBufHeader = + decoder->ppInputBufferHeader[bufferIndex]; + + // step index and reset to 0 if required + bufferIndex++; + if (bufferIndex >= decoder->inputBufferHeaderCount) + bufferIndex = 0; + + // work out the next chunk to load into the decoder + if (toread > pBufHeader->nAllocLen) + pBufHeader->nFilledLen = pBufHeader->nAllocLen; + else + pBufHeader->nFilledLen = toread; + + toread = toread - pBufHeader->nFilledLen; + + // pass the bytes to the buffer + memcpy(pBufHeader->pBuffer, sourceOffset, pBufHeader->nFilledLen); + + // update the buffer pointer and set the input flags + + sourceOffset = sourceOffset + pBufHeader->nFilledLen; + pBufHeader->nOffset = 0; + pBufHeader->nFlags = 0; + if (toread <= 0) { + pBufHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + // empty the current buffer + int ret = + OMX_EmptyThisBuffer(decoder->imageDecoder->handle, + pBufHeader); + + if (ret != OMX_ErrorNone) { + perror("Empty input buffer"); + fprintf(stderr, "return code %x\n", ret); + return OMXJPEG_ERROR_MEMORY; + } + // wait for buffer to empty or port changed event + int done = 0; + while ((done == 0) && (decoder->pOutputBufferHeader == NULL)) { + if (decoder->pOutputBufferHeader == NULL) { + ret = + ilclient_wait_for_event + (decoder->imageDecoder->component, + OMX_EventPortSettingsChanged, + decoder->imageDecoder->outPort, 0, 0, 1, 0, 5); + + if (ret == 0) { + ret = portSettingsChanged(decoder); + if (ret != OMXJPEG_OK) + return ret; + } + } else { + ret = + ilclient_remove_event(decoder->imageDecoder->component, + OMX_EventPortSettingsChanged, + decoder->imageDecoder->outPort, + 0, 0, 1); + if (ret == 0) + portSettingsChangedAgain(decoder); + + } + + // check to see if buffer is now empty + if (pBufHeader->nFilledLen == 0) + done = 1; + + if ((done == 0) + || (decoder->pOutputBufferHeader == NULL)) + sleep(1); + } + + // fill the buffer if we have created the buffer + if ((bFilled == 0) && (decoder->pOutputBufferHeader != NULL)) { + ret = OMX_FillThisBuffer(decoder->imageResizer->handle, + decoder->pOutputBufferHeader); + if (ret != OMX_ErrorNone) { + perror("Filling output buffer"); + fprintf(stderr, "Error code %x\n", ret); + return OMXJPEG_ERROR_MEMORY; + } + + bFilled = 1; + } + } + + // wait for buffer to fill + /* + * while(pBufHeader->nFilledLen == 0) { sleep(5); } + */ + + // wait for end of stream events + int ret = + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventBufferFlag, + decoder->imageDecoder->outPort, 1, + OMX_BUFFERFLAG_EOS, 1, + 0, 2); + if (ret != 0) { + fprintf(stderr, "No EOS event on image decoder %d\n", ret); + } + ret = ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventBufferFlag, + decoder->imageResizer->outPort, 1, + OMX_BUFFERFLAG_EOS, 1, 0, 2); + if (ret != 0) { + fprintf(stderr, "No EOS event on image resizer %d\n", ret); + } + return OMXJPEG_OK; +} + +// this function cleans up the decoder. +void +cleanup(OPENMAX_JPEG_DECODER * decoder) +{ + // flush everything through + OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandFlush, decoder->imageDecoder->outPort, + NULL); + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandFlush, 0, + decoder->imageDecoder->outPort, 0, 0, + TIMEOUT_MS); + OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandFlush, + decoder->imageResizer->inPort, NULL); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandFlush, 0, + decoder->imageResizer->inPort, 1, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortDisable, + decoder->imageDecoder->inPort, NULL); + + int i = 0; + for (i = 0; i < decoder->inputBufferHeaderCount; i++) { + OMX_BUFFERHEADERTYPE *vpBufHeader = + decoder->ppInputBufferHeader[i]; + + OMX_FreeBuffer(decoder->imageDecoder->handle, + decoder->imageDecoder->inPort, vpBufHeader); + } + + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageDecoder->inPort, 0, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortDisable, + decoder->imageResizer->outPort, NULL); + + OMX_FreeBuffer(decoder->imageResizer->handle, + decoder->imageResizer->outPort, + decoder->pOutputBufferHeader); + + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageResizer->outPort, 0, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortDisable, + decoder->imageDecoder->outPort, NULL); + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageDecoder->outPort, 0, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortDisable, + decoder->imageResizer->inPort, NULL); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageResizer->inPort, 0, 0, + TIMEOUT_MS); + + OMX_SetupTunnel(decoder->imageDecoder->handle, + decoder->imageDecoder->outPort, NULL, 0); + OMX_SetupTunnel(decoder->imageResizer->handle, + decoder->imageResizer->inPort, NULL, 0); + + ilclient_change_component_state(decoder->imageDecoder->component, + OMX_StateIdle); + ilclient_change_component_state(decoder->imageResizer->component, + OMX_StateIdle); + + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateIdle, 0, 0, TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateIdle, 0, 0, TIMEOUT_MS); + + ilclient_change_component_state(decoder->imageDecoder->component, + OMX_StateLoaded); + ilclient_change_component_state(decoder->imageResizer->component, + OMX_StateLoaded); + + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateLoaded, 0, 0, TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateLoaded, 0, 0, TIMEOUT_MS); + + OMX_Deinit(); + + if (decoder->client != NULL) { + ilclient_destroy(decoder->client); + } +} + +int +main(int argc, char *argv[]) +{ + OPENMAX_JPEG_DECODER *pDecoder; + char *sourceImage; + size_t imageSize; + int s; + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return -1; + } + FILE *fp = fopen(argv[1], "rb"); + if (!fp) { + printf("File %s not found.\n", argv[1]); + } + fseek(fp, 0L, SEEK_END); + imageSize = ftell(fp); + fseek(fp, 0L, SEEK_SET); + sourceImage = malloc(imageSize); + assert(sourceImage != NULL); + s = fread(sourceImage, 1, imageSize, fp); + assert(s == imageSize); + fclose(fp); + bcm_host_init(); + s = setupOpenMaxJpegDecoder(&pDecoder); + assert(s == 0); + s = decodeImage(pDecoder, sourceImage, imageSize); + assert(s == 0); + cleanup(pDecoder); + free(sourceImage); + return 0; +} diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h new file mode 100755 index 0000000..f525dab --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h @@ -0,0 +1,68 @@ +/* +Copyright (c) 2012, Matt Ownby + Anthong Sale + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _OPTION_H_ +#define _OPTION_H_ + +/* +Defines the methods for interacting with openmax il and ilclient to decode +jpeg images from the camera +*/ + +#include +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +#define OMXJPEG_OK 0 +#define OMXJPEG_ERROR_ILCLIENT_INIT -1024 +#define OMXJPEG_ERROR_OMX_INIT -1025 +#define OMXJPEG_ERROR_MEMORY -1026 +#define OMXJPEG_ERROR_CREATING_COMP -1027 +#define OMXJPEG_ERROR_WRONG_NO_PORTS -1028 +#define OMXJPEG_ERROR_EXECUTING -1029 +#define OMXJPEG_ERROR_NOSETTINGS -1030 + +typedef struct _OPENMAX_JPEG_DECODER OPENMAX_JPEG_DECODER; + +//this function run the boilerplate to setup the openmax components; +int setupOpenMaxJpegDecoder(OPENMAX_JPEG_DECODER** decoder); + +//this function passed the jpeg image buffer in, and returns the decoded image +int decodeImage(OPENMAX_JPEG_DECODER* decoder, + char* sourceImage, size_t imageSize); + +//this function cleans up the decoder. +void cleanup(OPENMAX_JPEG_DECODER* decoder); + +#endif + diff --git a/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile b/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile new file mode 100755 index 0000000..98f83f6 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile @@ -0,0 +1,6 @@ +OBJS=mmal_encode.o +BIN=hello_mmal_encode.bin +LDFLAGS+=-lmmal -lmmal_core -lmmal_components -lmmal_util -lmmal_vc_client + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c b/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c new file mode 100755 index 0000000..21ebf92 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c @@ -0,0 +1,261 @@ +/* +Copyright (C) 2016 RealVNC Limited. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Image encoding example using MMAL + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Format for test image +const unsigned int WIDTH = 512; +const unsigned int HEIGHT = 512; +const MMAL_FOURCC_T INPUT_ENC = MMAL_ENCODING_RGBA; +const unsigned int BYTESPP = 4; +const uint32_t RED = 0xff; +const uint32_t GREEN = 0xff << 8; +const uint32_t BLUE = 0xff << 16; +const uint32_t ALPHA = 0xff << 24; + +static MMAL_WRAPPER_T* encoder; +static VCOS_SEMAPHORE_T sem; + +// This callback and the above semaphore is used when waiting for +// data to be returned. +static void mmalCallback(MMAL_WRAPPER_T* encoder) +{ + vcos_semaphore_post(&sem); +} + +// Create a test input image in the supplied buffer consisting of 8 vertical bars of +// white | black | red | green | blue | cyan | magenta | yellow +void create_rgba_test_image(void* buf, unsigned int length, unsigned int stride) +{ + uint32_t* pixel = buf; + int i; + for (i=0; iinput[0]; + encoder->status = MMAL_SUCCESS; + + if (portIn->is_enabled) { + if (mmal_wrapper_port_disable(portIn) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to disable input port\n"); + exit(1); + } + } + + portIn->format->encoding = INPUT_ENC; + portIn->format->es->video.width = VCOS_ALIGN_UP(WIDTH, 32); + portIn->format->es->video.height = VCOS_ALIGN_UP(HEIGHT, 16); + portIn->format->es->video.crop.x = 0; + portIn->format->es->video.crop.y = 0; + portIn->format->es->video.crop.width = WIDTH; + portIn->format->es->video.crop.height = HEIGHT; + if (mmal_port_format_commit(portIn) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to commit input port format\n"); + exit(1); + } + + portIn->buffer_size = portIn->buffer_size_recommended; + portIn->buffer_num = portIn->buffer_num_recommended; + + if (mmal_wrapper_port_enable(portIn, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) + != MMAL_SUCCESS) { + fprintf(stderr, "Failed to enable input port\n"); + exit(1); + } + + printf("- input %4.4s %ux%u\n", + (char*)&portIn->format->encoding, + portIn->format->es->video.width, portIn->format->es->video.height); + + // Configure output + + portOut = encoder->output[0]; + + if (portOut->is_enabled) { + if (mmal_wrapper_port_disable(portOut) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to disable output port\n"); + exit(1); + } + } + + portOut->format->encoding = encoding; + if (mmal_port_format_commit(portOut) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to commit output port format\n"); + exit(1); + } + + mmal_port_parameter_set_uint32(portOut, MMAL_PARAMETER_JPEG_Q_FACTOR, 100); + + portOut->buffer_size = portOut->buffer_size_recommended; + portOut->buffer_num = portOut->buffer_num_recommended; + + if (mmal_wrapper_port_enable(portOut, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) + != MMAL_SUCCESS) { + fprintf(stderr, "Failed to enable output port\n"); + exit(1); + } + + printf("- output %4.4s\n", (char*)&encoding); + + // Perform the encoding + + outFile = fopen(filename, "w"); + if (!outFile) { + fprintf(stderr, "Failed to open file %s (%s)\n", filename, strerror(errno)); + exit(1); + } + + while (!eos) { + + // Send output buffers to be filled with encoded image. + while (mmal_wrapper_buffer_get_empty(portOut, &out, 0) == MMAL_SUCCESS) { + if (mmal_port_send_buffer(portOut, out) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to send buffer\n"); + break; + } + } + + // Send image to be encoded. + if (!sent && mmal_wrapper_buffer_get_empty(portIn, &in, 0) == MMAL_SUCCESS) { + printf("- sending %u bytes to encoder\n", in->alloc_size); + create_rgba_test_image(in->data, in->alloc_size, portIn->format->es->video.width); + in->length = in->alloc_size; + in->flags = MMAL_BUFFER_HEADER_FLAG_EOS; + if (mmal_port_send_buffer(portIn, in) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to send buffer\n"); + break; + } + sent = 1; + } + + // Get filled output buffers. + status = mmal_wrapper_buffer_get_full(portOut, &out, 0); + if (status == MMAL_EAGAIN) { + // No buffer available, wait for callback and loop. + vcos_semaphore_wait(&sem); + continue; + } else if (status != MMAL_SUCCESS) { + fprintf(stderr, "Failed to get full buffer\n"); + exit(1); + } + + printf("- received %i bytes\n", out->length); + eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS; + + nw = fwrite(out->data, 1, out->length, outFile); + if (nw != out->length) { + fprintf(stderr, "Failed to write complete buffer\n"); + exit(1); + } + outputWritten += nw; + + mmal_buffer_header_release(out); + } + + mmal_port_flush(portOut); + + fclose(outFile); + printf("- written %u bytes to %s\n\n", outputWritten, filename); +} + + +int main(int argc, const char** argv) +{ + bcm_host_init(); + + if (vcos_semaphore_create(&sem, "encoder sem", 0) != VCOS_SUCCESS) { + fprintf(stderr, "Failed to create semaphore\n"); + exit(1); + } + + if (mmal_wrapper_create(&encoder, MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER) + != MMAL_SUCCESS) { + fprintf(stderr, "Failed to create mmal component\n"); + exit(1); + } + encoder->callback = mmalCallback; + + // Perform test encodings in various formats + mmal_encode_test(MMAL_ENCODING_PNG, "out.png"); + mmal_encode_test(MMAL_ENCODING_JPEG, "out.jpg"); + mmal_encode_test(MMAL_ENCODING_GIF, "out.gif"); + mmal_encode_test(MMAL_ENCODING_BMP, "out.bmp"); + + mmal_wrapper_destroy(encoder); + vcos_semaphore_delete(&sem); + + return 0; +} + + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt new file mode 100755 index 0000000..cdb8413 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_teapot.bin) +set(SRCS triangle.c video.c models.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/Makefile b/host_applications/linux/apps/hello_pi/hello_teapot/Makefile new file mode 100755 index 0000000..0ebb234 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/Makefile @@ -0,0 +1,7 @@ +OBJS=triangle.o video.o models.o +BIN=hello_teapot.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/README.md b/host_applications/linux/apps/hello_pi/hello_teapot/README.md new file mode 100755 index 0000000..36fafcf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/README.md @@ -0,0 +1,4 @@ +hello_videocube +=============== + +Sample for Raspberry Pi that uses egl_render to display video on an animated cube. \ No newline at end of file diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h b/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h new file mode 100755 index 0000000..7dd30a9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f +}; + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/models.c b/host_applications/linux/apps/hello_pi/hello_teapot/models.c new file mode 100755 index 0000000..a5af141 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/models.c @@ -0,0 +1,515 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "models.h" + +#define VMCS_RESOURCE(a,b) (b) + +/****************************************************************************** +Private typedefs, macros and constants +******************************************************************************/ + +enum {VBO_VERTEX, VBO_NORMAL, VBO_TEXTURE, VBO_MAX}; +#define MAX_MATERIALS 4 +#define MAX_MATERIAL_NAME 32 + +typedef struct wavefront_material_s { + GLuint vbo[VBO_MAX]; + int numverts; + char name[MAX_MATERIAL_NAME]; + GLuint texture; +} WAVEFRONT_MATERIAL_T; + +typedef struct wavefront_model_s { + WAVEFRONT_MATERIAL_T material[MAX_MATERIALS]; + int num_materials; + GLuint texture; +} WAVEFRONT_MODEL_T; + + +/****************************************************************************** +Static Data +******************************************************************************/ + +/****************************************************************************** +Static Function Declarations +******************************************************************************/ + +/****************************************************************************** +Static Function Definitions +******************************************************************************/ + +static void create_vbo(GLenum type, GLuint *vbo, int size, void *data) +{ + glGenBuffers(1, vbo); + vc_assert(*vbo); + glBindBuffer(type, *vbo); + glBufferData(type, size, data, GL_STATIC_DRAW); + glBindBuffer(type, 0); +} + + +static void destroy_vbo(GLuint *vbo) +{ + glDeleteBuffers(1, vbo); + *vbo = 0; +} + +#define MAX_VERTICES 100000 +static void *allocbuffer(int size) +{ + return malloc(size); +} + +static void freebuffer(void *p) +{ + free (p); +} + +static void centre_and_rescale(float *verts, int numvertices) +{ + float cx=0.0f, cy=0.0f, cz=0.0f, scale=0.0f; + float minx=0.0f, miny=0.0f, minz=0.0f; + float maxx=0.0f, maxy=0.0f, maxz=0.0f; + int i; + float *v = verts; + minx = maxx = verts[0]; + miny = maxy = verts[1]; + minz = maxz = verts[2]; + for (i=0; i= 3) *dst++ = src[ind + 2]; + indexes += 3; + } +} + +int draw_wavefront(MODEL_T m, GLuint texture) +{ + int i; + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->texture == -1) continue; + glBindTexture(GL_TEXTURE_2D, mat->texture ? mat->texture:texture); + if (mat->vbo[VBO_VERTEX]) { + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_VERTEX]); + glVertexPointer(3, GL_FLOAT, 0, NULL); + } + if (mat->vbo[VBO_NORMAL]) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_NORMAL]); + glNormalPointer(GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + if (mat->vbo[VBO_TEXTURE]) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_TEXTURE]); + glTexCoordPointer(2, GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + glDrawArrays(GL_TRIANGLES, 0, mat->numverts); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + return 0; +} + +struct wavefront_model_loading_s { + unsigned short material_index[MAX_MATERIALS]; + int num_materials; + int numv, numt, numn, numf; + unsigned int data[0]; +}; + +static int load_wavefront_obj(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + char line[256+1]; + unsigned short pp[54+1]; + FILE *fp; + int i, valid; + float *qv = (float *)m->data; + float *qt = (float *)m->data + 3 * MAX_VERTICES; + float *qn = (float *)m->data + (3+2) * MAX_VERTICES; + unsigned short *qf = (unsigned short *)((float *)m->data + (3+2+3) * MAX_VERTICES); + float *pv = qv, *pt = qt, *pn = qn; + unsigned short *pf = qf; + fp = fopen(modelname, "r"); + if (!fp) return -1; + + m->num_materials = 0; + m->material_index[0] = 0; + + valid = fread(line, 1, sizeof(line)-1, fp); + + while (valid > 0) { + char *s, *end = line; + + while((end-line < valid) && *end != '\n' && *end != '\r') + end++; + *end++ = 0; + + if((end-line < valid) && *end != '\n' && *end != '\r') + *end++ = 0; + + s = line; + + if (s[strlen(s)-1] == 10) s[strlen(s)-1]=0; + switch (s[0]) { + case '#': break; // comment + case '\r': case '\n': case '\0': break; // blank line + case 'm': vc_assert(strncmp(s, "mtllib", sizeof "mtllib"-1)==0); break; + case 'o': break; + case 'u': + if (sscanf(s, "usemtl %s", /*MAX_MATERIAL_NAME-1, */model->material[m->num_materials].name) == 1) { + if (m->num_materials < MAX_MATERIALS) { + if (m->num_materials > 0 && ((pf-qf)/3 == m->material_index[m->num_materials-1] || strcmp(model->material[m->num_materials-1].name, model->material[m->num_materials].name)==0)) { + strcpy(model->material[m->num_materials-1].name, model->material[m->num_materials].name); + m->num_materials--; + } else + m->material_index[m->num_materials] = (pf-qf)/3; + m->num_materials++; + } + } else { printf(s); vc_assert(0); } + break; + case 'g': vc_assert(strncmp(s, "g ", sizeof "g "-1)==0); break; + case 's': vc_assert(strncmp(s, "s ", sizeof "s "-1)==0); break; + case 'v': case 'f': + if (sscanf(s, "v %f %f %f", pv+0, pv+1, pv+2) == 3) { + pv += 3; + } else if (sscanf(s, "vt %f %f", pt+0, pt+1) == 2) { + pt += 2; + } else if (sscanf(s, "vn %f %f %f", pn+0, pn+1, pn+2) == 3) { + pn += 3; + } else if (i = sscanf(s, "f"" %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu", + pp+ 0, pp+ 1, pp+ 2, pp+ 3, pp+ 4, pp+ 5, pp+ 6, pp+ 7, pp+ 8, pp+ 9, pp+10, pp+11, + pp+12, pp+13, pp+14, pp+15, pp+16, pp+17, pp+18, pp+19, pp+20, pp+21, pp+22, pp+23, + pp+24, pp+25, pp+26, pp+27, pp+28, pp+29, pp+30, pp+32, pp+32, pp+33, pp+34, pp+35, pp+36), i >= 6) { + int poly = i/2; + //vc_assert(i < countof(pp)); // may need to increment poly count and pp array + for (i=1; i= 6) { + int poly = i/2; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i= 9) { + int poly = i/3; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i valid ? valid : end-line; + memmove(line, end, valid - i); + valid -= i; + valid += fread(line+valid, 1, sizeof(line)-1-valid, fp); + } + fclose(fp); + + if (m->num_materials==0) m->material_index[m->num_materials++] = 0; + + centre_and_rescale(qv, (pv-qv)/3); + renormalise(qn, (pn-qn)/3); + //centre_and_rescale2(qt, (pt-qt)/2); + + m->numv = pv-qv; + m->numt = pt-qt; + m->numn = pn-qn; + m->numf = pf-qf; + + // compress array + //memcpy((float *)m->data, (float *)m->data, m->numv * sizeof *qv); - nop + memcpy((float *)m->data + m->numv, (float *)m->data + 3 * MAX_VERTICES, m->numt * sizeof *qt); + memcpy((float *)m->data + m->numv + m->numt,(float *) m->data + (3 + 2) * MAX_VERTICES, m->numn * sizeof *qn); + memcpy((float *)m->data + m->numv + m->numt + m->numn, (float *)m->data + (3 + 2 + 3) * MAX_VERTICES, m->numf * sizeof *qf); + + return 0; +} + +static int load_wavefront_dat(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + FILE *fp; + int s; + const int size = sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES; //each face has 9 vertices + + fp = fopen(modelname, "r"); + if (!fp) return -1; + s = fread(m, 1, size, fp); + if (s < 0) return -1; + fclose(fp); + return 0; +} + +MODEL_T load_wavefront(const char *modelname, const char *texturename) +{ + WAVEFRONT_MODEL_T *model; + float *temp, *qv, *qt, *qn; + unsigned short *qf; + int i; + int numverts = 0, offset = 0; + struct wavefront_model_loading_s *m; + int s=-1; + char modelname_obj[128]; + model = malloc(sizeof *model); + if (!model || !modelname) return NULL; + memset (model, 0, sizeof *model); + model->texture = 0; //load_texture(texturename); + m = allocbuffer(sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES); //each face has 9 vertices + if (!m) return 0; + + if (strlen(modelname) + 5 <= sizeof modelname_obj) { + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + s = load_wavefront_dat(modelname_obj, model, m); + } + if (s==0) {} + else if (strncmp(modelname + strlen(modelname) - 4, ".obj", 4) == 0) { + #ifdef DUMP_OBJ_DAT + int size; + FILE *fp; + #endif + s = load_wavefront_obj(modelname, model, m); + #ifdef DUMP_OBJ_DAT + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + size = sizeof *m + + sizeof(float)*(3*m->numv+2*m->numt+3*m->numn) + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*m->numf; //each face has 9 vertices + fp = host_file_open(modelname_obj, "w"); + fwrite(m, 1, size, fp); + fclose(fp); + #endif + } else if (strncmp(modelname + strlen(modelname) - 4, ".dat", 4) == 0) { + s = load_wavefront_dat(modelname, model, m); + } + if (s != 0) return 0; + + qv = (float *)(m->data); + qt = (float *)(m->data + m->numv); + qn = (float *)(m->data + m->numv + m->numt); + qf = (unsigned short *)(m->data + m->numv + m->numt + m->numn); + + numverts = m->numf/3; + vc_assert(numverts <= MAX_VERTICES); + + temp = allocbuffer(3*numverts*sizeof *temp); + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + mat->numverts = i < m->num_materials-1 ? m->material_index[i+1]-m->material_index[i] : numverts - m->material_index[i]; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + offset += mat->numverts; + mat->texture = model->texture; + } + model->num_materials = m->num_materials; + vc_assert(offset == numverts); + freebuffer(temp); + freebuffer(m); + return (MODEL_T)model; +} + +void unload_wavefront(MODEL_T m) +{ + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + int i; + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->vbo[VBO_VERTEX]) + destroy_vbo(mat->vbo+VBO_VERTEX); + if (mat->vbo[VBO_TEXTURE]) + destroy_vbo(mat->vbo+VBO_TEXTURE); + if (mat->vbo[VBO_NORMAL]) + destroy_vbo(mat->vbo+VBO_NORMAL); + } +} + +// create a cube model that looks like a wavefront model, +MODEL_T cube_wavefront(void) +{ + static const float qv[] = { + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + }; + + static const float qn[] = { + 0.0f, -1.0f, -0.0f, + 0.0f, 1.0f, -0.0f, + 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, -0.0f, + 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, -0.0f, + }; + + static const float qt[] = { + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }; + + static const unsigned short qf[] = { + 1,1,1, 2,2,1, 3,3,1, + 3,3,1, 4,4,1, 1,1,1, + 5,4,2, 6,1,2, 7,2,2, + 7,2,2, 8,3,2, 5,4,2, + 1,4,3, 4,1,3, 6,2,3, + 6,2,3, 5,3,3, 1,4,3, + 4,4,4, 3,1,4, 7,2,4, + 7,2,4, 6,3,4, 4,4,4, + 3,4,5, 2,1,5, 8,2,5, + 8,2,5, 7,3,5, 3,4,5, + 2,4,6, 1,1,6, 5,2,6, + 5,2,6, 8,3,6, 2,4,6, + }; + WAVEFRONT_MODEL_T *model = malloc(sizeof *model); + if (model) { + WAVEFRONT_MATERIAL_T *mat = model->material; + float *temp; + const int offset = 0; + memset(model, 0, sizeof *model); + + temp = allocbuffer(3*MAX_VERTICES*sizeof *temp); + mat->numverts = countof(qf)/3; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + + freebuffer(temp); + model->num_materials = 1; + } + return (MODEL_T)model; +} + + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/models.h b/host_applications/linux/apps/hello_pi/hello_teapot/models.h new file mode 100755 index 0000000..4a6cbd0 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/models.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MODELS_T +#define MODELS_T +typedef struct opqaue_model_s * MODEL_T; + +MODEL_T load_wavefront(const char *modelname, const char *texturename); +MODEL_T cube_wavefront(void); +void unload_wavefront(MODEL_T m); +int draw_wavefront(MODEL_T m, GLuint texture); +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat b/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat new file mode 100755 index 0000000000000000000000000000000000000000..584ab3d87d93b8b18c486956cc9461ab6ceeebcd GIT binary patch literal 635016 zcmY&hbzD{3*2M-D#Q+2W1x3Xc1C_Jawi8?I4ir1E6HqKvEGz^Bv5RvIgtOPN3$KNR zo!G6{!1(rvoXZ2x$|~zWx%B_~<7s8}rIeM`s*3vmcw}Ye zdS<<7cFaL6a`7{IYHJCnrgl*BREZS-oOBbP4zyFY*H}k*oo#1hiKT1A8Y@px_=%r# zZ24Nk)#r3G);P9C3=8rS{*~G(%TBH(T<=0pW0izRp_Z*GMs{znjN7%2@Xgb`jUyb^ z3Aen}#F8c*m2meBgl~=QV|@H+J$ik>GuyrBHa z6G@-1z3q|`-C!NP$2aDxGJN6&djFNE>q@_fDB=y7cuPs`wvh1FRmDZqH(~UdK<8rO zYu_;X+1g=t;#+taz0>coofyz-0dW;vT1+(Cwvg5^<9Ts$v+H7dK5A=8@wsU@eSXgl z2T`EgGI~$ZE2YGxky$ZD{c;wY0#*|5i$boVc-1L{s}wYd#p{A-4OJ^N6c5%+q@Qg$ zyn*QQdLq5kW?KVsXTYSaeK^ukjFwYq4Vxwz#L3j@^!%?;h;=yZuX3%0*kPVWyc?&s7R|Kogg2ibAX2CM(`PCz>?fkvcBP;FwWY5}c-)oV zndd`av8-}8;xhX57pY1QT7y^Hfg=CwUi5s(_rYRlu733S6Gesz*A4^eJ=6P)5GCV> z(EG>lA1$=hVZ>WHeVq8`mp9=gm$~9nsao`z7YVb(%U(67Dz(&lGod z)ymq3F|$RZ;k9WE&xXzwaS3(ldBmGwv8-Wz`uxwmp(5X(hV-5%5%a~x(+a(R$m9h= zjBG@_-J32Ft;1{xPrnx~3fWrIXFg6?B5q7AKtH?X>0!ug;*`#^*pGc_A8BFEoRLaxN%>k_;V_mo;y!pC#HsL^!eS3H;CYQN9jF&N27#e$O(GCr61Mu!Vjq0BM-#^L&x>P45&bznKl*T-D5_(; zr(mH3VW(rf|NWqCqL_}sJ11&~$aos60b?KPSDCBfGuVe!4Q6QgS?t5t3R5+_6Z>%G z>SPUE*oQnHCu>*(_F;eBsq`HCaJuVs`aJfbTkcu(9_+)gPjl$~j`}`K85K;t*oWiC zUK2(>1k8FypFuvD{T|WJA|EQfxlivzJ|sBYCobf}wgvZT4akRx%m?%w`Eae;WBNSu z;YIpWdJpnJq&&}BgPso!m%Soh{{$1u@9(=KP6lx_5pQqt%VysM_o95EkmD=)9d2X2X}f8>Y{bw^7MYxMKh-g#EZHZ z|F|7t=)?4=*7O~2n6^>uyd9@T=@0DTDP*ovM*AF8ix zL!XB}{8PCdy$AXb{iFlEANufZerMu^K3Gp5PZ&D+!E-cy20D3k!wC9W=;YFkf%H!3 zq|fU>;(|`D_ZvxTfKFP6j;80($=4mn(dVI)J*Q2i_dqANTPD-{p_5ZGrV%f6^1zA( zgy9#PX3nS2z%Tmsm`6Vgzi6L#9=#KO@wETEtbNe^qHmk|vQmH7ri%IU3D|9VWQrT9nTw2&kN~2SZKur`usw@?8J^-tbK4bchh*wRDeefW5|x5oQ0HNKxV`QZjy!;pOEwK;vW za;C-aGg{=Sb+rDOwa#cazOAFU5`xZX`P0@@ymdF8)dtkd%7>(>=e4s|S^04A;RUT% zv8;Tkx$Tnnvqe@u)Q`EUotm4K4;Q1ZYn%^rCf?F24Ou~J*s`j)jQO#g_G*}OG1+P4 za$3XaFgw|G!*W_fo5OZe)9VE~X|uGL+do zV7;w@JgyLiCT)D7zQ(8kt!Ymm-sR^yYH-4rZdY}odVc++ea`ow^v<9Csv*nMF=Cp>V zL+8rgM_bZ!-7jQI|Ns0#%2NONgJ(tEZ?-8 zkB}qp|L2?XyG@pF&ib@kvTwG18!5|_z03WB%vI_(_Ycy?FP(h{?$bbEVTy!|Ae{lF2ExCUv z?6_H$%N0ax(EBJk@YQ%)Lt^3vIiu8gTEj72zfblZPw)KMa)Z1bK7rPdUS*?vG9`%C zP-;_@oUZp&s29DDl0_~|%ldqDtUQrEliqXvRh(@9_P;(#j?C_(l+oMdg6mm*U23~% zxlqUGVY(fPB8(nptV0$?598Z53xmtmdlUUEdYFdWqY2~rr^T@{On(nPKk9ItoUdcN z$EHw%oR|H6y`Pk!I!5m_J8FmQ_P@Sba$k*o;JzCB@GZNq#y)UgjeT&i&FZVM4}1RC zS4-}zf9m`2Z+2geeaO*QOY1RNeKqzW$9y4k%omd93*-aO7sv;mFOUyBUmzcNzCb?Y zm@j0G`9ku1fqdZk0{Osm7V3iMEYwA@X<2g?>VoGi)CJF3s0*I6P!~DoEXi{g>LSOS zC3((5UGSWRy5M;e`oQxh^nvG1=mXE2&n=;3|DRa!5lIKn61J9e# z2cEm3lRS4rCwcCMPV(Fho#eS2I>~c4bdu+8=p@hG&`F-Vp_4p!LnnFehEDSR1N?&T zAK({!{{X+>`v>?1-#@@F`2GQY!S@gF3%-AVU-10{{DSWv;1_)V0KedS82BdN!@xKB z9tOV2_b~8HzK4Nt@;wZElkZ{Ro5h-C-NV2)`5p$o$@ehuO}>YLZ}NQ<`Uk#`LjS<` zQRpA|J_`K<-$$W;;QJ`_4}2em{(z_fhB{_&yMQ6yFEFInYky zJ3#bNeDBvm@1yvx4}BEh@1c+4J3aJKe2<4ditp~wNAZ1Ky52|e9Uc0pxnh?{Zl9qX zNeNWK7wsY3$I;t3W28x3Xdk2e-VmgGzjBc9k*AKv=S@A^+SZoOyR&&G>`2Me-Db{4;8`(lO>9;kY>OTC>(}p%||io+vFQ z?WVYvUVN*>SRJ5PY(ITd+S#Sh`U4MJiC@!?(V8FR&nJQ$&(JzcUa%JH%&D}tysrz1 ze;Zw*^*n1?L`;}*h1O8fIz;0NW!l{TA+_u}HJqF0~o#5@04 zZISt7FY#VJ(@+%reSmm}*fbWmL^AQ3gIkC%>m>31wQeh>K0QXfdnUIRpMIVo-q&#* z#i4^IiMQU+P9ka565_od(nT0st|Z=>J^PBG2iFmAk#hke=}0v3nl_CUGiq%iUUkb9 zG5yDO;=QzewupJRJB!yhL|lKnk9doZohM$VB@*xDwE5!nEFlh~5UYJO{JJM$g zv3w5kwr@XA3>rC~c#C~nB&2aM@s4h=T%^ukPQ26dtrk)HR}pVppLHVCYc275@82jQ z&#x!m=AEKN!ATp5Hz_kl6e$=*yjn!82%gl2c!xPhh%$a%iFe?|NRgP*mv~DUHi>Q# z0mM73QM`B*Ifi%(Cv6uKHclemN^|#!*by^`xBa9=d#?v(^l@m6KxwSKZ&Oh~RpygTQbM1fyE#G4d)MDz<% zh&Sw%6mgl2i8sE?3DF_2Iq_B)d0H%6(~@{M2cH$`-CGgwP{TRl;BiGk{Z@6`A+DP4 zD5%-5nfpbTo6i)~X}bkUqEy%S3TiVm(jxMF{-&TFcjY=Q#?1PwpawmnQ^mG>xrw($ z)5~H(!+gY>a3)QhJCdJx-E-d%@oE9$4LEm0Gz)7^ywy+b6AOoSB;LxO4vV{2dl7Hs zIg2R0W(e{2DSJjtn>dfMN}J=NjE)iSgd^vKhmH}i z{fx_^yp9oX@ZcMwf{wu(*z%63q+{?-&%7_ZbPV1ST^|t!?_bZyV)geh4ZMiA$?$Lu zyomQv^a>5Uh_}$(NDaJ*x5oPo8h8=!olDUgcoFZSd~q7bYo548<9NUSPS7~sLkqTQ z9Ph&JUx^pz+x+EU;>Gz6&Yw^6`JOsyBl&zMmn^H9_Ug&dC{hkW@41GR!d^7PvpY6|VBVOq9O|ge~q0cw+8;KYC{44Vy z@&48IxmIQp@j{P=h=-S8g#0x*_VEqsA!jDRv_=kAmN4K}^Azt`ze8@rK zh5u&SYQziw-QjwYc;UZ27oI0x`0wTNmx&kt`{mp<;)VZqX@84&;lGy_-X>o7Z^@2# zh!_5Qx9tJqMW1tPs+oAv+cfQXl6V{H{Y=4asl?k#?_vBqUnSm=df(En;w|DWrS~eO zGw%{F`jh8z4~Z8&$(_%SiMNj4hgh9>LcE{!-s45&A>#dZWtYkSfh689^Y@uvUp`H| zDcXO z#2d9Z*|cYO2J!YAl4Rm|Q&R#pj<@K>AdTbov!0`Iyd8(m(>UIsYYR1w*Zp9)#_?W! zwOr$PZF{cNI9}z+DvjgK9JN~G^Sx+zqVf6Wy7X4#^DX%4o5trm{IHee^K~kaPxASe zxL_^$eDl06B>8-wv@9a|d_B(;m3+Q`{p=*?^V|pJCFk=$i>pe`=g?=hCFk?SGYuu@ z^B|kXlJhw^xP|0={$t%%az5{x++K1%zl!T9IiKqc?IgK=?}c=cT)#7V_LW?}w&wyQ z*ROHYSjqLPY?>elKpHExCUG_=d;r%HQ#E^TfP|CHqmzglNfr zH0Mf!%;86p{b_=MOyu@-;msIY)kz|Udrw_qGQt}_Zy>vZb|Mp8o1n*Z?gN1s-y4Ao!R|H zC&L3dN58*2Qc7G*D>KOe-@ve^uqu==B`B>)YCuMAQKZ$s`pG3U1{?|`R?k5rN z)$D!}@p3B;swF-A^K3?w@hK+&|-dbM((LNB=CRXZO!I-yHq3h=yQ(wSMvM|edhTW`pok_{D|j$_z}FE1OChR8~5~ig6}urzkI&||KFE z1OChRNG84C;Jc%58-g^xFG9b;cSOzgeuM9Y&~NZv5c&EhMQo-)_qRZ7u6kG4bU-pUy>$l1}yT&Mveu;!vr8#&{Ij~*W zr|*(}y#th@-*ysSGs?|-fr%OiCtR>_uf?A8+UXr1x<2x_V?(3+ZOWj zzIp62#pvIrsBqYGf#Mn+URVU}I8CwC4Y3wQ`&elGqxJ0g@<3@+ESlDEYwB(K?i_gWZ{MEnP7rUAcO68F=@#OR z{M=TYzj=gsLmgU(W>zNR&HY^nuVuT4cX@puQPgD%@w!f{Ar>{;M7$e&RT72c))Mc+ zSstR=rB$Mzz7KYxZuEUQ@Z#V7Ep-!#cW?jsVnxax;vIH1MC{+Vop>9UnJrQW#Sw4Q z-$BA>(?;SwSa_s}zqy)tJzWM08NG~nEB@>*?#*6Ayc>c#iWPaoh_`UBw)7o2@ZviU zU0!V<-dy8jg#W#@#G7|WlnCgzig@jxtra(4EG6Esu`9*5ZwrVwVRpE1C=f!trOn7vJ~zduuN7E-@bz8(m5gG4C67RH^TSbq*1Bo}IT&y@-qzCbK?zdi?f770LM|rLi%CMHi8=h~O*yq=T zcpEKQOy7kAFTU%N_^<`>UQ0V88e}vf-mg|Cg{O@|ywmzgvE@}=;{6Rc%$)0%c`mYoCcNy^Fdp_R{`H45I)pcPv zClB$StbIl7fB8qDI<2YS89RMdP@CH06QaPVR|@K};fEA4S3XcsgNJ{cM89{}736cL z{kz4K^nVo8+Wy^Jg=@dF3i9gR+Bo{o8+h>@C*M}V3Us5z?t3Ek%}fRQFk(!)xDpja zyxpp&iOhK;h_~I^R8ezLf8ssU^@NzYv@7v8EuJE_PHIEE4Q80cP0uF8TfujacpYpY z-ZsCt)A!uKi|=>gt_i$a#6!A+0`Jjgcj;~l@g}{$F5LCcAl@$OMd7Mr#H&6!C7gAP zc&mhJ!a>J~_w?0+qNI)yubidd%jy{M9alCgzqc!j% z-d|JJYv4t^jz3pv;6=POXD-vgi+DSaTcm*(@%C#qUjr}V4VoOHffw=O`)nL+L0YalR{-7n6LxbGH?ie7;)80+P@7$B$f+&-Ze;K7X?ECtl?9gK`~+7x~;TUn}B8KEH0>hLFY@`@uv!xF zBA=fwt|AdH@_AH5MTvNk&-nLu@FJi4WGo`yF?#*l_gp}{sNW8ep~Q>&jVU;rcu~K_ zoP&rL^&9Lpf_PECp#=sKFY5Q-;GV>b`W-&3EAgU!@!bRPqJH;%izi;_mGM<9@j|ao zjEo{)=vD21BZ(J!)g|8w;)PyKEV+bup;u3$=MyjV>T8)`;)Pzl_nb+*&?|iJ0=&?x z22GQR7yA76>>=WXJ{R7RNW9SJisN<>FZ6l!&Mm|XeYSeGiFl#U&b!wVFZ8)#_)6l1 zKEErroOq$n_|6G5`Qyzrw@UVDfa ze)O! z;lD2?A0=M+@6dTk#0&rZTQ8A#;lKTA?;>7r-G8Ipwn_8@@Lzn_2)yv$FW!%d_v@fl zN~y2+h_{g5+vGiXlX%h3WV&7^-uikEGwIG5;_agMEg#oeh!?%e^IS<1{fUd-pIkeZ zNWADtzK8B1-T`_ag6~;@7vDD<>G+Izmj@g$jY)k-y#Ef}YYLcrn|PnU-)hQr`YQ2; zZ;Cd(9d=%#M{4z9jVaycIPu1WEj0!8OeWqW%c{>YjZNA|ylbaVrtg@6 zx5u6dCXV;dpj8^jTXIE&#_^U74A(f`#(fuPh!?zl9t3L~@9X1JHIBFXt}z)#Jb=De244JoD4%cFHbwRME9-o>g%_55zI8&ZC7-XyhCCAI3tk!aOXKscpZ-z9 z`C^~#ZC+@6zFWUM(D;0>PrXgwB?B-1EtvD!_FV_b`MlE|lAO;AW_d{DGxDlPs2hEM47~VvaF^`*-P3=*%u&CR>$g#v z*^=wG$?qVE`UUTS!XqWuZ+Vx267>t-3O~C`uHW@R9VO})yoGwTrSFV^7vBM3y|Nk~ zBU!KV42hDgS0$gVm8@6u$F7vnEAZ-iC0VadE)A2=EAZAS6)ah=mU>T<&@1q+C^eD3 zCk9@8|AF;c@2_MIeU>@&S?17Z34I3dvvRSL^|@2O^%D9FUR|Fh>+_O)%Ovy}yy}w0 z^xZJ<;=3R0M`>wiB>T}9tCN!bXlg$x*^fSj9hUGT@ZK!BU$P%*aoZ*Q2)w!LJi1l~LT>*)Jn;Klb|*nj7@x-N70uVnxAsF^Br_^*Wjg15nk6q&<+CHxn>9ry2+ zuZm>(@4nqzCHxn>y8qI5z`%>|{OtK(zadX#_Zxv@(q)c*Lw?BaH~iM7%7S{o0bae| zkU9Dd*+TC(z*~QYNpip8>AOb`*ZU3dw*I}HzV`)QeBTLoP1*gG9Ia#Y824~! zzmgO6=jb;&s~6=I9fMaqIwhy;IJ>`+vvmyKQ&$hlxjKgLYqR#r5FLZ}Sgl?3T`%zB zyIw8+*H6l%?0ypQ=IAG7iTPRmB;swQ_mdoNj($=~y`SWGxt~P5c|r^d$BXZGalG6= z<9u`U&yxFRoNtc)S)zaD^UcveOZ3ltzVEX8XPhtkXFgwir;E>*=LO_5&kM+Bo)?hM zJTD-hF)x4@`ONbI@)`32c#+RMFCd>WFMt>M%<}^BndhU?di~~@k7SPdNamQ2B<3UV zqJDWkLj7Vs0x#;9=OffF<|FW;e(~Kc#Ebgnc@TQV^C0w!=RxQd&x6n_%!A;CUhzB# zy}~>QUg#CigU~C?gW!c;;rm+PgFGTxZeOT{Fm=H;J>)v05ANP?>B1b z{>%3p@Lzlv3%u}OzDGj8!FNa2dcVQ3xUysF#K1Gw{hjYCZYyJ0y zfX@u5>Fs+VN(7EimYMUrDK5{W3D3A++q=c-HDc&)v$U$wPT6{HEn%-3KHiP4FBZ*A zQjU%jisylF!n-^hdWS2MMc!#gWTz;Hce6XB-o8+)JEb<#`MgX^yw zdk58sqPY5gys6BdyMbaExmTap6C-K;rsbEF2Je^Cn#<2Pqck2qpVnEw+Y#mVs!_DI zqtA|Z#(K;iX zxaM?r6f;+Z5!bsEJMnw?G~zN&%`c9W>?`uqg2dq(*il$W?FsjHOs zl@}A2<3}54IP!!4Tlj|`Bvk^qC-Q7E91my z;g`2RaqTEJT%^71N?bL+^%Zedn-kaH2JJ=OmDP!BV2qDA{Gcdtm3&)D{5*7txDH-< zq;#n4Ph2PaPgC4Ksl;{m!$Ohwrb1l7!3#wD5)FuJc9VHxe@so{O0}9LoF|nduIhFp z#er};;u_=ENeqy0739*Q4%Nlj)Sbi?ZnP2^L#h&&Z<%F^U2F;Bn!hqqWM&p4u9JUO z3-{(li0j*^6(VA90phA$YKho+MsNG)24Zp5_jX2f?6wgC{`q{I-#JZsuhS4--D!rntJB5Q3MV+sGyEsOj<2w z`EDbw5APO<->+5@m(S)&V*QRW#1*!}PxLfbAg;4Ej-rBbEpgpDzg7A3ya92ASZ@>4 zSNjlGe5(X8zoR#Cz3RMKEcsE1xW4-7zh61ZnYiBdUM_Af$VXhS+RPCPJ6ttj4+n4R zDn55vXh3e9{^=t6_S@x)xMud+qu_TK5!bAGJLq>C5!c~y+vs;55m)Nyc=|m`#O2>H zN)*sPk3GEBaD}kaG4}9WhhXtduUT+y9^75L)-kw>7APa`>KI%RoAxQQy@EAxA+G*^ z=V;(UTq9DZYv4j$2r7wT*D zHV@)LeZBl)ExEoPSzRJ7)K}`IAmT!O&Gnx~T+odsS0)n|bR%Dx@x%q)2+uo=xS$)o zTXrWd=!Ukt5ph8`#{G0BF6hSSH=l_My0PK>GU9@6OrEloxS)qMtiy>5dRX<`LgIoR zmM9QPT+qYCu0g~FJv`sD7jZ!kpY`z}F6iM4i;ZMG{M;axxS)r%oi-8|bbiA8^~438 zPx!osxS;d5qL&jFbpFn<`NRdCpIBooaY5&gA8JKh(D|h04#WkWKiYpUaY5&=7!rsJ z{&i<*JaNIlR>>IRf`6^~wT`&pU(u765f}Vx)!x|>J`4YP6zxx3@UQZYWr+*^wdPq8 zalyZ8F5g95L3%&X>Cz73LJzPmJ%PC3`|)Yf#09U9daVC$re0tEy1$pJI*+*gbWgYJ z>`Pn?bRYMyu0UM(Gkz>em9Nd`dPi5hPYblaSe+T8pqX7^U^r3wta0ijw`q2vYF$0 zGcUl5^J=Wua<7j!H9oK5d#`IaFL3Q&ep%!5a-VTV!+C+LZnq;EpVxtW2^yc*ys?Wk zKCiPM+h}}Vd0iixk%!&%CRlJ#(I z@B#@v1edOdlJ)SM)hr1;1Xne?krH|cuF-y-B}5L`=)RuXy$uKHz`Y0!Ca z&085MS?5puSuLUS;L>$o=FoWwod;J{-(U%y2baA^fMlH?vO|^7d2sa#v6awyaOH0v zrNO^w4-dsk_OGf1VkGv4O*)2u#SZQ+cjy>gMGBOWNje7C(oOp`hZhYL zj*I&!#DzYJ{UOc^{UM(h z_lGzy^oM+2=nwh4xIe^sp+DsFLVw8Tg}xVD$V2XXk%#De!G%1`(f3O9z2HI~qVEM4 z@{s#pQP3m{Y(7-N2jz zF6aj4FK|H*dH#YPV*Uab^pNK-=pp7Wa6u0-e}M~n$nzKU5c3zfpof^hzy&?TTnR4d zJkOQTdCZmIg3j|?37yAW2`=b7=1Oou=XtJ#&SS0w7j&NIO6WZ1WpKg2cwUBoVO|Co z{EO#h_!s77aKXQLUWR}1ybS;1c^Ur2^D_L4?;pnKzRY(H@MYXHfD69NcMI@k+$Vsm zgYL_GhtNXzWxhA4t@|?H6kJXwSFG-JYJNt zghZB6hE4gJc;}$ArPlkG2E?*dakjYL$}oJN@GPBKaLfHRMUd!t?3kc7DQOGiV++4!- z?-jGm8a;~e#&8GA?x!6HzkBCoaeM7Wc-*hjme7u$4B+as$A#7a9AkA@`afE)Jor{w zxqs@Ce(#orXL@DGzfacD^EB6=GGkFB;SJ~WS$2F~LHKtgTTB1I#f1Gvmat4}H=D4f zNh!;g`~3*dJ6hVZtYBTjC-1ph9_{>N0N3tSZnOsAkCXK~+@x*F=pjv&?h7tSWla_? zVe?$xh~7%iJwIm34Y{@u{^eA_(&kk(;TBJdSdPq(Bz&s9y+yTJOgOchv!zjy@r2XA zx>_cOHz8c1LRriD8~F*>d|Z~+0Nw@XO|r(>L(0a5ZIvcXFUjVcv+$%}&*jqP2kH5~ zx4&iC9Y(^r7Fb)V@b_D(xXM^ilE+kG4@%B3}g)7QILoJNHZw(eWjlD|1 zmY!Bc311#kj@AHPS2v@4d-D{nt?1v2^6u^|oc{iWY&!QOJ$HHaS8nZijBtT3HkQIJ zDTKF=FK&7La6jSqBb+P^qc;;yzw2gM)G3^>&q)tU?C1W3%ei@4oGUvL9{b$$zkL|9 zdcU;UmqzEc@aRQ(WNsE7cj=`p>wA@+AHHv8>2>xZ;o(gSS@zhSCH%2ONlT;O=B(%K zOIs{cb`yT~p{%8Q;2Of=KgwHXO&U#jen3S_>z!_dztpYtUp_SKxKBPW`GE5FY4}Ci z!(ix#|+*B0F)Trnn>CI5qT!h5)C!g#tEUpa-O|f4?`C~B&X-E-RmDxk`(BhmlV2)>biB;_wG7t3FR*5Oant2a zxh<`$JXIz*7B^MsVQYzxzpGRoT+GyBw}WMRgUd>1VQ2Dg?`F|Vn(`v7i0PG5-qPT9 zyz;qLVbkatl`N-%f|V=zZA|9;RV;^BRaVCR%xk*wAZrabuKzXiKD1u4(#-n+%=-Y$ z`vA=Q0L=RU%=-Y$`vA=Q0L=RU%=-Y$`S7kuZjJK+nDYUc^8uLi0hsdvnDYUc^8uLi z0hsdvnDYUc>mvPIWsU0snCk+V>jIeT0+{Q9o@dtuFxLez*99=w1u)kIFzdsxAx$;b z2Vm9*VAcm<)(2qL2Vm9*VAcm<)(2qL2Vm9*VAjbs3)^a}lfbN#z^s$Ntdqd3lfbN# zz^s$Ntdqd3lfbN#!0Z=T5Bmi$`voxj1u**sF#82C`voxj1u**sF#82C`voxjCeDR@ z6PSGyn0*tNeG`~{6PSGyn0*tNeG`~{6PSGynEMChC-)D)+&=(w{{YPW12Fdwz}!Co zbN>L${R1%f55U|%0CWF_I_3Tin0q#0?$dy|Hv{H=448W`VD7tsxz_^b{tB3TD&ST& zr7c5*RSGn7&I)HshdY;&og%9jY_=ZR>~4HyRwGo z0dUV#QV$jvbKcp%_H4A8ftj<%We5o z*O{KbX`kEDIiZyNJ9$;jVT=EjZDv)Y=cP9PD=VC-A~SpDsukNiL)!Ec^qhaE4}ZI$ zcY7C0ykCOM~%;2tVvq#1fkE zlyK~8YfJvZHWHs-ydbw_$0Rqx`~Civ86Mt*W9K}R`NEnKo>k4wQt#4=6!dJ#UtKJn z&R#MD$3Jzpls(-<0}lD*Xz4X6g78Ael9q|pjuN)>D{5)H;yvNk3vDb;X+FyLa|$Wq6} zlkn1gc`SJz7zlq|`9{zGlMxIyzgrEd5BhuipE4+$c`xl@?dn(!Oj}!s~zJu?(NqjPRWDU*y<> zy$SnXx+jNjA5XaPVlPX@m_C%l&Q~g0%0+rnem;3!-g2_-Qo=v3m$iJjcare<#icDv zTW1mu&uec9tx}5ebBSte8T7ms;oy3CEd$JL2(Lf)Sx#9th;aTTcV%4TX@mz~s%$yz zF`D$@Kmjky`nNTymhTm;V7XR%CE<40+%1)loF}}rzl&w|Co8H)pY8URmgh?ozOlpB zVx3l(@U#ATEe^lh6Mnqpv%Fb*7~ul@?#NAnvk6yeS;dl3Z=!kK!7oOy>0XwZakaI? ze_k3_2UW0S=3cF>@p@qVQOm<};L!zblXlHmWUq_mt5Xw7-!;-~hOm@=;Nc*;@Z z_sh1HJI#FLJlHBT6)@`+FzXdC>lHBT6)@`+FzXdC>oeBC z`V7qa49xlr%=!$>`V7qa49xlr%=!$>`b?k6_9N^$`w=kv5it7^F#8cO`w=kv5it7^ zF#8cO`w=kvFLI3i7nuDQnEe-+{TG=17nuDQnEe-+{g*zI?Z3d>=b)yzw*lsU2AF#o zVD4LhxmN+^{sfqN5@7B_fVuYo_A#Z1D{R*gWy3|lc`DZ5j_ARL1Y8j#U4NNd>y<($Y9kEiW(WHb? z>uRg^=zCuI`e~U#94xG+FHTj4cvdpD9$QSUEdMGgL30erd+pR#rL4ry3BJZA-5gc? z?Y;;l$WVH)17ZB#e#G9dwNcyBdbzUX=1t*OaI3Zy2 zt`bUezM|^q%4d{)?tct~i_p{aAa0c)sQ@~*bc&a60(N*Blf zq-rfvlay4$d(o!MF3soFK4r1-j|dE&rp>z+r)=1hSFP5=TFd`&rIK%uwdx#hpAxq< zSm~w|QvI+0Fw9IEp!CUKL@oPHzdQZdNI4Q(Ow9;7t~i~pp!A&Spw_YcQcC8vR~{U4 zr8W3YE23Zx7Xk~&N$+kbL%ZJ*rR;xdk+ss4mVuwdsH8OQRH1Xqn2x#B3GHLFUXIC% z-JSeu@ym_0$M3c&i}x2)BlmYTSFE~Pd9~YCZP6>)FfwqiB6i!U1)Rq!hlBel^A?v> z1I=co@p_?X<($>Wt9~l~w5zPV=;A?Z*q`P>YuJ{*oGkj}f#N*op73*Xkk5YKQNr(i z6>Bs8YF?wRDDNibQH#7hsg1gIOxe@GMxEPqwpQicUd5}ntxA8Br^f~*yo;T>X7LC^ zg%k6YUrS4<-XT4dbBRNh&DR~(10jc$_KRC6ajtIaJD=~$fPh*`rJfaO4Ixopw1$v) zLHlrJ-UHFnyN1L*q;>is1{QRX*oU!e^QpT+U(r6aSyxC6y0eM);oQNZYR8C@8up?3 z&*G}@v7^4&hvcyiYMa-MX&n6)H`0&~#{=!vLc6~t zBOf-}JE`R=Rik`ZSkX-ljo3-~@LK;Dif#KpQ$CE(toi@)A?C#>iMrUC_lel;(^H}@ zB1-=iQ;YaY)WzefHmcW&k`i^19#K@i*8Pfxy4dupgc>Nax5j;5xBJq6XSKisADQeRUU-(qU1_Zix{aO!}#k3OjrZ< zAZb8NXlETX-iMVd3TeC#Z89>=ybs<@?wffZoT87Kc^?`LOfd64y!^Dx z%=-`@G{?;QFjWa6zd$~0ZoORNd{~cm1Yn%^t zeX3}j4`p(d&^RCB-T#<5ANmx3V&;7CaZaZ-pf0x9nKkF^x@el3q;Xx;d9_dDx(L1& zr*U26`M6T!x`^2ttZ`jTP8y(bT}=DfNaMOFak_%Wbx|a*y~cG>cUln*X90bf@a~4j z`mm~2n#THYywEv~^`X07ldKO9-fh!ZAIeuA z8tcQ(G!I$>bh7Z12O8^S&p&rG*2xN^u4t^2fwzuntdo`A?bTQ(6MAgWSSLMB%-2{a zPb3c2SSMR8ZlSSG_6n$_u};p6@}f1sFR%~n7uW~(3+w~?1@?jc0{g&zfqh`Vz&@~F zU?12oun+7P*a!9t-Us+5<%8~% zeG~b>zKMKb-{gEi|A4wc|8U)AtA@JZ{sDEt{R8TP`v=qo`iCH^8@{Lu?jKMW+&`c$ zxPL%haQ}e1;QoQ@0{t8Gf%`Y;1NUsu2kz6L58RtUAGjZbK5!2Pec-+e`oO&w^nv>; z=mYmu(1&KloYY1AN{bf3;|(K=mr{fMON(nk9gKYrIIBgzxryMCBMiOwJE=j>+{B$y zosETRxTxC~xr;vK`WseMDXk`qa~B(}yBZISb5&g&%8BpkoeVoCxTxXZJVdO6zwyX$ zH}&hra$@q3riQjbu4>}6a^mDTf8*l(Wz-Poa^my5ItKePZvTCLM}sn|-wJoJJfWgt z`#`t<-XHkUP2Kd@O^i6;V%T2U?Z5Rj)cb)(LraS-E$t15hr9l_{^h$}NQZkbDQ=il zFbkt^%sg3Ib(m6GoZEH7;5pe<4XaQ_)G2=1(B-|G+Wa4P(XPj7LuHS$YNNn%;^x9+ zgS(Zxn)bb%h}yfyaBsRht$*OPSVKmryL$YkyLgj#jp0&ZclA??G9q8wC59RW%Bpe8 zON;S|^9;*|xX~KEH3>0vIa)@ov$3>Dd01Gf80M}{8dXMoTw<*>`t6~n*Ypq{y>csc zoXe{}pOh2p)4mvPUMa8c3ob9Je0^fLTFO&hman|f9MTLgo|IRgkMs~hktYn7DwbF6 z_m>etR}UGKbPx5-Kc&UvN_!38-jpTY$KQ4uoFdDsb(ugMb4DR0?<6lZxuU1QJ!JJ+Uh0W`<;61b(O~tvl4@75 zoEWg+fuY3PinDKmg0?x^;R$LFGulq3#_fim^~@pC*if#dMheWyrOVz^_qJHinsfV zT57F9ZFO*G7dqeLW$USlm&(%lz6$qI z_j#44^L6#DuljYWKPTOT!KvJ2(&shSPdID2=>=j$7M)n-e}Q$AO!ZJ_rf zpHJm7sBT9qQa&#$;;X*sUQr;QhYoC@&I$3PeD0LnN4-(agYwz&M{RXUD_6?rB^KDP6_rtCY)GCuasKyIFsjI%7;7WCUET1>Ui&{3t_^J-;bl&#`Ow0aN z)PvpJMVKMKX?;IcefqMz80DPDwC%B~YDz_M?ZR*4#-*y7SFI#gRQh7NGd)Euhlj?=PaCLRmwSlR+Bc2M*Va?Vg}92(RWBRwY|G-^eKpm{@m3EW zZ{~Q1mmFc{c$<~$Z{~O(q<1oNy#0nWHFLbT-_ zNpUluuX)!EGoNpz;+M^QzKwgFHuL$WE=)G_`9|#BW9IYqzZPre^L?0ijhWB)N1G*P zKHnjU^UQp{ubYIJIiC+cEUa-pzg}XkaX!EH%B^ueFHQSm=6o*m^@*ADIng1_%=tV# z@`Rc5dHB^sX3pmumG+uBpKpBIY3BO9XyvYP{l?h3=zpg*tA4$Ym(;j^H)a&lxPEab z!}S{?KAO3HyDoTO=K7uX;j)?Q*SX*sGuQ8sAt%hNS5*&H)L5@-E%VS=ufBY7)>y9| zCKuCKuLkWeps`+^Yny3iz1n>9g_-rLVnVu^^{STtB{S>Q%7v+B)@Q`a`iyv4pAj$X zGvZ}^M!c-gh?n&l@v=T6Ue;&C%leFX*^h9(>_<3X_9L7x`w`BU{RrpFeuVR7Kf?L4 zAK`r2k8r;1M>yZN+5U@sX8%P#v;QKW*?*DG?7zro_Fv>P`!DjD{TKPn{)>EO|3yA? zpM(14-Uju{{S4}tdl=L&_bsSj?p08~+@GL+xhFyWavy^F<=zAJi{H6>R6SP2s#}Z? zR@f-xePTqHH_v^Qv_gb?Er~bgS~^y-njaai2)I$^6Ym*Xv7_4@1|9+2i~T zi(V`z?6e@-`26)!rADPy;-AI-hQz%Je(WfD$bw!kPCKQ`DV4i(4mo%WUEG6*-z3^CRnSVWn!zL&6_@z(d~K|8{y?#(jh z%Abc~oYr}uA<}Co#Z~&W(GYiW0mbsE?3%uM1PI?h4a7B-N0a}Bpvx|XR=ybdwX4BgHaCf#-?^V-LgZIJR#5>`Y z$xtBhF7Xz7e#0>CQ6lkH@%w4GGSGu~TdZxN6sXXIcxUQ=OT@oJZCN=-XDR*l|gUo67Py5E=q-)$p+L;ym6{> zw*6BB>SePMq1aA-YCw(L?-Zdp3_WW=T@<+=qV%e>iFl1&`zWu!^(WqgziTR$c9bUG zzAf#P#=8p;ujyERWr?>S-m<^TDlLD+`XaBU_*PT~9O_EEVf*GO1wMxnZ>N{*m2S2R zh}Zq^dL?r3Fyg(vWr;HGQWfI8?lMxTcIUP)&Uf7dgHo=}G+)%(i62f%>CcUQQC~yS ziYhUVyM2*Y{@1D~p}#Vc!K<99rt~+t_#)n#j}|E%>Mrs{ycgcbC~pH7`6Ax9{ISY{ z(oKC4Z;HzbrPhOphS+DVbC444G9Vee)mt`G#ydHsfY&ZxS!LnIx+&njKHXknQmHLWmdf)*H2jcoDB#ITP_B-bpo0W{!76 zgB@m$S9`LacoFZbYm0~%@irbh!_4ufGz-e&)#KfNF4WBNwk>&tcyYczYi1HJ&NuLB zAr0~3d?P*<(!h)JJ>N99240+Rh?YUTINvqlXNWggKi~Gh_7N}6w_3>^#EbJa-bo=| zobQYauZb7=JoSdB#`)ZOWkcdcKEFw5NW94Bahs|VFY>u~K1U7vjC_7L+)BefBcJ>2 zyidHy=hwTg5ij!D?)`h>MLs|MR9M6LqJG~5wjo~B?~cDih!^$S$!RF@qJC?;bRk~U zZ`aX2#Ebg1n&3>lsNbH;^J>^<)Nd#MU&M?0{l2}ZhJ8l;ZtCq$ywIyd7e){-^lGyH zx6824&@0y-^NIJou2)CAClN37YIfPK#0$L&3$8`H(5t_F9f=ou^;|2eA+MlUh8^XJ z7kYIw)th*s&%+F%#0!0{@4tq4q0a+`ts!3M^EIzU#0!0{yloWmLZ4gvG$&r@v)hD< z#0!0%eaek^q0hr(>JcyW+39^F;vKE~kxkGt;{6z#OBvE5j(Fimk)z^>7k+g5`)cBS zIm}vdZ9bQH;YYr;{fQTTbYW&4;)NeI)c+k6@xqTHyqgm*{3v5wTjGWPit~}g3;!*d zcQ^6Ef76|I6EFOCrDYTG!hbz>EFfO^Zvp#(#0&pzQ$r9h{P&f09pZ)mp0epkyzt+X zzd8}Gs`ojcGdB`1dYk11jl_$7rl-4+c+tbm_K7E6^eqm}!ig8XO7zU(#QR?FPu4bU zM!d;-Ptt6E1LEDQ_aV*~x)JZs>2G~KL|5WndE>3I$mvbQo4mJ>Dbw`;@m60~!c@NQ z0pdMpDrUOslt8>a%G;Pa-Cjz(o#hW>y3I)9ZR`8cc&|eX;&r)|YHSwoOT5!}A2)6( z+mm>spPx5!yj$NqPvLmKLoLu?@kx9na}rh=V&vZ zuc_&5bM5T&^(@!H%;!75b7M1~@6tyBX3pn`f$?U}=RBKpX+5&@IjxU_#`!#XoP);s z{BxMK#`)YJ=9!uEIq$F}Gw1U}HPX!aT%&Qgne(~)ylrN#-@DVVn7Mu@*Qua!{nk0( zNaOnb@}!Z*^(!-~Yh1ryeM)Ftzb&S{F?0QXH61l`{RX@;nYn)RKEGjRz3Sli)69C+ zXl)CP^=h*Iw?tU4PEHTdSg)G>?x?X|S--BKv0hd3v(;FyN_5IFvtB*6yJ=>*acl{kQVp^&0!{%`Hnb_TNh` zBQ^Hl6%Py=`|tH1P8$2~q_mOWefaldi>eT>#TyWfb( zAFKIh_Zuf%R%qOB9PS*XJB$;{Mhad604?p60AAZF58}MJg-zcj4FW+y#fBAj`{>%3p@L#^)fdBIS2K@JY z_Wi~gj}B(O-+=$}{RaG(?~zvP{RZD1q2J*9BJ>-4M}&TZ?}gBB@Ldr44Zi=e)B6p+ z^ZDg=J%#UiKHNKVGqJhicT?>UKXWPJl$ISA5qsG zSM&exbMQJaq{(+=yAuQcc*F=4{b@?DsJ}O@ZSshI{)^l zV;2I-P~WE-8uRsruW4H=`8wv_@MGCk8_!9`d!*pO9l6S9PJFxeckU zX<5yW&~EbeeYc=vHDgCngRk|dL&ut$&6mc?*TD@29GhD>lUA*EqFawWHTzD?mai*L z4?OlgaUli%a-^U2H5!)W}At`pBvgjv%a>o#JtaTjAq{8HWFv7iFzW}Z-~S; z>2jRL;ml--C+7J<&Fp@2B!+MKr!+|w=gYpG*PYWeKk6&{RxXw2d#3kd**7t%rsnwU zOR}$D!~U8YdVe&?;W&#}&EoIsvxijPI$u1XsqEt;`;Pa#qe%O zTASaR#p5Q*zDpxY(U;h0*>^}4Q%ddOEc+VR+i2R=nUT)?-A%FT{lQ$> zck`DMns=Yd$iA)O(lzy$ILp5Ay$onWr{=Qn?Q0ciTts)-H}r9Jy5v1r_Koy(rKF4y z+4trU=2JCB7uL$Y7oH_(RyDdV`*!Y{s_CBeO@r6lVWN@x zn_}f>IGhowg12mVN(3_)*-(ma^}!Jss)Jp*FIw^ZBl1cQ3~meD1uc zqo&>Wda~~zk7b&aHc_&#ebhxwcFK0y_rkJonuW9P$iA)r*wFlyUo_z4k_{d-`n0+1 z`>k&~GQDOi`#zY_pKNN^kbNCnkD&fbYs^rGVOG@xh(t!U>LkEy~y*v##uNN6X zLAQ%F;Q4rtq0;t$HQ;u_y;-#Kgoo_AuXMPkU+@Omccn#wW>|$czR>Bxj4aJq4+q(| z)uFQFmD5i4{p#sKJD)|$zB!}2l6KD$+4o83QFQIXCfS#U%%o-zJyxG{zNY_=3;&gKVfq^Z{Sgx4Ek~ z?c9~(i#+e!7D)T*{q@D`joCPXe79K2zFt%2(U>2$vhVIzOKFkTUiKYcc?IDb6!z_< zjn<^Aud#2d&qp*l>TA5-9*;g~^3>PZH?*fM6{xSV?~;{1RII+nzP-lxqrd8F?EB;X zBr;T=&4oM%Hd`Q{*@Zl(IxZ(u^?l^o?%zt<-fxrai`RQ}@p;)7uXjdFuI!7~OYWwL zy4U+H!68xidQWM*5_PZl?94Way4UM%+B*^Z;`NRQ2};Dic)j!M4o$?qc)b;lypw%V z-wO5>6Y+Xc-wj305_R?Mu{l)sMSaUSEtGvx-&Qr^WM9iU%JS?(C-8Gl6|4yihG;LzR<5-LT}j@ zepT+=GT9e?HL>Wd>9u5E_|?G^O=VyBRiBvlvM>Cq z_sO2JFZ^oGtv<3Z{3`K6xahrr4pY_!2ku!O@ z>>K~zt7E^-ugJcOPZ}S$$p0t%R|5mQbNubrma?zax1Pt3%?y!!M<#?H zcl{V6`);p3=eY6ZC9>~^sjnm@0hIG=XI|)-*5PN z-RoUlar1fI>s|Tv#(CZA)qhiTUiW%in3he{yMBVExs;^1Zz25Rg z%@cL??d>0bURPhAds*jo^{siKQlhTD-$Gj?>gv1Gc1)tKzKcwjBsTb8eP>>dOVrhO z;q!y)`a?;53-eDU>gwBJ-MK^^pEafO&g=MmDye3oj?c{+_D|IDd5T4BqW}N++~MMZ zL>-^UdEQA}^FKZh9{f5{$LGs!ekbbqyd$X{DyCYUet%c7N!020j9a%voql&v zY@$xT8@`-K)alnREtn2v^xFXx>iA}({IS*>RO$CBRpNTI{lvBR9~y}tDO-I z6Lo%du=&(PonM`QmXN6Ps}5aL6Lo$yccPJ2=T~MAt7vt8wRn%aR_9m0BmA^FziO1w zQLFPSr}JI4I)84vsAHnepZj|(OVs(ZUDQQ&i(-jCpI`PZQRmP8e{8fme_ph~L#y-W zuYKETb^d&BMt`l&pR2YWq1E}bd;Vyxt{>feJvdRR{;(dzn9RGXGsT|bHp9iY|qqrVXmT3tViiX{{i-bg7 z|J|37m8k2#Ee@5{>iTb?r-xS8f3rt-)w=($|CZ@8O6&c<{;L@>Q>*L09@z`Dy8b(B z^J1-T-Y}XrIZ-!nWF#C))Xf`Sm+}(p{BPcVJodTVv_M#8p0t#00!v2lX7 z`v2yQhEwKgyZ>+A*wt#OcI^Mp8(Lh0Lf`MIjZQqJzQ(-a_xVWTQS~+EjetiV68EXE z(T_rU+G=;Guh9n=uk_JwR$rt4_8i|&yHp2X|b&69Y&q5qpF@p^UhBwnv>p2X|b&69Y&-TyaF;`Pq?-#m%e zyZ!&?Nv&>vMtybjGwQ3GpHW}k{EYhQ=4aGbH$S7kxBoXkKkhtA`{;l3GwQ3GpHW}k z{EYhQ&I?BrpLOR2@L6|W2vdC4ofp7o-FX3g)}0r?XWe}1KS9d;wes$*~=vQ|>f_`=9Bj{ImK7xLA=OgG>cRqrCb>}1KS9d;wes$+T_?7NF z2*1*u2jN$`^C0|6cOHaa>CS`jE8Tezex*AP!mo7aLHL#KJP5zioqyray7MpmS$F=0 zKkLrF@Mqom7yhg}|H7Yj=U@1wv5#|lu zH6qL#y6ZxiH+0v6FmLFt|6tzGUGu@bp}Wq5c|&(?2lGbvS`AoFPmhm|s?Yx0vZ>+A zdi?r*HvL;&zvTOS>hWB&96H{=WNohQ^?m#=o4SrIS$mk*=Wj{bH2Y%7y5f@hJbhU< z*|#eBy=NLW;NnKv^zK8+`a@9zK72om_8)bZ-+%dvJ9`YyBKO%IvbJ6A!S6n2QsZ8p zvVPRZlWWe;q@@lGWnD4Qjpt=wr*98jdEJB?6uZ%tTO7VY^$)nR-{u<>@WYjJ+uooK zQEr@b={l{hRF9ujxK1-4)nnrh*J#rE`s_LWDt#H;fcI^>LY|K9T>JiI%GR&T&m9g@ z-&xLFrRX5F?dr^FZ4OcTU}yGnJEYD!&fM?#LGpB{%L_jppl&DY^06=bY0eNA9{*q; zy{YQT+t=--v(H?4{D1`dy;wa1*KiMwnOlp?9$Z5)4+ zIy&lAi)W8qOYU21F^8|AIOp12#ceei-Kfp&|657h!s~F0IxA>I6({b!Y8fp_bYiXb zQmX&gfvdiXp<(OQJ^F65Xmq#(pFS{)_DyqOzge@$|C9s2zd4i6T03&g<{0W4>&Ph{ z)2XX%HTF0$g^UhXa_9UYvgv5c=llm#>y5U2Vf_Fq_s^EQ*X~O_BJFs)OE0?l$&Pot4WK%sEAyPV zPGpx`nTr>zYdOBye-R*$N7_&WjXE?-;$gvRXA0iA2~Fj2Nf!C)J_*l^fqI+FRt|0+>9p= zXh4;Jm~z$$chdW3%7a?EQ;w|}Cv>l`UZ)x7H*h7}$!45g!---~neohtH7N9_8PlYy zbV6g!i^tm1#%bp4kWhh6t<&RMD;&r@Mvq53)}X>DJ)StHCViW%$1#>QRi5;?TIuTa zYO@}Hx}pC5<7qu^xzdi(QuX-wDQjx=OOGqhv!aZu`n;sQIb9`vuG75~HGcPz`hK+& zytTBd;F4koL4%nk_(y){mcUZ~q>l~~{iLag$;th&@ zL5O#qUK%0Zeg0{Lcn=LpBgFf0a2g@r=vHZjcz0Q+5#sHe`hpN|`?W6!@#ge+L5SBT z{w5*6?atjIVx=qM$>*Kenc1NtP2fYt6=w*1*gF0!fe)|WpC#~Nl*u^)A1qVO68O+yg$ zD?x7#-M13-ma{mHL~qNs5cHNEv6-N^Djhcw^ycNhk)XG8r&bdD;cWfYq*eY9^m{eI zAEq~1L+}Uv@2d&^;LvR~!5?N0T1D`O%8geN{K5Xia)LiJ+O&+|4-}^EF}0@#;W-QKRe+t zkKkvKXXj9;^0WRCb0|UiZQ_{*w1#I-#%3y+4)EPfXTKBJ`1{9uo-tW2Gj7TB*MApu!A7e_NIi zBUs9Arr?LdlJ(Gl5*)sIx_sZ`!&E`nj7ft1)=v=p(;-4|i_6%OfBz9f8HX*ol=Uns zv&fR&X3rv;YRQuZ&Z0^&mh7sXNpsg&vftSl@;hzGUdhwR=#?ecdNh?1Y^~UO?Ie2J z#fs0Uco)yNV#GW4ycM^oF_#jT+Vc$0dDO_?o+G!+qe0I0TvlDDkF#%h{Ww-GNryoQc#^Wxp+YpE#Ai)ZGqp;OUbY(8QQ z(QPlbSiPDu+caYLnXBl_yGA^u)k=D?voUurSg!u2L=!%z_}ingH-o?NU%lBqc`J=m zqRw^8~#;uQtkXy;yH@04wnsU^-FvW*s3VJ=SCN*9kZe>fFKiz1tIe*6|H>gLBG zlhogjXwiZ%E4{t+Y{}5u{7S9(@|6QLG`uaB8gr1ooNU7xM-I~cQEm9t#)FhUrwtGG zIY=u$wqb*~1N3l1TW+7UpY}(!w>ai=pBZr+{!Lrzmi?;Z?4%kI^azYM-Z6AXLu*zuy1e9xspcSiM@II`ZO|H8p!AyV>k8T z6V=kF!sEW2`{D&ncIeNCRlgW8dH@^sd_j%(4`lSMeY5%qcHSH)nC@4C7YCJK$Ltb( zcCzH>60i4_pBty&U(lu70Kte|CBJX7A6m>-Z|%n!&f<_F{#^8@mW`Jsu*FXjj2 z7xM%1i}?Zh#r%N$V%`BCFzU=1J%c^Ca|!c@lcVJPEyFo`l{oPeN~)C!sgYzwigl zzwyc+F#p0IF#p0IF#p0IF#p0IF#p0IF#p0IF#p0IF#p0IF#j%B{(yNMeujA+eujA+ zeujA+eujA+eujA+eujB{t@1O>>+mzo>+mzo>+mzo>z$OJ;e2sG`7h2F@L!xS;J-Lu zz<+VRn5O&}=Zn8__Kfod{1@j7_%F^E@L!xS;J-Luq^q3be05Lt3!JaeFOI5nRXx=& zaGpZHz&Q&20_P|63!IzKFK}K$zrZ;O{Q~DB^b4Ge2C9DH-mVev{hC2XD>at$QFL8z z4j7eATlJgDdgXf`mFpK&c(<9X$Hr**^or*cs-F8pUo?2e{G#hKdV0ILtQ)`f<7VEe z^dh5$tbeX=$thh@sKvTgvbM`^#RXfF=~}M8tdAe_=LPP`WOcf=tXKc?=Z$8`^jyD< ztR262^3+QgDCWOLy!h&Q${XO#_gt&rSr`-T&u-my8K(iD@_)u8IkzhhJ`e@whJ#A zwt%Y4bmML*s+V7_&vu4WDLKoXhZ~Qh#`ithxNsQVR9qcyIG9|r8u6uZeaQY(V{S32 z2UXhGgo6SC=+4t7ysAl8n){(9yIBR3!@t_x;BpTt^~;G9^x9J2WW~Xuf_`J~iNi6l-!xP_sY6}jCvQ#xZ?iGvFZG`l+6@-*WlO~7zFo||-9vwvV^ zo@NoRiFK*MH3!6Ma?~?w^YfxL=jzyVHIpz+L0fxXI(mp^N_c5DkI|>_ffl?v?4@RV zi!xl;E>Y9O){^b|Y|-?5Zprhy#c0aUvEs^EoiuC8mgUPYYHAFpmgQGwMw(!)I{#gI z>HG6(S?;s{k?)~%`t12OS7X-LfMfG7Xh!Te;J)KGYgAGAWrQs^R4_i~{ zp^C3lYBK#$@tvxhOwUw&wKhB@#COc~DIvc8uajuDiqHOj68%u|Z78@-$lt+=*9iG@ z>U4$l+9zuM#9tyWmA^|>FB0RY4HK|+0R>F=jis=nS8_tGL&-|^G-5UKj^Zo8YdtNMPO zvx}Ok`tEPOixO0QE6v(T;GdgCJb`~>&u$^`&wJ}e0{^DnT}R+wRF5_EL-DU~yHy1K zHBVeYLry)=G{3x@1}Oe54_i(Z6#s(1uOaB8Tha=GK4z!I67=z`_9B8l9$%YB&_}n& zvkCh6Ie4ae|Jjj}EN2k(ar3`ufcMz)V3=y!V6Nd*1s zj|!)DO24O%jv(l_ezg#Se)qo}M9}Yf-vI>u?(Ni{px-wyr_*BPFYjW;6a3}-(lENC z{N)h`5&VU22P*GcNpnI12>$Y_RR@}_{N-(YYl6R6A8Sd+mA};4--6&To72Y9X61iR zZ-)^4FS1@=g8yY`x)S_vapN}BSovR_04DffotxeS|MPmIxc{uO;~M zYBj;JTO0)upX=PRjQCR9mE$KWzPZNb8S&M^wHp=Rs@@eC@#W-HV8mB3(3%mS=@D!0 zq~bgO#+ngd!(Y~n{Iztf#>n5fE!7$M+h9?Xk-vf=wHW#Pwy8EFf42_QVdQV?QYS|K z4m4Gt#ia75zuTFSzwA5CjQZZ$T%S?j$2Z&=^>t12WYl-ieJ?eO1XA5qjT!YV>*&p> z?}N~$jQV~Y=)8-s*@B{`s&UgMXpgmJI%pVQU8e>ilcN z;NS8c?HK&~l+~WWzl8lA82qd9r2|7BjiTEy^fCTHdxk!e|8-*MBmY-dhCW&!>(0=} z#B#kD`Y3A}$k0dpoIVVF+?&vsp%0_meHr?#HZ_2u-y#0$9HI34=}jO*zbT#jGxQq} zI*6g)h|0kX{dVXb!q9ImvrvY9_3Vc+^lKPBjNvaU*Y#oe%b3dp82+;QQV_#m+-D4B z_{+Fg!x{b(kuj3tFF`%VF#M&DM>xY@77iN6@R$0x#xeZw&(=W<|2tGZgyDbJdJSjz zUq+8n4FCJ@>sW^WeXkzL@V}0iCoudk_`*bn{}t*_X87NMh{+6p{uw+(U2A+sqYZ}h zIpxpe#*Je5^UZbP41caRU_8U0JMNvx@aHNaQyBhC6QUXZJpbx6CgsmNn@*Q=NZz3k z!KVX82=09`S}@5fLhy0c1VO!~lLg=Zo+|j+y5#?F89qbS9be91^f$zZ{)YI_-w+@A z8{(^``Wxaye?xreZ-@{54e_DBMXC7E-w+@AFY<@}i~OPgB7f+=$RGMI@`wKWTICP@ z7x_c~MgGu#kw5fb{m`~~wV`~~xAW#up3)O-qm!F+0~ z`~~wV`~~x=R{0C&Q}_$!(>lsuFrUI-Fh6@L|HJ$Y|HJ$Y|HJ$Y|HJ(JO8Fn=XZRoH zXZYU_H9v1s{N%gx8%HPDKzAB$$7zC>&It1rIPc1CFhA5 z<<)1BoP9rv{nM{2dqpn$L@vR)A4 z#gE6nBpdfevYz+alTCdy$?jmu`$L_lw&2-64^e7qKMtLLgj#$griaI9V}D-`S$mQS zmp5hiP3juUwZ`nY?HuiF=*9k_T5_=QpC6Cl}t>Oe+Vv^UC|%sM?VF+|qF;rJL7dk3qX> zd~g%K)w>&YtJ_%hgg|=K&x_OI22-@FCs!#9qXV}Z@Lu;QN*h*>fBqLk^P0G-Ie9LX z3aiVvsxP8kcW2&V6ib&EHQ+VDmB?h0>RoLes7{U>>y3A&8Q)#_<6SSBpXtoEd|S}c zTXlHHfiCnmy%w)5)t@T0tEql}C=F;_ga6o!rWS+h@RIkpG}9)l^U3aKn!9ys@rI<& znq`Y?aHDC)WO%n4+upB83oRV^v_oy`IIb$kdo-j?4(giYO`>bwRd~Se*7U{Lo^9R2 zHP^aV;k`i%HFr8x=G7V7HGZY-_`st}n&F2ku|Z6ZW?6s@e_LxnU&~hH3uVjGPg85I z_P~KMdRE|1h-YB zDMG#8R#-uM6n|E|TuI>1lR0Y${ONRVJ%K+VKAQ>ri9Vy=A5r}I_GvriD*p6dvx~r= ztigK-{IQ-gjlk#W*)inS&Vi~~%%PTw&!cnZlgB+T8dz=#fzRQ*oWSSJHmeDI9@Kmt zfzL@pH&F9RqsgqwK!Uy={|i<#e;}=D6h@9)2b1gf2!g&wPLEbUAEmAz%vOI#I)-9~ z%va~AxfJww5kX(q!ea^gO4!|sp!aVk?Fo9H`Za){_quEQ5%lg{Hk6?EI*Y=o;?2#J z6ppGsd$$!GB5z^`vgfe-f4r zCistkau~sX;yXqX{AZH+WP<-}eOH^{t3$I~34U65z>DCc&-yaKKO6jPPw>sT8+sA^ za%7tzf=~V^A4WyWA1|1P6MS()N_Cmbm*uGY;MM#-`EJP^ZyeW5*3Kbq1dUy~3#R-U zAb2S^RB-&~QG$qP%~NYeJd4L#GvcZFtO6sRr8O!r;&rmS^!82P#HRhyBY;m2w+@>9^D1|vTi%9D_vhy+KL z{A4>Y>ScP%hf%L`dz&)qbwCfO#a%2nDKWEeIoy_1*q?LxjANx-} z4E`LR)|A2LeolQEe7-(9ko^>&^>TVL_^GFUw1lnW$0@} zug(m;H~SOL(EH6NWB9w$dxM=L8G65eZ5TuEddr6}^xmTHAco!_PVL9*mEOHO1~T-1 zwMH+7|Cnr;#50xuv}+&5{gwY*%ZXt4&%w)M_^0xpZmq%?{!@HyD8qkV?hN8cKS?yA_cVqdPAZtf@WX?{C-MO0ho%mZ3_qL@F@|3&KYV5GuIHXNcg&yMr11 z0`Z_!-!Ks@MQ$PfA#@`L_`{Gfj!Kj>e` z5BeAKgZ_p5pnoAh=wHYW`YGy#eu{dbpQ2vqr>GbDDe8rO>Z$65eu{dbpQ2vqr>GbD zJNSeC4*sCOgFoo+;1Bvc_=Elq{-D3lSNuVL2Y=At!5_>U;4|iplZwxnH$G+e{dLya7IA-T-63-b^3 zh4}~i!u$h$VIG6tF^@s-n8%=Z%wy0y<}v6!QO#rDHhD1SG3ec0&129z<}v6U^CA2P z^CA2P^CA2P^CA2P^I>o0KbQ~UKPT0E2>-!+2>+4!Q27t$UicyAUHBp9T=*g8TlgX7 zTKFO6S@EC$8Y{-Q^j#5_ghWLuFLJqyrMUYTx1@e)~^+9&51koSxRjjo!D&AGWzqgHlJ^{l0u7WvWww5+OKL~ zEb=;Ag`-0j(~OIic&yuc>fN_I@0!1tW^^sh8t032a-;zty^u!t?pNll*6pcyY-R2< zw-Zg;V8{7sJ?M?SEx*hTqOy0@XLh?q(ZxyS`O=<+w7jSc2Ze7@bB!ry_?)6E+w?g7 zQ40AOR^Z>`T&eS!^6D(yfE>%0=j#>K-?4vJmNyLZr@$&!JnX-Ibau7{r(BpyQ~sK& z_k35;mSINRit7(`zPZ;u)9!tn7NRO>H?4zP9#pIv*kTTTYHz;asObHJ9oat&#BXab3 zZM6#2UcJxiV_1_+5B{U5%gty@@*i^TIEb$G{zW@)%%{&b#bl@1Lq~rW($GKmWsd&a zwq%}e*P-M-rK|y#^8LvZO73%NJHLsn^%wLLbZIlL`6hR5qQEpGlUlBtPHpoFdfg;h@v%y_W{m<I zy%zsFr_ONxbZ*lHLcQ$nTqV?N($hPHdUZE^N~l-N=1i&AW3L1Pf1plX1 z;?^HjF$7TupJN36l*u|x;7^Blmk9iMbod^DKd+ak5%}XiWTU#DDV!eqZzAwHsr42D zpEo3JBkWEpY?yG68JoC#ZrR4GSodpu1a64(^gQ1 z(%05^YY6&k(sVOfPl%&#?RF9LRd@YSx##NWj!WwPklSRK_mrTou%B}XdXKP~M}w8# z`)yc2(EFh>u>`#r|5-)QyVKV#1ijClwx7N#z5lD8sIIRjlm6%@1ikM#GF8p*m+7bF zbb6%xXZOjO1pnC+HILvw+KgCgs{AK@=6af-{HIglZnCcZj52PVQTNs4P`y=;2>ugt zCxYOwYyBn=eD&RvDFi?L*moAeM<_1bl6Go%cn<95qwfR@*%+= z{m+)%_w(Gl;ecw*K2z=)^c z44GP$Z>jv;_Oxf@r^vH1BR|)=S7PL6zVdwJ$0E>@k)NgSO&Ilx@8HI$S5}fMqh1y_ zT^RLh^xm26{;JP0GpfU=*Rf~S8TGPJ?}MXW3m;lD>g6`QG=o3=o_I6(^UKGZ!Jiu0 zjT!tIIo*rFpZV9^8T`>7?aJWK2A5h4{-htT%-~O$)>8HBjq3iJmJB`zdADTn*?F@c zpHqB(dyyD?zT8P&i&1>O^{yd<&-Hy>8GO!q@5tb@)3$O9eYG9aiJ`B3aUFS!($}?{ z?HT%NJHHJ>UysJOVCc)ytr1FRiF+{gzSO51 zL+_P$cUJc;U7*nN?HGDDFYCw9`@sf{8G29FcUJe_W|CPY8;1Whc-fcXKXc#r;ibxd z%&zuk_)lsGi?{_8Gcyb-k0Hr=%=U``l*?!7y2pcg?@^9p`W5&=%=U``YGy#{to__g;!?q2mKxVL4OB- z(BHuy^mp(F{T=*4e+Pfi-@zZu8{jkM4e%NB2KbD51ANB30X}2i0G}~$R8@S&ya7IA z-T-i04x&V?V!e5?Erb8QplhnQ#KhnQpG zhnQdChll+JiNBsYK8V70ex)z{Sum|V%lETP)n{m{@5i^RA#3{wCbHhq%T(6Cmu6}3 z@A}p`8qB}370$}qulSs-8~58F>;8*3$=b|yhOABg&Xl!NZXa3Wo&x;;8_iqF+HtY^ zEI_rszqOgHhZi@Ob(eR8soL5P)NCmWQlILA&juL_I)tZd@b5*RP6)<@trE1cjSw`{ z>nJ!m*Gtf+YCXa5qrqhG^bMsZv*7QZ)dX9#FcRE2AVq_JcOP(2FmLZ7!Pid)3ig`c zq-1^8QPA{UCBZ%~LdYX1m-6d37aUQ=QSjaoeZif79%%6IKV5bTem9K~yfCJXVCsGc z!AMhM!L{>$`Qqo>HXcTcd~#^h(&mC?u2mK6aQlx2-|znYhTvc4jeo8cDwRc{iggf`!XRNL;ZWOX58`#$ML58y3s{MV~JSS~)EjOuZK%xMsGsAk};% z@tyG6EOdwpj1YfvU799EXqttcE^kagqSErZM+1ypI?TIJ8;7QQeQG&M$ zel){+YLml)Yfg_5p0~Q_C-^nSPH^D2_rk}SgVbkEDBk~!9xoVG*HLiY(lfsJ{-cma zg8!`MHxqp<%J%{4?t_kdTymd4qvm#^qo;53gx}Af9}rx5JY3M~R%OB1{KLM`!=U_X zf(s`%ZH9HJF9tq9#CxY+DMq|N#0x~cK*S3~yg}AHt zFA(_!BELZ77l`}N(F}NA0xFA)3%g1IkX143^==nV+H0iidbx6&IBdILglKtil^1kEp96YO^8ouF5|nfRTRe@P!4)zn^m^UcS) zf71-OJQ0@#&og;_LC#$_ZYGa}nGT z*jez?ujrEXs7->=)wF_6mtPC6K5s7l!>)t<~5A+4-0|O2Za9t;eSB* z9}xZrg#Q8Ie?a&j5dH^*--9>sc_9292ww-n&w=o9Ap9E$-v+|3f$(XdO@)&C|ADg_ zzoV<`TXCh$@2LKSlKXVb_IBXOhx4exm=5wjK$UY{xwU^jHH+vfYpd4k*%;4{6!29& z8>9M*>m~J!QTxxdYh*83&)yNpcZ0u>SGz!2cMj>x+qx7{bpv(HL;d{WDgD*^P+uw9 zyuYmP4j9Cri@&M&zX!_t&YU4UWZ-wwJ`0vL;v7FMg$Dd+#Sc7E=|bC%JiW?udKDAE zr9Y&p=RbRL^*)(2U~g|;Qa+asukWL-)x4qPG5xr{_j~HFe*m8|`$%)|4d#)BpQvij zp?rPMXTtA2mUNqT&G+Z9OLwS_S0}b=b)Q0ac2j@b?Gb50da3sjk}2w6Ade1wLBI9; z@wH}|wEla4zH=s*oJI}e+oRr6`1@cE|CC3A^@p=l+I#uEppyjt^(_E)JWpfkJG zy+Qqqd+^vHcW8KiZ$38nA$^VO%Y{{v>Cox^>R!?pv?6IB_e#p7Cb2=hL0#jCoi>#J z<-egW%}4UyUvK62+Mc*bv7cM>>wA}|%)&1G{q|K_J-;Uhc;2FV>R!EOi|>>1kpAkv z!6dR@JCJkhKc^)x2J?gYFDWQv2)F-~L;Knd=cf6uY1Yj#T-W%mx~HOz#A$P}tHgQU zw70}**rK1r>FhL6;#?FtSmIol9V~G!X%H%Lrc@j$ah~iNA#qOV(^m4ClO7=XJQ@}# z`K*7tzvOe>^+A%)&Mv`{&lwj&B%hnR4wro9>=`Zj+_7we;Fx4KE)J(u>8 zy4NZ*Q0jikK1k}man%s1d$+csQuhVr!ldpmuZ@+uziu{3>OQP(JK?+ji0;DkGCkC_ zCdKa#ZwCsmvyFm<&({~I_e>R!UvwKL{H@hur0{lqqjAF5rWR9#r#e0T8QD(w`eH|k zE1K?0vtOGs;=S?Qgc0w?`^JoT-BU}cXIw^d(=w{>sCXC5He$qkzpIhxzqOw= zBd>OEDlqcu_oX}|uTvJ5W8`)06Dvku_kJtG$ZL_E1tYJiL(Ca@t?|p0k=G8!ri{E+ zJzyfgch8#2jQV$Qtjwr?ak?#|{*#R=G3p<6&6-jF+uh4E>R%??ic$Z{JIXNXzv)(K zM*Z6^FlW?1t(>|1-YoUE4#Cqc&#N+c8aC3N!Bg+f>Uy!_sZ(Ml22YK@SgU8z2J^Q% zRVPl-#LW)nX3Mz^z?Y4>Jv&&?|+wO=pJ!G_lOg^N1V_-;)L!I z=LMyE#0lLaPUs$SLidOhx<{PwC*%|UgnYuEkWcs%@(F)JKH*QuC;SQdgg+sl@F(OG z{)BwOM^Sh9DC!O$Mcv_}s5^WVb%&3l?(k969X^V>!$(nf_^7V#;3@hDc#8f3o}zDn zr|1{pDf$F>iv9qeqA!4_=m+2_`T%$e{|8UQ&WEt+#P3wktVAz8a)z@1+3(cXZK&wu z+=ozZR<)SwGzpcp$-`khYE3b9ty-e1=;_0`ne7jn`?zGi;=^z*z3B)2*M7MC{M2$I z*tfz@S~$=yoUp{lN9&-|p2+a>?sp!G0r^!O+Jd{VNX&5lq$^yD)o zJsHN{9SW#H`fzsmRX|oJNARnMU#M9?7>_(tNSpVBspmqA==;GiUOVwC)$SX{aZcaJ zar+21-ujI;P94r=?|f6w^pD`8iuttYa2TIDmam?f8L94#{y-mQjN-b-K9ZgOXzrNv ziOd>~=H=D}^svlmwygJs#w;Ag`qqV1DQ+ZRJyJ;LT843WULhH$jN-MLcT~Z73}5;D zjw;n3%i7{R^|>Qs)q61c)V`Iv$6Q?>vTPsD@4kGbtWRUrHHFW#+I=ifZ&pBg#$$Nl z#{xPzdK9mB|3U{Bg>!2AxAgbpIG$GO9hnzK@D$rTn!7qu-M9T-JqsSmyC3D#%iWP& zRr68xt4JK5V&)I*X#OE<^ zqQrN^aH7Q5v2m2dcktVIi7(neQsV1UbDYHIx@)53FS!0>$zOc^DU!eXd#6bLg5#%1 z{!Uh&BKaF&J6ZCVv}K~?ugjqcl0WZGk&?e25mThTw&u}N->0V2q`to*r%8RA2TYUt z*4P~_^=&+9s?^uw-ejq7n|%|dzF`$7NPRyAs(bbopV$AKCOmFiJYD!}-DigI_Fv5z z!q<9(rwdPyT2B*xu4p?|c&TSNS@>wsAxe0tv6v=2UU_u7V8)>tg0D-(2!4A|vNkm= z!A%EBetu-qRQY#`$cgE@-l8O9pg0XtPZi1?dF{8ex%#9iRs})q5pDF&unUq$a8xY0uv&|X& z>ye}8b;Uo--{9ZWd8Q2h@oN(X|NMeY82sCBYQo^(i8sa!eXM?7nxPNBY3f->rH?Cx z>OCx_kCcw)>e=E*wqIw)&__g;sp>zGJkQ6Jp%0UzCJcR?>~11`WPpbWL%)a*`bB)u zFXDrK5g+u6_@Lhe$GGWXONNnd#LP0DxmIa7 zwh@0gmq#foSI1A~)1zAY-1gch`Z)P7jrsnC!Z-Y)@F4Ym*Oi|XVDpW(l>0;9oQtSK zGQyuZ>jjN0e8BPOPlo6`>p44$>f7R?`!>rzO>TgNcZ<-Q2r0KYxhar z!}^n&27RGLM}E+XwuQ9l)-Q^C@R`o%{h;z)K2l|OJs$rdhj9PoV|9OW>2>-%W8_P6 z+ND0vVL~<1k?{fX=c7SqPNpQ%ivpR^$51NCq$rb?;r z$#VW5Dr=NU^V9#Sd)J=R<(L1cPS$hEZ2yo;jV)%{s-e^Q{H zdVXI22VH)iN&k$#li9D=w0zE2%G&szepL8M6ASaHp!GMZO7AEty-@w`x3uiqH@Xo0 zoFdEL8V>=h2|Lh17EY zdrJ6SNDoiGp~49T)N$+^Dk%CwPmP{Y^u|KfOCM9MO@(B0`7sr^6jIcWWa>6u_*lTvGpsVL&!7Z*OXf!Ta z*6R$@1@GnN2)?ZNPO#ScJi($-uhnxk`4Zpe^gN00Q+xqFY#6S`cCqfZt+I)S9trClch8>w&ZtQ@It?IT%I-*Jbsq`uwrvZcP09_C7Ym)?6V z_5IW7t<*PC`&R0^dGQ;mZ`0$ignu(SsCzUO|8k4cg@60LqznHJsxvqEm;WtG_!l}m zSNLZa{#y8Fr1wVnS3ByJ@bAc*9MMN$L^?qqxZfH2h}!v_ppWJY)aSD(eI$mz6n!*} z&K7-ax|u8b=raD5=wslP9MMO$LD{0;{)R6I`i)LXA?UZax>pSPol*RZpx=FM(+Te&(NluISk8Y+@R!8g6iQP5 zlE2^u!C$rpz7&6{+B94IWu#S(_{(Rt5B#O0SEl%1;mQ<(|GA|+A^6{*Cr=3ex3o_( z!T+lJJ}3BJ!tX#!&`Z--GyKEesg(4Qg)4h;Pa@Lq>d`6e7MS zBMlkxRm?JA#CN-k0V6)6Y<)(2*DvWa;;Uh2pze=Q*NT1_GV(Vdz?hLgvmPeuKB6a7 zs1W%}*=5YgU%&P0dZ5am^K|uWl*(Umf*~V+v%?G-`3tr-V$|2}dMQSIC)G7$)OYVT zb4Gp5x0^HS>$FpScAlzl|Ei{p`nH>6%&2eME~OatO)WBF)OYTCb)8c2&uy3qgMR@R zN;CM^cUu_-{~WiMVeoH%e+vfxZVWSL@ULG#QwIM+9vCzDce3AjG^DJ z70em>E&gN0(C^D7uPH;nK7Gv9 zXL!6JZH3Yde|f#C9K&BO3{lUsD}RX(wPyHBlDa7z{<3wY6~kYq&az5L47948>$eep0ii}H?B_^ zb=_L|U$(iLi}Prpi@CZt>K)nER_~80f9~5>UFTQ+yk?qu=1%$Yg-tf<`RzP)Ez*YJ z&+5KdhCiQcRgU4$Q7_BzQsvJhCc^ywUGP1s*Wqco?7*ssJdVL{c9UR zkGv9`e#2VUji#3q+)&0+uM*{SEPc7Yz`Y-Z_{)_yf{~~|rzsMi@FY+h-SLF}=7x_c~ zMgA}!puU(7P+!aks4wON)EDys>WldR^~HRE`eHsneK8-PzL*bCU(7GyALbYE5AzH7 zhxrBk!~6pNVSWMsFu#C*m|ws@%rD>{<`?h}^A+@g`3m~Ldai zSI`IME9e9B74(7m3i`nO2mNCHgMKmpLBE*)pkK^?&@bjc=oj-J^o#iq`o;VQ{bK%u zelefIUofA-UofA-UofA-UofBAD1X6x3V*?T3V*?T3V*?T3V*?T3V*@;4FALY4FALY z4FALY4FALY4FALY4FALY4FALY4FALY4FALY4FAJ<0sf400{j{01Nbw}1@LE_2jI^* z2f&{(|HGd#_rsqt@57%l=fj^%jW%GPnm=jJijwof)rZD>>-KlDHZqa(!clc??auci z+OD40N8jlFrkuKFT0r}`ysXPVtHh^|sQJ|2R@Up5R^{6}-cwo`2U)k>Q-kC3-m2$> zYsz}_;}sdU1qI-jWegaV3dvG=q4)N7Ly zd+Ob#@5`O}?YG<1GEiTg8$VI=PNleS%3I1TYtB<%W|DbFE1uw(N^ka9bLqMdsLMS& zj*7ibBZoS0&kQX!w5!3j51ytYD{678#V6?2#M<21{5Tm!s{2Q8@Pm}QRMk*jvktsOU5?uDz)t^Benw?JG4256`8u#;%uce3uEzQew^DGY z>g?ygh3M#Ca^3!#GGh#Q|I{=p>SU}w7xp2|ouaN)re3DXm(;y0b|J%3)2qdX_k3KtvB`W!(s z8vdX@jWTIfz#lpn{e-&IR9tesN;YYRyr=JRx~{l9x!QJW(p}wivStNUJ5`$PAI>80 zon?6B#Jb(;_TB+w#0%@7vH|kX`gT5vgQ|D<9X_DV>T3PENy;ocd@Hj+Q z^YyvK{Y_LyaeJ-$Opym+rMT49@w7#2%-{R6sGhsBwyW+uJn83%v1HeXtuid`yN5R zix*!Z=-2b^8G?R4jXgrp?`rD=N>=(k9T-p0Z>Q&*)#vJLrGzFb$4bAZHD0J^sXtK} z?E)e|X!mJYnf4SdeAHiSNn#2?Q#dzBWg1^)|yqe%I&$Cw&{Kfv> zGlKsu?fZz}e_jo45d1I1=sdyyeyut}@W12hb`ku~=+-8J|Gm#zMex51o0d`!<$u$T zEFtsnw`q29D#7ndc|0WeyxZ1m1b=^Rc$P|K6i}OL>RE8*=YvjeC;0fE+3N}Z?bT^H zr7GX9@OTl4Uq4wu@M$`nD(8og+Ybbt)aT-$Pc-R%MliF@LBXW|;spl|StsaTZrT6Q zbl2fcrQ06Iu_7(*-r^Kzu(nCwg}Zz4Qk-^hm*Q?k28V&c2OVIjBx~Vb+!@>%w7}qS z*DvS2f82*W=Q-!@Bzx~qzUytWxA)(_Cr_NuS?}+$jjpFV7?& z!t+a?DHY-Qt=pKI@ciM*8<9ynm^7`w-s0 zi(gU^-oL+Mef2knZZpY~j`03v(SE`E*X2|e!uwZrKz73W*KI*g!u$8+XD(d>)Zf@o z%1zk6g=?iI?B5m({q*~SCrpib83_Bg)*JnMmG0kBef1ibx_@iM<|XXkEAs*f`!{lP zA;SLc6jy|>f3J@$N;v+qtneiqfB)#UZ*csDe#}BR{&FtNML7PRwD2b!f6cBJCLDi7 zkLh)B_4s=urS3&FoiFGC3a{XA2h zTB?7^0_zd{%h96|!N0d}8WVgjyEYTS=Q%rb5`1=+EkN+uIk5!6=T4a_5`3O-Tm+wE z`_&@&d^k-*g3l8MG$r_a$?t!^N6CHbe{)5WmA`*uUrl>|gW&_AmMX`xkwH{fj>MRrfFY0Q(nxfc=X;!2U%aVE>|D zaQvZP67={(zu@>ozu@>ozu@>ozu@>ozu@>ozu@>ozckn55B-AU4}FF61AT?_1AT?_ z1AT?_1AT?_1AT?_1AT?_1AT?_1AUcW&kyuf13f>`e>i{9e>i{9e>i{9e>i{9e>i{9 ze>i{9f7$f>MgQUaMgR5F^B4Vx^A~*zU(l!U1$_!%(5LVPeF|UDr|<=R3SZEt@CAJe zU(l!U1^o>F(9iG>{S5!m&+rfZ4FAy2@DKeA|Ip9y5B+>a{X;*)Kg@EQFNpV9pb)o1iRd`9QP=cCsn<;`n1Zz*=no##<+sh!!w-+z?m z)?EjRYjiYUU6Y%K3>hN3dPQ@=!T$3(Kep%8JowxDfX-M z?z`0-f0&iGXB;5un{73%yFTgshE8EF5CUCxRX7xCBbg`Ftti?(_=)4u(ETRGmI&(Hc7iKg z<3lF@x(G^lWvpX${oGX3U7v2wIqs^}B`$Ea@OAo~Etm>ETseykU3Q@!M8Yar8n?UAKYw zcFsTxpA6u1p|g0`i@{Xj$T3fCuGJA#-6=eRA%9Zh8zpYjRF-O4tGq|M-|u>QQa+e;Uo-mS>bx%NtAZl(wF-v$MfB@ zpicvPnftT8n$n-@({jBA&iNX->3ho(lxF2Uvn!}H6+EQ-?!!0ZI$MRZJbG>dlcc0$ zF@+?4nTPibEhT-zOG~!IXl}cvw498qC=Y*c>*<+LQTCoHA;Il->an8@hcCJ7Zm7>^=(4%)zi65%bu@zd`OY*W5^kD;-|N$~@LQ(- zt(4TWeF@szITgJgo`de__h{x{%t5UW2asQ){vG2<0Bw1nQby18aQ3q`8|fj zxk;tz)m0xFrSDJg7gcD&lmuSX)5FfOt)*$5JG}l}G`pv@lhYTU@`QTNF??`Pkv-S8nAF7{=$qtz&JN;;64x%1;&KjQs&rQeq^{LjDsT^0) z!wZshlk-0o^ViMMyrg|^X)u2kf34rv6P0F=^!@8MZZd1D+4;7cjQ(W~ckYvcY9Fs7 z9Y1dNw0J(4(qEgya|%y$JN5nX9s0-}|Lb#C+1(NJM4NTx-(+g)`E_n{R@#^Kd8EEpnrn2K{9?$IugY~m-0k`q5b2~4t@AN+2J5w6GPWe@ zl3v%co}1I>yU@b2o1AH;GEbQkDg9#3$Mx(wpKCT9BDd4U$F*$~=GnI*N=lb)692x@ zpJuZeAREf>b&pkJZ|83*DVD!?+&(p!&K+FD=TAO~pQz90#pa}*!_P;?-(ML)?IRAE zyf@RuFWaK~p=VyI-fCZ5z{fzEr(Y5HT6SEVui984wl$^x^=K?Sgp19_7Bm6VPI1kS zJ>@lJHv8jO^2-zbWs83vGr9IgkAIobGJ0cvGJ^ZMjPfOZXGy^Yc@Z8*2v)kTVWZGW1%SQ)>O3mcaroi$&ys>dNxx9Uu zS+mTKdrci6UGgn9zxhnm`z804wC<7S$RA0lV6B!iBkG!~>yv@hs?`#nxNU;t{lzHS zSuLBV&b1#NKHHbVFS*U0Wwm(fv3m4vX*N0#-jB0J2U0}k%CyKcnJ}%YXW=y(b+P-_o;dg4u&Ks9Z?BY`#*}a*h$@tJT{aTa1U+XNFqaT z+*y)sK4NPB^Uyq8&{(1x7Brhy44_ow7joUYwVnBoL{Y^~MLknu9sI0vUmEi1FVm=b zH2=)5-}OAE-$7rqgAY0aX~$*#J#Nm!{JEVU%}G!6dH7kL_~&!8XK2y#6P%? zl#yXG(oo7FH#h}_$kIQvQ2wcXxO-e(X;?cOb-R(@EHBbf?u_xF@;wtw)XA!Hc}GjL z^ORnrbL@2fv}%Vl|K2E?`mT)U;QB#4{&XKIy5SF#zlxh>XQbsYcg=&kspa0k#p#=*qFBG2l1hIgeQ{?F`urk5 zCR}`OzMKdky_7gL&h~+)?tI6qh7slcvyYuql}&rJJ<82cYWyBHGNI>OG%{I zzPeOBO`Iw4M;^(RCQyG@ea}pn(sHPUejjr9H`64siZs&qr`$h2)b&X-8uaJ^hc%Aj zd^1{6@;Qh2YTPNl{C8LKJ6D$5zx#)C#dfBu3p$$B*KhL({Ve*m=Uwx3-!-njcfLtH zJCF|c_T^~3|Kh$_eLkh{x-a_f;dTA`QpPX8m>nh4$t71ks{d||*`* zxq2ig_4w&r{*W2CxW1Iuy&-caT={%o9b1gEnU+eee{I`4h{Fl|$?06Jiy|&8T zEX>0fzV@Y#ll9)=cd|*lTlHx8)vCtXzp`9e5J)4VBFxRo4dqg2KbmlKl3CfQtqdFe z-29e)wh3?1mlpjzz@J{<@T40tK(A%LoEN-L!-11W)21!UJp;>R;~@?D(~QvHO@n{3 zaJQbVs7Hp>R6bd5UZ~ebJGWslg{8~w8T7B8Yw@Kh+BmV1yGqN0T(@@wo&3yFa#=E@@1xD@iL#_B0R$qetj1M|v_HT7w0m+bLdhLBW!6G+>} za9rzn{cP6152a5SA9rFyxNIt`{-in=STR&YkRY8Z;$r2ptqmSIquQM7Tee!?P{@I&DzppTbi|>$@a6+ zekR+`M*Ep;KO605y=IiRpN;l2*?ujrKFyem2_AWc%4DOJw`mXg?d<&qn*%W&7Ew z{mizXS^HURKeP6;*nVd1XR-av+RtM9nYEwE_A_ZelkI2HekR+`r2R~`pGo_fY(E?A zXJh->Xg@n`KRdOb+4eJQKeO#;)_xY-&#e6{wx3!1S!_Rx_A}XjChcdk{Y=`=Wc!)4 zpUL(!X+M+gXVQK)wx5mmv(xsoQ~Q~1KeP5T+kR&4XR-Y(+RtM9S+t+U_Ooa|lkI2H zekR+`r2R~`pGo_fY(MMovh+H8|LbScem1tBjrOzC_Ony_nQcF__A}dlX6bqnv>1lA~YtwPsqKgVN@Lf0w`)*!GJ zp=%PZIlAtE^#@&t;7r{!I8FaltKJ*?Jg0%%*V-W4m%?PPOHlf_zb79CIX+~w7Tqq&u|)RAMhDY zW4!}D!)dH>z-Ks(bq)9or?IX9pW(E3T|?J5;561X;4_@Yx(0lP(^%Jl&u|*+8t@rT zV_gG2!)dH*z-Ks(bq)9or?IX9pW!stHQ+Ox#<~W4hSONrfX{Fm>l*MGPGemIKEr9O zYrtnXjdcz945zWK0iWSC)-~WWoW{Bae1_9l*MQG(8tWSH8BSwe13trPtZTq$IE{4; z_zb79t^uFnG}blXGn~e{27HFoSl58ha2o3x@EJ~HT?0PDX{>9&XE=>@4fqVFv91B1 z;WXAY;4_@Yx(0lP(^%Jl&u|*+8t@rTV_gG2!)dH*z-Ks(bq)9or?IX9pW!stHQ+Ox z#<~W4hSONrfX{Fm>l*MGPGemIKEr9OYrtnXjdcz945zWK0iWSC)-~WWoW{Bae1_9l z*MQG(8tWSH8BSwe13trPtZTq$IE{4;_zb79t^uFnG}blXGn~e{27HFoSl58ha2o3x z@EJ~HT?0PDX{>9&XE=>@4fqVFv95vl2To&M13trPtZTq$IE{4;_zb79t^uFnG}blX zGn~e{27HFoSl58ha2o3x@EJ~HT?0PDX{>9&XE=>@4fqVFv91B1;WXAY;4_@Yx(0lP z(^%Jl&u|*+8t@rTV_gG2!)dH*z-Ks(bq)9or?IXvLDx6nG}blXGn~e{27HFoSl58h za2o3x@EJ~HT?0PDX{>9&=cnrQ3|-gwNBxWi<=*Hwlv(A254@as)rF7)jkq?lP4xW;d@Y^rz6wdcE?K zm4nw_>QA*UEjQze<}y=n52DqcU5)H9d;SNLW|5dy{_-IBr8nkUl*GnGyBr$=y9g-emjcL@f8)n=q{l06uCY0s&WfSoJ zq8YNaDSeMiFueA%N%wDa8hQ4BdH3$B84=%#2F%=LUSGdq)+P(3N+nmAz@XbE?d%RT zbMH9w;@Ulv^k-+PFD6~rhqOwktqI_05cCQg_zjw-2^Jg~lyV;nM#65BcZ^%yR zHWs4tCvTXkTMAL9$ih@T-4#=HaAC^1uL$LSmSCzKC_)3His^L#4w&<^iqVhwKpMYy zm&tcJkm|J2>l-#(VLJ6LMFWbLr4m=hnX-oJ_mLA9QyCWpRW$4>{9`I3fu=<9Wia?w=%UgHscz2~M9 zZD^*~+|k#&KWBB?G&%#NsJp@x&Q^<#UCKmF&WzJ*xYwoM^JS$=<(wv5e``M|F&pK0 ze9GnfZzCGFH9JjN_Q*Z%N@Maox?slX{(Y0RG3~p5(FE)M9g?XD-JW*YRESS7DR(!e zq!+H5ySjhZy=zWSciu2LXYDeVj_5qHbaAg~q3TY8vf7<%&JpmAaLAX3}?gJ8|r<3^!~)r9e0^7jl0nY=VFsG*$Q(#p(mx>z0_n_ zFwXcd>`QNRuP~pkI`l8R{pgC{Dl>5MDOcAL1L?}mRc5T7Q?b ztZV&gTSy*rDq@@|e{e80-;&SF)$81km^+-_Z1gu-YMpXD`)w2*i4HJB&p&i`iyuv= z+UAXcQLyUwb<1Pm5_}5#_i+p?f=Ml!=7@zqa46sX%CRs9_AIHGAZn+jiB#KIOB zlO~d4;R#%6*XUae41pyDW()~}8}LJYWH<>g5Bg;0A^Lh#tC8v;!$Y|E&$+A|qOUi3 z&OW5xG2DZHY8=Bln5V8We1mgp8N)Q#rhYLTgJ)_M!!8)6PBFZKTj~_UE4ZajF}#9X z>J-B(xTQ`pynJ-B(xOHRg zW&WgI^;fsjzrV_Z^mtCGZmClYui#dZpxfM1y;`MisZ$KE;FdbY@Ct6JQ!MHg+)}3) zUcoJOO5hdTQl|u7!7X)4;1%3brvzTXty106$!I-)L)9&HO5hdTQl|u7!L1DCvq}~9 zs+qc_P6@n%Tk4d+E4Zaj3A}<^>Xg7MxTQ`Byn=>Xtes@Ct6J zQv$ExmO3Ty3T~-W0Xg7MxRsXtes@Ct6JQv$ExmO3Ty3T~-W04;FdZi@Ct6JQv$ExmO3Ty3U2ibiWDF9>OXZ$of3Ejx6~4;FdZi@alZSZ!zkWz^kj%h6JfoV!MvD<;1oet5ah8jnyfES8z+665DC4 zPKj+ZR;R@F7^_oa8;sQ{vE9Y$l-Sl{bxLesu{tHTuUMTD+gGekiR~*^r^NOZt5ah8 ziq$ExeZ}gO*uG+QN^D=TIwiKRSe+8vSFBEn?JL&465Cg-eI>T9Se+8vSFC*{wy#*7 z65Cg-PKoU+R;T2z|MeBCQ)2sy)hVx6a7&%?dIh)CDX&*>OP%t11-H~GuUBwOo$`7G zx6~=GS8%Iimj?2qjMJo4w-W9(l2PiFL)}uRyk5aAb;|1%+)}5!UcoJO%Ig)}Qm4FL z!L4NOKnYi`3aDER+LV%3>Qz^DOP%t11-H~GuUBx(H&;dJu3nWXg?jxTQ{cy@Ffnl-Db`rA~Rhf?Mj8*DJWCPRXT5-o6s;E1k8y zeI@D?+rAQYifv!X;c1sSM7>IolHM4e*WSE5d_ z?JH5I*!GpEQ*8T6)G4-oC2y-_;)&{2kh=Bkb|&7SUQJQA)G4-oCF&I0z7lnc;T7ES z+nJ-_&Vs(n(72LucX#0vW2im@3%z?JA7;~WQE54Ci?@3iUls7leYl@T| zNefej^4)AX<8tYBlMNmG~j%9KPOQ@BnWik#*v zCf}5}HIu5*{!@je-L-eFRqt!i=lH@B7&svA=*03=q`CfG{OuiA{=*e$c8$ssQr!`k zyk=1fzFSpVyDqq1gcql67plr{OCQF*SXY2@U#}^DR7!A7)9+5aJ5^Iwp35B5wMlWh zo0KGouDWiGFF`rq8`;~<5i)#meY#yCmyE8N+#D{{h>{n{C7rVbI@;*GI(zhL{_|hG z$9(NURG{~2uAO~r$cmehwEEXj?)-I`xzM;XEq-u{uhfk;f30dr&Gz}qxzv+QVC4|X z@w~9yEi}+fJfUl(zgCurpiZXiTYp;irK(Kr(A1l@dt0<<|V$=&=d z&C%`EsaCUG(sleelit54(Z<#6vvRRHv%eFa-yF){);}=+^)TL4Y}b}Po?Fe8%ilH$b?Q^WbD_MmXcl_&$VG!H zpXQY*e5uao($x5guf!k!W>(bAPkV9|k-R@1nY9HoQPSm=CBvr+Cj5RX`dp}*GVrqd3ZAiEgtJjr2>k`lt5oP@!`APyS=i!9Pr&d zq}Ede}DYZzm~*(er)=-d}CYxbTQ6VAQ0X+UUFo71YrdPWk9}ppmeTZj|6{X4U#}-=e#$ZT zqDif&&P5!y~;mSii$&$wgmrx6{kk#QkOF z>i+fQD?|Bi{$a3xyYKd=`=Lv9U8tHAS(1%*K3`~N>i&H^I+Naqc$qn>`*;1y9JFLg zta-2dH(7jT`uO!9)A-72Zo8>1rJJ+d{2m_44G!z|Y%0w+&bz1hRq@Vr+;@tZ{kN~= z8`6-nyc=X5JS!~IGwS_zS9dnidOVlhT7tG!X=b+Q@f>l-pXR4-U>51|yl{9Pirv@T zOxEK$=1T#p`g)XU(>#}a&QXJUHT%sB+P<1!x9CYfCgd|u3+Z+<45QMuvzo74PI1ty zp47!RwRybQSCUL`NfVZTbWKzfVg^*x-zndBRZs`+4=zuGeJ;7`=(+zWq$r&WO>p6S zuc+sJbc;)_lX{Lf>7@59|N7N+uwpLhxw$?iIC7Z3C#>dGjrvn`hMXZV3RcyBHzeeg zy3|*Fnwxt{2rPn0LkkZGfj@AlUNuJu%z-@{mOKoGGw>#QN#-Ef0%I!la>T(CxKcT5 zU_1%9b|Y27u7xw41|U1od@p0zxu8V9@5wQrH0qn`ZmCo3^-A4Rrx;$rEp>|F72Hy%7+%3Gb&BB?+)}3)UcoJOis2R9Ql}VR z!7X))aXiBJ;Prg8ze`}~)F@GHAnR@;nSGUwDhF5S) zof3Ejx6~_QW`8RAsjs)Px}{DD zyn_=P6@n%Tk4d+E4Zaj3A}<^>Xg7MxTQ`B_AlI0 zr*vJYn!qh}O5hdTQl|u7!7X)4;1%3brvzTXEpXg7MxTQ`B zyn`5{YwFvrN{F@bxWNRcm=oADS=mTOPvyU1-H~Gfmd+r z)3lb7s9v>Kx3&+cCbRYY9jb1rQv$ExmO3Ty3T{TZ_6cy&tPRXue}of6x1a7t{;@$Ho@Wtw_bQ{7Uh#5S8p zof6w=9(79K72Hy%#P*m+of6w%9(79K72Hy%#I_ck65CgBN^D=TIwiKRSe+8vS3K%O z30b83x2w9PPKoU+PBAi%EY$s*MBP%S#P$`dQ)2rHPKoU+I3>2P;FQ?DVs%PvU$HtR zwy#*7@*dA{OPvziSFBEn?JHKN#P$`dQv$ExmO3T2ui%u|E4ZajdA)*L>Xg{NV(lxj zeZ}gO*uG+QN^D=TIwkN5ZmClOui%zCCAP2Nl-Db`rA~Rhf?Mj8*DJWCPIXg?jxTQ{cy@Ffnl-Db`H9uT`52IeCSGUwDuUBwOo$`7Gx6~=GS8z+6 zV%t}6ifv!PDYktj&QwR)r*Jhnpl+#CZ2JmMvF$6-zGB-~qE30ef?Mho+rAQYifv!X z@P3DRp?bAX-MSU`7cW+?_NrUzl=t%pZmCncF68YiIK{TF;1t`wf>Uh!O4KQ~eI@D? z+rAR*E4FXgU!6`b&t`eI@D?+rAR*t2o(Ds#g4zzs*rwqJ;TbKiFUwP+1+gIK>(Ds#g4zztGm;-HJ3FbiCSAsdv z_LX-Iw0-5B18rY<=Rn(6-Z{|rm3I!bedV14ZC`okK+JB( zbuRQ^4zzvc!5nD&%7Zx&=P%sC9BBK>gE`Rll?QX6?JJ(@--jw4Jir0^Tdw(E^*31S z5AeTDbH*1pCCPWJQ%Wa2?^;y5H2pHbDQ#y?iub8;#=Jb6)wWyPcn|Wi`+0jFIu9BI6EC z+T%*{b*zjz*WTG{_usBvU&cz#W?AC;A8qUMTN@>PBa=I0I=6SdUK1sos~wC@KH;^q z!-FU(HEgcqM&m?hOMM)@YD(<4OWmAfK1a!}Z0YtSNybUu z*NzH~ge>&`|JSp382$bB0Ea$_Hp?Csrp$|-^5sc$b8X&HbN*6q=~MZ;>%_L+=9~Vl zv03p&u52^XnI84WNR3++UB}|Kxz_4)E}D4DxpiGrR~>zx@4roQ)>!=58L7|5l_j$? z?b!~_@%kJthD~<7FWQ36$L6LP+nw_D*mBc4Nt`Kux3>%}>M#$lwKFtt zxFqlLkL&h~6y|4xG17nTWY_OI*1G81Sn2SzkZXO98m=ha*35Z_oq1DUbB@(*`L%EB z+}*B;bFyyh^Qq&G)aQuegAQ`C&%SiJXId)BPMPxgg!wb&W^>|6Zz=xwK$GukZR5XS zxa_@|-sHLT#+AL%7}*%G&2{GV0@p^}*5=_YU1evMcWuyZ9eVV`*{9uM=Sdc#cFr@iG(=ti@$ZzfY;UsvA$ zYMNU)ce)DcwvKLi<=RrLt!tZZ>*~PyuGx2!x;E;zo*pdgia9mc`?><|A8;O>;pg3! z_*8KYd3?>(&3=%_t!PetN3J%Pbbp!T=jKZpI5JO9ejJjIZ?OOAxU-Dy|bO2rr`xJ;9yw1PUE-Li+X|T- z>YSM*hxsG!03UhNn_{Njb9K@4`ogmkln^z|_4kS1lJsb4SV>Aj_be|L1!Z5=xM%K7bQVMmm1%ULws zDNS-ZU?T`y4k$w*_O=3x*r;L%m?Q0Y4s9`tG@?Zd_MC zY7}4OfgA9n_^pZ_xB)-Z3pd=jtbXvfNp84tO#R4`C9@lDj8H#%g-wo!8}LKDV7LK4 z)C-0i@I$>|xB)-Z3lH3YAKm6o_9WHg@Q(VSUU=XJ{AfMzup4f`kKfz1b;FHj>c=0a zj>p3d_@Q11+<+hI1;Y*apIK6M_^~Wtn+I;dk2%9zdf*29P%qqY1AhFB zoaLUb+ghc5Ea{cp4L9P|5A{Oe2K-Pj7;eB1^@8CB{7^3#Zom)q!UH$pNB-C-58Qws z+aq&%;Kp|K<9vzDZnyzIp6B)VZVP^>7Xmlnhk7A!1AeF%3^(A1dckl5eyA57xB)*- z44m(Q8}LKD@W72t>W6yahKKN@rB4;_w%)2A>V?1!_@Q11+<+h3cgE=Vt$WKQ^`l$o zP@b#%>$LizUNGE%AL@k%ZoE-H)C&*X_y|9;U2($=_@Q37r|NsSNBvMQ1a82O=BBW8 z)#I?p^>#+RV7LK4)C-0i@I$>|xB)-Z3lH3Qu70Q&9=HKN)C)J<2+W<^d3IK)8*UUh zw#1=c2;6`l>V?1!`0?{p0`E~buB#vF1;dSJ>W6y4aATMHp!Zn$wVNfC#7A#fvW)0__VLg2=)>W6y4aO0Qp>c^fT3^#_IJRPE5Fx*%+ zE;2;>!2>tqTW1MTFFbIA8yyT*FWhjW%a$p@+6Hd8@o?ALVD&<5^RRj$wsSn{1;Y*a zp<8@W72XZ*RG^H#~3yerRua-ME!Gvs=Az!;Q@9hk7Bl zH@MN*+!C$FVWRq>UU=PrAL<3$-tedw3^(A1df|1Wy85ABc;E*7(BAO60YB6WH{7VA zeyA5-H{geQA+|SIz3{pLKhz7a8}LKD@VWs%)C;d0@I$@uz>V4JhxUf|{5qw6Xm5DW zuW2cubb-q0CpN-W%1r+!@jb8Rf#fFGEVY;SnyBHJ6@S;+PVWB#$d z!I*h$Z!qQ@xB)*Z>1+cx;D`2x!}f+-dn06wZVP^3KC!*wolk6Uc;^$$DLSukEuBx2 zsPi+_5B0)jdxJ5bzzz7JUO3?f{79|y33>y5Xm2>+#+ox7+?Y>nZ@>!!H{b{66Wbe% z`NZ}Hs~0ZY8;tqH_J&8jaKeol>W6yaom1e4_J#v))Kxz)pV;2;&L_4vyz`0e4aR(8 zdxJ5b*xvA{7v4GLfck;?1a4$iKQN!*a~XbUZ#dB#@B{OS?G5jIVta!zpV;1D%qO-t z81sqk4G-oM^hOHxLwmzJr@#;O!aJwH5A6-_wo0fU>VQdr^9f$pMD;_x@NR3K`hoex_6B1U1%1idjt{lI)ORo}yRXK%z~KC!*Qm`~sa{J?x-dxJ5b zc->GxFrUDUlj;ZN6Sx6CFrUB;hx&o}1if)l{ZKEQaHEU*f%(Mt24g<4z2U)pf;mO! z6%XbUxB)*fpTG_Hf%yb(xQqVkRxey|BTD_id;&NA3Hm)A^9kH&s(##^wjku^GyTuA zUvh3wxiI9+;gRwrv60!-V~r!A=4jbgri{6B^mj*u7mZq_kN!thB4b5p^TQmBbqbam_^eLU~--uUdEJ8_SGM3LJ+iQCa8IzIG!6b-+S%e|pz8u!^R zQPi+hI`^I4HQY}>MbUv4tK-j&sqYTb|J*-a-nc=BlDc2(e}C%Frg1bY~Gjr>P(Yz^7+(n#;W@g_o8j`lVr~I^6CM;7|>KS>`ljO-m zDlj(n&J|lY5E&t#7#>3v`xlaU@I~m8XcVBbGo{gaD zoey!UWNRGNe~+LC$JfNg_%@}drL)nIu$}STvJUO|J%DB&Ug3V!u@3z;F@SQdae3~Y ztWBA21<=x~9?$6MH7UAjL7LR01Wz1l=+2)-XzZz396zxf-JM#J?xwuVPj~3G#q(Ft z>(gWq>V4L{|0S3{xr@onHMLBN&mmM|YFXJ=zPocu(-3+XA#p2D_NQZ4x0%cNFT@}4 z)9VK`Jzxs;ce_6)i=^L+9x%^acK68m2df9qB1i6?AnAIi4!uvs*(8MnyWol<>1 zX?nifp?`yHL+R^WF|Ed@l8*y}D5Ucvv#Ds1T|#D zdaZC~@AuC9;eR@AcGMW!e_F3IbNFuj@oJ-~Yl|}G^RScdCFMqwt85u_rPMHw-=R_T z>Ax~&R{AfV;(;US=8N*?@{@4hUULY=a*+9PXFvOY>Pa(wYMGl4vPqk%b?MF7RwnRp zL#ekTJ;i+pH_KxBNxvr3jl}mf{eB%GqbF^5HXG30R2_OI?!&Vv%ANI+yXT1q@qP99 zYWB5TklB~uZuM^zWuCq~=(owEJS`?gQMLiCgNmH|;kl7)EH#~LVty>`#djNzppR9? z$F^;Fgxw(#^nQBExGe5m^5UA`2P=X( zIb&PAW7#z?uD~T1RXkWycI;Xc|EhHzsb^Uj=~{$Hy0$svsy;bmCY0zSQ!12=pLaIVv{U!n z{!+yK@<*b{+$>B=rb%$O2~IS728K!Xf=xW`e_oji8N15Q5wV^=*&gft8p0*^aSrzB zdeJ;h+e7-E2c5e(zH(%A=g^&ny2CWxM@%?X?ACsalh8@mbLCHt`@PTRj)+PuDubmq5dKB=I6usHGf3Nm<1C; za;0f1mCI$5a(;6h$y(Ks2fqhMvh|~#c}Lcfc0B`R^MahN-7V_Kf9(RK;ngLsmZfS- z%2xq$xrDFrjHxcYt`w9azxOt2lLSlQ)Wu|9iCEL4aUrRezKmq~nnJJ7m&oVSR+h+( zh3RL}fqbifh{Sg(OI3@AJ6ZP-`H*T}NQ_?p@996=cu&ohj#6nOC9K&2t{y$h>CO=; zL!a&EvH>Mr%U(yw>Am|o{`wZz!%Pt}s>cD=Yx$ZTq`g}5Fb52pY=(9Y6W4>2yzl*W zvo=#B*)!}aCw3@AK8^gvUGORY*`ygA8@quWHQ#aZS6wLVMqZEJ%apU+nIFXyRM`kElqZm=5%?syI!^!B~>bx;c@Q# zrjXAFS+TAh7b`X0yzekj_?*hu{&@4YbbSgWiuSo-Mq~|hW;y@%hZi8weM86{&c+BG_yFcn>pEK zC{;)|({#M>Ew*yKo>b?K+q_uZ+3cztNp(u@Hz!goiTfJefi^6Cq1UBvV;W}Dd-9gm zd!qjFI&Ofw39X7rtJh&@UFS30Y_=X7uUubf8QS-7JthzEZnflq|X~y=xU{cIC=TLmtXOE|BmNojnb5vT>HPuV={#;j@pY)3R`rc0T$IO(b zQ|LR>VvSy3BIyXTa(|*(_p~X^%fH?HRp+(w{kIN1y?E2ytM$}WXz!xMami^+hTCTJ zx^k3ecTT$T{Je=PUx2dpD@Fav95osKOi$Bt7`hq4CPlSG6Fa>jbxOk~OZMC5flq7N z?W@-aTUVL}UO($9)Hs9+OL6+We@)Z0a0m@cR*2HpnrY&82hp+jdFgh*U#8pqN>s2; zcIq(Vn%UqfM+1LJPf?S9n19xnpojy>snDQI)GDknrE2lQylh{9rYHE*@s=mexs;`; zN_BsFzM0L-VZk(TOja7#@_;${xTCA*x(Ir^Al^jnyY9-bkK55+^Rh@jGoo|^73s9Y zJo&eQxtO*uO^?}ZPX95=v@h42PE`NHlpXn-DY2~wy=b@0jCsA?PAEym+tjS36we&8qJQzG=ViF(x4yJ`@D^S&C5)@Z^r2lx*Rp5MbbfqTTetr*j*DHx zhwAISKf~v7pZqagbx%0WnmLi14?4+}_J`4q#XY#f`pX>NO&fb{72bR|hWFO$L#On5 zH@D~B_uOn3LYY1l6=@dAT^fYYkX`xZO7se@_cMt87?w>s_CC(dvgzOO_d( zr`#DR&atIvOY=_hV|WHWI_8~OowK#1TRM}APJ3q#7pyO4@d z|C*%BO39mvIb?2$`)2pEyfUe9N$IursyWp#ox~S&NQ({$Cb{o7-Vswr#%(xa<_x&a z6FxMRU*GF}I_jS0nfsec;eA(3+R&lW;cY%X)3>|vDI6)y5|?qNnC_-XI{ofn;k$gn zKis@H(?}LSPA5w@bud5t9rAT{QEC0Cxf#`>xO{IOBGV%3n851U#s6A88Q;!n-gWYk z*h{VCuP+tMm~H>^qt~6}_~LTr$&i~op=FpP-PY8MN*FCSS{2|f&y&YY?l4H+++W3W zc5MkhvsHhm9Qc@9U5E$?oz`4k%p`@290<{uCedq3NRHj_L#_mrlxf|KBrBB6ai(KV z@r`LDKEZ zR~^-zleLPJFR7OC$rRl=q(f)v+T#viEE~qZmTW9}cBGS9!$P_LP=}O^Dk^tRH0G0w zic6WUA+r8kb-vd-yHx(Kp5$m6#QXaC$kD`BQl?;euK(M=d?ra}>EkKOotNL@Ly6(? zI)6saHoB86ujj`f#=qks6IzR7#B}~x_Z|Oow7z7%bc|~>e8a=HIpv`5Pu_9w8Ru+M zTE5lDA&;Z)^17+{WOK2Sa_Gcm-Wiup`Zje)CcUofk`dqd!oxbUs@q|HH{v!|NYP9# z{CSX%T)e=O%e0lBU3PHR3Htx1_9f5z&=Bb}wV1T2(VVL`43SL({H0;!Ql614SjIQa zF30X1;htG5%dOwiN~cw?xYwfc;!{7F6yKdjrumhUx+7k(tjndp`z$KYreEP1VMQfn zvw||@pqmFLD=XJb0SQ_DH@^=Fkj{mf&lKYo(RBC+85v`BU zEJ*99_S;oQKYiSNWY&;U>Dci=AE)Z}H7;G6I*tYUznvuhp2Uzfo<+&@?@8PAd)woq zT(j0iNUv1!reXc3t})Xh;X656)@_F(O^Z7;#^K)Sz>HA`{c~oT%pW$QC9W2mfl8b-ZkvC3evZIER=6 zsbY=E)<#ZSrR{3HuFB)oW=)%S{B&?znnEK@sdVr7;?ky6cl>q}mH7={ z9aM*MWckOuT>FgQyPY&DAvr~Uy~Dd6m!&rQb5c`ti92@=pt}=GQTo}(`C&pjx?S7Q z;f(wF#>h8j__K!8dK+`)KQEg;flX*s%tdanbtuK%&1SZ{y7LmB{xq}RG}EnEcYaV* zf2(!IZIY(!%JJ` z$(7mlAtxo_{gqGbaca?-S0JkF|4)D%Ds1Td`3Td5ip#9Ts`M&%TwJb94dr2-jCA`-N4-{D3(0&h16A>)iA!0f ztE^ve*~I=9YCa$At>0TYXCBsx4XOXBm)!U0@60~-G<)g|kf`*3n$S`c9e;P~E}g%` znAahb%&K$!BwvBuCPVRCj)#@o$go3COkCL|=1uc%5mQYT{43dYyG-?3zOg#2Ct$ewAtesDLbMT$>IhmI&F?;1AEZ`;BPW;p{YRX+G{z zwH2{%YBEQ*@Ke2RZPC$s-{mK%Byde@=Uw^8`N zlzL><%67)bXVmp(+4Q#qbJ^R~)9W@}^6EyDZ<&RcOXU@7^v1R)N>gudSeWG4k8_B&*{qh~YkILf!py!K)rV&`)^GHfoxG~zn`+94k$UaoUCF)Ie5U&39ik^ysu_&1U#f)ugLI$7 z{{?q`-KZwy8K|!(W;NaaTc=Xz9H8?wYh?Dlvr?_q{q^`d1I*XIeT2uNAAV-b2vecX z`zlqP{`gsOz08i}1u9Rk0lGl#rl!V#cU76vgLKRHpESRATBi0_9H_sUQZ#mVW!GS3 zAM95x=frCNSSN8v`-yt&{46S8&04Y1CCBT@6`QKb#dgQ;E*hir)P70*lQ*~85scKM znl)ALR;{T{PZ+7s9LuiqEqGS79X?ubxUei%?2iOB;=8f>Udw3ge4fJU_NFAgs>Gax zozLu#P1!I(cMMXT3W9J|`qnACau>tcB-sOg&)ni-$q`|8wRtfGmp zn6>Hq>IZ+EQ*Hh(YTD)OriZ?lN|*k1crdDZ2i>Vb8vXrqEffF#qP;G?=a%{@b;VfY zx4Y`f|9zvzmAjIde5aqj(+K}2qyOpT6IX`o>F=~x->1!L%FZ37``~`}bNdIHr%H~{ zu@5S!S?x;L8Lue4;IAoa|8F^Mhxh8~ms9`tzBF%J5*gisIfO#*{q^YNLySVYM#b4oKsu>l0CJ4<#a!@ zsYOd2OL;(5dvmYZa;lX+eC-p}Z+sei;g^OwaZfju8uv}A7rd_;9bTq>d%lrf*Cv%d zJTHS@p65y1W8%N+&{v6if996O?h@9d@@Tn}1kk`Kq&i z^R?&k{qxSL4!c|EY$e_>by`Jr;*6^LyB>$kQz^6K`x$1?;U)R)-9F6ZEaP(DGq(vS*5~I z-_}!qxOyta_e@^ekQ``j$mzutMZ@v|4~hxx~<$k|s^%08p)juIsTe4A`FbnXDV z;PSsULw)k=KL<3i52v22`OA%_I&E+Ko7)bHYp%%LM+ZlLFy-;K#ifzqdd|=9o587a zZ);s@gzh@%Ez@Jvd)tE61N6)b)*POAbX)n_ZS)_DGTNKVHf?)zb8-E~ow{~Xzq(s? zWVoz`mV3!AsMWS+#p5H?#tr>!-O&q8CEQPl9_VQ+o|$AaR?nl;;CqgwOBiShEpDWD z>KwM!_;%)x6+Ly02YXCxSp!q7#9;kAzNh88OB$JWRR`;_$ z^w(sQ)Vi5Ik|l*TALILy-YcXhEv;$ST$p8k{QSH+yygX4v-fDz_*e^7y*=L*U?I~V2<6cW?pG&H$*UZ>&8V}8356;E+symp^ zF4&ySK85#{ofxidB$Cn=cq+H*(=fB`j$MD-?pM^mFJmuDu4kL(Kd;W*4cq(26YP@- zg>|k;#$+E|(H87kQ#WqV&m4KagnjaSExo2mDl-)COB~jqx}KbFS1{*JMVmRHxZa;5 zYJU8vw!QLt8l5BD(;WV}g-r;4P`Sr_YnlvbW&8D8qV~2*Z4Z}jWS0laW1BBdHC--` zuqiecQ^$gZ=9Qd-?GgK-D)swhvtnUiyYTWEwWo49lW#*edvR?_-Qdk>!KS77c?vD? zzrQ$J;@h9Mx9`-%_2?7jVwo>@vERk^s!{iEC;t6oU)#6P2WoM*Z<4<`JIvPmu%~MF zdOCAH%P4!N_fx9ikzS^HqY?I_1B()u*BKc5a>_)T^h4j+n44`97tX?7w>6}i+$H!Jnm`Y&TQCXcntCUsJ?`ejhXMvk(f;w{zoUn{FYACI(E>lanuovo!h7aDCR zx7r^|s#{Z?={wf8T-G$U?#IGv)x;#bqT&3+->Uxj&A4Umgrj3|p)6ZX9TP?9XEs z;CsyWyMezC%UsXcy-U=%&I9brXF8dW-&v?Cm&dPrb~o=Ge@}fdWq^HiSsPPtICjUD z1MQ{t%Ct*$iDeSNptwy<*J$e^DRH=B3&Wz?TVS*Vy|kkv$0+B=PtA3t8?ndRL$({gr&wNr_j4L zG`9bK`B89g_lx*m$RRs@UMW+jM{AvDNgA6&|IbwYv7vqo?}Hqb5$}ubT0w80_Q2Fw zQrQ+QoL-kWal@?2)4&!id0h4Q^s>1VZfe^dOjcX|y=BV%QP(zVvqR-BkkU>ymF&th zzpE4PXSGd^=dfuvX4F|0=dt7V-!NN9oKgc|s*VOhIjPI$NyNk)aq`Q4<m0?Vs~@tYwBav1>N7v@+d6dLA(Qx3H+}l|fol01znIUPzpSIH zD{i~q^Hba(9H_?~sjf0@{oHgrfZg}k+1o;!8=1~)#^_Ve?Tal-^}MM)aje#PA8xx> zwP@m`jkssCEEc>w_*!DB6Zo_Gt*Oavs+UoNZjRMG>Yfgc6i!s#@%{F`%e_AFr3$U| z^R4jS)-IcaO!#?aYeV_;$toecH%oQ>>*ee^^VV-n{!*=U6TJVme4$OTj(-i-QztJq zFCF?ec&_KOct3V6Gi6(TdpYB?`pc1r%|99MnMy}G>EcNb&E$_8CXSw3T<40GwzoR% zF-bL_(m8flvv-T;O3eB}e*IPFMEmN|1?ET;-!Ec2-s75fe5~)oedfPrwRF{Xsr0@z zt4)!C&+E5ZpI7($PBlZ%^wj0DtyisHC~r>P8L1C9Yp#auo1DCV?L__W>BL~#2P4$< zDKF}Nv2!NNse7u&g-xnVyC(L2leGA`@eR!8I<0K2+xOJGl=sa({5{w+FZ`=^%r9U+ zJW$Y{`)Q6Danh#gPaUGK~Ar@8*yI2Ek?Pu={dwY^e(p{jSRNbHw>!|aOL zAE|lAW}3xodfU=nKT~_2K5n*`Y-c|!mCuyJ&uIDmS~uN$UK(?~MFG9>#|HXDmGgml zSYKBjSWurV^Lfx6-$(k{n}I5Gr&O@>>xugM;HHT~maR$r@ZVngVeFVG`2H8M@_P#C z&&oHlO%_E|#`_ah>5U_7IA2cHXT&o>w&-|U_1M!Y-0^v{rOQa0_2G)GTgp7EzNk7* z@0q)0+v+|)DV5k;H%~u4`Oc(rdO-@M3;uU0`KzowbTromwf&ib!Ia?>^?^$5V%Ia~ z4YrRRpfARZ`TfhEk{6Ars`m|h+RnS$A-QUXGpfm@p140hecQ*yny5c7jJ4%%?@GM& zcBR zYUuoFv4a!G;d@0?QRk*+HRt{tqt|>?!fZ%6z}!gar<33Q)U4b#*X-}vNoU{wgZZn* zGBfDKme{ZVGQDt*WqYP)b*ozGZT4S|sfA0s*x~g?s8-lV?+$5Y7ms*L{kEZ)PVQXR zzL@nJb@`uX@N)%EniOmBvqnCN=u`b#>i2)1s%}iIr1oDQtxNt|Sj}!<)Er8gq|45E zKgfh14mEK7Ful8OA@f6MsagL!e$G^nre@XNt)^PxUV3NBK4$3e__yRUyXnKhY%~49 z3AOO)UUpse-rKIu|4+Sr&V&dXl>o1a4FmH@H?>|rsdMV zjBKWx|2{6r+B}z@v8I{sGHP(_=g-y#|E8X(TMu26a5z-TygVmKKT#(|Vy4+M%!D$y zCR48_gu|biF-?c)wiR+EJXpKk^muZBzV=a4&A+nlF-KDL)91_Q503TwS!F(s`;(g{ znEm_itLZCS*nVX{Fn^aWp+Ehvn9ck2Q8V=K1bsSJ7JIAf4)a(4qpD7k4!S_+bYtyI1m+sxTRFYABC^jBZE+GQGSe_5ZdS6?le_no@;x3axoOKS%eKd;LDQqX=- zq`3X4Q97MB`CT)2aU**UKaVj_lhWp&LJjSAb=#}`UTNIhuqD{95Cs;1B27xe9S z?x-otJDZ2wweDK2ux?XfyqR;fq<(+SGkVmDS?2F$CG~S(Kdsw+SxSwcksZ_TsSJktZy4a~-&x$pj+ENdEnAKkS{F&tUUz?yO%|0GdcAY;Le8nz@P&9*Ax$fGMn=1j?dux?|+ic+{trF{m`(7E*2_nrky*W zik5g;mrHl4W}5=|SwO?b+2qgWZvCm@59;lTeQf3y8Iq4(D6Mn7P|Fs+yD0f!tL}Pd zwri&H*z?JH&_sPyjSODtaWVO5$^km*{C1TXTbjJF$y2(+@@o2n(|MC$-F`w1t=dbM zZnbXfy-#baN!`Zj53lV=zK|k?YQJZ^t~o20sodm@D)nu5`)!>RX6})EIyW3%zUawd z`KQn7Yn=+)Qh62yb?)}lgQxE>!x!97?)&3Jog&A)d-`d+Z1&QP7V`g`Wgz0Z@EUYMxykJ}PWa!5ro zb<_R2UQ$=4Pgb_WT~&Hr8(sPOLN&8rme`XWhUuPJK2q;CpJi&T>#g^^JzM4YHQ5w9 z(OrklUX$jm)*Jh#wh!`rW(HhurCT@r(~PbBnrYmtryfvnv&ny^xEZr%q&_gcqv>7v z?JXrvP1N1zrHnoK!gO`-D)yzrXH=FHVLhS&em3%VjrH*kY4jVV>zj60TIpHe{-gTq zduGn~8amB|)2ja3EH-oZr}UJ9cg=3qS|_EQiR-C@rsAA#y4?BUCQr)6=Ea)>^r(Dw zOomzYO!Aa5`ogI{gXAqXG2yp~`pS$C5?>scMU}cRRv-N@lR8z}n zLb_1Ce7Z=TkbeKKj{4w|iDv%T3u;$^y86L)2h5Q|VLhjHF`fBDNqhX-A+`EgR{eLs zsy1`Zqbldi|EQX=np*yI#Q8VWk2QwsPAi{LUq3xvoj*KOU#VYGb-lk|72nWDr&%*2 zwma7$RpIkKdQI7t3CI7J3xDs}Oix(4Ft+DzZawUu=k!#al>7qzy-cLo%!IvzjwFr< zu-_G#k1YFWGQ-j!I3R9cT4P}UCbRde%-VfA&t1qE%JG!J4=rfQ^nzgp04 zI6tFi?E<}Zz5neo6^i9l?F;tPtETNSJNKqdC{uH^Zu&_ZGhul|wQf9C7a!Bc9N&2` zIbZ2E`lVGFb(3-H66Y*vshe!esAnI^ua++=tp{9Fdf6*`V>{=S)qk8-dfVcq+sf3Q zs`Bl?&)iznP`$kR8@1r;K3d&Lsor`wD;`&TfB72a)#{c*^bft?Oel9|l}gdJuYOp2 zhFO>CT{Sp;U45}zUibp#5~%7*)4P7rid^Ui0+a zd8)%dgY?+)*zp(4QYrT~)M<}q)UO|(rPlN>s<))jdfl7NRFkfg)#3p|^vtF0RjWyx zRkK6*_oD^tsJDJgqf;d|(|tdF6+cV4t3LbLw1oL%ZmHb(JMGNFXPAK-POIk@SJP={ znwENeHb&(-)vRdj`$1#Q=x8`W$7w9wCV+iQ+*#`l2Uh3{RyudUfr z_?SAI4c{C3Qbt|&=3ezuERQabN9(hv7N~K1lhlL6A-Z0-UTVw5Wop#%KDtKzG3u1P zt7f)rrW<_qnR+sP6aC_psR<=6=GP7SG|+e7o?*UzK8xP8zl6@YGOyj-;JO-7IG;Xp zv#kBI;IArG#-jS^tcC1reJ`s`@5l7+kN29t7vuZe6lkW0HEL(_+{>uz{aRY@_$Q;D z_;XmN>YGk4EUxw2eRip3zYbOnDh<&Wrw>=fyUbOcPW92rMcz>(_ME`?scEKXy>n3g zRlSnV-fU{ZnE%z#=>}EMp+)$awQHW#iJ`2z>X-O_S+8f&`I1xVi}%agb?Z~M-eR^^`bG~tT{n?6a`jz_`b;t4f^oUPxs^z7% zKJ?F3^?J>h)aV97^!Ps~sz#p=R`Y)Dqrc6xO68inSv}RZncmmsx=NijtL~m-TEcWV zWO}8ndi=~8COhse@9c6zZC;VrCiE_$7gYRJtpuNU@S5K?_maAqrI0Q1Vo`l^Z(2N0 z_u?-sl+N~M9$jKkJ5#SueZ76h4OR4ZM*UXbin_rEdsIg7*>^ULUY)bGdh698y7(tk z)R0Bh)sh4F`DC}YsSjsd9btMnd6qz4U7z{;Gz)Jj3M9@q)g% zd8_J%_j*-mT}_{C{<%s%TGkFZQbsopZB%=26to|mtfHsrnQ3au+&i(?zUrf& z>9kv!w;QXhWt!=qv*PET)|jh)7&<-SmvJL>=bZ1U#L6>Fs}z0p?~^B}0(gCG`>~EL zcD|pw|5;hPd}LMZ8KYDhyuQ}r=R%%-V~#qXVXs-fsEeNY`EpfnOk4B(seZcRgQ03I z{_dx0)26!N#P;gzsMfzMETS7MsG{n=G*sUh@TU4a`=Hp!FZ<{!KYyB^T8Bq((xGy`;Ly+(?{o5=l+{++Lj)sbL0!FcdO;K>!#snGgrPEyN>T&Q4&7e z^~FE2YH-)SH|pzRzh+VQ*Y7bs>-W|*zN@BsA8%t4OODjvtv($ay*{J9+4Xt-Ma5rZ zKU`J1d9~7d*HcB*xRk^6_LkFCigGKriHF6zEp2bwg^ZC-`{$KlTb zxIPf)i~HgCKs+zLhtmUbyEq;mH{x^gI^1o<;o@>Q+K8*g*>H3qt`=wSn)V>J<KjbBYqZ7!^=keES`p!jrdtS4KEwKjbBYqZ7 z!^>9uES`p!t@v3y4KG{qvv?X_HsWXT^r5axV}s_5(o5AS1uq-%vv?X_#`n2srp43n zvK2pzr{QHQeil!|%U1j>o`#pL_*py+FI(}mcp6@|;%D(RyllnK;%RuP@Ujs< zi>C+wFe^6l?a_KczP<`xHsWXTG`wuY&*Eu#*@~aV(;4^Fx39s^Z^6^>vK2pzr{QHQ zeil!|%U1j>o`#pL_*py+FI(}mcp6@|;%D(RyllnK;_2!MV`A6e9)lmU@PdMujrdtS z4KG{qvv?X_w&G{;G`wuZ&*Eu#*@~aV)9^C>9;U1nPs7Vr{4Ab^m+`Xz_8RdtyllnK z;%RuP@Uj&@i>IqD>l|A#eXQQMy|IFqjrdtS4KG{qvv?X_win=M@ie?_#n0kt zc-e}d#nbSz6+ero;bkj+7Ei;=R{SiUhL^4QSv(CdTk*4a8eX>IXYsU6-#pe}$T(f9 zUJV5=8}YMv8eX>IXYn+=Y(Ima#nY`y53?yAPB(AB)9|ttKZ~c~Wh;IbPs7Vr{4Ab^ zm#z3&JPj{f@w0duUbfbewTmU`lr2jsc-e@b#nbSzb-QsQyo`T0 z6yJ@Lu>)J#fvdpN@Uj&@=Y^-?Wh;JOTexO*c-e}d#nbSz6+ero;bkj+7Ei;=R{T5( zo`#pL__+i;UG4Pqu>zNq^!f@pWrsIzhY#Ro>-P2lUbb&=KR4LVt=rEHylmZ09>B}i z?co8uY~Ai1z{}R{+u~*Gc5DMLTenN&&)U*f{4AdSqh9Y=2i(hA(Jm_cbKKA3X?WSX z{n^0F*6q*YW$X55120>*KO1=2y8YR}%hv7B;$`dhXYsOi`?GU*4b^Eh{m#y2M#mjL&i>Kk`xSz$-@N(SG;%Ru@u{9e!Q}Prtk8MBLB);c0j|?q~5dyd3wlcp6@g`&m4lDLED1-(S{lf~Vo- zxSz$-@N(SG;%Rs}?q~5dyd3wlcp6@g`&m4Fr_1-TbLl4N-*3;2!OL+!i>Kk`xSz$- z@N(SG;_11Svf3r^b2@nXV3!-l!p~2^)9|u!`?Fm)_ma5}Kj(+1;pMoW#nbR|+|S}^ zc-gr9*}}`l?a%h)(5taCUys*odXJC6%W*%8r{QJe_Gb$(8@E4Oc-gr9S-foA{w!WL zZhsap8@E4Oc-gr9*}}`Xj*Rcm7G5@Pf41^s+_P}|vz2=mZhyA$ z@;33acp6^b>h@=g{du$7pT)~H-TrLhnar?8C zdmnCpwsMcd?ax;3ak%~2nl;VU{d!|{p0+(>a$m#k&qnTRxc%A6eGRuiV_$ezx&7JR zz$|Ar#$$B< z;jhHxzJ}YMjojC8`?Hn%8g75Kqi|oN8usVR@U-05aQk!ozJ}YMD!px*Kqr@k^35Me>QSo!|l&j?rX^X4czAtFKh9$ zcv|jjxc%9{%Ub*_o|gL>Zhy9NU&HOsR_<%K{n^NU4Yxm=G8QSo!|l(e6z*%}!~UEDo|gL>Zhtm%U&HOsM(%64{n^NU4Yxm=heKNGg4mxwhNtDe zhTETw+}CjXvyuB6Zhto0=Dwjab{noAu8PY3tla(_$bAjBKO4EP5paKw-`8;avyuB6 zZhtm%U&HOsM(%64{W*SL!|l)U`x#- zM%HEB)NK46R_fN-W9N7EP(!DV(YtC)sG0Km7pfty`$zxPJ2rOUI@SEILHf^xoC)_{ zD~;CX;Y}Wkjs${zH zx=-rbu~myos0iK%x-{G|R_@#OYSPRxy2zs0u?hG)vL4~V`t{joVm*EcyHZ^q~H!^{qXlRP8rcsadZL(7W%JQ}1l)s^%9RtD1Zu&E-4*o*->e4(={oV%&|u*7J6 zt6_Ha-UoS9olnN;UbjxhzN#Kp*H0zs$=kNYnvA#?OP6uHzP4~Vx`8diDJM zu{57QqvpLeMt}7%l~UPetB#om>tE_WufENkR=;;#>HIbJs$1Vw(Jentt;Y_yt?-lF z)TCJh^yR{@t5?=lQEN+%)^m$jR=eh;RHyL!4%nAMy_EO2*bAqV^!(S)#9Bp8#0KH_ z{V(A}Y~HB9W4Rk7=?9#I4(tFNnHQ_X)-x>}>Z zRNG-s=s_z}>sq68>hEWES3~giw)o&ZwIg#*wHaUUs_z=BCpVmqJ%``-@7uXmo|lqi zxA65A{x_tuzqm1$2ET9mW%puf^gevw$s|4IQU>)&@04l`zTS6#s;<6%qq)kAuXoen zIjZ~IU8*&{-jX}g>HHm2=+sM7>zDJD*Vm(!REgII=ynrUs+=Qh`-^?$ie!Dj}wiS zi(;Med|uObkQ(wr|5)CI<8-k)PpSRg8^r#=^Lb~+JnH$rwPMBbd|vr!c9ryf+t}4Q zNxI$H5^B~D(_;tG*bOT7S5- zsh*d8Osw$G0ebPFWVK>L(b#)%MZLmfRPPsWBzA!xKAKTe&7Qd@@fw`axk^FxQ|ldx z&%p!F&&sRHy!%t)hj{Hr#+6r%`lgR{!t4Fk-EL|}r?#;#?hMi^TmDZi+OsEC8L#VQ z|CZLJFBMdOwM(u0FK(l&y_GdV93`%*^54vw;wACZ$CWbF6c>q;`u1*FL;NEiO1pZ= zW^smCW{Zni*R4EI8a;&-zAIp#D8#Hvbav12d@R!657CaS*uf$vMR3N?*Z^2W6 z_)5HWZ&C--5xyD#Z^2VWd?nt3r;PYYyai7M;w$kMJQawq#9Q!GAiffB!Bc_wO1uS6 z1>!657CaS*uf$vMlo4Nvx8Nxwz7lW2Q$~Cx-h!u$_)5G5PZ{x*cnh8i#8=`icq$NI ziMQaXKzt?Mf~NxUm3RxDGU6-o7CdFdSK=*r3jcmmY4H|3WyDwFEqKa^uf$vMlo4Nv zx8Nxwz7lW2Q-SzOyai7M;w$kMJQawq#9Q!`5nqY7;3*@%5^up%MtmjSf~TzbO1uS6 z8S#~P3!XCKEAbXQWyDwFEqKa^uf$vMR3N?*Z^2VWd?nt3r;PYYyai7g@s)TBp0eUA z@fJK~#aH4jc*=;c#9Q!`5nqY7;3*@%5^up%MtmjSf~So5O1uS68S#~P3!XCKEAbXQ zWyDwFEqKa`uf$vMloelzx8Nxwz7lW2Q$~FCExZL!8S&K&cnh8~;w$kMJY~dJ;w^Z} zh_A$3@RSi>iMQY>Bfb)E!BbXzCEkLktoTa21y33Am3RxDGUBVR;VpQ|h_A$3@RSi> ziMQY>Bfb)E!Ba+jCEkLkjQC2t1y33Am3RxDvf?Z87CdFeSK=*r%80Kv!CUZ@5nqY7 z;3*@%5^up%MtmjSf~So5>MeK+dyx@eiMQY>BfhEyZ^2VWd?nt3r>yu&yai8L@s)TB zo-%H)3*af^cDVqaGH!oMhNq0%*^=QYvpXGo-%H~3g9W@_NxG%GH$;L;3?zws{o!dZodlPDdYC50G={#zY5?f8Mj{r@RV`;RRB*Jw_gSDlyUo208bgWUj^_~+*jf) zc*?r{%D_|B?NqfU<8M#(;`<0RFL$_a9xh8b`m6hv2 zw_h2#4s`pKk?TOWUm3X$bo-T&>p-_(8MzL0`<0RFK(}9+_;n!nD+5m{w_jPg4s`pK zmFqyaUm3X$bo-TAhwDJMUm3X$bo-T&>p-_(8MzL0`<0RFK(}8Rxej#um67W}w_jPg z4s`pKmFqyaUm3X$bo-T&>p-_(8MzL0`<0RFK(}87avkXQD2==Lis*MV-oGIAa0_A4XTfo{JtavkXQt3a*;-F_9wb)eg?0=W)!`<0RFK(}8R zxej#um67W}w_jPg4s`pKk?TOWUm3X$bo*5x*MV-o3gXv++^+(;4s`ofAlHFzzcO+i z==Lik*MZ_I@s?Z%y8X(^b)eg?j9dr0{VI^_K(}87avkXQt3a*;-F_9wb)eg?0=W)! z`&A&A1Ole!K3^& zkEIMf{zw&)4<6;Oc`SA4@kg4FeDElL&0}dpk3Z6dZ&Y9V3|yA35Ba{5AXR zIF=J5nFAlW+?o6}`uFB%B*!(^QuX<>&yWB96BtPnf6PBayf29Vc7aeKm!v9ZKH|@#PvE~_7$ZrlK7aNFLj^-c zFp{L|^JiZOzf)0+B&quR*%!v|Q4AwVsy=`AMeutR$4HVk&m`fZ_+Kx9|8}uZNtdMR z^XJjx`0tm(NRq10pM44ZZE?91V^sfdvzRi8im^7uR|VI)bL zXOi%f_zWuJcc>7m;*wN-{ybU{zsFM;NmBLsv#*5T<7tc}srvlcSH|yG6(dR7kNKC4 z_to&ZR0%!fl2ql)NBnv8DSRH)F_NU}^Jo7wK93q0NmBLsv#*NJqb5d@RDJ&JtA(nC z5-^ga>hov+3_jyTj3jCEOcJh+&oYM3qDDx$Bvqe3kJiNJp)r!A>houxfX|~AMv_#0 z{_GR+dDO;8lJ;Z%)#7~}d`7WQU6-UPXFlT3qY9sKJ&Yu&`uy2ze6G)8BuUlh&%PEu z*ZLSqQuX<>uZ_>O0Y;Kkeg5q0gzAJEVkAkMXOeJTd_9funbr$6c1fx}e;$1npKBA0 zB&quR+1JPC+7u&6sy=`A4e+@>hmj=h$NcNW`)2q|8-|*@Bvm={5q}H zQ}a+;m!#_R=g}7UdfH(mN!91iz9qh%_83W0_4%`Jg|DXrMv}B2^T+o@!AD1YHP44$ za7n6i<|F<*+8STaix^2#_4%`JgRiX?q)#uN? zJ-+6y7)jFRnIzl+Uu`#hWgSD^U6QKLpGRN7SN;-4l2m>E>|ey!+yf&?sy=`Ao$xjH z#7L6%WBwiE{mb}@JBNC?Bvqe3k9NV=+#4fFsy=`AUGX*d!AO#-&!2raeEoeflBDYM zXWt!k`e7tV)#uOtC2;AFktA)NNy0rqWB|V6o}qy*N!91iqc4YE4h_OclB&<2eJ{`% zjFBW&pFjKF;4%awN!pM3zZ~y}f=ZvzFqfn%XFlT3qkX|;I7X6Geg5qGfy)StB&quR z+4l#Rkr+u*_4%_O07j!QlBDYMXFm`GM`I*On`e^nAW$0vDuY8~U6QKLpGSv)%Q%cA zsrvlc4-E|sC1E5<)#uNC7zmEXNRswr{zK#a1dtmZn&^^L<;+L?d2|HWO~Oc$s?VSO zNU(baBT1@0fA*um?p2H=srvlcj|RKR7)es~`LiDbj#Ds_q|Gx)cr5t726E#6a8Sm$V^qZk~ zU6QKLpGT+TkywC{Bvqe3`x$r)-or?es?VSOOgskfVBbBrWuKjyzU-mk%<^l|75m!v9ZKH|@#pWt!&A4ZZ?eg5nhooKl$M7!x+GPf zKaZ}!E?Ei@mTJ}NRq10pZx|ruHRrJN&7MXt?_;r z9`lW%-7ZO0&V0n5M>pZ|{uU!isy=`AoAG#mhmj;zpFjI8c)a&uBuUlh&weX@@4XmF zQuX<>--c(#K8z%3^Gp&>#4^X5E8l2m>E?2qDka{(hssy=`AKjL|F5hF?3kNKaD_kZG9^Hb=O zOH!3HAMxkWV|dN?&!7Fzcn)2`NRq10pZ#$>!>(c^N!91i{sf*|*D#W#>houR z63?yc7)jFRnIwD)&#D{v3QmV^x+GPfKac)`XV_mDNmBLsv;P&(ty>sLQuX<>KZEDi z-xx{Ke$4;Rcz+wutlvU+T#~As`G`M{p2c(PAB-fa`uy4dj_1~0j3lZ0{Mnzw^Xwi* zl2m>E?Ek>C@L!B1srvlcpU3m>K1PzXc_s;8z%%awo>><||G6YppFfZOiLdh^Mv_#0 z{_HQ|Sr`g4N!91i{xY6_;V_f5AM?MBW0ymb@D)7&qAp2Q&V0n5N3Y`fmmLVhMA=5^Jo7ro}<~qOj7mvv%in$X!bCZRDJ&JAK>|#Bg`ai zo=L+0;dz`h{1DI4TrNq~=g*@d{1tfa_+NwPcC>x|JQ~LT<&i*=s?VQ&BpeCn#YmF& zWB!@qeZFuMANgI9s+{?VKaZverwA9oNRq10pMA=3%J35yNmBLsvriRH6)uR8Bvqe3 z`_$po;X)WmQuX<>PZLfPE{u^RZJtTOX~Sv5MZ)RuQPd@=`uuq`eK>u%7)Fv*eg5n- zgfoPTVf%t;j$P>QuX<>&lb)WE{BmMRi8im?BVR;@)$|d=9wg%Bb+1rWH={2 zD!3$7pFfZ03g-$}#7L5=&!2s6JmV{2BuUlh&pr>H>yhouxAFqn47)es~`Li#8*F!aoB&quR**}5T!!sC3QuX<>FNoJe zb&Mow^Gp&hgjYfhJktw@Yq}&=pFfWl!Sg-=BT1@0fA&T3sz}60lB&<2eKEWqVi-x% ze$2m0yjOTN6c1~cq$+1V;?JWc@Or3)kt9{0Kl_q+Y-(d9N!91iz7$?Lbuf~o>hot` z8n2nU7)es~`Li#B*GxT(Bx&bjlnd8)Nvb}79xad8Lj#N?srvlcKZ(~y zLyRP;`uy2fz$>Q_Mv}B2^H=e{FE>?`9n^BhK! zRDJ&JtKji#hLI#ypFjJj@ak%gkt9{0Kl`Wgx@v)uByFBa!d3B#YKd1&wQwt!r0VnM z(P!|Qc^)H4sy=`A)$y8Xjgcf(pFjH=c>T1&NRswr{*B{(TfDYvhTFL$RXOt!e;!T1 z>#99Ql2m>E>=W_2>VT0XRi8im7+zN$F_NU}^JlN{7{7p#Bvqe3dyQA%ix^4L=9wg1 z3$MCPcvaO7cXmmtK7SsqgV$9Tj3lZ0{MpyV>#8e8l2m>E?Carm)eR#_+K>6SjrZO0 z3VSyEl1oyRGavEi(fW7=_P|Jzs?VQ&1HATnVkAk`=g+<&UVATNBuUlh&%O~}d%ZA{ zr0Vl$-x$w>-WW;J=9whi1h3CNc;z(>_jO6CK7Ss44zIm_7)es~`Ll0^*Is{&B&quR z**C{)ZvaM;v>)^D9`6U@mDeIX$R(-DnUDDMXiK~f2V*2j)#uN?6<(!7Fp{L|^Jo7& zUY|oTlBDYMXWtsH&tVuzQuX<>Z-dw8aEv5r^Gp(Mi)YCQyhhuFN4g|cpFfYb$Ln(x zMv_#0{_H#8^*I_NNvb}7_8sy19D|W0?Z^BF#{02&b-oZD=aN+A%t!or^hLZrlQ5E` z>hovc39r@h7)es~`LpkgSMmgmB&quR*>}Nfcp^rURDJ&JyW%xG2_s3`Jd=dG;nn*J zo+;hKueu~vpFfYjgje!pj3lZ0{Mq-wYj_Gql2m>E?0e!h{2E4*v>)>y8}DDoEBNK` zRF|YGXFlT3qrLDNo`#VmRi8im-gphafsrItpFjIPcs;*~kt9{0Kl{FTwNJ-LlB&<2 zeLuX;XJ8~rn`e@6f4stH;uSm~Jj*4i`uurxAf8XNF_NU}^JhN@ul6|@NmBLsvmcDt z`CN=7X+P%wdc2>9SNV|eTP{ge&V0n5M~C8d{x(LERDJ&Jhv9Yp4n~qxeg5o+<8?kC zBT1@0fA%BrntvA~Nvb}7_9J101sF-v=9wfs3Kn<|ukz91_g#{z&!0!f;C221Mv_#0 z{_Mx%Irkw(l2m>E?8m_f3o(+U{h0r}c)tiHND6=Cl2ql)NBntoJY4WGMv_#0{_H2f z1)pFfN!91iej;437$ZrlK7aO;;DS#vlBDYMXa5SkummGX+B}nlUxhE0!UU7U%UqJG z&!0!9zy-@OlBDYMXa5>pumU4Vsy=`Auj6^S5+h04kNGc(_p9KHso~FDlB%5fh(C`` zgD+NNBuUlh&;AYg;&Y57srvlczX@Ng!AO#-&!7Et_~HwUB&quR+0TG4{)dqyZJtTO zGhvOjaK^0gI+vvC^XJjo@Wq!HNmBLsv!4TBtj9=_s?VSOT=-%GMv}B2^IsM3H{zK) zFTBYmsmhs;`19ynFw16)B&quR*}n~^Y{5v9s?VSOJ8;TYj3lZ0{MpZkQ?_9wN!91i z{#`gF86!#BJd=bMz$O9A@m|=tBvqe3kG>DHSd1j8`uy2{0Hhov+5$y8~Mv_#0{_H=7cXnYUN!91i z{u6j-H%5|Feg5nh!#m$%BuSfRlJKW6&UY}%lJFjvr0VnM(WUUqUW_EE`uy21gMId4 zBuUlh&we?)vmYZ#+K>6~i1**aIxE5lT#~As`G`M{u7r0EVkAk`=g)o>UIT|PlBDYM zXa5-tbr>T_sy=`AtKp^}Fp{L|^Jo7#+;jvZN!mPy z2_s3WK7aOW;hfNvb}7_8aj!IgODdRi8imO|aN67)es~`Lo{)fBlM)ByFBa!eO~V97^GR zkgCt0M^l9!e~_xrpU2X;_o&H#lB&<2N9Fx%k3LBIG5_OXIr8ZJYZ>D2Vk1>K^AUgc znd0wdW0I=RpM942d)b(z>houxE&g6MCaL=T+4KEtOj7mvv(FWOFB_Azc_tga18WQZ zoH&D#+=`Fi!aT}fv)_h41I}V3lkxGpJCna=AHenJFp>rzf4DRGYxWj?KaY{zj*koO zO#YhvS8(@5jN}e{{OQi*ui1YMgI~f(?!?DscP4+${u_At3Py4lKCZem`D^yO;oNH& z$#3y--JQu_v;Pimy@8S3gO8i;O#YhvUikDcjO0Fi+;V4houR2tK`wkt9{0Kl{V*={<}jsrvlc z{{WxrRi8im6R>kM!X#CnKl_vLa*7C( zRDJ&JPr=J6BTQ2D`LjO_FQYVL{cdOA zi?k6Ysrvlc{|1|+i!e#m=gzX*S2jW9{o=ghov+7o3|v!X#CnKl@uSa)Ahww0R~8 zKNo+$+u!i=6A>n<`uy47hL;OQn563SXMYD?E)-#ss?VSOKk#zl2$NKO{_O9<%S9qg zQuX<>zXvZDjW9{o=gE>@!3%M4rY-lB&<2ea1+}NL7p^srvlcXNqKsRKrM;s?VQ&=1AtqGZ;xy_4%{U z63G&&j*%o)pFjJok*tv#7)es~`LoX!$rh=JktA)NNx}o;?{~`{$sS3-NRq10pM8!< zjz}U#l2m>E>~ltPMq(IAQuX<>&lSlPQ5Z>5_4%{U9myTh7)es~`LoXx$rGuCkt9{0 zKl{9qyph@%NmBLsv(FdF7pa4hBvqe3`}~pok-8X3QuX<>FAymZsfUpyZJtTOW8&|3 zdm{2g zFBvHrX@-#`ZJtTOQ{wM;D-|gfX^xR3Ri8im((rN%j3lZ0{Mna*ms?^aN!91izAU`l z3L{CXK7aP*;N|BrlBDYMXI~y(ZjF&7Ri8imC*kEb7)es~`LnM8FSo@=lB&<2eMNY= z9Y&H=eg5n#!OQJ2lBCTuNqBbr{ce@vuL3W3#7L5=&!7EM@bU{7NmBLs zvws?1ei0){sy=`ARpI4M7)es~`LnMEFL%aBlB&<2{WI`#7mOsS`uy2fhnKrzBuUlh z&%Oq{+zlg1sy=`AHR0v%7)jFRnIz12U?srIFJUA})#uMX5nk?rkt9{0Kl>QG+!G^7 zsy=`A3SNF0BT1@0fA$()?uC&gRi8imTJUmjj3lZ0{Mpxrm-}EON!91iz7D+H7b8ik zK7aOg;pKi9NmBLsv#$p)_s2+*HqRvCrSbQ>Jqs@nz(|s+&!2sLczGa3l2m>E>>I$# zgD{e$>hovc5MCaPkt9{0Kl?`T@(_$9srvlcH-?voVkAk`=g+5_4%`J0WXinNRq10 zpM6Vsc??F9RDJ&JTfxg?F_NU}^Jo7&ygUvgNvb}7_O0RNB#b1f`uy3qftSZ)BuUlh z&%Q0ZJOLv~sy=`A?cn8!7)es~`Lk~iFHgcqk~YsI;cfBvyLEthovc9bSGDBT3pklZ1E0-|zMkygVHvNvb}7 z_C4U`85l`Y_4%{!2`|sYNRq10pZ&}5@+^!bsrvlc_kx#aVxxfn@O_4%{!3op;ZNRq10pM5`g`7Mkjsrvlc_lK9?#z>Mj&m`eP@%Ot8fS2FF zNRq10pZ!31c|JyxRDJ&J2f@qlVkAk`=g)pHyu1J-Nvb}7_Cw(1_b`&A>hotm6kdKG zBT1@0fA+)R@@E)HQuX<>p8_wh#z>N?&!7Ek@bc#v zNmBLsvwt04UW1V&Ri8imsqpd_7)es~`Lmw}FaHlCNvb}7_HV$;YcZ0f>hov+CcL~3 zBT3pklZ0=^-}N>fUj7mzNvb}7_A}t+^%zM~_4%`(2`_KJNRq10pZzR&c_T)WRDJ&J zXT!^zFp{L|^JhN?Ufzt6Bvqe3`?>J)7K|jR`uy3?gO|5rBuUlh&;BiVc^gKORDJ&J z--efyF_NUsGfDVC{9SMFz{>$fl2m>E?B~PF1|vzTK7aP_!pjyTNvb}7_6y+U?HEZ? z_4%`Z4_^KXBT1@0fA;Ug%R4ZVr0Vl${{g)GHAa$Deg5n}gqL?>BuUlh&we4i{0&Bu zRDJ&J7s1QBFp{LrGf6m2{QYhp!OOcblBDYMXa6z0{4GY3RDJ&JKY^FO!$^{<&!7Eb zczF*-l2m>E>_3H<_hKYT)#uNC3B0@yBT1@0fA&k^<^32*QuX<>Uj{FKkC7x*pFjKM z@bUqSB&quR*{^_?4`L)qn`e@6_W1kVR>I4NFp{L|^Jl*bUOtSGBvqe3`_JIzA25=n z>hov68eTqvkt9{0Kl{(&<)au$QuX<>Ujr}yh>;{!pFjIA;N_n%lBDYMXa7HV`4~o$ zRDJ&J*TTy`V*3{-7)es~`Lo{u zFQ39llB&<2{YH5CG)9tCeg5n>!OOp3BuUlh&wew!{3}M1RDJ&Jx4_G1Fp{L|^Jl*m zUj7XuNvb}7_S@j)vlvNI_4%_;hL?ZGNRl?sB;hjg_qzq~@;Qtosrvlc8+iE-j3lZ0 z{MlQ0`8-CFRDJ&Jx5LX9Fp{L|^Jo7RynGQONvb}7_B-I^KQWS|>hov+HN1QYBT1@0 zfA%}!<;xgJQuX<>{{~*Zf{`RupFjIu@bXoRBx&Vksrvlce+w^P z$4HW@&!7Ew@bV3eB&quR+3$guZ(<}#)#uNCFTDI0Mv_#0{_OX`%eOF+r0Vl$zaL)y z8zV`oK7aP#!^^iZlBDYMXMX@*zJrk@Ri8imgYfb{7)jFRnIx>^?{_-{FW<#TlB&<2 z{b6|d9!8Q>eg5o!fS3QpNRq10pZyVd`94OHRDJ&JkHX6jFp{L|^Jo7fy!;E z?0SfA+t@%ju#_QuX<>KMOCXk1|Qs=gzW^_1i84vm=gSfA&}5houR6JE|2Ws<7TpZ#C(a{efjRDJ&J zZ^6q2qD)ft`Lq8UUVb9VBvqe3``hqx!6=hdeg5q4z{`cAOj7mvv;PNPE*xc&HqRvC ze)0Fa-G!HnM46=O^Jjk#UM?DClB&<2{lD;Xu_%*Neg5q4!^_2^Oj7mvvwr|DmxwY+ z)#uOtKX|!hlu4>SfA$aI7j3lZ0{MkqF&qvD2 zKOsSqHqRvCk@5GtMe%>-B#@-)^JkwTnj%^rBT1@0fA%S(DWgwfBuUlh&puT&RkQ*| zl2m>E>{CZmM=N3^N!91iK20=Dv=T;=RDJ&J(?-)qD`O-{)#uMXT{K;^3PzGteg5pz zN7F~2!bp;;&!2sUXol$17)jFRnI!y5{QYhjqZy-BF_NU}^JkwankiZhBT1@0fA*Q9 znWN8OBuUlh&pt~uOSC#hl2m>E?6XF*Mr&XsN!91iK3g6EKpb z>houxBbp;{!pFjJY(VWp3Mv_#0{_Jx_b43+KlC*gy3D1bX-z|4EcT{5}N!91i zK2J1Hv=&B^RDJ&J^G5SVYhxry)#uMXUo>B|4n~qxeg5q8NApMPVkAk`=l_4Q&H~D+ zy8qsTbNbqj4GJc9p<-fy2zH}lVPO{rik;Xkc3~HGU|?exVt02pzP|f&pToMFHOn>6 zx@(`W0?&KT-ZR|a`Txngws&pstFItd-9LG^_HOO{^cBRa`zN2PeXjPk^cBRa`zN2f zeeU-D`U+yz{gZcZ@7}()zJl21B!(|&KJV6}y+`{1eFd@V{>kTQpQnAGzJgeF|K#(w z&)dF^zJgeF|K#(v&)2@LzJgeF|K#(x&)>eDzJgeF|Ktm_FVMcezJgeF|Ktm{FWA0; zzJgeF|KvT@%Nyz|h*kGbzL0u(BYg$2>i)?WRxfX?uOPNLiQ%i7&$}(6Ufx7sL9Due z@c|tFK@1|AXeQ!`4Z~oE%X({s{1EjQoTG#UqP(8 zfAXc&%UkLzh*kGbzO;IID}4pA>i)@>Q7><;uOL?4KY1_p@?d=hvCT;g-_m^EZCUm5 zHu?%;)%}w%r(WJxUqP(8fAZzk%iHNIh*kGb-dnxAy}p82b^qimsF!!pR}ic2pL|92 z@(_ImvFiTGS5hzUsIMSa-9P!t>gApE6~wChCtpRqytBT7Satv8tE!iG(N_@LoW$^b z&F9_vsF!!uR}ic2pL{j-@=$#RvFiTGS646ZrmrAY-9Pyn>gC<_6~wChCtp*&yobJm zSatv8ebvi*>MMv<_fOtWy}Xycf>?F`j%?FYl|b zAhtP);aQqby*h(cKFbEN>i)@}YCg*bvFiTGd43II)%}w{+kBP{V%7bVKi_w7u0s0EAtAB>oCwWc2o_6a%eFfLoKL^z(c}>27HtAq}1vk_` zhtwx|O}>%#X1KnB8|$A#>yx}D-$Yw-n7)FW>YowyNnVq0rkyxkU%}1w&k^-WUXyR3 z4LDL?!9n`xsQM(Y$+uL$AFZ$8R{H0d`XsN(w{D-g?O649aB%x9ZO7FoF+8#PyxTUq ze!RLlh*kGbzODZ46V%N?th#^l?eyO`QQaKGs{1G3UjL1g)XhPxx_|N=^xqh%ZVqDA z{gV&Tf8%6za}cZUpL|FCH%?JE2eIn@$#>F!<5YEX5UcK=d}sYPPE$7rvFiTGcWLj@ zcDlMbh;2?{m?yAy)vt4gx;cnd_fI}lzs{NJ<{(zxKlyI@bZ2eIn@$#>VUbGEuU zh*kGbzK4FDbJWd2th#^lJ@xCHt8NZr)%}z2rC;Yfb#oA_?w@>b{W|BXn}b+&|K$7V z*SSF59K@>oC*QYy(Y8_Q<{-8?iD90=+E2fi3)LAxth#^l{q=jfNR1N2s{1EDK);uZ z)hj`)x_|Ov`n_DD)(K+O{gWT4-^-=yo*-7;KlwrWygIIO{h*kGbex!cC*QqaqSatv8N9p%_z1lR0 zRrgPRw0^%gs8fSjb^qkY==Xc08aIel_fLMTe!n-hL#(=g^5fdqXuG)`Vw;l~<_WCh z^=G<8-5kWK`zJp^f2Ld2%|WcXfASOcXSz+@9K@>oCqGGlrZMW~AXeQ!`AGelZdW%4 zvFiTGPu8F54s~-7tL~rt6#bd*R5u5)>i)@3)t~7ub#oA_?w|ZL{h97oHwUrm{>e{o zU$5<+c8G0GVz_8N?{_2+(2-5kWK`zJq7f9{9W%|WcXfAaJ7=YCk-9K@>o zC%-^{?nl(kL9Due@=@)B+Qzj*Y;zLBGd7=hyHIW(RW}E*>i)?ulAFiW%|WcXfAWjv z=5cj%5UcK={1Ul&Lfst1s{1FuRBoPBHwUrm{>d+so2S&xL9Due^2_DsX?1fDtL~rt z3b`4tZVqDA{gYoQH_xb>gIIO{-oVq!PRrgOmTK=9_ zHwUrm{>iVAzZcZaL9Due@@wVqMRju!tL~rtI{AA^-5kWK`zOC%{$5r$2eIn@$#0Oq zSJcfxth#^l8|Cj+b#oA_?w|Z7`Fl+b9mJ~pC%?IU@3sl;5Zj!@@B+=}-ENWN*VWBI zth#^lTjhA7x;cnd_fLMC9KWG%4r0~)laG<(H`UESth#^l+vWHzb#oA_?w|Y)IeuH+ z9K@>oC%;pU-%&RQvFiTG?~>zp)y+Yyx_|P!<@h~ya}cZUpZuQo;cf4?Lu_*r!%H@w zce_{rJA9yS4r0~)li#N+AF7*!Satv8_v^|>>gFI;-9PyQy7IBQIfzyFPd-*xK2bLZ zvFiTGAJmmk)y+Yyx_|PAbmcR3a}cZUpZsB6`CQ!`#H#xze?(WlP&Ws$>i)^cwIA2^ zWjn++Co#NY^Le*N^>2TrZVqDA{gXeY|E<1OHwUrm{>dNLzx|E6IfzyFPyU4d?Qhl1 zL9Due@+b9gf2VE^V%7bVKc#>Bdv$XVtL~rtY5m(jsGEaWb^qk!^>6>EZVqDA{gXeV zfBPqOa}cZUpZwYOGunP`huG#MhIs<(IsFR1sGEaWb^qkg>sRi)@J)35N4x;cnd_fI~d{i3!>?GW3X#4t}_y{_NIzv|{7R^31OMEx%Q zr)~~n)%}ydq2I-S>gFI;-9P!8`ro`Qfmn6_vu6#0VNYjfmn6_i)@p(yq^*K&-ld@}Jwsw#|`1Y;zLBJc0F#wtvn9V%7bV|Elfp znn0|&fAZh7{oN9XRrgQ+yS9I>1Y*_wlmDUZpF4qAb^qjlYWuq<5UcK={IB*e+j}Gs ztL~rtZ*BiP3B;=VC;vy=KW_rD>i)?mwLjH1UjnhsNeuG@*1!7xVEzPR)%}zIPv0Lb zkU*@ufAatI{lS6>#H#xzZ_|6QX9BV6{>i)OJ-AQ;vFiTGr_y_H;RIsU{gb!rJ-CSe zPe_7Tb^qjv-h+!K5UcK=Jk@(}u>@k({gY?OD{YG>5Zj!@Fi&9RdY>+lK&-ld@8M_fKBxeY#WvvFiTGE4@#bP9Rp@KY2s%(`6EfRrgOmwaoQOAXeQ!`80Z;E}KBC zx_|O%^*&uLfmn6_i)^6Pu^|ooj`1J62m-!HG|&EDAk#K0c~CJ+W~DvFiTG7tuYjNdmFz z{>c~BJ+Wy5vFiTG7t=klSpu=@{>c|l{%zYlf!O9GhIs;O3EekaBoM3apL|K(H-i$0 zRrgQ6lfL;y|rTkvFiTGSJu6?lNvgRRrgQ6iteqQ)zCq#x_|Ojb#LvWh7MxY{ge06y|t?v zI*3*GPrjP&t)Xh@AXeQ!`Rcm2c2h$KvFiTG*GOjSvU>ut%}EUN1lF3m|MpNr2eIn@ z$@}X5+fxl4#H#xz@2C53FEw-!tL~qCE!}^6tD%Efb^ql3b^q<7h7MxY{gbb)`)^-0 zbP%iVpL~Gszx~wEL9Due@`1Yl_E$p(vFiTG*U>xT05xi)?$(z|B3 z8ajwo_fNjD?$JZl&_S%afAUTAt~pE%9mJ~pC*M@>nh|Q~AXeQ!`DS|89Il2AV%7bV zZ=NjF<%k4go0Ayk39K#j&N@;J9mJ~pCm*DD)=_HcAXeQ!`IdTT9j%5AV%7bVZ>4wE zF>2@_R^31O)_P|htA-9@)%}wX);sGsHFOZG?w@=c-N(nPp@Ud;|K!{1oppj5I*3*G zPrjYrStqKYgIIO{gFI; z-9PzI-Roznn}b+&|Kz*r-FKF{IfzyFPriH7r_0$1#5N}}%oA99=pA~Fx;cnd_fNj3 z-l6BJn}b+&|Kxk=9eSR+IfzyFPrkR_q35fcgIIO{V1)XhPxx_|Nm)CU)oCm*hN@s;Z4 zAXeQ!`Js9jU!`siV%7bVAEtNl)#~OTR^31O2)&C(tDA#Zb^qjt>s@?}x;cnd_fLLA zvRRjF6Nqh2Vwfkej#RH)r)~~n)%}wnrFZ)E>gFI;-9P!!dZ*u@ZVqDA{gWS~clwR$ z<{(zxKl!nGr{AP*4r0~)lOLyd`pxR*AXeQ!`SE(E-=b~~V%7bVpP+a8t?K3=R^31O ziF&8srfv>m)%}yJo4brjAhtP)VV=Mmsk`BJb#oA_?w|Z*bth#^l({(r8qizmj)%}y7p}XN;b#oA_?w|Zj z-3|Atn}b+&|Kw-sZn$6F9K@>oCqFycqss#c#5N}}%oAAW=#CkyZVqDA{ga=oJLW-k za}cZUpZq-a(?ja!AXeQ!`T4qI9#%I8vFiTGFVG$Hh`Kq5RrgOmN_WgSb#oA_?w|ZZ z-7$}oC%;&C%;W0jAXeQ!`6bCgU7koFwmFGmp1`_Pch!^X z<{(zxKlx?4tDaIf2eIn@$uHMk^|ZP+Px;cnd_fLMa?z{==<{(zxKlv@X^Ilgs2eIn@$#2!2H&NXj#H#xzzfE`E8|vmD zR^31OnB>$hZzd4ioWw9sVBM~}^DT9A5UcK={0`lnZ>yVwSatv8ck1qZN8KF6s{1Fu zOLynH>gFI;-9P!=x;x)fHwUrm{>krA7rw7<4r0~)li#bm^8F~BshfjXb^qiK>kj^0-5kWK`zL=yckmbL<{(zxKlwP_!C$JIgIIO{FWn?P)H62m-!^`!3d@6^pfth#^l zr*xNpuWk-v)%}w{t-JgOb#oA_?w@?T?(!ei%|WcXfAVK^m;a=04r0~)lRvAw{AYD@ z5UcK={5jp_zo?soSatv8&#O~^RW}E*>i)@J&|UtUx;cnd_fP&}a!Z%r6Nqh2Vwfke zUQ!eMp>7Uh)%}ydtS0zV-5kWK`zL=zP4JhxIfzyFPyVW!;BR$v5UcK={53VfKkDWn zR^31O1U11Vb#oA_?w|a1HNn5?<{(zxKlwy8!T;3FL9Due@;B7G|EZgUSatv8ZzlJ3 zX-grtIf>!tn@_#*{2Iin`zPo5HHcOBPtNme5UcK=oafgdR^2~2&#ytOx_@$>UxQe6 z|KvQs2C?e?$pLrbyKBWdmcm8D$eHF@k(p2FClBIPQt$zyZM6vnnxDOY(-9(&SAVeH1#DOY(- z9$PU@3S$$dO}WZz^4NjtQW$+deacl{lSjwTkizKl8B?zEnmqb@rZm`5Gp=@|80HDA z=&zC~1x_|QM`1wl{{Ng-CA@V$~F4 z)%}ytqOItYLae%f@>#VNtECXD?w@=%ZN=&-#H#xz@9a5FcBB{{)qLu0cJ0X;Da5M# zC!a%mvStdg>i)^+)SmQBAy(Z#c~|X8zZ7EC{gZdoR;-mmth#^lxwIAiQ;1dfPd>M{ zV(k=S)%}xq*H#QjAy(Z#c@J&Hz!YND{gZe0949+c4D$rmJZiXgQixUePd=~qWZe{E z)%}ytr#)FOg;;g}^V+$q!{K2tVOgZo2C$}?w@>7?a5{-#H#xzUrc+l zc?z-W{>c~Do@|jqth#^lCA1ZTQixUePrjtKV#^d_)%}w%rLEX1g;;g}^V+$q!{K2thKZ!`=k)7?w`EB_GI4_V%7bVudO}VFNIij|KtO-C;O)mtL~qCptjp<~)%}xisXaMH%M-+^`zPN@dvdInCx}(|PrkMG?F`tL~qCNA1a}TAm?F`q4zc5UcK=d_T3IzSC~iIyjbRrgOmObvIbmM4f+_fLMH8tyVJPY|o_ zpZp*-+~r!HAXeQ!`N3+hE3`a8th#^lL)2bZYE^<*b^qkU)m~R=Rf1S`|Kx|Ny{^`t z1hMM=$q!R|jn8Iv1hMM=$&Xfh-KaeYV%7bVAEWlVNqZ8+s{1ED zR_%4O_9Tc^_fLMD+UpkWNf4{RDyEZ3?RrgOmQtfqzHYbQx_fLMZ+Urj3Nf4{?F`i)^k zReL?8Jqcpf{ga=k_Ig-*62z+eCqG~9^@#Q)h*kGb-q~}U>_{=p6Id6h;l^omf>?F` zi)?uQo}v2%?V=F{gYp;_Ig5_6U3_fC%;7P^`!PB zh*kGbeyQ5)DeXxRtL~rtGPT#!+LItw-9P!|YOnFylOR^zKY3@*ak3-DFi&7zp@w@# zn-j#U`zOCr4fm`zCx}(|Pkxme?m2Bv5UcK={AxAa^V*ysR^31OXtmc1+MFO(-9Py? zYOfcyCqb;bfAVY9UN31+f>?F`)%}xq_8ccWQVjD1 z)(vX7SG74oth#^l8`W^HX>)>Db^qiyso^GQbAniP|KvBT;a=C~1hMM=$!}46P1NQD zvFiTGZ&iD}p*;y=)%}y-ruKSMdlJN|`zIfx_IgWu62z+eC%;|o^|tmTh*kGb-q~}U z>_{=p6Iges;oi~a1hMM=$?sIdy{pX$V%7bV-=&6oPn#3Os{1FuTMhTVHYbQx_fLM0 z+Uo;tP7tf^pZs36*N57ZAXeQ!`F(1ykF+O2th#^l`_*0_Yfpk$b^qiKsJ%YXo&>S# z{>eLgj*}fJhIs;OtQzi9ZB7uY?w|ZYHQZ;~oFG=+KlwvyxX-mYL9Due@`u%MUubiJ zSatv8kEp%A)aC@S>i)^cslC3^o&>S#{>dLzdws1v31Zd#lRu{R`bK*a#H#xze_ZYL zt@b2{RrgQc*>jxiNHNS4SWl?ozSHIevFiTGpH#zrugwW!)%}w{rH1=Kn-j#U`zL=| z4fmrqCx}(|Pd;Al^^-Oyh*kGb{*2n|XYEN4tL~rtS+&?F`)>Db^qkAsJ;Hy<^-|o{>fied;Oz531Zd#lfS0+nxs7mV%7bVPf&aP zt33%~)%}yduJ-z$_9Tc^_fOv0bDZo*G0YQK6V-74X>)>Db^qjVsNvc&h*kGb{-zqP zO9rv({>k4`!%dYzth#^lx7A+l8N{mlCx1unm1GdB?w|Z!wO5)!th#^l_tail2C?e? z$=_FdMhO zgIIO{&gIIO{2!_ARFth#^lpVe@4W)Q3HpZpg!T-OX@)%}zIs)p;9L9Due^54{6b7c^# z?w|a3ZN=Of#H#xz|3h2RJ%dO>5`zN2IJ()j)Satv8|7uSb$RJkTKl%T(6$@q%tL~rtKmB~t zGlN)l|Kx4jiiI+WRrgQcMO(3O2C?e?$*0m*ERsR2x_|P{p5tUkiea9>YS*4Ds{gO& zf>?F`{gbEKlf^TLRrgPxX-}5OAXeQ!d9JNkGJ{xk|Kx?XVyO&b)%}x~ z+KQz!h*kGbUTG_q$sktUKY2r2(JO;kb^qj@J;%w86vI4$HMRC+*$iUU{gY3lJy|Y; zSatv8(`rwa&mdOaKlyaplinG`s{1FOUR$w32C?e?$!E}3te8Qpx_|N+wG}I65UcK= zd?szh${EC}`zPi)@R(Vq0lAXeQ!`K;QL z)iQ`x_fI~X_GI-8V%7bV&#tXlBZF9V|KxLME7r^)R^31OoZ5=M8N{mlC-17Q=$ApP zx_|O++KRO@h*kGb-q~}U>_{=p6IgR;Px@yNtL~qCZtcn18N{mlC-1I38IVD&x_|N> z+LM79#H#xzpGWPrP6n~+{>kUnR;-&rth#^l`Lq@5We}_GpL~98#rhe-%e?a782#H#xz@2Nf6D1%sa|Ktm4Pd3gVR^31O!rGHfGKf|8 zPrit@V$%#_)%}w%s;$^8gIIO{eLg zj*}fJhIs;ON$ts&8N{mlCtpf?vQ-AL>i)@>)}CygL9Due@@2FqgENR#_fOtSTd_?B zvFiTGm(^Bmn?bC)fAZzD729PHtL~qCd2Pk^8N{mlC-1GT*dc>hb^qj@J;%w86vI4$ zwSx9!NCvU${>fL=p6r-Gth#^lm9!^2We}_GpL}KQ$<7(Xs{1EjMO(2;2C?e?$ye1@ z?3zKWx_|OM+KQnW#H#xzUrk%FTL!V}{>fL@R_vZZth#^l&Yt6BNBu;hKfm^N^NBXB zx_|O_o6oXAth#^l_nXhML9Due@(-KOvO%o6fAWu;&$2+62Osf*yM4K=|D-ND+(KP`RDBY|gVf4LtDBd~j;V=N_s`X>)Vs&3 zp@Ud;|KwY%caKv;2eIn@$p@=)L>i)@xsxQw{vj(y1{>gV! zU!JXI4Pw>(lkcwk@*Fj55UcK=Jnrdpn|JoPwQcv$)wtu&Yu@+g)wbO~SNGEWeZE>D zh;961cyGNoFHj%!&qme6s{7~azIumVsI~}V)%}z2rw+JC%@M?^`zPOD@8XNqC_${c zfARy=6PKu6f>?F`|Kx}0j=56p6~s0tF+5ytbd}m@i)^2;jV4=+_klB_s`WMbmv{C77Sw5{gX!vUf=A(>ucNYpQ}gdyWShrltHYzfAVO} z8=KvEV{P00bM+YA!8fT{gIIO{WVhDDs{7~a3A#IP zQ(p$L>i)@3)ZIBoeHp~6`zJq1cjxWu%OFeL{%If>yjbno7$ zrW~5xUlXhDpQ~r-?tDOf8N{mlCqG+v=UDY+5UcK={2bk#52`PNSatv8=j!f!NUa&f zs{1EDPj~0TYRw>4-9P#H`p)SQ^<@yN?w>qbb6m4K$JMsoKUYWT4t`Y48pNvmC%;g4 z@MG%KAhtP);fr+dKCY%bAbX-FR^2~WFVWrkr1~<5RrgPRsqW6F)R#f5x_|P^bay_j zz6@g3{gYp=yK}tyGKf|8Pkx2&&S%t@L9Due@+zp&F*}zw(b78 zdbRH0=hdu1th#^l(Yk|QP_qWH%}ESjqkH#7_2m)SOEt0T{<(Uc?#`FhmqDz$fAZ^f zcfO+53}V&&li#4b^Hud_5UcK={6^iKucFP z>i)^2H77Q^b7F1V{d4tJ-NA3DS%X-0|Kzvn4t`Ue8pJjyF+4{1?ptcgk=ffdvFiT0 zdWY`Lchr|bth#^lJ9T%ytG*0k)%}y-rMvSz^<@yN?w|Z_-JS2NHG^1n|K#`R?)*S~ z8N{mlC%;$UUw){*3}V&&lSgZQ)a=fWYTNFgtM}^;{#eZ##H#xze?WKeC+gH7wmFI6 zvATCZRa2gueO42z?w_j<>F)eoeHp~6`zL=`cjp)C%OFlFrHL>dcxjJ5V=TGX(AXeQ!`7^pZe^y@xvFiTG zpVi&@i~2H%RrgQ+obJwF)tW)9x_|QLb$9-zz6@g3{gc0-?=OE>Uk0)2{>h^?|7dpS zAGK}w&()W72mh&N4Pw>(lfSGx_%C&85Zj!@@GH7^|5j7pnEg`|tL~qxuj%fbq`nMd z)%}xC(B1j3`Z9=B_fP)1?#}if$s zImD{_Cy&;gDu-Bg|KxA$4sOpOR^31OJGz6D9AcZ37=BmxZkoTRyEChaRrk-;_jPyX zImD{_C;vcqXOTm!x_|Nyb$6CI#H#xz|44Udl|!t$fAWuYcQ$f}RrgQ+iN3#_I)_+w z|K!n{)8r7V?w|ZK-NDo55UcK={BzyG)8!D`oW$@Kx_76~ztr71Lrtu@f3AL|yK}}I zV%7bVf33T7rW|6`{gZ#AyR##QSatv8-|Ft1Ifqzv|K#84?wlouSatv8-|PF!S#yY0 z_fH~~>i)@p)Ezu~4zcR~$$!!vJVy?(%}EUZtb2FP{1@GwU29_1{d4tKbz!$0 zV%7bV|E4aSD~DKh|Kz``3+K)uR^31OAL_#HImD{_C;wAj*dvEnb^qjl>HEuha)?#; zPadr~Zw|5Q{>lGQU(T09th#^lN$Sh_bBJwDV)$P*VNv)v1bmk z>i)^w)P)P>5UcK=yoyyO}SKFstcE{iBdBa z!d^MVs{1FOT3xto4zcR~$)`~lE|){Bx_|O%)rHIF5UcK=e7gLLtalEv>i)^6S6{A> zL#(=g@)^{ZE9MZZ?w@={_2o)A#5N}}Jd>Jo<-9{(xJpf|x__?DtS($Nhgfz0Cb>V6`#H#xzpG{r3dJeJb{>f)o7p{>*th#^lIr5*gHFJnn_fI~j`m%2h zvFiTGyQ(kyeQBnmiU`gB&iXP1rE!DzC|VY6mvT;X>;BjdQN@ntWk({3bbEL|wjV&Q)HM zFRK3DEQgD!r#H{J%4_n))y-Sva0zwrpq#6`CSNiy@-1^1O}tgkRSfe9%hKxIt#gP~ z_fNizdUtRRvFiTGd#QJ~$stzVKl!ri-EDJ-RrgQ6oO*Y=9Aee|lP|B{-9Cp{b^qkO z)w?_75UcK=di)^sR8Dth#^l?bOXDsl|g> zb^qkstD8rv#e-ON|KvNUn@?7Y2eIn@$%m+$Pf?2pvFiTGcT_i@sumAoo0Ayk6PBIS z&8MlwgIIO{l8q zC*Muoe70IVh*kGbzPq~l9JP25tL~qC4|VgoYVjae-9P!B>gMy*;z6vsfAYQ5&F8Dd zgV^RIhWUhLZ*}tpYVjae-9Pz0YKu{7@gP>+Kl#3Diwo7_L9Due^8M5n7pcXASatv8 z`>QQ3R*MI*>i)?OP+MH077t?8{gV$hB;{-9Pz}YK!aD-$AUp zfAXW$7B{HBgIIO{+vFiTGk5^mVs{RgQ)%}y7ptiV8{T;-r`zJq9Z81ju9mJ~pCqGGTal86Eh*kGb zK2mLQhx$8+RrgPRvfAQK^>+}f?w|Y=wZ&cP?;y50iD5orIaO_OxB5GXRrgPRn%d$X z^>+}f?w|a0wZ*;a?;uv)KlvGIi~H2yL9Due@-x*I_p85ySatv8XQ?e7P=5!p>i)^k zR$Gi!e+RMZ{>jf#TRf=#4r0~)lb@@$cu4&n#H#xzKTmD(u=+cQZBAmCPgu@ZTRfuv z4r0~)lV6~=7^nUYV%7bVk5XGas{RgQ)%}xSsJ3`a{T;-r`zOChZSlDJJBU^HPkyo5 z;tBP45UcK={1Uarlj`pvR^31OrD}_()Zam@x_|P^)D};xzk^tH|KyjeEykhB;{-9P!2YKv#p-$AUpfAXu;7SE}_gIIO{zYdvFiTGN2@Jf zP=5!p>i)^EQCqyI{tjZ*{gYp-ws=YX9mJ~pC%;Z@@v{0mh*kGbe!be_74>%ztL~rt z2DQbj>hB=7If-FDVYyLl@tXQOh*kGbev{f_g8Dm%RrgPRv)bZy^>+}f?w|Y?wZ%mB zcMz-YpZr#}#T)AHAXeQ!`E6>8H`U)kth#^lF=~sq)Zam@x_|Q9)fR87zk^tH|KxY5 zE#6Uo2eIn@$?sHKysQ2WVw;l~{;Bz?)?I3g_tf7(th#^lyVVx&tG|O-b^qk|s4YHF ze+RMZ{>krETYRYg4r0~)li#Pd_(=U7#H#xzzh7+}f?w@?D z+Tv67cMz-YpZr0!#b@g8AXeQ!`9o@p&(+^SY;zLBlbWAuJ*>9)Lj4`Ys{1E@L~Ze< z`a6hK_fI}fZSj@*JBU^HPyVRd;%oJH5UcK={4uq~H|p;oR^31O<7$g<)!#v^x_|N~ z)E3{Vzk^tH|Kv}qExuQO2eIn@$)8eN{Gk30V%7bVKdrX-QT-jnHYYL6CoJRD7C))K zgIIO{1ZSkA>JBU^HPyT}1 z;&=6T5UcK={6)3JAL{QQR^31OOKOWh)!#v^x_|PQ)fRuLzk^tH|KzWzE&f)22eHjb z4D$)gt7?mX)Zam@x_|Q5)E1M}-$AUpfAR@xi+|PML9Due^4HZC|5JYlvFiTGC#o&} zQ-24s>i)^!P+PPW5UcK={7tn*mjYtd{gc0?wwS7bSatv8Z>ufZ3y4+sPyUYDA}JuY zIf-FDVR=_=kroiE?w|ZUwMAAyth#^l_th480kP`-$v;qA6a~bp`zQZUZBZ5wtL~rt zBeg|UK&-ld@{iRPjRIoT{gZ#9wwStrSatv8pQi+%nDL#k7s{8lPr}!KStM1=FpW<^Uth#^ye2UMZu=u!m9iC&!_kt z3ajqlKcC`rD6G1F|9pzip|I-y$@#1bVw;nntBI#8zEIarUwo-HouTGeYRwr7uJW3z zU#lr+D&RM2#Eyciye9uvEjV)lzf;@IQgD^mi1uJW4vN43}N1^h`Z zHAlf!UX%Zz_7F^{u`LAl7ZUy{JT|QUARbG?-uKu38fPbi`yBA#LHTj?F z<{kz7OC3Bi^W23l>cZs;h*kGbKCSM{a_fH=8bngOU)%}yl z9lt^WvFiTGXVCqdcx!R#VSfzkib^qius{>XoAXeQ! z`7G*;J_W?8`zN1OJ+WE=vFiTGXH&1NUO=q6fAZPYC2JHAtL~qC4t39(1;ncRC!bS& z)3<YfAVz;h*kGbK3_2@U$1~zb^qkin(G%3tL~qC0rlku z1;ncRCtpx~xnTjZ%}EUR)cbj(BKmUUnpk!JT#dfmq<~m;|K!n^n-&nO?w>sRai)^2Q@1K0R^2~&bn4ax z#5N}}yo~yJaM4Shx=l^2x__=Nt4`gvfLL|^eTHEh*kGb-dmlz zLjkes{>fKRrw%C~R^31Oit5xI3y4+sPri~mb*BPi)%}yNT+CSPTtKY4fAUq-ySo$+ z+nmJks>OtS*P@SlcW6zlx__>&rrzDHfLL|^fJpGh*kGbzNUJ2 z&jMoA{gd}q@9tGVth#^le(K%53y4+sPrjCVcb@`c)%}zASMTmyK&-ld^0kX@#eN0E zHYYJWpm-$TzZj@)KAi)?$RyQA}W({K1{gZE^ZXTg# z4Pu*<7~Zt#SsbpuT&OsrCRW`)S2tHTAF0+1V%7bVZ=r5JO05~hs{1D&q;5W1tr^6s z`zPO0-F%E%Gl*69PrjA9`B=4P5UcK=d~0>{aca#VR^31OV0H8H1;ncRC*MZhe1e)a zh*kGbzOB0XL^W#=+nmJkcEvKqN$Sg9#mJgib^l!5LEU_^S~G}M_fI}V-F%8#Gl*69 zPrjqN`Bb%L5UcK=d?$7DX==?NR^31O&g$mV)tW)9x_|Or)Xit8HG^1n|Kz)>o6jsD zR^31OP<8WJYStiD-9Pzm>gKc6tU+vZ62rR}s}|>|FZ&ed*2Jp&=jxv7=JV8=L9Due z^1al}=c_e?Satv8d#jr-P-_OU>i)_1Q8$lLYX-6E{>k@MH(#jM3}V&&lkcZ)zDTVZ z#H#xz-(TH)aRIUF{>cweTU?@M4Pw>(lMho{T&iXbVw;l~KCl>2T&BJpSX^EctL~qx z2dgcvP-_OU>i)?OQCnQ8)(m3R{gV$@TU@2q3}V&&lOL+KxLU0l#H#xzKTK^gT74PB zs{1D&p|-e2eHp~6`zJqK-F&UOGl*69Pkw~j;yN{J5UcK={7ALM_3G3hwmFI6qxAjm z4Qk5GiW_TU)%|nz7`4Ss>dPQj-9P!UYKxoImqDz$fAZth7PqJ`gIIO{;_jMQb^lyFO>J?H`Z9=B_fLMh+TvdIWe}_GpZpB9#eM3_ zAXeQ!`I%~q`_-C3th#^lv(y$3s4s(9b^qjNt1ZT=FN0Wh|K#VWn;%qn2C?e?$i)?uQKLMmz6@g3{gYp+MtMqo8N{mlC%;UM z^0c}$h*kGbez_WDyqYzLRrgPRg&O4gQ+Gl*5YWYGT#>bMdPQj-9PztYMqzWmqDz$fAZ_q zI2{42C?e?$#2v5&2OqNgIIO{cYXSatv8x2u`nR$m6O>i)^^ zP&2)wz6@g3{gdCRW_nkB8N{mlC%;S0^q#sih*kGbez%(GeKl(ktL~rt9yQYk>eL{% zIf>zW)kzxySatvY`4pc+Vb%Tn=Tm$Rg;n?OpHJ~Q z6jt58e?GU{`nN2Lt)kZ`{z@94uw_s@1IZcITTjiKRKUOL2Pr9gilx=(*k^> z?Fv4wrvFxdw;P{u@tXVzwflG4rQnnLIpq8LDzC|((w6?9%?UoO7XPt6$!qfQYVM!3 zC&6db&_CBFc}@PTTKO04M({bc@2~YqUXwqs#{ErO5qv?-`g?scUjNa||I`9pUi?+_ zCAH??&69tq^{*)YY37sE?pGH7)_g@9^}qTmuetiFn({yOckneeVq3{oUXxEy3w9~t z>uS5HO0Hs zh*kGb{*Jy!FG`5*mj6H1*>Uwi`9F3ny-th#^l@3c#Eln|@#pZt66 z(wrs4s{1GZLA%togjjX|gt)r*h*kGb9{aFZ39;(_$-9)V7K@hS`zOz}OT9{nZBAmCPgn}=(y}GQ zs{1D|wM)yD5UcK=ywWZ$UqY<9fAWTQsdovn>i)^6)-J73Lae%f@@ce7E0z$e?w@>G z?b1pm#H#xzpH929atX2O{>i7;F0E2Rth#^l8MI5QmJr*V#4w+*%&1-JQ$nn|fAX2M zORJR-tL~q?L%Xzk39;(_$!FFstx-a(y8p=6EN9U!^{t6j_s`W?wM+d?OBS+msNi?w@=y?b5a-#H#xzUtGJiT?w)3{>hinE^S{zY;zLBe8RG%c4>zaV%7bV zFQr`?QbMe{fAXcZOFNbjtL~qC8ST5R^31O^4g`{ONdqXPu^R*v_}cC>i)@B&@Sy+LTqyq!+gTBqIPMo z5@OZ;ldq&*+Pj2Ub^qimYnS#ZAy(Z#`6}9_eM^Y#mj9ib`F`c9+NJ$#;yg~`f7d$i zqg^_ngjkJ}ll_ygrd=9VLae%f^3}CV2bK`4?w@=O?b1Od#H#xzUsJnua0#)^NeuG| zOJD8MAtl7B`zP97)FyXAksWfvPjxQlr-9LHk(g`KRcFX^WI*(mCv4mKSlau|E z$1a^zLae%f^4O)3CB&-xCy!k^xrA7C|Kzbtr<4$@?w>q%>C_Tpo0Ayk6PDPe(@Ka{ z_fH<}czOx3>i)^M(k`7*Lae%f@~yQ?XOi)^M z)h?Y=Lae%f^6j)s=avww?w@>n?b3N#xgb{EKlu*YrSr9NL2Ppp!+gRrMEi4rRxXHD z_fNi~_GgqIX*ybdL`Gn;l?axhG)F4*fKl#DhrJJ>=L9Due@cy1E{)Nm2C?e?$wz3HZdb1dvFiTG57#c;q16py)%}wnp{aW21R^31O@oLHkw7Nm8y8p<>YHznFAFPR0_a9du(%ud# zAFhd2_s`Xn)Rd2Cb%R)S|Kuapl;gCzL2Ppp!~XdcpHgAf{gdRY7dG{Esd9^oq}_AkJI*R^7jUKE>xySatv8 zd{zaq&B>F?$CF3(9Zm2QEz)B(Pu21~Ugx~#>S)brWWJr`f9u$-^`!U_Ypgk&(=I!Yw%q2we7)CYUo$$le{LsP_6u`b~|{H+V{2kB(KRYR^v|4 zCI>H3v%X%RjMo#J`5Ri=y~;OhUZ&Q3t9f!_Z4%Fyt0~{smIkrv{>iUUQ@*1u4Pw>( zlV7Q(d{eXt>545F0th#^l(Q3*MwWUFYh2C?e?$*)sWexfZ6V!P%4jb{F-R`ihavzj=MlW$Dn`35!R z=i1UBR^31OjcUp-w536;x_|PU)RbRpOM}>M`G2pOf29>2QGQ($t8sF&f3Du5UHV2_ z8pNvmC%;uq`K`7zh*kGbew&)|J8fwYtL~qCjCSdJZD|mz?w|a2?a~k0(jc~5{y%Nz zKWasfDSxVo^EmnG6rS(YF8!=64Pq6~ye7X(yY!2;G>BFAPky&{=~r!O5Zf*PUp4dJ zw4x`Jzt_ZSoSf{RtM_V`{?L{NvFiTG@6#^*sVxm+)%}y-uU+~}TN=cw`zL=uCjQoz z2C?e?$;aw0|3|wT#CFU7kIj6N_V(2B-I?oa~>gkE*BJD~MJ1PyU!XJE?F`k4gB7+H{r* zVwIfNg#ILIV*@&_fI}i zZQ8YhSatv8u~FSBh*kGb{-(BTt_ot+{gc0?Hl4eI*lzjnXy)Ckx7DUSYT`Ujc2w^9 z9kuB^6~wChCx2JlHE#v6>i)^!Q=86LL2S4D&)&@EuijUiE>IJzadNVMu7034U9f^! zb^qias!e-V5UcK={3EsLLKVcS`zQZc+qG~7vFiTGKT(@5QbBCD{C98Wi&meiO&6<) z^Elak3eTTuqZY3qR`JYh^3SzhOH>f6?w|Y%ZP$_&#CFU7{LOr+>Pv0c(lxOfCnx*o z>Q~yXWh#hO_fP({wyReKvFiTGztKi5TS2V4fAVj&UCUJvtL~rtJ8jqU6~uPS|Dw&j zclEutYlWIPkCTf|;rRz`)QT0vDxP^w{-b`LT&aRsb^qi)m5-JyR}kAR|4TLVRjQx$ z6Va+Qu^J~Q`{(K}`uV3%1+nV>$$!<)ORH57tL~rtH*M7F6~wChC;wePQ>{@!th#^l zKlBsVnia%$%YW}?-naTwKZ*6LiSszwdkW8g>F0#CDu`7)^P2o`{S?=~f>?F`@-9P#N^mE|46~wChC;v}BC9YRN zth#^lHf`7X6~wChC-0)|+Mt5iZu#%q%r~s2(spfB6X$WV?-ZWfwNV>a5UY6RHF=`# z+N6S5b^qk4wrkT0V!P#kKr`R0%CudZ*Tia^oa~>gxwdPI3S!m$lNZ{qK^4TR`zJ58 zQCn6JtL~q?(spfCL9Due@`ko+>k4AK<$uFwKDe4%+qF$ioX5!xr|>+DHfq}nVinK4 zCZAT@wOs|V>i)^6({^oNL2S4DZ`RCrsHWF;4XKIMI62urS7*?6?N~vqx_|N+wOu<^ z5UcK=d?szw&K1O}`zPi&)yY{UhR^2~&H*M5@6~wChC!b5(wSNV%>i)^+)^;6GL2S4D4{heds_xpZ18d?u zP7a;Ia}RCQK^4R*o_S3^kGAXJ3S!m$lh3Q|I;4WwZu#G$N6RUA@vVX45 zukAXlf>?F`V_xC&ys<$riHKfYRA+jT-s ztj5X7{<*q@w(GHfZ6~wChCtpU}bxH-X z-SU4_Ge5QJrR_ScCeGvJQB!zcRvUGC1+j`}UXw4U?K-1^Satv8%WJ#NtRS{q{*Q0w zXH~tmU1!(CYMh+xpQ|fqyUwW~R^31OirTJoD~MJ1Pri~i>bweK)%}yNtnE6#f>?F` z#~|yjgyo8b9GH^*X0$&s{1GJtL?g?f>?F`oa~>gTWGuPtsqw2KlvbS*L@Yl zs{1G3QX6%D1+nV>$+yyWJy1ccx_|PmwOwN?i0zjDG0ptJYOuEJp_(|4lVhgvyp1;M z;R<3E&%7qzR@?PR1+nV>$+y#XjjJHGTmJ8E=8sm}Yr7t+iPbnc**{lz&~`muL9Due z@*&!;Cn|_l_fNi~HtNX=V%7bV@1*T|s)AT`|KvMsyPmEfwp;!mZ06&uU9?@#)Wmt5 zd~gcSyK1AJtsqwM%xm(Y+OFp+h*kGbzMHn|`3hpY<^R!U{zA38w(G^3SdEjD{d08> zZP!Z`#H#xz-&5Q5as{#K{>k^!M!ix&th#^ly|rDhRuHT1pL`!}*J~BTcFX_xW^~fugUki)?O(RRIGL9Due^5NR94=RZ5 zmj4OO{KM){ZP!ONaULfpOyT)3ZPdpV#44V7O+G@~^+^S>>i)?O*LHncL2S4Dztzk? ztB%lieO?o*adNVMt{$oF`l5nZb^qi?X}i9xAXeQ!`O(^_uPTUD_fLL|w(IK(V%7bV zAFJ*9rh?dR`Tww)e_I`=?fR}J&g0~VQ+PgJ8})q!v5IG2lb@jN`k{hYb^qiiYP)`{ zAhuinKWpYc>1T^U)z3At8Yd_F=jupp*Dv}BBZyV^Pkyqt>sS4R5yYzdCqG3S^_zae z2x8U!lb@>X`d#1W2C?e?$xqXE{h^;ig4k~P|F)U`sh>Z#t^TTs^EmnK6rRt}M*Xdy zLV{SuGq1_d)OP)&pF)CIb^qjNX}c!rd*L9qTmFA)=Ktzvlp)psYGO4`PWI2$bF^Lm z=_i#SR^31Ox!SI_2E?lSCqGXc)ujQk>i)^k*LF?SfLL|^ZRJQMgwBi{gYp&?V7p)vFiTGFV{v*(|}lY|KwL_yQXbGth#^lE45wI zH6XTI{*z`ted8)^*9i)@Z z(ss?+fY@&NpT3!QZQQKw>Q)oyadP^Gd%i^*HCF>-)%}y-s_mM)0kP`-$!}Aec5gsz zxBSoC%zHG(s7>doiPbnc**{lrSDVh;fLL|^;~ur?LN#$7C%aDJ`Chf@!VQR3JoB3TK5f?`4Tx3u zPkz7JbkPRHcFTW{X1-YC0d3dfHL)5eC;R8>SZ&u54Tx3uPyV2`Ysm)0s{1E@NE@|O z17g+vlRvEOTDk$T>i)?e(RMAJ>Z$PZNfAYt*UA-F++b#c#HS-l3PsHD{)x>I?oa~>gPsZP}H6T{qKlxMfw`>iF zRrgQ+bo?z_17g+vlaJTm^{m=}Satv8&**RB`ZOT6TmE}B^VJ&9>Tlv!uZiu=)LY(T8KfASaf_d$Id5Zf*PD>U`zL=@e_J=O0kP`-$zRjo)~(Zk*lzh> zt(mXen4rHiTCXO~k6b-}-IVfLL|^fINuX0`EMJr}F(8l%3IhJMw!r{4OlHZCzAJGs*P zobRo-eyazGD$e|-zpvgstq~-u-JkycdNa6YkZ6zhUwP=)iU;b=;M(I7)v=TB`SbL_ zdNa6Akf?Tl`iJVx;JQJg+WqMtuJ=>x1&M0+r+=j09tG>_q@p^l>ageCu%y0T9>h0ksL899I>7T5(hnoh8_IUrbhJLen zs@`F3J}yxmJNceJPoJ(gj9UbWYWJsqrrt1a86>LRpZ?i;!?;zDsCIw)=jsjP)`oe)p`rLOOU8`fBM(zUD&QcqT2oGU#~Zn zy9J4A_osiO-c;@$B&yw?{>^$*xkr#_kN4kp==Y4b>P_Wd;}Y|+liR+}`R#gNws(-I z;>>UQcj|5CK0%_|{psJWx0(9}iS~H^orZqDn4q?6|8a@x*va?&c{*Wj*8xGI+WqM# zs_i;3NL0H&{lv9V2L*|0_ottvw(H;^QSJWplh$?}5+vH={r4RDLu0bquEWM9=3^)K ze4q2=wNZx$i7L+grk|p=>xdvx?f&#r)^;5kB--Qs_Z#}7VyfD%qsJwxV<+G9=V{b- z9TOy~-Jd>dyN(SK)$UI}s*O4>NL0H&{nWKx#|MdO_ottxw(Eo-(H`%A=+K`S)7EyK zG%hh8J9+5)oTsaeIyp#GappJu^tD~51c_?*r=Ov=>(n689`AqD(4Q7F)^?pfE>Rsj z`JO*dXR7TwBS=)cKmE+LU1tV~YWJs~r8erUAW`l9^t0A>ogE~q-JgE8+OBhgM0>pd zi9>&G%wF4d-nhhk?Bt2>b3Ssop+=&LGr#GN9`3S{sCIw)V~4wJB--QsPm8hsz58p& z54YG*9Xt7+KRx%?NL0H&{mH{!HWJnDPk-ugmyJZV`_rF3++`zC?f&#<4tLo|w8#6O z8)N-lbt_=(E*pvY*vWH)=B(k?+Z^?Of%Ai$v;Hq|!MLaVrk|_!|H2^WuARRqc*<}3 zd1~7)4szbw@JoWH{HC9;_W9Bv=dXRfEO^Rq`UPr}FAs9T+TJUIr~Ia0sCM%5VB5YU{2Ka>?4d8-l0&reCV| z?ZzONt{uB6c#1GLu$HNfx;aQxyFdN1wOv0564mZczg%tC4}(Ot`_q4*w(CbhqT2oG zKUmxK;~-J({`AY&cKswsRJ%X@hiap42@=)rPygZCuAc^pYWJuANNv~8f<(3Z(|@$K z>*qnD%}Io>A8x&UtTyV_AW`l9^dGP7`bChac7OU$)OP(cNL0H&{U>X?eibCD-Jkwb zwOzjs64mZczd~)-Z-PX%`_q5AHtMz@QSJWppQ-KoZIGyTfBMhXcKt3$RJ%X@=W4rt zA0*nGMEI8B*4yW6qizop)$ULKh1#w^1c_?*r~hJY*B^sKwfob5skZA+L899I>Azgt z_2(c_?f&#D)^`0RNL0H&{a0$E?g$dq?oa>K+OEF_iE8(!|62V`{acWzc7OVn>hI{^ zgG8H?2;Vl`di#3)-MuqNRJ%X@H|lTgKY~QH`_q53{x;tgB&yw?{#*6;_@6^3KG@sPrpk2&Hi_gsCIw)Z`a@WyMsiv`_q4?{;vNgNL0H&{da2z{u?B!-Jkw@ z^|$|jL88q`gzp$`y?wv7;+`N;?f&$u);`=DB&yw?ezn?@`+`KZ`_r#p8*+b;sCIw) zHEMGn2olxqPrqjE%!5Iq+WqO*s$F_0NL0H&{o1i~JRBsd-JgD)+O9`}M78_VuUp&o zXpm@g65+dtTW{;tc0Cp(s@)9Yt?f&$e)pk7>B&yw?e)HO{=YvF> zlL$XF+H9?Z7c7OWa zYr7^)64mZczejD?L`kCB{pt6t?V31ARJ%X@UbS75B#CPGr{BA_Ytkgq<|M+e4Y%I* zsqLC9NmRQ({l2wblP8I4_ov^lwrh$cQSJWp``30&nIx*+pZNBFqh}!)m*xO%m1a zPk(rA*K|pu+WqN|sO_3QNmRQ({gJg@GbD*>_oqLqwrj>DQSJWpN7r`Elq9O%pZ=KI zu9=fWwfoZ_TiZ2DlBjln`r~T5W=#^+?oWSwZP#o`qRmN!xq)>;ZP)BcqT2oGPps{l zBS}=dKmAFyU2`UhYWJrCdd~S|CZZIf*bgu+FOOS};jeyFdNewOtD(iE8(! zKc}{9;UrP*{`BY8b}f=5s@#^Z*V0L%+WqM-t?gPSNmRQ({bjLR zESn^%-JkyQ*gcj@64mZce?{yOA4n3_?oWSZ>=_?S64mZce^u-i%O{Cy_ou%)_KpuF ziE8(!zb5vH4=0Ig_ou%$_KlAui8dz@UVOOqc3tcjA59X~?oWSx>>nRX64mZce?uG) zA5Rk1?oWSX92lQS64mZce^VS3pG*?f?oWSn92}oY64mZc|AROrR!9=n?oa>2I5a+; zB&yw?{zq|Gd?ra$yFdMp3AbPydTJE>=tu)$ULK%Q!y1 zk|e6#pZ-^ILVPtzRJ%X@uj9n{T9RmU65$nwTW`OKlVYVLQSJWpx5dfv^(0a4{`9|% zQ{o#*qT2oGe;235H_*_X-Jkw{aaC-XB&yw?{+_rxHcArJ?oWSjToW57iE8(!zb~$h zO_D^l`_tba*TtquqT2oGABgK?vn0{xB*JSBx85F%8)EY$QSJWp55=Oj_>{`Ak)mz!OZM78_VKVM&Nc1;r1?oa!p)4yC_ZuU$P)$ULKN`1N6D@n9DiSV|=t+!X}%gx?NqT2oGU#l-S`y`2K z_osiozTE7aB&yw?{*C%_vtN>^c7OUe>&wmlNut{Q>EEg^HwPq%YWJsqyT05Um?Wy* zpZ=Zta&u6UsCIw)ck9c|!AYXp{plyD?K&h$v^k0J?!&FO32VC!O%m1aPd`y@*I`Mb z+WqM#uI)NJNmRQ({Uo(rMj!hEP?oU5eZP#&0qRmN!4;*g2MQzvdNut{Q>9e-$gd|bz{`8~T zt`n0)wfoagUE6h1lBjln`e|yrPEHcl?oU5$ZPzJDqT2oGr>pHcHAz&vKmGKzU8g09 zYWJs~p|iE8(!pSiZ{tRzwG{`9ldcAcFh zs@!p)6Z7hb#9WVc7OWWYrD=%64mZcKSyoX`AMSM{pshd?YbaIRJ%X@ zT(w;nCW&hIr=PpF>!Kvl<|M+W4!7RssqMNrNmRQ({k*kZmn4a5_ott)w(HU)QSJWp z^VfD=mL#g(pMHVbuFI1|wfoaASle|)lBjln`h{w{u1pfu?oYpPZP!&vqT2oG7pd*K zI!RQ!KmDS$UDqUuHYX82f4KFwSZ&v}Nut{Q=@+lDsQFl0>!p(=Suob#s!ac7OV1YrB4sB&yw?e!1GNA0~-v z_ox3rZP$;IM4OWcUpd@*`(SO?kCQ~T`_nI9+x3$qQSJWpAFA!TB}r7fKmCVmyMCG^ zs@Nut{Q=|5iE^@}7??f&$isO|b?lBjln z`cKw&{VGYcIf?Mi!>zYZ)pq?lNmRQ({R*{Rzey6+?oa>e+OFG@M78_Vf2OwUw@ISf z{pmkj+x5F7QSJWppR4WqeUhklfBMhYcHN#Ns@)%PD+WqOjUE6hclBjln`tQ_s{U=FOyFdMR zYrFoNB&yw?{(H4u|4S0p?oa>y+OB(&M78_VuUgx6Z<45XfBMyGyY5R8)$UKfdTrPJ zNute3gzp?~y{%E(^+1xSc7OUcYr7sy64mZczgBJ6LrJ3A{pr`P?Rq##RJ%X@I<;Mo zB#CPGr(d_W>(L}p?f&%Z)pk9WB&yw?e*N06$CE_0`_pew+x0|}sCIw)4QsoeOcHHQ zB7Dzq>usajuBVbjwfoa=T-)_@lBjln`b}!Po=Fnb?oYpIZP&9&qT2oGH>>S>E=g3o zKmF#lUC$?pYWJt#qPFXWBvI}D^jp?;y_h7b-JgD|+OC(9M78_VZ(ZB& zTW{OccD<4$s@+K{_?f&$;)ONj-B&yw?e%IQrcaub$lL)^s+QvyVtryFdMjwOw~<{y!$c7OUaYr7U0 zk*Icm`m<`g795eNc7OV_Yr7U2k*Icm`g3Z#79NqPc7OVFYr7U1k!W)g;n{~ut{A zE*ptz_ottGxXVVO+WqP09qzJ`sCIw)`G>n~B&yw?e!<}`8;NT7r{{(miE8(!Uv#+3 zMxxsN=@%dFvXQ8EfBGedyKE$?-JgEx;Vv7AHYX?Wp8^Ufpnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} z0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7j zD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7> z3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36A zpnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} U0tzUgfC36Apnw7j{J# +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "cube_texture_and_coords.h" +#include "models.h" +#include "triangle.h" +#include + + +#define PATH "./" + +#define IMAGE_SIZE_WIDTH 1920 +#define IMAGE_SIZE_HEIGHT 1080 + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; + MODEL_T model; +} CUBE_STATE_T; + +static void init_ogl(CUBE_STATE_T *state); +static void init_model_proj(CUBE_STATE_T *state); +static void reset_model(CUBE_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); +static void redraw_scene(CUBE_STATE_T *state); +static void update_model(CUBE_STATE_T *state); +static void init_textures(CUBE_STATE_T *state); +static void exit_func(void); +static volatile int terminate; +static CUBE_STATE_T _state, *state=&_state; + +static void* eglImage = 0; +static pthread_t thread1; + + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_SAMPLES, 4, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + + // get an appropriate EGL frame buffer configuration + // this uses a BRCM extension that gets the closest match, rather than standard which returns anything that matches + result = eglSaneChooseConfigBRCM(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); + assert(state->context!=EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + + // Set background color and clear buffers + glClearColor((0.3922f+7*0.5f)/8, (0.1176f+7*0.5f)/8, (0.5882f+7*0.5f)/8, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glClearDepthf(1.0); + glDepthFunc(GL_LEQUAL); + + float noAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(CUBE_STATE_T *state) +{ + float nearp = 0.1f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(CUBE_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 1.2f*1.5f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void update_model(CUBE_STATE_T *state) +{ + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 10.0f) + distance = 10.f; + else if (distance <= 1.0f) + distance = 1.0f; + + return distance; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void redraw_scene(CUBE_STATE_T *state) +{ + // Start with a clear screen + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + draw_wavefront(state->model, state->tex); + + eglSwapBuffers(state->display, state->surface); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void init_textures(CUBE_STATE_T *state) +{ + // the texture containing the video + glGenTextures(1, &state->tex); + + glBindTexture(GL_TEXTURE_2D, state->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + /* Create EGL Image */ + eglImage = eglCreateImageKHR( + state->display, + state->context, + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)state->tex, + 0); + + if (eglImage == EGL_NO_IMAGE_KHR) + { + printf("eglCreateImageKHR failed.\n"); + exit(1); + } + + // Start rendering + pthread_create(&thread1, NULL, video_decode_test, eglImage); + + // setup overall texture environment + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + + // Bind texture surface to current vertices + glBindTexture(GL_TEXTURE_2D, state->tex); +} +//------------------------------------------------------------------------------ + +static void exit_func(void) +// Function to be passed to atexit(). +{ + if (eglImage != 0) + { + if (!eglDestroyImageKHR(state->display, (EGLImageKHR) eglImage)) + printf("eglDestroyImageKHR failed."); + } + + // clear screen + glClear( GL_COLOR_BUFFER_BIT ); + eglSwapBuffers(state->display, state->surface); + + // Release OpenGL resources + eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroySurface( state->display, state->surface ); + eglDestroyContext( state->display, state->context ); + eglTerminate( state->display ); + + printf("\ncube closed\n"); +} // exit_func() + +//============================================================================== + +int main () +{ + bcm_host_init(); + printf("Note: ensure you have sufficient gpu_mem configured\n"); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + + // initialise the OGLES texture(s) + init_textures(state); + + //state->model = cube_wavefront(); + state->model = load_wavefront("/opt/vc/src/hello_pi/hello_teapot/teapot.obj.dat", NULL); + + while (!terminate) + { + update_model(state); + redraw_scene(state); + } + exit_func(); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h b/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h new file mode 100755 index 0000000..0f50e93 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h @@ -0,0 +1,30 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + + +void* video_decode_test(void* arg); diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/video.c b/host_applications/linux/apps/hello_pi/hello_teapot/video.c new file mode 100755 index 0000000..4baae8f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/video.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video decode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +static OMX_BUFFERHEADERTYPE* eglBuffer = NULL; +static COMPONENT_T* egl_render = NULL; + +static void* eglImage = 0; + +void my_fill_buffer_done(void* data, COMPONENT_T* comp) +{ + if (OMX_FillThisBuffer(ilclient_get_handle(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed in callback\n"); + exit(1); + } +} + + +// Modified function prototype to work with pthreads +void *video_decode_test(void* arg) +{ + const char* filename = "/opt/vc/src/hello_pi/hello_video/test.h264"; + eglImage = arg; + + if (eglImage == 0) + { + printf("eglImage is null.\n"); + exit(1); + } + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *clock = NULL; + COMPONENT_T *list[5]; + TUNNEL_T tunnel[4]; + ILCLIENT_T *client; + FILE *in; + int status = 0; + unsigned int data_len = 0; + + memset(list, 0, sizeof(list)); + memset(tunnel, 0, sizeof(tunnel)); + + if((in = fopen(filename, "rb")) == NULL) + return (void *)-2; + + if((client = ilclient_init()) == NULL) + { + fclose(in); + return (void *)-3; + } + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return (void *)-4; + } + + // callback + ilclient_set_fill_buffer_done_callback(client, my_fill_buffer_done, 0); + + // create video_decode + if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; + + // create egl_render + if(status == 0 && ilclient_create_component(client, &egl_render, "egl_render", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) + status = -14; + list[1] = egl_render; + + // create clock + if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[2] = clock; + + memset(&cstate, 0, sizeof(cstate)); + cstate.nSize = sizeof(cstate); + cstate.nVersion.nVersion = OMX_VERSION; + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = 1; + if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + status = -13; + + // create video_scheduler + if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[3] = video_scheduler; + + set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); + set_tunnel(tunnel+1, video_scheduler, 11, egl_render, 220); + set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); + + // setup clock tunnel first + if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) + status = -15; + else + ilclient_change_component_state(clock, OMX_StateExecuting); + + if(status == 0) + ilclient_change_component_state(video_decode, OMX_StateIdle); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf; + int port_settings_changed = 0; + int first_packet = 1; + + ilclient_change_component_state(video_decode, OMX_StateExecuting); + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + // loop if at end + if (feof(in)) + rewind(in); + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to egl_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + // Set egl_render to idle + ilclient_change_component_state(egl_render, OMX_StateIdle); + + // Enable the output port and tell egl_render to use the texture as a buffer + //ilclient_enable_port(egl_render, 221); THIS BLOCKS SO CAN'T BE USED + if (OMX_SendCommand(ILC_GET_HANDLE(egl_render), OMX_CommandPortEnable, 221, NULL) != OMX_ErrorNone) + { + printf("OMX_CommandPortEnable failed.\n"); + exit(1); + } + + if (OMX_UseEGLImage(ILC_GET_HANDLE(egl_render), &eglBuffer, 221, NULL, eglImage) != OMX_ErrorNone) + { + printf("OMX_UseEGLImage failed.\n"); + exit(1); + } + + // Set egl_render to executing + ilclient_change_component_state(egl_render, OMX_StateExecuting); + + + // Request egl_render to write data to the texture buffer + if(OMX_FillThisBuffer(ILC_GET_HANDLE(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed.\n"); + exit(1); + } + } + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); + + ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + } + + fclose(in); + + ilclient_disable_tunnel(tunnel); + ilclient_disable_tunnel(tunnel+1); + ilclient_disable_tunnel(tunnel+2); + ilclient_teardown_tunnels(tunnel); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return (void *)status; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt new file mode 100755 index 0000000..b253f3f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt @@ -0,0 +1,9 @@ +set(EXEC hello_tiger.bin) +set(SRCS main.c tiger.c) +add_definitions(-D__RASPBERRYPI__) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/Makefile b/host_applications/linux/apps/hello_pi/hello_tiger/Makefile new file mode 100755 index 0000000..f95e244 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/Makefile @@ -0,0 +1,8 @@ +OBJS=main.o tiger.o +BIN=hello_tiger.bin + +#LDFLAGS+= +CFLAGS+=-D__RASPBERRYPI__ + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/license.txt b/host_applications/linux/apps/hello_pi/hello_tiger/license.txt new file mode 100755 index 0000000..07599fc --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/license.txt @@ -0,0 +1,53 @@ +OpenVG 1.1 Reference Implementation +----------------------------------- + +Copyright (c) 2007 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and /or associated documentation files +(the "Materials "), to deal in the Materials without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Materials, +and to permit persons to whom the Materials are furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR +THE USE OR OTHER DEALINGS IN THE MATERIALS. + + +Path data for the Tiger sample program has been extracted from Ghostscript's +tiger.eps example file distributed under GNU General Public License. + +Ghostscript's License document: +" The files in the src, lib, toolbin, examples, doc and man + directories (folders) and any subdirectories (sub-folders) + thereof are part of GPL Ghostscript. + + The files in the Resource directory and any subdirectories thereof + are also part of GPL Ghostscript, with the explicit exception of + the files in the CMap subdirectory. The CMap files are copyright + Adobe Systems Incorporated and covered by a separate license + which permits only verbatim distribution. + + GPL Ghostscript is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + GPL Ghostscript is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program so you can know your rights and responsibilities. + It should be in a file named doc/COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place Suite 330, Boston, MA + 02111-1307, USA." diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/main.c b/host_applications/linux/apps/hello_pi/hello_tiger/main.c new file mode 100755 index 0000000..a15dda6 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/main.c @@ -0,0 +1,533 @@ +/*------------------------------------------------------------------------ + * + * OpenVG 1.0.1 Reference Implementation sample code + * ------------------------------------------------- + * + * Copyright (c) 2007 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + * + *//** + * \file + * \brief Tiger sample application. Resizing the application window + * rerenders the tiger in the new resolution. Pressing 1,2,3 + * or 4 sets pixel zoom factor, mouse moves inside the zoomed + * image (mouse move works on OpenGL >= 1.2). + * \note + *//*-------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#define UNREF(X) ((void)(X)) + +#ifdef HG_FLAT_INCLUDES +# include "openvg.h" +# include "vgu.h" +# include "egl.h" +#else +# include "VG/openvg.h" +# include "VG/vgu.h" +# include "EGL/egl.h" +#endif + +#include "tiger.h" + +/*--------------------------------------------------------------*/ + +#ifdef __RASPBERRYPI__ +static float rotateN = 0.0f; +#endif +const float aspectRatio = 612.0f / 792.0f; +int renderWidth = 0; +int renderHeight = 0; +EGLDisplay egldisplay; +EGLConfig eglconfig; +EGLSurface eglsurface; +EGLContext eglcontext; + +/*--------------------------------------------------------------*/ + +typedef struct +{ + VGFillRule m_fillRule; + VGPaintMode m_paintMode; + VGCapStyle m_capStyle; + VGJoinStyle m_joinStyle; + float m_miterLimit; + float m_strokeWidth; + VGPaint m_fillPaint; + VGPaint m_strokePaint; + VGPath m_path; +} PathData; + +typedef struct +{ + PathData* m_paths; + int m_numPaths; +} PS; + +PS* PS_construct(const char* commands, int commandCount, const float* points, int pointCount) +{ + PS* ps = (PS*)malloc(sizeof(PS)); + int p = 0; + int c = 0; + int i = 0; + int paths = 0; + int maxElements = 0; + unsigned char* cmd; + UNREF(pointCount); + + while(c < commandCount) + { + int elements, e; + c += 4; + p += 8; + elements = (int)points[p++]; + assert(elements > 0); + if(elements > maxElements) + maxElements = elements; + for(e=0;em_numPaths = paths; + ps->m_paths = (PathData*)malloc(paths * sizeof(PathData)); + cmd = (unsigned char*)malloc(maxElements); + + i = 0; + p = 0; + c = 0; + while(c < commandCount) + { + int elements, startp, e; + float color[4]; + + //fill type + int paintMode = 0; + ps->m_paths[i].m_fillRule = VG_NON_ZERO; + switch( commands[c] ) + { + case 'N': + break; + case 'F': + ps->m_paths[i].m_fillRule = VG_NON_ZERO; + paintMode |= VG_FILL_PATH; + break; + case 'E': + ps->m_paths[i].m_fillRule = VG_EVEN_ODD; + paintMode |= VG_FILL_PATH; + break; + default: + assert(0); //unknown command + } + c++; + + //stroke + switch( commands[c] ) + { + case 'N': + break; + case 'S': + paintMode |= VG_STROKE_PATH; + break; + default: + assert(0); //unknown command + } + ps->m_paths[i].m_paintMode = (VGPaintMode)paintMode; + c++; + + //line cap + switch( commands[c] ) + { + case 'B': + ps->m_paths[i].m_capStyle = VG_CAP_BUTT; + break; + case 'R': + ps->m_paths[i].m_capStyle = VG_CAP_ROUND; + break; + case 'S': + ps->m_paths[i].m_capStyle = VG_CAP_SQUARE; + break; + default: + assert(0); //unknown command + } + c++; + + //line join + switch( commands[c] ) + { + case 'M': + ps->m_paths[i].m_joinStyle = VG_JOIN_MITER; + break; + case 'R': + ps->m_paths[i].m_joinStyle = VG_JOIN_ROUND; + break; + case 'B': + ps->m_paths[i].m_joinStyle = VG_JOIN_BEVEL; + break; + default: + assert(0); //unknown command + } + c++; + + //the rest of stroke attributes + ps->m_paths[i].m_miterLimit = points[p++]; + ps->m_paths[i].m_strokeWidth = points[p++]; + + //paints + color[0] = points[p++]; + color[1] = points[p++]; + color[2] = points[p++]; + color[3] = 1.0f; + ps->m_paths[i].m_strokePaint = vgCreatePaint(); + vgSetParameteri(ps->m_paths[i].m_strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(ps->m_paths[i].m_strokePaint, VG_PAINT_COLOR, 4, color); + + color[0] = points[p++]; + color[1] = points[p++]; + color[2] = points[p++]; + color[3] = 1.0f; + ps->m_paths[i].m_fillPaint = vgCreatePaint(); + vgSetParameteri(ps->m_paths[i].m_fillPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(ps->m_paths[i].m_fillPaint, VG_PAINT_COLOR, 4, color); + + //read number of elements + + elements = (int)points[p++]; + assert(elements > 0); + startp = p; + for(e=0;em_paths[i].m_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, (unsigned int)VG_PATH_CAPABILITY_ALL); + vgAppendPathData(ps->m_paths[i].m_path, elements, cmd, points + startp); + i++; + } + free(cmd); + return ps; +} + +void PS_destruct(PS* ps) +{ + int i; + assert(ps); + for(i=0;im_numPaths;i++) + { + vgDestroyPaint(ps->m_paths[i].m_fillPaint); + vgDestroyPaint(ps->m_paths[i].m_strokePaint); + vgDestroyPath(ps->m_paths[i].m_path); + } + free(ps->m_paths); + free(ps); +} + +void PS_render(PS* ps) +{ + int i; + assert(ps); + vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER); + + for(i=0;im_numPaths;i++) + { + vgSeti(VG_FILL_RULE, ps->m_paths[i].m_fillRule); + vgSetPaint(ps->m_paths[i].m_fillPaint, VG_FILL_PATH); + + if(ps->m_paths[i].m_paintMode & VG_STROKE_PATH) + { + vgSetf(VG_STROKE_LINE_WIDTH, ps->m_paths[i].m_strokeWidth); + vgSeti(VG_STROKE_CAP_STYLE, ps->m_paths[i].m_capStyle); + vgSeti(VG_STROKE_JOIN_STYLE, ps->m_paths[i].m_joinStyle); + vgSetf(VG_STROKE_MITER_LIMIT, ps->m_paths[i].m_miterLimit); + vgSetPaint(ps->m_paths[i].m_strokePaint, VG_STROKE_PATH); + } + + vgDrawPath(ps->m_paths[i].m_path, ps->m_paths[i].m_paintMode); + } + assert(vgGetError() == VG_NO_ERROR); +} + +PS* tiger = NULL; + +/*--------------------------------------------------------------*/ + +void render(int w, int h) +{ +#ifndef __RASPBERRYPI__ + if(renderWidth != w || renderHeight != h) +#endif + { + float clearColor[4] = {1,1,1,1}; + float scale = w / (tigerMaxX - tigerMinX); + + eglSwapBuffers(egldisplay, eglsurface); //force EGL to recognize resize + + vgSetfv(VG_CLEAR_COLOR, 4, clearColor); + vgClear(0, 0, w, h); + + vgLoadIdentity(); +#ifdef __RASPBERRYPI__ + vgTranslate(w * 0.5f, h * 0.5f); + vgRotate(rotateN); + vgTranslate(-w * 0.5f, -h * 0.5f); +#endif + vgScale(scale, scale); + vgTranslate(-tigerMinX, -tigerMinY + 0.5f * (h / scale - (tigerMaxY - tigerMinY))); + + PS_render(tiger); + assert(vgGetError() == VG_NO_ERROR); + + renderWidth = w; + renderHeight = h; + } +#ifndef __RASPBERRYPI__ + eglSwapBuffers(egldisplay, eglsurface); + assert(eglGetError() == EGL_SUCCESS); +#endif +} + +/*--------------------------------------------------------------*/ + +void init(NativeWindowType window) +{ + static const EGLint s_configAttribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_LUMINANCE_SIZE, EGL_DONT_CARE, //EGL_DONT_CARE + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_SAMPLES, 1, + EGL_NONE + }; + EGLint numconfigs; + + egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(egldisplay, NULL, NULL); + assert(eglGetError() == EGL_SUCCESS); + eglBindAPI(EGL_OPENVG_API); + + eglChooseConfig(egldisplay, s_configAttribs, &eglconfig, 1, &numconfigs); + assert(eglGetError() == EGL_SUCCESS); + assert(numconfigs == 1); + + eglsurface = eglCreateWindowSurface(egldisplay, eglconfig, window, NULL); + assert(eglGetError() == EGL_SUCCESS); + eglcontext = eglCreateContext(egldisplay, eglconfig, NULL, NULL); + assert(eglGetError() == EGL_SUCCESS); + eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext); + assert(eglGetError() == EGL_SUCCESS); + + tiger = PS_construct(tigerCommands, tigerCommandCount, tigerPoints, tigerPointCount); +} + +/*--------------------------------------------------------------*/ + +void deinit(void) +{ + PS_destruct(tiger); + eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + assert(eglGetError() == EGL_SUCCESS); + eglTerminate(egldisplay); + assert(eglGetError() == EGL_SUCCESS); + eglReleaseThread(); +} + +/*--------------------------------------------------------------*/ + +#ifdef WIN32 +#pragma warning(disable:4115) /* named type definition in parentheses (this comes from a visual studio include file) */ +#include + +static LONG WINAPI windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CLOSE: + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_PAINT: + { + RECT rect; + InvalidateRect(hWnd, NULL, 0); + GetClientRect(hWnd, &rect); + render(rect.right - rect.left, rect.bottom - rect.top); + return 0; + } + default: + break; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +/*--------------------------------------------------------------*/ + +int main(void) +{ + HWND window; + { + WNDCLASS wndclass; + wndclass.style = 0; + wndclass.lpfnWndProc = windowProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = (HINSTANCE)GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon(wndclass.hInstance, MAKEINTRESOURCE(101)); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = "MainWndClass"; + if (!wndclass.hIcon) + wndclass.hIcon = LoadIcon(NULL, IDI_EXCLAMATION); + RegisterClass(&wndclass); + } + + window = CreateWindow( + "MainWndClass", + "OpenVG Tiger sample (rendering, please wait)", + WS_OVERLAPPEDWINDOW, + 200, 200, 400, (int)(400.0f / aspectRatio), + NULL, + NULL, + (HINSTANCE)GetModuleHandle(NULL), + NULL); + if (!window) + return -1; + + init((NativeWindowType)window); + + { + MSG msg; + ShowWindow(window, SW_SHOW); + while (GetMessage(&msg, NULL, 0, 0)) + { + DispatchMessage(&msg); + if (msg.message == WM_QUIT) + break; + } + } + + deinit(); + + DestroyWindow(window); + return 0; +} + +/*--------------------------------------------------------------*/ + +#elif defined __APPLE__ + +/*--------------------------------------------------------------*/ + +#include + +//TODO + +#elif defined __RASPBERRYPI__ +#include "bcm_host.h" +int main(void) +{ + uint32_t width, height; + bcm_host_init(); + int s; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + s = graphics_get_display_size(0 /* LCD */, &width, &height); + assert( s >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = width; + dst_rect.height = height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = width << 16; + src_rect.height = height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 1/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = width; + nativewindow.height = height; + vc_dispmanx_update_submit_sync( dispman_update ); + + init(&nativewindow); + + while (1) { + render(width, height); + rotateN += 1.0f; + } + deinit(); + + return 0; +} +#endif + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt b/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt new file mode 100755 index 0000000..6845e24 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt @@ -0,0 +1,263 @@ +OpenVG 1.1 Reference Implementation +----------------------------------- + +Copyright (c) 2007 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and /or associated documentation files +(the "Materials "), to deal in the Materials without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Materials, +and to permit persons to whom the Materials are furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR +THE USE OR OTHER DEALINGS IN THE MATERIALS. + + +Version +------- +Official RI for OpenVG 1.1 +Released: May 13, 2008 + + +Release Notes +------------- + +This release is based on OpenVG 1.1 and EGL 1.3 specifications. +This release is Windows-only, although the source code +compiles at least on Mac OS X 10.5 and Cygwin. Project files are +provided for MSVC 6. + +This archive contains sources for OpenVG RI, VGU and EGL. There's +also a precompiled libOpenVG.dll that contains OpenVG and EGL implementations. + + +Package Structure +----------------- + +bin + win32 + libOpenVG.dll OpenVG Windows .dll + tiger.exe Windows executable of the Tiger sample +lib + libOpenVG.lib MSVC 6 dll import library +ri + openvg_ri.dsp MSVC 6 project file for libOpenVG.dll + src .cpp and .h -files of the reference implementation + win32 Windows backend for EGL + macosx Mac OS X backend for EGL + null null backend for EGL + include Public OpenVG and EGL headers + EGL + egl.h + VG + openvg.h + vgu.h +samples + samples.dsw MSVC 6 workspace file for tiger sample and libOpenVG.dll + samples.dsp MSVC 6 project file for tiger.exe + tiger + main.c + tiger.c + tiger.h +readme.txt +license.txt + + +Samples +------- + +Tiger + +The release contains a sample application that renders an image of a +tiger. Note that the sample doesn't start immediately, since it takes +a few seconds to render the image. Resizing the window rerenders the +image in the new resolution. + + +Known Issues +------------ + +-EGL functionality is incomplete (some functions lack proper error checking, some + attribs may not be processed, etc.) +-When opening samples.dsw, MSVC may complain about missing Perforce connection. Just + ignore that. + + +Changes +------- + +Nov 25, 2008 +-Clamp color transform scale to [-127, 127] and bias to [-1, 1] + +May 13, 2008 +- Changed 8 sample MSAA configs into 32 sample configs +- Changed max gaussian std deviation to 16 (was 128) +- VG_DRAW_IMAGE_MULTIPLY converts luminance to RGB if either paint or image color is RGB +- Fixes A40102 by storing input floats as is + +February 12, 2008 +- fixed arc transformation. +- fixed point along path corner cases. +- partially fixed A40102 by not filtering invalid float input. + +December 12, 2007 +- fixed an overflow bug in vgFillMaskLayer error handling. +- increased accuracy for Gaussian blur. The new code avoids an infinite loop with a very small std dev. +- fixed a bug in Font::find that caused deleted fonts to be returned. + +November 20, 2007 +- reimplemented 1,2 & 4 bits per pixel images +- fixed vgGetParameter for paint +- fixed https://cvs.khronos.org/bugzilla/show_bug.cgi?id=1095 RI handling of child images with shared storage + -vgGetParent: return closest valid ancestor, or image itself if no ancestor was found. +- EGL refactoring & clean up +- divided OS native parts of EGL into separate files that should be included in that platform's build +- added a generic OS backend for EGL (not thread safe, no window rendering) +- fixed https://cvs.khronos.org/bugzilla/show_bug.cgi?id=1943 RI does not handle channel mask correctly for lL/sL/BW1 +- removed EGL_IMAGE_IN_USE_ERROR from vgDrawGlyph(s) +- added configs without an alpha mask to facilitate CTS reference image creation +- implemented more accurate stroking by rendering each stroke part into a coverage buffer and compositing from there + -fixes https://cvs.khronos.org/bugzilla/show_bug.cgi?id=2221 RI errors at end of curved strokes. +- bugfix: joins used path midpoints, interpolateStroke and caps didn't => seams (visible in some G60101 cases) +- vgCreateMaskLayer returns VG_INVALID_HANDLE if the current surface doesn't have a mask layer +- vgRenderToMask bugfix: temp buffer is now cleared between fill and stroke +- bugfix: vgCreateImage returns an error if allowedQuality is zero +- bugfix: vgSetPaint returns an error if paintModes is zero +- bugfix: vgCreateFont doesn't return an error if capacityHint is zero +- bugfix: writeFilteredPixel writes also into luminance formats + +October 12, 2007 +-Upgrade to OpenVG 1.1, including + -Implemented MSAA, added 4 and 8 sample EGLConfigs + -Implemented VG_A_4 and VG_A_1 image formats and EGLConfigs + -Implemented Glyph API + -Implemented new masking functions + -Implemented color transform +-Implemented native EGL backends for Windows & Mac OS X => Fix for bugzilla 1376 RI uses non-standard EGL implementation + *RI now works with CTS generation's native_w32.c (native_ri.c can be removed). + *dependency on GLUT has been removed. GLUT code is still included and can be compiled in instead of native by defining RI_USE_GLUT. +-16 bit EGLConfigs now expose 4 mask bits, 8 bit configs 8, 4 bit configs 4, and 1 bit configs 1. MSAA configs expose one bit per sample. +-EGL now works with any display, not just EGL_DEFAULT_DISPLAY +-Simplification: removed code to handle less than 8 bits per pixel. Smaller bit depths always allocate 8 bits per pixel. +-Changed rasterizer data types to RScalar and RVector2 so that it's possible to alter RIfloat precision without affecting rasterization. +-Accuracy: increased circularLerp precision +-Bugfix: matrix inversion now checks if the input matrix is affine and forces the inverted matrix to be affine as well +-Bugfix: fixed eglCopyBuffers (copied from dst to dst) +-Bugfix: fixed eglCreatePixmapSurface (allowed only VG_sRGBA_8888, didn't give an error if config had more than one sample per pixel) +-Bugfix: bugzilla 2465: RI asserts when setting maximum amount of gradient stops + +February 27, 2007 +-changed to MIT open source license. +-bugfix, bugzilla 820: RGB and luminance are now treated as different color spaces. +-bugfix, bugzilla 1094/1095: vgGetParent now returns the input image in case its parent is already destroyed. + +December 1, 2006 +-bugfix, bugzilla 649: allowed image quality is now taken into account when deciding resampling filter. +-bugfix, bugzilla 650, bad stroking accuracy reported by TK Chan and Mika Tuomi: curve tessellation is now increased from 64 to 256. RI_MAX_EDGES has been increased from 100000 to 262144 to facilitate the increased number of edges. +-bugfix, reported by Chris Wynn, affects I30206: degenerate gradients in repeat mode now render the first stop color instead of the last one. +-changed float to RIfloat, added an option to compile RIfloat into a class to test reduced precision float ops + +September 6, 2006 +-bugfix, bugzilla 591: CLOSE_PATH followed by a MOVE_TO doesn't produce an extra end cap anymore +-abs values of arc axis lengths are taken only just before rendering +-undefined bits of bitfields are now ignored in the API + +September 1, 2006 +-changed colorToInt to use mathematical round-to-nearest as recommended by new language in section 3.4.4. +-implemented VG_PAINT_COLOR_RAMP_PREMULTIPLIED +-implemented VG_STROKE_DASH_PHASE_RESET +-implemented new language for filter channelMasks (section 11.2) +-tangents returned by vgPointAlongPath are now normalized +-implemented VG_MAX_GAUSSIAN_STD_DEVIATION, rewrote Gaussian blur code +-vgGetString: if no context, return NULL. VG_VERSION returns the spec version (1.0). +-bugfix, bugzilla 542: vgSeparableConvolve now convolves the edge color with the horizontal kernel and uses that as the edge color for the vertical pass +-ellipse rh and rv are replaced by their absolute values whenever read for processing, the absolute values are not written into the path data + +August 18, 2006 +-bugfix, M30301: the arguments for vguComputeWarpQuadToQuad were the wrong way around, destination should come before source. +-bugfix, M10102: check for degeneracy in vguComputeWarpSquareToQuad is done before the affinity check so that degenerate affine matrices also produce the bad warp error +-bugfix, bugzilla 491: Chris Wynn's vgComputeWarpSquareToQuad -case. There was a wrong mapping between vertices, (1,1) was mapped to (dx2,dy2) +-bugfix, bugzilla 519: vguPolygon wrong error check. vguPolygon didn't have an error check for the count argument +-bugfix, bugzilla 518: vgGetParameterfv/iv wrong errors. vgGetParametrtfv/iv error checking was for count < 0 instead of count <= 0. +-bugfix, bugzilla 517: wrong cap flag checked in vgPathTransformedBounds +-bugfix, bugzilla 494: egl.h has wrong values for OPENVG_BIT and OPENGL_ES_BIT. Copied the enumerations from the 1.3 egl.h on the Khronos site (OpenKode/egl/egl.h) +-bugfix, bugzilla 492: gradient filter window was biased +-bugfix: when appending paths, there was a loop over coordinates to replace arc axis lengths by their absolute values. However, if the path wasn't empty, the loop accessed wrong coordinates. Fixes: Qingping Zhang's cases 2&3. +-bugfix: image filter write mask was ignored when writing to VG_A_8 images. Fixes: Qingping Zhang's case 13. +-bugfix: if image filter processing format is premultiplied, color channels are clamped to alpha before conversion to destination format +-bugfix: in eglReleaseThread the EGL instance was freed when its reference count reached zero, but the pointer wasn't made NULL, causing the use of uninitialized instance. +-bugfix: vgClearImage didn't clamp the clear color to [0,1] range +-bugfix: a zero-length dash at a path vertex produces a join +-bugfix: vgSetParameter now checks paramType for all object types +-bugfix: convolution filters incorrectly restricted the area read from the source image to the intersection of source and destination image sizes +-bugfix: EGL surface creation now defaults correctly to EGL_COLOR_SPACE_sRGB +-antialiasing is done in the linear color space as the spec recommends. +-image filters clamp the result to [0,1] range. +-Color::pack and Color::convert assert that their input is in [0,1] range +-in case a projective transform is used, VGImageMode is always VG_DRAW_IMAGE_NORMAL +-the default value for VG_FILTER_FORMAT_LINEAR is now VG_FALSE +-added Matrix::isAffine for easy affinity check +-Color::clamp clamps color channels to alpha for premultiplied colors +-VG_BLEND_LIGHTEN: color channels cannot exceed alpha anymore +-RI now supports flexible pixel formats. Any bit depth for RGBA is now supported. +-eglGetProcAddress is now properly implemented, it returns a function pointer for eglSetConfigPreferenceHG extension +-eglQueryString now returns "eglSetConfigPreferenceHG" for EGL_EXTENSIONS +-location of egl.h in RI. use EGL/egl.h, VG/openvg.h, VG/vgu.h +-OpenVG 1.0.1 spec changes + +use the latest openvg.h + +2.8: AA happens in linear space + +3.4: alpha channel depth of zero results in alpha=1 when read + +4.1: return VG_NO_CONTEXT_ERROR from vgGetError in case no context is current + +5.1: VG_SCREEN_LAYOUT (default = screen layout of the display) + +5.2, 5.3: vgSet, vgGet, vgSetParameter, vgGetParameter: handling of invalid values of count + +5.2.1: new default for VG_FILTER_FORMAT_LINEAR is VG_FALSE + +8.5.3: get rid of VG_PATH_DATATYPE_INVALID and VG_IMAGE_FORMAT_INVALID enums + +10.2: get rid of old extension image formats, add the official ones + +10.5: when reading/writing pixels, clamp color channels to [0, alpha] + +10.8: when a projective transform is used, always use VG_DRAW_IMAGE_NORMAL mode + +10.8: VG_DRAW_IMAGE_MULTIPLY: if color spaces of paint and image don't match, no conversion takes place, result is in image color space + +12.4: clamp the result of additive blend to [0,1] + +October 20, 2005 +-Gradients are filtered to avoid aliasing +-Subpaths that ended with a close path segment were capped and joined incorrectly. Fixed. +-Alpha mask was allocated per context, not per EGL surface. Fixed. + +August 22, 2005 +-Updated to spec amendment +-Fixed bugs +-Implemented eglChooseConfig and eglReleaseThread + +July 22, 2005 +-Updated to 18th July 2005 version of the OpenVG 1.0 spec. +-Updated to 20th July 2005 version of the EGL 1.2 spec. +-Fixed bugs. +-openvg.h, vgu.h and egl.h are now contained in include/vg directory. + +May 4, 2005 +-Updated to April 26th 2005 version of the OpenVG 1.0 spec. +-Can share images, paths, and paint between contexts. +-Fixed path tangent computation. +-Implemented image filters. +-Fixed bugs. +-Changed directory structure a bit. + +March 29, 2005 +-Updated to March 28th 2005 version of the OpenVG 1.0 spec. +-Changed rasterizer to use 32 samples per pixel in the high quality + mode (renders faster at the expense of some aliasing). +-EGL allocates sRGB rendering surfaces. +-Includes GLUT dll against which tiger.exe was linked. + +March 24, 2005 +-Initial release. diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c new file mode 100755 index 0000000..ae2eff9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c @@ -0,0 +1,1952 @@ +/*------------------------------------------------------------------------ + * + * OpenVG 1.0.1 Reference Implementation sample code + * ------------------------------------------------- + * + * Copyright (c) 2007 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + * + *//** + * \file + * \brief Path and paint data for Tiger image. + * \note + *//*-------------------------------------------------------------------*/ + +const int tigerCommandCount = 4151; +const char tigerCommands[4151] = { +'F', 'N', 'B', 'M', 'M', 'L', 'L', 'L', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', +'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', +'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', +'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', +'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', +'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', +'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', +'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'N', 'S', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', +'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', +'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', +'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', +'S', 'B', 'M', 'M', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', +'C', 'C', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'N', 'S', +'B', 'M', 'M', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'L', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', +'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', +'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'L', 'L', 'C', 'E', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'L', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'L', 'C', 'C', 'C', 'C', 'C', +'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'L', 'L', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'N', 'S', 'R', 'M', 'M', 'C', 'C', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', +'L', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'F', 'N', 'B', 'M', 'M', +'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'L', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', +'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', +'N', 'R', 'M', 'M', 'L', 'C', 'L', 'N', 'S', 'R', 'M', 'M', 'L', 'N', 'S', 'R', 'M', 'M', 'C', 'N', +'S', 'R', 'M', 'M', 'C', 'N', 'S', 'R', 'M', 'M', 'C'}; + +const float tigerMinX = 0.0f; +const float tigerMaxX = 612.0f; +const float tigerMinY = 0.0f; +const float tigerMaxY = 792.0f; + +const int tigerPointCount = 17005; +const float tigerPoints[17005] = { +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 0, +792, 0, 0, 612, 0, 612, 792, 10, 0, 1, +1, 1, 1, 1, 1, 5, 85.25f, 487.75f, 85.25f, 487.75f, +85.5742f, 485.199f, 84.25f, 484.746f, 83.7617f, 485.242f, 65.6641f, 538.125f, 43.2461f, 535.746f, +43.2422f, 535.746f, 62.6445f, 543.746f, 85.25f, 487.75f, 10, 0.1892f, 0, 0, +0, 0, 0, 0, 5, 85.25f, 487.75f, 85.25f, 487.75f, 85.5742f, +485.199f, 84.25f, 484.746f, 83.7617f, 485.242f, 65.6641f, 538.125f, 43.2461f, 535.746f, 43.2422f, +535.746f, 62.6445f, 543.746f, 85.25f, 487.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 89.2461f, 490.75f, 89.2461f, 490.75f, 88.7422f, 488.613f, +88.2461f, 488.746f, 87.0508f, 489.27f, 88.0234f, 545.156f, 66.2461f, 550.746f, 66.2461f, 550.742f, +87.0977f, 551.469f, 89.2461f, 490.75f, 10, 0.1892f, 0, 0, 0, 0, +0, 0, 5, 89.2461f, 490.75f, 89.2461f, 490.75f, 88.7422f, 488.613f, 88.2461f, +488.746f, 87.0508f, 489.27f, 88.0234f, 545.156f, 66.2461f, 550.746f, 66.2461f, 550.742f, 87.0977f, +551.469f, 89.2461f, 490.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 119.25f, 443.75f, 119.25f, 443.75f, 121.387f, 442.992f, 121.246f, 442.746f, +120.352f, 441.504f, 66.2578f, 455.586f, 56.25f, 435.75f, 56.25f, 435.75f, 59.9062f, 456.168f, +119.25f, 443.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, +5, 119.25f, 443.75f, 119.25f, 443.75f, 121.387f, 442.992f, 121.246f, 442.746f, 120.352f, +441.504f, 66.2578f, 455.586f, 56.25f, 435.75f, 56.25f, 435.75f, 59.9062f, 456.168f, 119.25f, +443.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +116.246f, 432.75f, 116.246f, 432.75f, 118.539f, 432.383f, 118.25f, 431.746f, 118.023f, 430.641f, +62.25f, 426.965f, 58.25f, 404.75f, 58.25f, 404.75f, 56.0391f, 425.516f, 116.246f, 432.75f, +10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 116.246f, +432.75f, 116.246f, 432.75f, 118.539f, 432.383f, 118.25f, 431.746f, 118.023f, 430.641f, 62.25f, +426.965f, 58.25f, 404.75f, 58.25f, 404.75f, 56.0391f, 425.516f, 116.246f, 432.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 112.25f, 438.746f, +112.25f, 438.742f, 113.82f, 438.164f, 113.25f, 437.75f, 113.059f, 436.52f, 57.3437f, 441.016f, +50.2461f, 419.75f, 50.2461f, 419.75f, 50.9883f, 440.492f, 112.25f, 438.746f, 10, 0.1892f, +0, 0, 0, 0, 0, 0, 5, 112.25f, 438.746f, 112.25f, +438.742f, 113.82f, 438.164f, 113.25f, 437.75f, 113.059f, 436.52f, 57.3437f, 441.016f, 50.2461f, +419.75f, 50.2461f, 419.75f, 50.9883f, 440.492f, 112.25f, 438.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 100.246f, 458.746f, 100.246f, 458.746f, +101.527f, 457.406f, 101.25f, 456.746f, 100.121f, 456.262f, 52.0039f, 484.699f, 36.25f, 467.746f, +36.25f, 467.746f, 46.0586f, 487.012f, 100.246f, 458.746f, 10, 0.1892f, 0, 0, +0, 0, 0, 0, 5, 100.246f, 458.746f, 100.246f, 458.746f, 101.527f, +457.406f, 101.25f, 456.746f, 100.121f, 456.262f, 52.0039f, 484.699f, 36.25f, 467.746f, 36.25f, +467.746f, 46.0586f, 487.012f, 100.246f, 458.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 92.2461f, 454.75f, 92.2422f, 454.75f, 93.3906f, 452.969f, +93.2461f, 452.75f, 92.125f, 451.672f, 41.0976f, 474.484f, 27.25f, 456.746f, 27.25f, 456.746f, +34.9258f, 476.105f, 92.2461f, 454.75f, 10, 0.1892f, 0, 0, 0, 0, +0, 0, 5, 92.2461f, 454.75f, 92.2422f, 454.75f, 93.3906f, 452.969f, 93.2461f, +452.75f, 92.125f, 451.672f, 41.0976f, 474.484f, 27.25f, 456.746f, 27.25f, 456.746f, 34.9258f, +476.105f, 92.2461f, 454.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 89.2461f, 449.746f, 89.2461f, 449.742f, 90.6992f, 448.723f, 90.25f, 447.746f, +89.6211f, 447.262f, 35.9609f, 462.906f, 25.25f, 442.746f, 25.25f, 442.742f, 29.625f, 463.676f, +89.2461f, 449.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, +5, 89.2461f, 449.746f, 89.2461f, 449.742f, 90.6992f, 448.723f, 90.25f, 447.746f, 89.6211f, +447.262f, 35.9609f, 462.906f, 25.25f, 442.746f, 25.25f, 442.742f, 29.625f, 463.676f, 89.2461f, +449.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +100.246f, 448.75f, 100.246f, 448.75f, 101.969f, 447.469f, 101.25f, 446.75f, 100.43f, 446.512f, +56.3516f, 480.887f, 39.2461f, 466.746f, 39.2461f, 466.742f, 50.75f, 483.941f, 100.246f, 448.75f, +10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 100.246f, +448.75f, 100.246f, 448.75f, 101.969f, 447.469f, 101.25f, 446.75f, 100.43f, 446.512f, 56.3516f, +480.887f, 39.2461f, 466.746f, 39.2461f, 466.742f, 50.75f, 483.941f, 100.246f, 448.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 79.25f, 480.746f, +79.25f, 480.746f, 79.6367f, 479.02f, 79.25f, 478.746f, 77.8789f, 478.578f, 46.418f, 524.777f, +25.25f, 516.75f, 25.25f, 516.75f, 42.0195f, 529.398f, 79.25f, 480.746f, 10, 0.1892f, +0, 0, 0, 0, 0, 0, 5, 79.25f, 480.746f, 79.25f, +480.746f, 79.6367f, 479.02f, 79.25f, 478.746f, 77.8789f, 478.578f, 46.418f, 524.777f, 25.25f, +516.75f, 25.25f, 516.75f, 42.0195f, 529.398f, 79.25f, 480.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 79.25f, 473.746f, 79.25f, 473.742f, +80.8164f, 471.527f, 80.25f, 470.75f, 79.1914f, 470.723f, 38.5078f, 509.051f, 19.25f, 496.75f, +19.25f, 496.75f, 33.2148f, 512.609f, 79.25f, 473.746f, 10, 0.1892f, 0, 0, +0, 0, 0, 0, 5, 79.25f, 473.746f, 79.25f, 473.742f, 80.8164f, +471.527f, 80.25f, 470.75f, 79.1914f, 470.723f, 38.5078f, 509.051f, 19.25f, 496.75f, 19.25f, +496.75f, 33.2148f, 512.609f, 79.25f, 473.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 79.25f, 468.75f, 79.25f, 468.75f, 80.8516f, 466.828f, +80.25f, 466.746f, 79.3086f, 465.875f, 35.2305f, 500.246f, 17.25f, 485.75f, 17.25f, 485.75f, +29.6289f, 503.301f, 79.25f, 468.75f, 10, 0.1892f, 0, 0, 0, 0, +0, 0, 5, 79.25f, 468.75f, 79.25f, 468.75f, 80.8516f, 466.828f, 80.25f, +466.746f, 79.3086f, 465.875f, 35.2305f, 500.246f, 17.25f, 485.75f, 17.25f, 485.75f, 29.6289f, +503.301f, 79.25f, 468.75f, 10, 0, 1, 1, 1, 1, 1, +1, 88, 77.2461f, 466.746f, 77.7383f, 459.973f, 78.8242f, 452.746f, 80.25f, 449.746f, +80.25f, 449.742f, 76.7773f, 435.676f, 86.25f, 420.746f, 86.25f, 420.742f, 86.0195f, 413.238f, +88.2461f, 409.746f, 88.2461f, 409.742f, 92.1797f, 400.477f, 97.25f, 399.75f, 101.73f, 398.887f, +111.324f, 395.508f, 122.246f, 393.75f, 122.246f, 393.75f, 141.02f, 378.477f, 137.246f, 364.75f, +137.242f, 364.75f, 137.059f, 346.355f, 133.246f, 344.75f, 133.246f, 344.75f, 145.859f, 356.918f, +135.25f, 338.75f, 130.25f, 317.746f, 130.25f, 317.742f, 158.617f, 341.516f, 141.25f, 321.746f, +130.25f, 292.75f, 130.25f, 292.75f, 152.02f, 312.918f, 144.246f, 303.75f, 140.25f, 293.746f, +140.25f, 293.746f, 188.098f, 323.918f, 154.246f, 291.746f, 154.242f, 291.746f, 163.02f, 295.316f, +168.25f, 291.746f, 168.25f, 291.746f, 175.34f, 293.559f, 174.25f, 291.746f, 174.25f, 291.746f, +151.578f, 280.355f, 147.25f, 259.746f, 147.25f, 259.746f, 156.859f, 271.117f, 153.246f, 258.746f, +154.246f, 246.746f, 154.242f, 246.746f, 158.18f, 270.238f, 157.25f, 228.746f, 157.25f, 228.742f, +178.859f, 248.676f, 166.246f, 225.746f, 166.246f, 207.75f, 166.246f, 207.75f, 182.816f, 225.355f, +176.246f, 211.75f, 176.246f, 211.75f, 186.777f, 220.957f, 182.25f, 203.746f, 182.25f, 203.746f, +181.5f, 192.797f, 186.25f, 204.746f, 186.25f, 204.746f, 203.938f, 238.777f, 197.25f, 209.75f, +197.25f, 209.75f, 196.457f, 188.836f, 201.246f, 204.746f, 201.246f, 204.746f, 202.18f, 193.676f, +212.246f, 185.75f, 212.246f, 185.75f, 210.977f, 241.637f, 225.25f, 201.746f, 229.25f, 183.746f, +229.25f, 183.742f, 232.539f, 194.117f, 232.246f, 199.75f, 232.246f, 199.75f, 248.379f, 217.879f, +241.25f, 190.746f, 241.25f, 190.746f, 257.617f, 216.117f, 254.246f, 201.746f, 254.246f, 201.746f, +245.738f, 183.996f, 247.246f, 178.75f, 247.242f, 178.75f, 265.977f, 216.996f, 267.246f, 218.75f, +267.246f, 218.75f, 265.098f, 172.117f, 277.246f, 211.75f, 277.246f, 211.75f, 283.137f, 198.516f, +280.246f, 193.746f, 280.242f, 193.746f, 288.859f, 202.477f, 288.246f, 205.746f, 288.246f, 205.742f, +293.039f, 215.016f, 296.25f, 199.75f, 296.25f, 199.75f, 298.098f, 189.719f, 300.246f, 192.746f, +300.246f, 192.746f, 304.258f, 166.836f, 305.25f, 191.746f, 305.25f, 191.746f, 307.34f, 206.879f, +299.246f, 219.75f, 299.246f, 219.75f, 300.297f, 223.156f, 297.246f, 227.746f, 297.246f, 227.742f, +312.18f, 203.797f, 304.25f, 235.746f, 304.25f, 235.746f, 316.578f, 226.676f, 318.25f, 226.746f, +318.25f, 226.746f, 302.937f, 252.195f, 312.246f, 246.746f, 312.242f, 246.746f, 306.898f, 258.355f, +326.25f, 244.75f, 326.25f, 244.75f, 309.098f, 262.758f, 328.25f, 251.75f, 328.25f, 251.75f, +337.258f, 245.156f, 329.25f, 255.75f, 329.25f, 255.75f, 313.059f, 273.758f, 337.25f, 253.75f, +337.25f, 253.75f, 350.02f, 235.918f, 351.25f, 232.75f, 351.25f, 232.75f, 339.898f, 264.957f, +335.246f, 267.75f, 335.242f, 267.75f, 344.301f, 308.078f, 389.246f, 290.75f, 389.246f, 290.75f, +397.098f, 271.996f, 402.246f, 291.746f, 402.242f, 291.746f, 416.02f, 299.277f, 428.25f, 268.75f, +428.25f, 268.75f, 432.738f, 283.879f, 432.246f, 286.746f, 432.246f, 286.742f, 439.34f, 285.637f, +438.25f, 286.746f, 438.25f, 286.742f, 452.98f, 282.117f, 454.246f, 282.746f, 454.246f, 282.746f, +461.777f, 275.516f, 462.246f, 279.75f, 462.242f, 279.75f, 472.34f, 276.398f, 470.25f, 280.746f, +470.25f, 280.746f, 479.82f, 263.195f, 480.246f, 258.746f, 483.25f, 274.75f, 485.25f, 271.746f, +485.25f, 271.746f, 486.859f, 279.918f, 486.25f, 280.746f, 485.098f, 282.559f, 507.98f, 273.758f, +513.246f, 250.746f, 515.246f, 241.75f, 515.242f, 241.75f, 522.059f, 257.918f, 520.246f, 262.75f, +520.246f, 262.75f, 526.02f, 261.438f, 526.246f, 256.746f, 526.242f, 256.746f, 530.859f, 282.117f, +525.25f, 288.746f, 525.25f, 288.742f, 530.418f, 289.598f, 531.246f, 285.75f, 531.246f, 293.746f, +531.246f, 293.746f, 539.66f, 292.676f, 539.246f, 295.746f, 539.242f, 295.742f, 544.5f, 299.719f, +546.246f, 294.75f, 546.242f, 294.75f, 533.059f, 333.156f, 553.246f, 311.75f, 553.246f, 311.75f, +561.219f, 300.156f, 557.246f, 320.75f, 553.301f, 341.516f, 548.898f, 343.277f, 554.25f, 343.746f, +554.25f, 343.742f, 555.059f, 347.676f, 553.246f, 349.75f, 550.66f, 351.195f, 554.25f, 349.75f, +554.25f, 349.75f, 554.25f, 349.75f, 559.461f, 345.035f, 553.246f, 368.746f, 553.246f, 368.746f, +560.777f, 367.477f, 547.25f, 399.75f, 547.25f, 399.75f, 550.66f, 402.238f, 546.246f, 411.746f, +546.242f, 411.742f, 555.059f, 406.637f, 558.25f, 408.75f, 558.25f, 408.75f, 557.699f, 410.156f, +554.25f, 414.746f, 554.25f, 414.746f, 530.418f, 474.84f, 553.246f, 450.75f, 553.246f, 450.75f, +565.895f, 435.73f, 559.246f, 460.746f, 559.242f, 460.742f, 548.832f, 487.223f, 549.25f, 491.746f, +77.2461f, 466.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, +88, 77.2461f, 466.746f, 77.7383f, 459.973f, 78.8242f, 452.746f, 80.25f, 449.746f, 80.25f, +449.742f, 76.7773f, 435.676f, 86.25f, 420.746f, 86.25f, 420.742f, 86.0195f, 413.238f, 88.2461f, +409.746f, 88.2461f, 409.742f, 92.1797f, 400.477f, 97.25f, 399.75f, 101.73f, 398.887f, 111.324f, +395.508f, 122.246f, 393.75f, 122.246f, 393.75f, 141.02f, 378.477f, 137.246f, 364.75f, 137.242f, +364.75f, 137.059f, 346.355f, 133.246f, 344.75f, 133.246f, 344.75f, 145.859f, 356.918f, 135.25f, +338.75f, 130.25f, 317.746f, 130.25f, 317.742f, 158.617f, 341.516f, 141.25f, 321.746f, 130.25f, +292.75f, 130.25f, 292.75f, 152.02f, 312.918f, 144.246f, 303.75f, 140.25f, 293.746f, 140.25f, +293.746f, 188.098f, 323.918f, 154.246f, 291.746f, 154.242f, 291.746f, 163.02f, 295.316f, 168.25f, +291.746f, 168.25f, 291.746f, 175.34f, 293.559f, 174.25f, 291.746f, 174.25f, 291.746f, 151.578f, +280.355f, 147.25f, 259.746f, 147.25f, 259.746f, 156.859f, 271.117f, 153.246f, 258.746f, 154.246f, +246.746f, 154.242f, 246.746f, 158.18f, 270.238f, 157.25f, 228.746f, 157.25f, 228.742f, 178.859f, +248.676f, 166.246f, 225.746f, 166.246f, 207.75f, 166.246f, 207.75f, 182.816f, 225.355f, 176.246f, +211.75f, 176.246f, 211.75f, 186.777f, 220.957f, 182.25f, 203.746f, 182.25f, 203.746f, 181.5f, +192.797f, 186.25f, 204.746f, 186.25f, 204.746f, 203.938f, 238.777f, 197.25f, 209.75f, 197.25f, +209.75f, 196.457f, 188.836f, 201.246f, 204.746f, 201.246f, 204.746f, 202.18f, 193.676f, 212.246f, +185.75f, 212.246f, 185.75f, 210.977f, 241.637f, 225.25f, 201.746f, 229.25f, 183.746f, 229.25f, +183.742f, 232.539f, 194.117f, 232.246f, 199.75f, 232.246f, 199.75f, 248.379f, 217.879f, 241.25f, +190.746f, 241.25f, 190.746f, 257.617f, 216.117f, 254.246f, 201.746f, 254.246f, 201.746f, 245.738f, +183.996f, 247.246f, 178.75f, 247.242f, 178.75f, 265.977f, 216.996f, 267.246f, 218.75f, 267.246f, +218.75f, 265.098f, 172.117f, 277.246f, 211.75f, 277.246f, 211.75f, 283.137f, 198.516f, 280.246f, +193.746f, 280.242f, 193.746f, 288.859f, 202.477f, 288.246f, 205.746f, 288.246f, 205.742f, 293.039f, +215.016f, 296.25f, 199.75f, 296.25f, 199.75f, 298.098f, 189.719f, 300.246f, 192.746f, 300.246f, +192.746f, 304.258f, 166.836f, 305.25f, 191.746f, 305.25f, 191.746f, 307.34f, 206.879f, 299.246f, +219.75f, 299.246f, 219.75f, 300.297f, 223.156f, 297.246f, 227.746f, 297.246f, 227.742f, 312.18f, +203.797f, 304.25f, 235.746f, 304.25f, 235.746f, 316.578f, 226.676f, 318.25f, 226.746f, 318.25f, +226.746f, 302.937f, 252.195f, 312.246f, 246.746f, 312.242f, 246.746f, 306.898f, 258.355f, 326.25f, +244.75f, 326.25f, 244.75f, 309.098f, 262.758f, 328.25f, 251.75f, 328.25f, 251.75f, 337.258f, +245.156f, 329.25f, 255.75f, 329.25f, 255.75f, 313.059f, 273.758f, 337.25f, 253.75f, 337.25f, +253.75f, 350.02f, 235.918f, 351.25f, 232.75f, 351.25f, 232.75f, 339.898f, 264.957f, 335.246f, +267.75f, 335.242f, 267.75f, 344.301f, 308.078f, 389.246f, 290.75f, 389.246f, 290.75f, 397.098f, +271.996f, 402.246f, 291.746f, 402.242f, 291.746f, 416.02f, 299.277f, 428.25f, 268.75f, 428.25f, +268.75f, 432.738f, 283.879f, 432.246f, 286.746f, 432.246f, 286.742f, 439.34f, 285.637f, 438.25f, +286.746f, 438.25f, 286.742f, 452.98f, 282.117f, 454.246f, 282.746f, 454.246f, 282.746f, 461.777f, +275.516f, 462.246f, 279.75f, 462.242f, 279.75f, 472.34f, 276.398f, 470.25f, 280.746f, 470.25f, +280.746f, 479.82f, 263.195f, 480.246f, 258.746f, 483.25f, 274.75f, 485.25f, 271.746f, 485.25f, +271.746f, 486.859f, 279.918f, 486.25f, 280.746f, 485.098f, 282.559f, 507.98f, 273.758f, 513.246f, +250.746f, 515.246f, 241.75f, 515.242f, 241.75f, 522.059f, 257.918f, 520.246f, 262.75f, 520.246f, +262.75f, 526.02f, 261.438f, 526.246f, 256.746f, 526.242f, 256.746f, 530.859f, 282.117f, 525.25f, +288.746f, 525.25f, 288.742f, 530.418f, 289.598f, 531.246f, 285.75f, 531.246f, 293.746f, 531.246f, +293.746f, 539.66f, 292.676f, 539.246f, 295.746f, 539.242f, 295.742f, 544.5f, 299.719f, 546.246f, +294.75f, 546.242f, 294.75f, 533.059f, 333.156f, 553.246f, 311.75f, 553.246f, 311.75f, 561.219f, +300.156f, 557.246f, 320.75f, 553.301f, 341.516f, 548.898f, 343.277f, 554.25f, 343.746f, 554.25f, +343.742f, 555.059f, 347.676f, 553.246f, 349.75f, 550.66f, 351.195f, 554.25f, 349.75f, 554.25f, +349.75f, 554.25f, 349.75f, 559.461f, 345.035f, 553.246f, 368.746f, 553.246f, 368.746f, 560.777f, +367.477f, 547.25f, 399.75f, 547.25f, 399.75f, 550.66f, 402.238f, 546.246f, 411.746f, 546.242f, +411.742f, 555.059f, 406.637f, 558.25f, 408.75f, 558.25f, 408.75f, 557.699f, 410.156f, 554.25f, +414.746f, 554.25f, 414.746f, 530.418f, 474.84f, 553.246f, 450.75f, 553.246f, 450.75f, 565.895f, +435.73f, 559.246f, 460.746f, 559.242f, 460.742f, 548.832f, 487.223f, 549.25f, 491.746f, 77.2461f, +466.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 44, +549.25f, 491.746f, 550.379f, 491.531f, 552.805f, 490.293f, 554.25f, 488.746f, 554.25f, 488.742f, +561.66f, 476.598f, 556.25f, 496.75f, 556.25f, 496.75f, 545.82f, 528.52f, 555.246f, 515.746f, +555.246f, 515.742f, 562.098f, 508.277f, 558.25f, 522.746f, 554.328f, 541.309f, 551.25f, 548.746f, +551.25f, 548.746f, 551.25f, 548.746f, 564.301f, 543.039f, 535.246f, 586.75f, 544.246f, 582.75f, +544.246f, 582.75f, 522.938f, 626.199f, 499.25f, 631.746f, 490.25f, 638.746f, 490.25f, 638.742f, +532.621f, 680.316f, 518.25f, 720.75f, 518.25f, 720.75f, 511.059f, 726.52f, 500.246f, 716.75f, +500.246f, 716.75f, 493.461f, 711.117f, 487.246f, 712.75f, 487.246f, 712.75f, 452.98f, 711.559f, +451.25f, 711.746f, 448.578f, 711.559f, 410.301f, 752.477f, 338.25f, 732.746f, 338.25f, 732.742f, +332.418f, 730.918f, 327.25f, 731.75f, 327.25f, 731.75f, 307.34f, 749.84f, 253.246f, 724.746f, +253.246f, 724.746f, 242.656f, 722.559f, 241.25f, 722.746f, 239.137f, 722.559f, 236.059f, 722.559f, +227.25f, 715.746f, 218.457f, 708.477f, 218.02f, 707.598f, 216.25f, 705.75f, 216.25f, 705.75f, +197.777f, 693.52f, 192.25f, 692.75f, 192.25f, 692.75f, 179.738f, 685.598f, 175.25f, 674.75f, +171.246f, 673.746f, 171.246f, 673.742f, 169.18f, 665.359f, 168.25f, 663.75f, 168.25f, 663.75f, +163.457f, 660.078f, 162.25f, 653.746f, 162.25f, 653.742f, 152.898f, 647.316f, 153.246f, 642.746f, +153.242f, 642.742f, 151.578f, 636.758f, 150.246f, 631.746f, 150.246f, 631.742f, 142.777f, 626.199f, +143.246f, 622.75f, 143.242f, 622.75f, 135.297f, 607.719f, 136.25f, 599.75f, 136.25f, 599.75f, +129.578f, 600.68f, 126.246f, 597.75f, 126.242f, 597.75f, 125.617f, 592.758f, 124.25f, 592.746f, +124.25f, 592.746f, 120.777f, 591, 123.25f, 586.75f, 123.25f, 586.75f, 121.656f, 583.52f, +121.246f, 581.746f, 121.246f, 581.746f, 122.098f, 578.68f, 117.25f, 572.746f, 117.25f, 572.742f, +110.219f, 551.84f, 112.25f, 545.75f, 112.25f, 545.75f, 112.859f, 540.84f, 110.246f, 538.75f, +110.246f, 538.75f, 105.816f, 539.52f, 115.246f, 526.746f, 115.242f, 526.742f, 115.938f, 525, +112.25f, 522.746f, 112.25f, 522.746f, 93.5f, 518.398f, 91.25f, 500.746f, 91.25f, 500.746f, +75.8984f, 484.078f, 76.2461f, 478.746f, 75.8984f, 475.824f, 76.1953f, 472.359f, 77.2461f, 467.746f, +77.2461f, 467.746f, 76.3398f, 458.117f, 106.25f, 456.746f, 137.059f, 456.355f, 549.25f, 491.746f, +549.25f, 491.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, +44, 549.25f, 491.746f, 550.379f, 491.531f, 552.805f, 490.293f, 554.25f, 488.746f, 554.25f, +488.742f, 561.66f, 476.598f, 556.25f, 496.75f, 556.25f, 496.75f, 545.82f, 528.52f, 555.246f, +515.746f, 555.246f, 515.742f, 562.098f, 508.277f, 558.25f, 522.746f, 554.328f, 541.309f, 551.25f, +548.746f, 551.25f, 548.746f, 551.25f, 548.746f, 564.301f, 543.039f, 535.246f, 586.75f, 544.246f, +582.75f, 544.246f, 582.75f, 522.938f, 626.199f, 499.25f, 631.746f, 490.25f, 638.746f, 490.25f, +638.742f, 532.621f, 680.316f, 518.25f, 720.75f, 518.25f, 720.75f, 511.059f, 726.52f, 500.246f, +716.75f, 500.246f, 716.75f, 493.461f, 711.117f, 487.246f, 712.75f, 487.246f, 712.75f, 452.98f, +711.559f, 451.25f, 711.746f, 448.578f, 711.559f, 410.301f, 752.477f, 338.25f, 732.746f, 338.25f, +732.742f, 332.418f, 730.918f, 327.25f, 731.75f, 327.25f, 731.75f, 307.34f, 749.84f, 253.246f, +724.746f, 253.246f, 724.746f, 242.656f, 722.559f, 241.25f, 722.746f, 239.137f, 722.559f, 236.059f, +722.559f, 227.25f, 715.746f, 218.457f, 708.477f, 218.02f, 707.598f, 216.25f, 705.75f, 216.25f, +705.75f, 197.777f, 693.52f, 192.25f, 692.75f, 192.25f, 692.75f, 179.738f, 685.598f, 175.25f, +674.75f, 171.246f, 673.746f, 171.246f, 673.742f, 169.18f, 665.359f, 168.25f, 663.75f, 168.25f, +663.75f, 163.457f, 660.078f, 162.25f, 653.746f, 162.25f, 653.742f, 152.898f, 647.316f, 153.246f, +642.746f, 153.242f, 642.742f, 151.578f, 636.758f, 150.246f, 631.746f, 150.246f, 631.742f, 142.777f, +626.199f, 143.246f, 622.75f, 143.242f, 622.75f, 135.297f, 607.719f, 136.25f, 599.75f, 136.25f, +599.75f, 129.578f, 600.68f, 126.246f, 597.75f, 126.242f, 597.75f, 125.617f, 592.758f, 124.25f, +592.746f, 124.25f, 592.746f, 120.777f, 591, 123.25f, 586.75f, 123.25f, 586.75f, 121.656f, +583.52f, 121.246f, 581.746f, 121.246f, 581.746f, 122.098f, 578.68f, 117.25f, 572.746f, 117.25f, +572.742f, 110.219f, 551.84f, 112.25f, 545.75f, 112.25f, 545.75f, 112.859f, 540.84f, 110.246f, +538.75f, 110.246f, 538.75f, 105.816f, 539.52f, 115.246f, 526.746f, 115.242f, 526.742f, 115.938f, +525, 112.25f, 522.746f, 112.25f, 522.746f, 93.5f, 518.398f, 91.25f, 500.746f, 91.25f, +500.746f, 75.8984f, 484.078f, 76.2461f, 478.746f, 75.8984f, 475.824f, 76.1953f, 472.359f, 77.2461f, +467.746f, 77.2461f, 467.746f, 76.3398f, 458.117f, 106.25f, 456.746f, 137.059f, 456.355f, 549.25f, +491.746f, 549.25f, 491.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, +0.15f, 18, 93.2461f, 466.746f, 65.3398f, 510.477f, 81.2461f, 448.75f, 81.2461f, 448.75f, +90.8594f, 410.598f, 233.246f, 451.746f, 233.246f, 451.746f, 233.246f, 451.742f, 419.098f, 485.398f, +431.246f, 489.746f, 443.738f, 494.199f, 548.246f, 486.746f, 548.246f, 486.746f, 542.246f, 505.75f, +471.02f, 556.68f, 449.898f, 531.156f, 435.246f, 535.746f, 419.98f, 539.957f, 422.621f, 529.398f, +419.246f, 528.746f, 415.578f, 527.637f, 372.461f, 554.918f, 365.246f, 553.75f, 358.379f, 553.156f, +330.504f, 579.285f, 347.25f, 544.746f, 364.539f, 506.957f, 282.699f, 501.238f, 264.246f, 513.746f, +245.738f, 525.879f, 272.25f, 493.746f, 272.25f, 493.746f, 292.379f, 471.316f, 254.246f, 489.746f, +254.246f, 489.746f, 216.699f, 503.879f, 190.297f, 475.719f, 187.246f, 474.75f, 183.258f, 473.957f, +177.977f, 470.438f, 177.246f, 477.746f, 176.219f, 484.52f, 167.957f, 502.891f, 133.246f, 473.746f, +111.098f, 455.695f, 96.25f, 479.75f, 96.25f, 479.75f, 93.2461f, 466.746f, 10, 0, +0.91f, 0.5f, 0.228f, 0.91f, 0.5f, 0.228f, 19, 367.246f, 551.75f, 359.82f, +551.238f, 331.914f, 577.352f, 348.25f, 542.75f, 366.641f, 503.719f, 284.141f, 499.316f, 265.246f, +511.746f, 247.18f, 523.957f, 273.25f, 491.746f, 273.25f, 491.746f, 293.82f, 469.398f, 256.246f, +487.75f, 256.246f, 487.75f, 218.137f, 501.957f, 191.738f, 473.797f, 188.246f, 472.75f, 184.699f, +472.039f, 179.418f, 468.516f, 178.246f, 475.746f, 177.656f, 482.598f, 169.543f, 500.785f, 134.25f, +471.746f, 111.18f, 452.957f, 96.25f, 476.75f, 96.25f, 476.75f, 93.2461f, 465.75f, 65.3164f, +509.219f, 82.2461f, 444.746f, 82.2461f, 444.746f, 91.5781f, 407.238f, 235.246f, 449.746f, 235.246f, +449.746f, 235.242f, 449.742f, 420.539f, 483.477f, 433.246f, 487.75f, 445.18f, 492.277f, 549.25f, +485.75f, 549.25f, 485.75f, 543.25f, 504.746f, 471.578f, 555.398f, 451.34f, 529.238f, 436.25f, +533.746f, 421.418f, 538.039f, 424.059f, 527.477f, 420.246f, 526.746f, 417.02f, 525.719f, 373.898f, +552.996f, 367.246f, 551.75f, 10, 0, 0.919f, 0.55f, 0.305f, 0.919f, 0.55f, +0.305f, 19, 368.246f, 549.75f, 361.258f, 549.316f, 334.051f, 575.75f, 350.25f, 540.75f, +367.641f, 500.695f, 285.578f, 497.398f, 267.246f, 509.75f, 248.617f, 522.035f, 275.246f, 489.746f, +275.246f, 489.746f, 295.258f, 467.477f, 257.246f, 485.75f, 257.246f, 485.75f, 219.578f, 500.035f, +193.18f, 471.875f, 189.246f, 470.75f, 186.137f, 470.117f, 180.859f, 466.598f, 180.246f, 473.746f, +179.098f, 480.676f, 171.125f, 498.68f, 136.25f, 469.746f, 111.258f, 450.215f, 97.25f, 472.75f, +97.25f, 472.75f, 93.2461f, 463.75f, 66.6172f, 506.637f, 82.2461f, 441.75f, 82.2461f, 441.75f, +92.2969f, 403.879f, 236.25f, 447.746f, 236.25f, 447.746f, 236.25f, 447.746f, 421.98f, 481.559f, +434.246f, 485.75f, 446.617f, 490.355f, 549.25f, 483.75f, 549.25f, 483.75f, 543.25f, 502.746f, +472.141f, 554.117f, 452.777f, 527.316f, 438.25f, 531.75f, 422.859f, 536.117f, 425.5f, 525.559f, +422.246f, 524.746f, 418.457f, 523.797f, 375.34f, 551.078f, 368.246f, 549.75f, 10, 0, +0.928f, 0.6f, 0.382f, 0.928f, 0.6f, 0.382f, 19, 369.25f, 548.746f, 362.699f, +547.398f, 335.496f, 573.832f, 351.25f, 538.75f, 369.738f, 497.285f, 286.43f, 495.867f, 268.246f, +507.75f, 250.059f, 520.117f, 276.246f, 487.75f, 276.246f, 487.75f, 296.699f, 465.559f, 259.25f, +483.75f, 259.25f, 483.75f, 221.02f, 498.117f, 194.617f, 469.957f, 191.246f, 468.75f, 187.578f, +468.199f, 182.301f, 464.676f, 181.25f, 471.746f, 180.539f, 478.758f, 172.711f, 496.574f, 137.246f, +467.746f, 111.336f, 447.477f, 97.25f, 469.746f, 97.25f, 469.746f, 93.2461f, 461.75f, 68.7969f, +502.516f, 83.2461f, 438.746f, 83.2461f, 438.746f, 93.0195f, 400.516f, 237.25f, 445.746f, 237.25f, +445.746f, 237.25f, 445.746f, 423.418f, 479.637f, 435.246f, 483.75f, 448.059f, 488.438f, 550.246f, +481.75f, 550.246f, 481.75f, 544.246f, 501.75f, 472.699f, 552.84f, 454.219f, 525.398f, 439.25f, +529.75f, 424.301f, 534.199f, 426.938f, 523.637f, 423.246f, 522.746f, 419.898f, 521.879f, 376.777f, +549.156f, 369.25f, 548.746f, 10, 0, 0.937f, 0.65f, 0.46f, 0.937f, 0.65f, +0.46f, 19, 371.25f, 546.746f, 364.141f, 545.477f, 337.492f, 572.156f, 352.25f, 536.75f, +371.18f, 493.559f, 288.457f, 493.559f, 270.25f, 505.75f, 251.5f, 518.195f, 278.246f, 485.75f, +278.246f, 485.75f, 298.141f, 463.637f, 260.25f, 481.75f, 260.25f, 481.75f, 222.457f, 496.195f, +196.059f, 468.035f, 192.25f, 466.746f, 189.02f, 466.277f, 183.738f, 462.758f, 183.25f, 469.746f, +181.98f, 476.836f, 174.297f, 494.473f, 139.246f, 466.746f, 111.418f, 444.738f, 97.25f, 466.746f, +97.25f, 466.746f, 93.2461f, 460.746f, 70.9766f, 498.617f, 84.25f, 434.746f, 84.25f, 434.746f, +93.7383f, 397.156f, 239.25f, 444.746f, 239.25f, 444.746f, 239.25f, 444.742f, 424.859f, 477.715f, +437.25f, 481.75f, 449.5f, 486.516f, 550.246f, 479.75f, 550.246f, 479.75f, 544.246f, 500.746f, +473.262f, 551.559f, 455.66f, 523.477f, 440.25f, 527.75f, 425.738f, 532.277f, 428.379f, 521.715f, +425.25f, 520.75f, 421.34f, 519.957f, 378.219f, 547.238f, 371.25f, 546.746f, 10, 0, +0.946f, 0.7f, 0.537f, 0.946f, 0.7f, 0.537f, 19, 372.25f, 544.746f, 365.578f, +543.559f, 337.02f, 569.352f, 354.246f, 534.75f, 375.258f, 492.078f, 289.898f, 491.637f, 271.25f, +503.75f, 252.938f, 516.277f, 279.246f, 483.75f, 279.246f, 483.75f, 299.578f, 461.719f, 261.25f, +479.75f, 261.25f, 479.75f, 223.898f, 494.277f, 197.5f, 466.117f, 194.25f, 464.746f, 190.457f, +464.355f, 185.18f, 460.836f, 184.25f, 467.746f, 183.418f, 474.918f, 175.879f, 492.367f, 140.25f, +464.746f, 111.5f, 441.996f, 98.2461f, 462.746f, 98.2461f, 462.746f, 92.2461f, 458.746f, 72.9375f, +495.156f, 85.25f, 431.746f, 85.25f, 431.746f, 94.457f, 393.797f, 240.25f, 442.746f, 240.25f, +442.746f, 240.25f, 442.742f, 426.301f, 475.797f, 438.25f, 479.75f, 450.941f, 484.598f, 551.25f, +477.746f, 551.25f, 477.746f, 545.25f, 498.75f, 473.82f, 550.277f, 457.102f, 521.559f, 442.246f, +525.75f, 427.18f, 530.355f, 429.82f, 519.797f, 426.25f, 518.75f, 422.781f, 518.039f, 379.66f, +545.316f, 372.25f, 544.746f, 10, 0, 0.955f, 0.75f, 0.614f, 0.955f, 0.75f, +0.614f, 19, 374.25f, 542.75f, 367.02f, 541.637f, 338.043f, 567.223f, 355.246f, 532.746f, +378.02f, 488.836f, 291.34f, 489.715f, 273.25f, 501.75f, 254.379f, 514.355f, 281.25f, 481.75f, +281.25f, 481.75f, 301.02f, 459.797f, 263.25f, 478.746f, 263.25f, 478.746f, 225.34f, 492.355f, +198.938f, 464.195f, 195.25f, 463.75f, 191.898f, 462.438f, 186.617f, 458.918f, 185.25f, 465.75f, +184.859f, 472.996f, 177.465f, 490.262f, 141.25f, 462.746f, 111.578f, 439.258f, 98.2461f, 459.75f, +98.2461f, 459.75f, 92.2461f, 456.746f, 75.1172f, 490.156f, 85.25f, 428.75f, 85.25f, 428.75f, +95.1797f, 390.438f, 242.246f, 440.746f, 242.246f, 440.746f, 242.246f, 440.742f, 427.738f, 473.875f, +440.25f, 478.746f, 452.379f, 482.676f, 551.25f, 475.746f, 551.25f, 475.746f, 545.25f, 497.746f, +474.379f, 548.996f, 458.539f, 519.637f, 443.246f, 523.75f, 428.621f, 528.438f, 431.258f, 517.875f, +427.25f, 516.75f, 424.219f, 516.117f, 381.102f, 543.398f, 374.25f, 542.75f, 10, 0, +0.964f, 0.8f, 0.691f, 0.964f, 0.8f, 0.691f, 19, 375.246f, 540.75f, 368.461f, +539.719f, 338.273f, 564.66f, 357.246f, 530.746f, 381.219f, 487.355f, 292.777f, 487.797f, 274.25f, +499.746f, 255.82f, 512.438f, 282.25f, 479.75f, 282.25f, 479.75f, 302.457f, 457.879f, 264.246f, +476.75f, 264.246f, 476.75f, 226.777f, 490.438f, 200.379f, 462.277f, 197.25f, 461.75f, 193.34f, +460.516f, 188.059f, 456.996f, 187.246f, 463.75f, 186.297f, 471.078f, 179.047f, 488.156f, 143.246f, +460.746f, 111.656f, 436.516f, 99.2461f, 456.746f, 99.2461f, 456.746f, 92.2461f, 454.75f, 76.8555f, +486.477f, 86.25f, 424.75f, 86.25f, 424.75f, 95.8984f, 387.074f, 243.246f, 438.746f, 243.246f, +438.746f, 243.246f, 438.742f, 429.18f, 471.957f, 441.246f, 476.75f, 453.82f, 480.758f, 552.25f, +474.75f, 552.25f, 474.75f, 546.246f, 496.75f, 474.941f, 547.719f, 459.98f, 517.719f, 445.246f, +521.746f, 430.059f, 526.516f, 432.699f, 515.957f, 429.25f, 514.75f, 425.66f, 514.195f, 382.539f, +541.477f, 375.246f, 540.75f, 10, 0, 0.973f, 0.85f, 0.769f, 0.973f, 0.85f, +0.769f, 19, 377.246f, 538.75f, 369.898f, 537.797f, 339.715f, 562.738f, 358.25f, 528.746f, +382.66f, 485.437f, 294.219f, 485.875f, 275.246f, 497.746f, 257.258f, 510.516f, 283.25f, 477.746f, +283.25f, 477.746f, 303.898f, 455.957f, 266.246f, 474.75f, 266.246f, 474.75f, 228.219f, 488.516f, +201.816f, 460.355f, 198.246f, 459.75f, 194.777f, 458.598f, 189.5f, 455.078f, 188.246f, 461.75f, +187.738f, 469.156f, 180.633f, 486.051f, 144.246f, 458.746f, 111.738f, 433.777f, 99.2461f, 452.75f, +99.2461f, 452.75f, 92.2461f, 453.746f, 77.7188f, 482.578f, 87.2461f, 421.75f, 87.2461f, 421.75f, +96.6172f, 383.715f, 245.246f, 436.746f, 245.246f, 436.746f, 245.246f, 436.746f, 430.621f, 470.035f, +443.246f, 474.75f, 455.258f, 478.836f, 552.25f, 472.75f, 552.25f, 472.75f, 547.25f, 495.746f, +475.5f, 546.438f, 461.418f, 515.797f, 446.246f, 519.746f, 431.5f, 524.598f, 434.141f, 514.035f, +430.246f, 512.75f, 427.098f, 512.277f, 383.98f, 539.555f, 377.246f, 538.75f, 10, 0, +0.982f, 0.9f, 0.846f, 0.982f, 0.9f, 0.846f, 19, 378.246f, 536.75f, 371.34f, +535.879f, 341.578f, 561.055f, 360.25f, 526.746f, 384.098f, 482.195f, 295.66f, 483.957f, 277.246f, +496.75f, 258.699f, 508.598f, 285.25f, 475.746f, 285.25f, 475.746f, 305.34f, 454.035f, 267.246f, +472.75f, 267.246f, 472.75f, 229.66f, 486.598f, 203.258f, 458.438f, 199.246f, 457.75f, 196.219f, +456.676f, 190.937f, 453.156f, 190.246f, 459.75f, 189.18f, 467.238f, 182.219f, 483.949f, 146.25f, +456.746f, 111.82f, 431.035f, 99.2461f, 449.746f, 99.2461f, 449.746f, 92.2461f, 451.746f, 78.3594f, +478.238f, 87.2461f, 417.75f, 87.2461f, 417.75f, 97.3399f, 380.355f, 246.246f, 434.746f, 246.246f, +434.746f, 246.242f, 434.746f, 432.059f, 468.117f, 444.246f, 472.75f, 456.699f, 476.918f, 553.246f, +470.75f, 553.246f, 470.75f, 547.25f, 493.746f, 476.059f, 545.156f, 462.859f, 513.879f, 448.25f, +518.75f, 432.938f, 522.676f, 435.578f, 512.117f, 432.246f, 510.746f, 428.539f, 510.355f, 385.418f, +537.637f, 378.246f, 536.75f, 10, 0, 0.991f, 0.95f, 0.923f, 0.991f, 0.95f, +0.923f, 19, 380.25f, 534.75f, 372.777f, 533.957f, 344.207f, 559.746f, 361.25f, 524.746f, +384.66f, 478.078f, 297.098f, 482.035f, 278.246f, 494.75f, 260.141f, 506.676f, 286.246f, 473.746f, +286.246f, 473.746f, 306.777f, 452.117f, 269.246f, 470.75f, 269.246f, 470.75f, 231.098f, 484.676f, +204.699f, 456.516f, 201.246f, 455.746f, 197.66f, 454.758f, 192.379f, 451.238f, 191.246f, 458.746f, +190.621f, 465.316f, 183.801f, 481.844f, 147.25f, 454.75f, 111.898f, 428.297f, 100.246f, 446.75f, +100.246f, 446.75f, 92.2461f, 449.746f, 78.5586f, 475.656f, 88.2461f, 414.746f, 88.2461f, 414.746f, +98.0586f, 376.996f, 248.25f, 432.75f, 248.25f, 432.75f, 248.25f, 432.75f, 433.5f, 466.195f, +446.246f, 470.75f, 458.141f, 474.996f, 553.246f, 468.75f, 553.246f, 468.75f, 548.246f, 492.75f, +476.621f, 543.879f, 464.301f, 511.957f, 449.25f, 516.75f, 434.379f, 520.758f, 437.02f, 510.195f, +433.246f, 509.75f, 429.98f, 508.438f, 386.859f, 535.719f, 380.25f, 534.75f, 10, 0, +1, 1, 1, 1, 1, 1, 18, 92.2461f, 448.75f, 78.5391f, +472.637f, 89.2461f, 411.746f, 89.2461f, 411.746f, 98.7773f, 373.637f, 249.25f, 430.75f, 249.25f, +430.75f, 249.25f, 430.75f, 434.938f, 464.277f, 447.25f, 468.75f, 459.578f, 473.078f, 553.246f, +466.746f, 553.246f, 466.746f, 548.246f, 491.746f, 477.18f, 542.598f, 465.738f, 510.039f, 451.25f, +514.75f, 435.82f, 518.84f, 438.461f, 508.277f, 435.246f, 507.75f, 431.418f, 506.52f, 388.301f, +533.797f, 381.25f, 532.746f, 374.219f, 532.039f, 346.477f, 558.227f, 363.25f, 522.746f, 387.23f, +470.762f, 295.941f, 481.848f, 280.246f, 492.75f, 261.578f, 504.758f, 288.246f, 471.746f, 288.246f, +471.746f, 308.219f, 450.195f, 270.25f, 468.75f, 270.25f, 468.75f, 232.539f, 482.758f, 206.137f, +454.598f, 202.246f, 453.746f, 199.098f, 452.836f, 193.816f, 449.316f, 193.25f, 456.746f, 192.059f, +463.398f, 185.387f, 479.738f, 149.246f, 452.75f, 111.977f, 425.559f, 100.246f, 442.746f, 100.246f, +442.746f, 92.2461f, 448.75f, 10, 0, 0, 0, 0, 0, 0, +0, 7, 138.246f, 415.75f, 138.246f, 415.75f, 130.457f, 402.676f, 153.246f, 387.746f, +153.242f, 387.742f, 154.879f, 386.617f, 135.25f, 390.746f, 135.25f, 390.746f, 128.258f, 393.438f, +126.246f, 404.75f, 126.242f, 404.75f, 121.219f, 409.719f, 116.246f, 415.75f, 110.656f, 422.035f, +138.246f, 415.75f, 138.246f, 415.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 8, 292.25f, 467.746f, 292.25f, 467.746f, 311.848f, 438.297f, 311.246f, +432.75f, 309.758f, 421.598f, 309.539f, 411.035f, 313.246f, 406.75f, 316.578f, 402.238f, 326.25f, +365.746f, 326.25f, 365.746f, 326.25f, 365.742f, 325.82f, 364.398f, 339.25f, 405.746f, 339.25f, +405.742f, 352.219f, 423.797f, 330.25f, 443.75f, 330.25f, 443.75f, 291.5f, 475.719f, 292.25f, +467.746f, 10, 0, 0, 0, 0, 0, 0, 0, 15, +160.246f, 385.746f, 160.246f, 385.742f, 172.699f, 378.035f, 157.25f, 343.746f, 164.246f, 346.746f, +164.242f, 346.746f, 163.02f, 334.035f, 159.246f, 331.75f, 167.25f, 334.75f, 167.25f, 334.75f, +172.699f, 326.117f, 168.25f, 320.75f, 168.25f, 320.75f, 186.777f, 312.035f, 186.25f, 304.746f, +186.25f, 304.746f, 192.938f, 313.797f, 188.246f, 320.75f, 184.137f, 327.879f, 176.219f, 323.477f, +177.246f, 343.746f, 167.25f, 339.746f, 167.25f, 339.742f, 173.578f, 349.879f, 173.25f, 356.75f, +165.246f, 354.746f, 165.242f, 354.742f, 181.793f, 383.512f, 170.246f, 384.75f, 163.457f, 385.957f, +160.246f, 385.746f, 160.246f, 385.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 5, 196.25f, 367.75f, 196.25f, 367.75f, 199.098f, 372.316f, 196.25f, +371.75f, 192.938f, 370.559f, 158.617f, 354.277f, 152.25f, 343.746f, 152.25f, 343.742f, 189.859f, +370.559f, 196.25f, 367.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 5, 207.25f, 358.75f, 207.25f, 358.75f, 210.539f, 363.516f, 207.25f, 362.75f, +204.379f, 361.758f, 170.059f, 345.477f, 163.25f, 334.75f, 163.25f, 334.75f, 201.297f, 361.758f, +207.25f, 358.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 222.246f, 375.75f, 222.246f, 375.75f, 225.059f, 380.238f, 222.246f, 379.746f, 218.898f, +378.477f, 184.578f, 362.195f, 178.246f, 351.75f, 178.246f, 351.75f, 215.816f, 378.477f, 222.246f, +375.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, +196.25f, 327.75f, 196.25f, 327.75f, 196.457f, 334.035f, 193.25f, 332.746f, 190.297f, 332.277f, +150.699f, 312.918f, 144.246f, 302.746f, 144.246f, 302.746f, 190.297f, 330.516f, 196.25f, 327.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 198.246f, +339.746f, 198.246f, 339.742f, 199.098f, 344.598f, 196.25f, 343.746f, 193.816f, 343.719f, 164.777f, +330.957f, 158.25f, 320.75f, 158.25f, 320.75f, 190.738f, 344.156f, 198.246f, 339.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 24, 182.25f, 286.746f, +171.246f, 278.75f, 171.246f, 278.75f, 182.379f, 286.957f, 186.25f, 285.75f, 186.25f, 285.75f, +178.859f, 273.316f, 178.246f, 267.75f, 178.246f, 267.75f, 189.418f, 281.676f, 195.25f, 280.746f, +195.25f, 280.746f, 203.938f, 280.797f, 204.25f, 268.75f, 204.25f, 268.75f, 210.098f, 280.355f, +213.246f, 279.75f, 213.242f, 279.75f, 214.938f, 272.879f, 213.246f, 265.75f, 213.242f, 265.75f, +218.02f, 273.758f, 222.246f, 271.746f, 222.246f, 271.746f, 229.457f, 274.195f, 228.25f, 261.746f, +228.25f, 261.742f, 228.578f, 249.996f, 227.25f, 246.746f, 227.25f, 246.746f, 233.859f, 275.957f, +236.25f, 276.75f, 236.25f, 276.75f, 245.297f, 277.719f, 250.25f, 267.75f, 250.25f, 267.75f, +246.18f, 276.398f, 251.25f, 273.746f, 251.25f, 273.742f, 263.34f, 272.438f, 267.246f, 264.746f, +267.246f, 264.742f, 259.379f, 278.156f, 265.246f, 274.75f, 265.246f, 274.75f, 273.02f, 274.637f, +274.25f, 267.75f, 274.25f, 267.75f, 283.578f, 244.277f, 286.246f, 242.75f, 286.246f, 242.75f, +277.418f, 266.277f, 279.246f, 266.746f, 279.242f, 266.742f, 276.977f, 279.477f, 282.25f, 262.75f, +282.25f, 262.75f, 279.18f, 278.598f, 285.25f, 277.746f, 291.5f, 276.836f, 296.34f, 265.836f, +305.25f, 268.75f, 305.25f, 268.75f, 316.141f, 262.316f, 318.25f, 338.75f, 182.25f, 286.746f, +10, 0, 0, 0, 0, 0, 0, 0, 15, 187.246f, +388.75f, 187.246f, 388.75f, 203.5f, 395.637f, 247.246f, 388.75f, 247.242f, 388.75f, 255.418f, +388.598f, 263.25f, 398.746f, 270.379f, 407.957f, 299.859f, 415.879f, 307.25f, 413.75f, 317.25f, +406.75f, 318.25f, 405.746f, 318.25f, 405.742f, 331.98f, 393.879f, 332.246f, 385.746f, 332.859f, +377.156f, 316.578f, 324.355f, 306.25f, 306.746f, 295.457f, 289.156f, 284.898f, 275.516f, 264.246f, +277.746f, 264.246f, 277.742f, 240.898f, 282.559f, 212.246f, 277.746f, 212.246f, 277.742f, 180.617f, +279.918f, 177.246f, 288.746f, 174.457f, 297.516f, 190.246f, 313.746f, 190.246f, 313.746f, 190.246f, +313.746f, 194.699f, 323.477f, 193.25f, 339.746f, 192.059f, 355.156f, 192.5f, 385.957f, 187.246f, +388.75f, 10, 0, 0.9f, 0.4f, 0.55f, 0.9f, 0.4f, 0.55f, 8, +211.246f, 386.75f, 220.656f, 366.598f, 188.246f, 294.75f, 188.246f, 294.75f, 185.898f, 293.117f, +202.023f, 286.469f, 213.246f, 288.746f, 225.219f, 292.059f, 269.246f, 287.75f, 269.246f, 287.75f, +295.457f, 304.559f, 309.246f, 353.75f, 309.246f, 353.75f, 309.246f, 353.75f, 320.98f, 379.797f, +301.246f, 383.746f, 282.258f, 386.836f, 211.246f, 386.75f, 211.246f, 386.75f, 10, 0, +0.7f, 0.2f, 0.35f, 0.7f, 0.2f, 0.35f, 6, 209.246f, 352.746f, 212.844f, +366.922f, 214.586f, 379.902f, 211.246f, 386.75f, 211.246f, 386.75f, 280.059f, 379.797f, 292.25f, +402.75f, 297.043f, 411.34f, 313.277f, 377.598f, 313.246f, 366.75f, 313.242f, 366.75f, 243.539f, +351.195f, 227.25f, 363.746f, 209.246f, 352.746f, 10, 0, 0.65f, 0.15f, 0.3f, +0.65f, 0.15f, 0.3f, 13, 214.25f, 334.75f, 214.25f, 334.75f, 216.258f, 326.996f, +213.246f, 322.75f, 213.242f, 322.75f, 211.859f, 321.719f, 210.246f, 321.746f, 210.246f, 321.742f, +211.859f, 317.316f, 218.25f, 315.746f, 218.25f, 315.746f, 220.656f, 310.719f, 223.246f, 310.746f, +225.938f, 309.836f, 231.219f, 303.676f, 235.246f, 304.746f, 240.02f, 306.316f, 252.25f, 310.746f, +252.25f, 310.746f, 252.25f, 310.742f, 258.5f, 314.238f, 268.246f, 310.746f, 268.242f, 310.742f, +270.789f, 311.16f, 271.25f, 315.746f, 271.809f, 320.727f, 275.219f, 324.797f, 277.246f, 326.746f, +279.617f, 329.195f, 290.18f, 343.277f, 289.246f, 343.746f, 287.539f, 344.156f, 214.25f, 334.75f, +214.25f, 334.75f, 10, 0, 1, 0.45f, 0.5f, 1, 0.45f, 0.5f, +12, 209.246f, 387.746f, 209.246f, 387.742f, 206.137f, 363.516f, 209.246f, 354.746f, 213.18f, +345.035f, 212.297f, 342.836f, 211.246f, 338.75f, 210.539f, 334.035f, 215.379f, 323.035f, 221.246f, +316.75f, 234.246f, 314.75f, 234.246f, 314.75f, 251.457f, 318.637f, 261.25f, 315.746f, 261.25f, +315.746f, 271.473f, 314.078f, 275.246f, 330.746f, 275.246f, 330.742f, 280.5f, 337.559f, 288.246f, +340.75f, 296.34f, 343.719f, 304.258f, 389.477f, 300.246f, 398.746f, 295.457f, 407.078f, 279.617f, +411.918f, 262.25f, 394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 10, +1.1f, 0, 0, 0, 0, 0, 0, 12, 209.246f, 387.746f, +209.246f, 387.742f, 206.137f, 363.516f, 209.246f, 354.746f, 213.18f, 345.035f, 212.297f, 342.836f, +211.246f, 338.75f, 210.539f, 334.035f, 215.379f, 323.035f, 221.246f, 316.75f, 234.246f, 314.75f, +234.246f, 314.75f, 251.457f, 318.637f, 261.25f, 315.746f, 261.25f, 315.746f, 271.473f, 314.078f, +275.246f, 330.746f, 275.246f, 330.742f, 280.5f, 337.559f, 288.246f, 340.75f, 296.34f, 343.719f, +304.258f, 389.477f, 300.246f, 398.746f, 295.457f, 407.078f, 279.617f, 411.918f, 262.25f, 394.746f, +244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 10, 0, 1, 1, +0.8f, 1, 1, 0.8f, 7, 211.246f, 305.75f, 211.246f, 305.75f, 210.098f, +308.078f, 205.25f, 308.746f, 205.25f, 308.742f, 180.617f, 312.477f, 171.246f, 325.75f, 171.246f, +325.75f, 163.898f, 332.277f, 168.25f, 319.746f, 168.25f, 319.742f, 180.18f, 297.078f, 187.246f, +293.746f, 187.246f, 293.746f, 205.699f, 289.598f, 211.246f, 305.75f, 10, 0.55f, 0, +0, 0, 0, 0, 0, 7, 211.246f, 305.75f, 211.246f, 305.75f, +210.098f, 308.078f, 205.25f, 308.746f, 205.25f, 308.742f, 180.617f, 312.477f, 171.246f, 325.75f, +171.246f, 325.75f, 163.898f, 332.277f, 168.25f, 319.746f, 168.25f, 319.742f, 180.18f, 297.078f, +187.246f, 293.746f, 187.246f, 293.746f, 205.699f, 289.598f, 211.246f, 305.75f, 10, 0, +0.8f, 0.25f, 0.3f, 0.8f, 0.25f, 0.3f, 9, 299.246f, 375.75f, 299.641f, +384.941f, 301.789f, 394.418f, 300.246f, 398.746f, 292.766f, 412.461f, 274.098f, 406.535f, 262.25f, +394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 209.246f, 387.742f, 207.297f, +372.797f, 208.25f, 361.746f, 208.25f, 361.742f, 249.258f, 374.516f, 250.25f, 368.746f, 250.25f, +368.746f, 251.898f, 371.879f, 262.25f, 371.75f, 272.137f, 371.879f, 297.152f, 373.168f, 299.246f, +375.75f, 10, 2.2f, 0.65f, 0.1f, 0.15f, 0.65f, 0.1f, 0.15f, 3, +251.25f, 387.746f, 251.25f, 387.742f, 256.738f, 381.996f, 253.246f, 371.75f, 253.246f, 371.75f, +236.938f, 353.836f, 239.25f, 338.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 5, 198.246f, 293.746f, 198.246f, 293.746f, 193.816f, 308.078f, 203.25f, +300.75f, 203.25f, 300.75f, 208.777f, 298.398f, 207.25f, 296.75f, 206.137f, 294.879f, 199.977f, +290.477f, 198.246f, 293.746f, 10, 0.55f, 0, 0, 0, 0, 0, +0, 5, 198.246f, 293.746f, 198.246f, 293.746f, 193.816f, 308.078f, 203.25f, 300.75f, +203.25f, 300.75f, 208.777f, 298.398f, 207.25f, 296.75f, 206.137f, 294.879f, 199.977f, 290.477f, +198.246f, 293.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, +5, 204.25f, 292.75f, 204.25f, 292.75f, 200.328f, 303.941f, 208.25f, 297.746f, 208.25f, +297.742f, 212.937f, 295.266f, 211.246f, 294.75f, 206.227f, 293.383f, 211.242f, 290.566f, 204.25f, +292.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, +204.25f, 292.75f, 204.25f, 292.75f, 200.328f, 303.941f, 208.25f, 297.746f, 208.25f, 297.742f, +212.937f, 295.266f, 211.246f, 294.75f, 206.227f, 293.383f, 211.242f, 290.566f, 204.25f, 292.75f, +10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 209.246f, +292.75f, 209.246f, 292.75f, 205.609f, 303.941f, 213.246f, 297.746f, 213.242f, 297.742f, 218.168f, +295.414f, 216.25f, 294.75f, 212.824f, 293.383f, 216.523f, 290.566f, 209.246f, 292.75f, 10, +0.55f, 0, 0, 0, 0, 0, 0, 5, 209.246f, 292.75f, +209.246f, 292.75f, 205.609f, 303.941f, 213.246f, 297.746f, 213.242f, 297.742f, 218.168f, 295.414f, +216.25f, 294.75f, 212.824f, 293.383f, 216.523f, 290.566f, 209.246f, 292.75f, 10, 0, +1, 1, 0.8f, 1, 1, 0.8f, 5, 216.25f, 292.75f, 216.25f, +292.75f, 212.871f, 303.723f, 220.246f, 297.746f, 220.246f, 297.742f, 225.434f, 295.172f, 224.246f, +294.75f, 220.527f, 293.383f, 223.781f, 290.344f, 216.25f, 292.75f, 10, 0.55f, 0, +0, 0, 0, 0, 0, 5, 216.25f, 292.75f, 216.25f, 292.75f, +212.871f, 303.723f, 220.246f, 297.746f, 220.246f, 297.742f, 225.434f, 295.172f, 224.246f, 294.75f, +220.527f, 293.383f, 223.781f, 290.344f, 216.25f, 292.75f, 10, 0, 1, 1, +0.8f, 1, 1, 0.8f, 5, 224.246f, 292.75f, 224.242f, 292.75f, 220, +303.809f, 227.25f, 297.746f, 227.25f, 297.742f, 231.969f, 296.066f, 231.246f, 294.75f, 229.855f, +293.25f, 230.91f, 290.434f, 224.246f, 292.75f, 10, 0.55f, 0, 0, 0, +0, 0, 0, 5, 224.246f, 292.75f, 224.242f, 292.75f, 220, 303.809f, +227.25f, 297.746f, 227.25f, 297.742f, 231.969f, 296.066f, 231.246f, 294.75f, 229.855f, 293.25f, +230.91f, 290.434f, 224.246f, 292.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 5, 231.246f, 291.746f, 231.246f, 291.746f, 225.938f, 305.438f, 236.25f, +298.75f, 236.25f, 298.75f, 241.34f, 296.195f, 240.25f, 294.75f, 238.699f, 292.676f, 240.02f, +289.156f, 231.246f, 291.746f, 10, 0.55f, 0, 0, 0, 0, 0, +0, 5, 231.246f, 291.746f, 231.246f, 291.746f, 225.938f, 305.438f, 236.25f, 298.75f, +236.25f, 298.75f, 241.34f, 296.195f, 240.25f, 294.75f, 238.699f, 292.676f, 240.02f, 289.156f, +231.246f, 291.746f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, +4, 200.246f, 310.746f, 200.246f, 310.742f, 214.5f, 313.797f, 221.246f, 310.746f, 221.246f, +310.742f, 227.699f, 308.957f, 229.25f, 309.75f, 230.34f, 309.836f, 234.246f, 310.746f, 234.246f, +310.746f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 4, +237.25f, 300.75f, 237.25f, 300.75f, 250.578f, 315.996f, 264.246f, 310.746f, 271.496f, 308.328f, +270.379f, 312.035f, 271.25f, 314.75f, 272.137f, 318.195f, 272.359f, 322.816f, 278.246f, 325.75f, +10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, 256.246f, +318.75f, 256.246f, 318.75f, 251.898f, 330.516f, 249.25f, 316.75f, 245.738f, 302.355f, 242.219f, +298.398f, 240.25f, 295.746f, 240.25f, 295.742f, 240.457f, 289.598f, 249.25f, 289.75f, 249.25f, +289.75f, 261.578f, 290.477f, 262.25f, 293.746f, 262.457f, 296.637f, 260.699f, 309.398f, 256.246f, +318.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 7, +256.246f, 318.75f, 256.246f, 318.75f, 251.898f, 330.516f, 249.25f, 316.75f, 245.738f, 302.355f, +242.219f, 298.398f, 240.25f, 295.746f, 240.25f, 295.742f, 240.457f, 289.598f, 249.25f, 289.75f, +249.25f, 289.75f, 261.578f, 290.477f, 262.25f, 293.746f, 262.457f, 296.637f, 260.699f, 309.398f, +256.246f, 318.75f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, +2, 271.25f, 310.746f, 271.25f, 310.742f, 275.656f, 313.355f, 278.246f, 311.75f, 10, +2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 2, 279.246f, 328.746f, +279.242f, 328.742f, 282.039f, 334.148f, 287.246f, 334.75f, 10, 0, 0.7f, 0.7f, +0.7f, 0.7f, 0.7f, 0.7f, 6, 191.246f, 288.746f, 191.242f, 288.742f, 211.418f, +284.758f, 216.25f, 286.746f, 216.25f, 286.742f, 225.938f, 286.516f, 216.25f, 284.746f, 216.25f, +284.742f, 202.617f, 284.316f, 194.25f, 285.75f, 194.25f, 285.75f, 181.059f, 291.797f, 191.246f, +288.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, +207.25f, 390.746f, 207.25f, 390.746f, 226.379f, 390.797f, 228.25f, 389.75f, 228.25f, 389.75f, +236.5f, 356.035f, 232.246f, 347.75f, 232.246f, 347.75f, 231.219f, 344.598f, 228.25f, 350.746f, +228.25f, 350.742f, 207.898f, 386.836f, 204.25f, 388.75f, 200.859f, 391.238f, 205.699f, 390.797f, +207.25f, 390.746f, 10, 0.55f, 0, 0, 0, 0, 0, 0, +7, 207.25f, 390.746f, 207.25f, 390.746f, 226.379f, 390.797f, 228.25f, 389.75f, 228.25f, +389.75f, 236.5f, 356.035f, 232.246f, 347.75f, 232.246f, 347.75f, 231.219f, 344.598f, 228.25f, +350.746f, 228.25f, 350.742f, 207.898f, 386.836f, 204.25f, 388.75f, 200.859f, 391.238f, 205.699f, +390.797f, 207.25f, 390.746f, 10, 0, 1, 1, 0.8f, 1, 1, +0.8f, 7, 122.246f, 393.75f, 122.246f, 393.75f, 132, 391.898f, 146.25f, 388.75f, +146.25f, 388.75f, 151.137f, 364.398f, 154.246f, 358.75f, 158.18f, 353.836f, 154.219f, 353.836f, +150.246f, 356.75f, 146.297f, 359.996f, 130.02f, 375.398f, 128.25f, 379.746f, 125.617f, 385.078f, +122.246f, 393.75f, 122.246f, 393.75f, 10, 0.55f, 0, 0, 0, 0, +0, 0, 7, 122.246f, 393.75f, 122.246f, 393.75f, 132, 391.898f, 146.25f, +388.75f, 146.25f, 388.75f, 151.137f, 364.398f, 154.246f, 358.75f, 158.18f, 353.836f, 154.219f, +353.836f, 150.246f, 356.75f, 146.297f, 359.996f, 130.02f, 375.398f, 128.25f, 379.746f, 125.617f, +385.078f, 122.246f, 393.75f, 122.246f, 393.75f, 10, 0, 1, 1, 0.8f, +1, 1, 0.8f, 6, 146.25f, 388.75f, 146.25f, 388.75f, 152.637f, 387.094f, +153.246f, 384.75f, 154.855f, 382.223f, 152.25f, 378.75f, 152.25f, 378.75f, 152.25f, 378.75f, +151.324f, 374.961f, 150.246f, 377.75f, 148.68f, 379.719f, 145.52f, 388.145f, 146.25f, 388.75f, +10, 0.55f, 0, 0, 0, 0, 0, 0, 6, 146.25f, +388.75f, 146.25f, 388.75f, 152.637f, 387.094f, 153.246f, 384.75f, 154.855f, 382.223f, 152.25f, +378.75f, 152.25f, 378.75f, 152.25f, 378.75f, 151.324f, 374.961f, 150.246f, 377.75f, 148.68f, +379.719f, 145.52f, 388.145f, 146.25f, 388.75f, 10, 0, 0, 0, 0, +0, 0, 0, 10, 146.25f, 388.75f, 146.25f, 388.75f, 150.258f, 383.316f, +154.246f, 383.746f, 158.18f, 383.316f, 158.598f, 383.77f, 161.246f, 382.75f, 166.758f, 381.996f, +166.316f, 384.195f, 173.25f, 382.75f, 176.48f, 382.348f, 179.297f, 383.316f, 182.25f, 381.746f, +185.457f, 380.676f, 188.977f, 381.559f, 190.246f, 383.746f, 191.617f, 385.957f, 197.25f, 390.746f, +197.25f, 390.746f, 197.25f, 390.746f, 182.816f, 388.598f, 179.246f, 387.746f, 179.246f, 387.742f, +155.098f, 386.398f, 146.25f, 388.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 6, 195.25f, 388.75f, 195.25f, 388.75f, 188.262f, 384.969f, 188.246f, +382.75f, 187.383f, 379.688f, 193.25f, 375.75f, 193.25f, 375.75f, 193.25f, 375.75f, 196.625f, +370.559f, 197.25f, 372.746f, 197.941f, 375.836f, 196.238f, 388.379f, 195.25f, 388.75f, 10, +0.55f, 0, 0, 0, 0, 0, 0, 6, 195.25f, 388.75f, +195.25f, 388.75f, 188.262f, 384.969f, 188.246f, 382.75f, 187.383f, 379.688f, 193.25f, 375.75f, +193.25f, 375.75f, 193.25f, 375.75f, 196.625f, 370.559f, 197.25f, 372.746f, 197.941f, 375.836f, +196.238f, 388.379f, 195.25f, 388.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 5, 154.246f, 382.75f, 154.242f, 382.75f, 161.832f, 370.5f, 162.25f, +382.75f, 162.25f, 382.75f, 162.684f, 384.215f, 160.246f, 383.746f, 154.066f, 384.324f, 155.738f, +388.836f, 154.246f, 382.75f, 10, 0.55f, 0, 0, 0, 0, 0, +0, 5, 154.246f, 382.75f, 154.242f, 382.75f, 161.832f, 370.5f, 162.25f, 382.75f, +162.25f, 382.75f, 162.684f, 384.215f, 160.246f, 383.746f, 154.066f, 384.324f, 155.738f, 388.836f, +154.246f, 382.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, +5, 162.25f, 382.75f, 162.25f, 382.75f, 170.734f, 370.227f, 170.246f, 382.75f, 170.242f, +382.75f, 170.043f, 383, 168.25f, 382.75f, 162.891f, 383.625f, 163.27f, 388.594f, 162.25f, +382.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, +162.25f, 382.75f, 162.25f, 382.75f, 170.734f, 370.227f, 170.246f, 382.75f, 170.242f, 382.75f, +170.043f, 383, 168.25f, 382.75f, 162.891f, 383.625f, 163.27f, 388.594f, 162.25f, 382.75f, +10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 170.246f, +382.75f, 170.242f, 382.75f, 178.711f, 370.832f, 178.246f, 381.746f, 178.246f, 381.746f, 178.105f, +382.82f, 176.246f, 382.75f, 172.004f, 383.93f, 171.773f, 387.504f, 170.246f, 382.75f, 10, +0.55f, 0, 0, 0, 0, 0, 0, 5, 170.246f, 382.75f, +170.242f, 382.75f, 178.711f, 370.832f, 178.246f, 381.746f, 178.246f, 381.746f, 178.105f, 382.82f, +176.246f, 382.75f, 172.004f, 383.93f, 171.773f, 387.504f, 170.246f, 382.75f, 10, 0, +1, 1, 0.8f, 1, 1, 0.8f, 5, 177.246f, 382.75f, 177.246f, +382.75f, 186.207f, 369.719f, 186.25f, 380.75f, 186.25f, 380.75f, 188.398f, 381.992f, 186.25f, +381.746f, 180.078f, 383.051f, 180.957f, 387.953f, 177.246f, 382.75f, 10, 0.55f, 0, +0, 0, 0, 0, 0, 5, 177.246f, 382.75f, 177.246f, 382.75f, +186.207f, 369.719f, 186.25f, 380.75f, 186.25f, 380.75f, 188.398f, 381.992f, 186.25f, 381.746f, +180.078f, 383.051f, 180.957f, 387.953f, 177.246f, 382.75f, 10, 0, 0.9f, 0.9f, +0.7f, 0.9f, 0.9f, 0.7f, 6, 137.246f, 378.75f, 129.25f, 379.746f, 126.441f, +385.738f, 124.25f, 392.746f, 124.25f, 392.746f, 124.25f, 392.746f, 131.117f, 391.402f, 145.25f, +388.75f, 145.25f, 388.75f, 145.832f, 384.672f, 147.25f, 378.75f, 137.246f, 378.75f, 10, +0, 0.9f, 0.9f, 0.7f, 0.9f, 0.9f, 0.7f, 7, 209.246f, 383.746f, +207.469f, 386.437f, 206.02f, 388.371f, 205.25f, 388.75f, 201.992f, 390.891f, 206.547f, 390.477f, +208.25f, 390.746f, 208.25f, 390.746f, 226.02f, 390.477f, 228.25f, 389.75f, 228.25f, 389.75f, +228.668f, 387.18f, 229.25f, 383.746f, 229.25f, 383.742f, 218.32f, 385.66f, 209.246f, 383.746f, +10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 10, 268.246f, +535.746f, 298.758f, 531.289f, 326.832f, 570.492f, 329.25f, 580.75f, 330.703f, 591.789f, 319.25f, +604.75f, 319.25f, 604.75f, 321.023f, 608.246f, 315.699f, 623.734f, 310.246f, 633.75f, 304.082f, +644.063f, 286.594f, 642.992f, 267.246f, 643.75f, 249.875f, 645.031f, 229.547f, 619.379f, 228.25f, +617.75f, 226.641f, 615.508f, 233.418f, 573.398f, 235.246f, 566.75f, 236.32f, 560.813f, 233.246f, +531.75f, 233.246f, 531.75f, 271.082f, 541.781f, 237.773f, 540, 268.246f, 535.746f, 10, +0, 0.92f, 0.56f, 0.32f, 0.92f, 0.56f, 0.32f, 10, 229.25f, 616.746f, +227.469f, 614.828f, 234.121f, 573.484f, 235.246f, 567.75f, 236.973f, 561.129f, 234.246f, 532.746f, +234.246f, 532.746f, 270.063f, 542.387f, 238.398f, 540.695f, 268.246f, 536.75f, 298.273f, 532.141f, +325.836f, 570.633f, 327.25f, 580.75f, 329.637f, 591.543f, 318.25f, 604.75f, 318.25f, 604.75f, +320.133f, 607.699f, 314.906f, 622.906f, 309.246f, 632.75f, 303.504f, 642.867f, 286.332f, 641.813f, +267.246f, 642.746f, 250.277f, 643.816f, 230.32f, 618.629f, 229.25f, 616.746f, 10, 0, +0.94f, 0.67f, 0.49f, 0.94f, 0.67f, 0.49f, 10, 229.25f, 615.75f, 228.297f, +614.152f, 234.824f, 573.574f, 236.25f, 567.75f, 237.625f, 561.445f, 235.246f, 533.746f, 235.246f, +533.746f, 269.371f, 543.539f, 239.023f, 541.391f, 268.246f, 536.75f, 297.793f, 532.996f, 324.844f, +570.773f, 326.25f, 580.75f, 328.574f, 591.297f, 318.25f, 603.746f, 318.25f, 603.746f, 319.246f, +607.156f, 314.113f, 622.078f, 308.246f, 631.746f, 302.922f, 641.668f, 286.066f, 640.637f, 267.246f, +641.75f, 250.684f, 642.602f, 231.094f, 617.883f, 229.25f, 615.75f, 10, 0, 0.96f, +0.78f, 0.66f, 0.96f, 0.78f, 0.66f, 10, 230.25f, 615.75f, 229.125f, 613.473f, +235.531f, 573.66f, 237.25f, 567.75f, 238.277f, 561.762f, 235.246f, 534.75f, 235.246f, 534.75f, +267.91f, 544.25f, 239.648f, 542.086f, 268.246f, 537.746f, 297.309f, 533.852f, 323.848f, 570.914f, +325.25f, 580.75f, 327.508f, 591.051f, 317.25f, 603.746f, 317.25f, 603.746f, 318.355f, 606.609f, +313.324f, 621.254f, 308.246f, 630.75f, 302.34f, 640.473f, 285.805f, 639.457f, 267.246f, 640.746f, +251.09f, 641.387f, 231.871f, 617.133f, 230.25f, 615.75f, 10, 0, 0.98f, 0.89f, +0.83f, 0.98f, 0.89f, 0.83f, 10, 231.246f, 614.746f, 229.949f, 612.797f, 236.234f, +573.75f, 237.25f, 567.75f, 238.926f, 562.082f, 236.25f, 534.75f, 236.25f, 534.75f, 266.891f, +544.965f, 240.273f, 542.781f, 268.246f, 538.75f, 296.824f, 534.703f, 322.855f, 571.055f, 324.246f, +580.75f, 326.445f, 590.805f, 316.25f, 602.75f, 316.25f, 602.75f, 317.469f, 606.063f, 312.531f, +620.426f, 307.25f, 629.746f, 301.762f, 639.273f, 285.543f, 638.281f, 267.246f, 639.75f, 251.492f, +640.172f, 232.645f, 616.387f, 231.246f, 614.746f, 10, 0, 1, 1, 1, +1, 1, 1, 10, 268.246f, 539.746f, 296.34f, 535.559f, 321.859f, 571.199f, +323.246f, 580.75f, 325.379f, 590.559f, 315.25f, 602.75f, 315.25f, 602.75f, 316.578f, 605.52f, +311.738f, 619.598f, 306.25f, 628.75f, 301.18f, 638.078f, 285.277f, 637.102f, 267.246f, 637.75f, +251.898f, 638.957f, 233.418f, 615.637f, 232.246f, 613.75f, 230.777f, 612.117f, 236.938f, 573.84f, +238.25f, 567.75f, 239.578f, 562.398f, 237.25f, 535.746f, 237.25f, 535.746f, 264.988f, 545.457f, +240.898f, 543.477f, 268.246f, 539.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 6, 319.25f, 576.746f, 319.25f, 576.742f, 289.078f, 568.559f, 276.246f, +570.746f, 276.246f, 570.746f, 258.937f, 577.578f, 249.25f, 553.75f, 249.25f, 553.75f, 245.297f, +545.68f, 243.246f, 543.746f, 240.898f, 541.277f, 319.25f, 576.746f, 319.25f, 576.746f, 10, +0, 0, 0, 0, 0, 0, 0, 11, 324.246f, 579.746f, +324.242f, 579.746f, 291.937f, 565.918f, 281.25f, 566.75f, 281.25f, 566.75f, 262.898f, 571.418f, +253.246f, 555.75f, 253.246f, 555.75f, 244.418f, 545.238f, 241.25f, 543.746f, 241.25f, 543.742f, +240.457f, 541.719f, 247.246f, 545.75f, 259.25f, 540.75f, 259.25f, 540.75f, 275.219f, 529.84f, +286.246f, 547.75f, 286.246f, 547.75f, 290.18f, 559.758f, 290.246f, 561.746f, 290.18f, 564.156f, +313.5f, 570.316f, 315.25f, 570.746f, 317.02f, 571.199f, 324.277f, 575.816f, 324.246f, 579.746f, +10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, 6, 271.25f, +539.746f, 264.141f, 539.832f, 254.93f, 544.086f, 255.246f, 550.746f, 254.93f, 557.832f, 264.141f, +564.723f, 271.25f, 564.75f, 279.258f, 564.723f, 285.387f, 559.152f, 285.25f, 552.746f, 285.387f, +545.402f, 279.258f, 539.832f, 271.25f, 539.746f, 10, 0, 0.4f, 0.6f, 0, +0.4f, 0.6f, 0, 6, 267.246f, 557.746f, 262.383f, 557.391f, 256.785f, 555.738f, +257.246f, 555.75f, 258.559f, 561.055f, 265.555f, 564.723f, 271.25f, 564.75f, 276.422f, 564.723f, +280.59f, 562.547f, 283.25f, 558.75f, 283.25f, 558.75f, 277.203f, 559.598f, 267.246f, 557.746f, +10, 0, 1, 1, 1, 1, 1, 1, 4, 281.25f, +558.75f, 281.25f, 558.75f, 276.098f, 561.957f, 276.246f, 559.746f, 276.246f, 559.746f, 280.059f, +554.699f, 281.25f, 558.75f, 10, 0, 0, 0, 0, 0, 0, +0, 6, 270.25f, 549.75f, 267.187f, 549.5f, 264.961f, 551.727f, 265.246f, 554.746f, +264.961f, 557.227f, 267.187f, 559.457f, 270.25f, 559.746f, 272.687f, 559.457f, 274.918f, 557.227f, +275.246f, 554.746f, 274.918f, 551.727f, 272.687f, 549.5f, 270.25f, 549.75f, 10, 0, +0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 15, 155.246f, 563.746f, 155.246f, +563.742f, 152.02f, 587.477f, 154.246f, 592.746f, 154.242f, 592.746f, 166.539f, 603.316f, 166.246f, +607.746f, 166.246f, 607.742f, 165.656f, 627.078f, 164.246f, 627.746f, 163.02f, 628.84f, 154.656f, +635.438f, 148.246f, 628.75f, 148.242f, 628.75f, 136.617f, 608.598f, 137.246f, 601.746f, 137.246f, +599.75f, 137.242f, 599.75f, 129.137f, 599.797f, 127.246f, 597.75f, 127.246f, 597.75f, 126.059f, +591.879f, 124.25f, 591.75f, 124.25f, 591.75f, 121.656f, 588.797f, 124.25f, 585.746f, 124.25f, +585.742f, 121.656f, 583.078f, 122.246f, 578.75f, 130.25f, 574.746f, 130.25f, 574.742f, 132.656f, +558.438f, 144.246f, 552.746f, 149.859f, 550.156f, 153.34f, 557.559f, 155.246f, 563.746f, 10, +0, 1, 1, 1, 1, 1, 1, 15, 154.246f, 565.746f, +154.242f, 565.742f, 151.27f, 587.172f, 153.246f, 591.75f, 153.242f, 591.75f, 164.34f, 601.426f, +164.246f, 604.75f, 164.242f, 604.75f, 163.547f, 622.809f, 162.25f, 623.746f, 161.172f, 624.395f, +153.645f, 630.336f, 147.25f, 623.746f, 147.25f, 623.746f, 137.41f, 606.18f, 138.246f, 599.75f, +138.246f, 597.75f, 138.246f, 597.75f, 130.68f, 598.258f, 129.25f, 596.746f, 129.25f, 596.742f, +127.906f, 591.129f, 126.246f, 590.746f, 126.242f, 590.746f, 123.945f, 588.359f, 126.246f, 585.746f, +126.242f, 585.742f, 123.945f, 583.211f, 124.25f, 579.746f, 132.246f, 575.75f, 132.246f, 575.75f, +133.848f, 561.035f, 144.246f, 555.75f, 149.324f, 553.582f, 152.457f, 560.242f, 154.246f, 565.746f, +10, 0, 0.925f, 0.588f, 0.363f, 0.925f, 0.588f, 0.363f, 15, 164.246f, +626.75f, 162.645f, 627.816f, 154.406f, 634.16f, 148.246f, 627.746f, 148.242f, 627.742f, 136.816f, +607.992f, 137.246f, 600.75f, 137.246f, 598.746f, 137.242f, 598.742f, 129.523f, 599.414f, 128.25f, +597.75f, 128.25f, 597.75f, 126.52f, 591.691f, 125.25f, 591.75f, 125.25f, 591.75f, 122.23f, +588.688f, 124.25f, 585.746f, 124.25f, 585.742f, 122.23f, 583.109f, 122.246f, 578.75f, 131.246f, +574.746f, 131.242f, 574.742f, 132.953f, 559.086f, 144.246f, 553.75f, 149.723f, 551.012f, 153.117f, +558.23f, 155.246f, 564.75f, 155.246f, 564.75f, 151.832f, 587.402f, 154.246f, 591.75f, 154.242f, +591.75f, 165.988f, 602.844f, 165.246f, 606.75f, 165.242f, 606.75f, 165.129f, 626.012f, 164.246f, +626.75f, 10, 0, 0.95f, 0.725f, 0.575f, 0.95f, 0.725f, 0.575f, 15, +163.25f, 625.746f, 162.27f, 626.793f, 154.152f, 632.887f, 148.246f, 625.746f, 148.242f, 625.746f, +137.016f, 607.387f, 138.246f, 600.75f, 138.246f, 598.746f, 138.246f, 598.742f, 129.906f, 599.027f, +128.25f, 596.746f, 128.25f, 596.742f, 126.98f, 591.504f, 125.25f, 590.746f, 125.25f, 590.746f, +122.801f, 588.578f, 125.25f, 585.746f, 125.25f, 585.742f, 122.801f, 583.145f, 123.25f, 578.75f, +131.246f, 574.746f, 131.242f, 574.742f, 133.254f, 559.734f, 144.246f, 554.746f, 149.59f, 551.867f, +152.898f, 558.898f, 155.246f, 564.75f, 155.246f, 564.75f, 151.645f, 587.324f, 154.246f, 591.75f, +154.242f, 591.75f, 165.438f, 602.371f, 165.246f, 605.746f, 165.242f, 605.742f, 164.602f, 624.945f, +163.25f, 625.746f, 10, 0, 0.975f, 0.863f, 0.788f, 0.975f, 0.863f, 0.788f, +15, 163.25f, 624.75f, 161.895f, 625.77f, 153.898f, 631.609f, 148.246f, 624.75f, 148.242f, +624.75f, 137.211f, 606.781f, 138.246f, 600.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.293f, +598.645f, 128.25f, 596.746f, 128.25f, 596.742f, 127.445f, 591.316f, 126.246f, 590.746f, 126.242f, +590.746f, 123.375f, 588.469f, 125.25f, 585.746f, 125.25f, 585.742f, 123.375f, 583.176f, 124.25f, +578.75f, 131.246f, 574.746f, 131.242f, 574.742f, 133.551f, 560.387f, 144.246f, 554.746f, 149.457f, +552.727f, 152.68f, 559.57f, 154.246f, 565.746f, 154.242f, 565.742f, 151.457f, 587.246f, 154.246f, +591.75f, 154.242f, 591.75f, 164.887f, 601.898f, 164.246f, 605.746f, 164.242f, 605.742f, 164.074f, +623.879f, 163.25f, 624.75f, 10, 0, 1, 1, 1, 1, 1, +1, 15, 154.246f, 566.75f, 154.242f, 566.75f, 151.27f, 587.172f, 153.246f, 591.75f, +153.242f, 591.75f, 164.34f, 601.426f, 164.246f, 604.75f, 164.242f, 604.75f, 163.547f, 622.809f, +162.25f, 623.746f, 161.523f, 624.746f, 153.645f, 630.336f, 147.25f, 623.746f, 147.25f, 623.746f, +137.41f, 606.18f, 138.246f, 599.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.68f, 598.258f, +129.25f, 596.746f, 129.25f, 596.742f, 127.906f, 591.129f, 126.246f, 590.746f, 126.242f, 590.746f, +123.945f, 588.359f, 126.246f, 585.746f, 126.242f, 585.742f, 123.945f, 583.211f, 124.25f, 579.746f, +132.246f, 575.75f, 132.246f, 575.75f, 133.848f, 561.035f, 144.246f, 555.75f, 149.324f, 553.582f, +152.457f, 560.352f, 154.246f, 566.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 5, 151.25f, 572.746f, 151.25f, 572.742f, 127.27f, 584.398f, 126.246f, +585.746f, 126.242f, 585.742f, 136.289f, 576.258f, 137.246f, 576.746f, 138.047f, 576.258f, 151.25f, +572.746f, 151.25f, 572.746f, 10, 0, 0, 0, 0, 0, 0, +0, 5, 132.246f, 579.746f, 132.246f, 579.746f, 152.457f, 576.039f, 152.25f, 570.746f, +152.457f, 567.996f, 152.191f, 553.234f, 146.25f, 554.746f, 137.059f, 557.559f, 141.02f, 573.398f, +132.246f, 579.746f, 10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, +5, 141.25f, 575.75f, 141.25f, 575.75f, 151.332f, 574.195f, 152.25f, 570.746f, 153.117f, +569.438f, 153.848f, 560.301f, 148.246f, 558.75f, 142.832f, 558.098f, 140.379f, 569.34f, 141.25f, +575.75f, 10, 0, 0, 0, 0, 0, 0, 0, 44, +236.25f, 528.746f, 235.504f, 530.93f, 236.949f, 530.785f, 239.25f, 531.75f, 241.117f, 532.039f, +254.539f, 536.219f, 255.246f, 538.75f, 256.297f, 541.938f, 271.25f, 536.75f, 271.25f, 536.75f, +272.797f, 536.219f, 277.246f, 533.746f, 277.246f, 533.746f, 282.918f, 532.039f, 290.246f, 531.75f, +290.246f, 531.75f, 292.816f, 530.5f, 296.25f, 527.75f, 296.25f, 527.75f, 312.617f, 516.199f, +326.25f, 523.75f, 326.25f, 523.75f, 348.258f, 531.379f, 341.25f, 550.746f, 341.25f, 550.746f, +338.359f, 560.199f, 342.246f, 563.746f, 342.246f, 563.746f, 342.098f, 568.117f, 350.25f, 560.75f, +350.25f, 560.75f, 352.879f, 556.457f, 354.246f, 550.746f, 354.246f, 550.746f, 362.559f, 538.637f, +359.25f, 557.746f, 359.25f, 557.746f, 359.039f, 559.316f, 355.961f, 563.277f, 356.246f, 564.75f, +355.961f, 565.918f, 354.246f, 569.75f, 354.246f, 569.75f, 350.68f, 573.398f, 353.246f, 580.75f, +353.246f, 580.75f, 355.301f, 596.277f, 353.246f, 594.746f, 353.246f, 594.746f, 351.559f, 596.277f, +341.25f, 585.746f, 341.25f, 585.746f, 339.02f, 581.539f, 332.246f, 579.746f, 332.246f, 579.746f, +329.34f, 577.797f, 325.25f, 579.746f, 325.25f, 579.746f, 322.738f, 579.777f, 316.25f, 571.75f, +316.25f, 571.75f, 319.66f, 572.297f, 322.301f, 567.457f, 325.25f, 566.75f, 327.578f, 567.02f, +329.559f, 569.879f, 331.246f, 570.746f, 333.078f, 571.199f, 336.25f, 564.75f, 336.25f, 564.75f, +336.598f, 561.957f, 330.25f, 556.75f, 330.25f, 556.75f, 330, 551.617f, 328.25f, 553.75f, +328.25f, 553.75f, 324.938f, 554.039f, 323.621f, 549.859f, 322.246f, 544.746f, 321.418f, 539.738f, +317.25f, 539.746f, 317.25f, 539.746f, 315.039f, 531.156f, 313.246f, 534.75f, 313.246f, 534.75f, +313.5f, 540.617f, 307.25f, 533.746f, 307.25f, 533.746f, 305.578f, 532.039f, 300.246f, 534.75f, +300.246f, 534.75f, 293.039f, 536.656f, 295.25f, 538.75f, 295.25f, 538.75f, 297.656f, 541.277f, +310.246f, 538.75f, 310.246f, 538.75f, 312.398f, 540.617f, 303.25f, 544.746f, 303.25f, 544.746f, +302.937f, 547, 304.25f, 551.75f, 304.25f, 551.75f, 305.359f, 555.359f, 313.246f, 561.746f, +313.246f, 561.746f, 323.18f, 562.84f, 320.246f, 564.75f, 320.246f, 564.75f, 313.277f, 570.316f, +307.25f, 561.746f, 307.25f, 561.746f, 304.477f, 555.137f, 285.25f, 538.75f, 285.25f, 538.75f, +280.059f, 534.898f, 282.918f, 542.379f, 278.246f, 538.75f, 274.117f, 534.898f, 251.25f, 544.746f, +251.25f, 544.746f, 238.738f, 546.109f, 235.734f, 528.793f, 232.246f, 531.75f, 232.246f, 531.75f, +237.813f, 522.855f, 236.25f, 528.746f, 10, 0, 0, 0, 0, 0, +0, 0, 12, 450.25f, 711.746f, 450.25f, 711.746f, 422.18f, 703.199f, 419.246f, +682.746f, 419.246f, 682.742f, 416.461f, 657.438f, 439.25f, 637.75f, 439.25f, 637.75f, 439.34f, +631.039f, 441.246f, 627.746f, 441.246f, 627.742f, 439.777f, 622.238f, 460.246f, 630.75f, 490.25f, +639.75f, 490.25f, 639.75f, 497.418f, 642.477f, 503.25f, 651.746f, 508.859f, 661.84f, 525.578f, +682.52f, 521.25f, 709.75f, 521.25f, 709.75f, 522.938f, 722.559f, 516.25f, 722.746f, 516.25f, +722.746f, 507.098f, 724.758f, 499.25f, 716.75f, 499.25f, 716.75f, 491.699f, 712.879f, 489.246f, +713.746f, 450.25f, 711.746f, 10, 0, 0, 0, 0, 0, 0, +0, 8, 510.25f, 712.75f, 510.25f, 712.75f, 512.73f, 722.91f, 507.25f, 717.746f, +507.25f, 717.742f, 499.664f, 711.293f, 491.246f, 711.746f, 491.242f, 711.746f, 475.465f, 708.875f, +470.25f, 694.75f, 470.25f, 694.75f, 466.266f, 664.828f, 475.25f, 658.746f, 475.25f, 658.746f, +480.305f, 650.309f, 488.25f, 657.75f, 495.793f, 664.828f, 512.844f, 698.082f, 510.25f, 712.75f, +10, 0, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 8, 510.25f, +712.75f, 510.25f, 712.75f, 512.309f, 722.313f, 507.25f, 716.75f, 507.25f, 716.75f, 499.48f, +710.906f, 491.246f, 710.746f, 491.242f, 710.742f, 475.719f, 708.531f, 471.246f, 694.75f, 471.242f, +694.75f, 466.691f, 665.289f, 475.25f, 658.746f, 475.25f, 658.746f, 480.469f, 651.031f, 488.25f, +657.75f, 495.68f, 665.289f, 512.387f, 697.961f, 510.25f, 712.75f, 10, 0, 0.4f, +0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 8, 509.246f, 712.75f, 509.246f, 712.75f, +511.887f, 721.715f, 507.25f, 716.75f, 507.25f, 716.75f, 499.293f, 710.52f, 491.246f, 710.746f, +491.242f, 710.742f, 475.973f, 708.188f, 471.246f, 693.746f, 471.242f, 693.742f, 467.113f, 665.746f, +475.25f, 659.75f, 475.25f, 659.75f, 480.637f, 651.754f, 488.25f, 658.746f, 495.563f, 665.746f, +511.93f, 697.84f, 509.246f, 712.75f, 10, 0, 0.6f, 0.6f, 0.6f, 0.6f, +0.6f, 0.6f, 8, 509.246f, 711.746f, 509.246f, 711.746f, 511.465f, 721.113f, 506.246f, +715.746f, 506.242f, 715.742f, 499.109f, 710.133f, 491.246f, 709.75f, 491.242f, 709.75f, 476.23f, +707.844f, 471.246f, 693.746f, 471.242f, 693.742f, 467.535f, 666.203f, 476.246f, 660.746f, 476.246f, +660.742f, 480.805f, 652.477f, 488.25f, 659.75f, 495.449f, 666.203f, 511.477f, 697.719f, 509.246f, +711.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, +509.246f, 711.746f, 509.246f, 711.746f, 511.043f, 720.516f, 506.246f, 715.746f, 506.242f, 715.742f, +498.926f, 709.746f, 491.246f, 709.75f, 491.242f, 709.75f, 476.484f, 707.5f, 472.25f, 693.746f, +472.25f, 693.742f, 467.957f, 666.66f, 476.246f, 660.746f, 476.246f, 660.742f, 480.973f, 653.195f, +488.25f, 659.75f, 495.332f, 666.66f, 511.02f, 697.598f, 509.246f, 711.746f, 10, 0, +1, 1, 1, 1, 1, 1, 8, 508.25f, 710.746f, 508.25f, +710.742f, 510.621f, 719.918f, 506.246f, 714.75f, 506.242f, 714.75f, 498.738f, 709.359f, 491.246f, +709.75f, 491.242f, 709.75f, 476.738f, 707.156f, 472.25f, 693.746f, 472.25f, 693.742f, 468.379f, +667.117f, 476.246f, 661.75f, 476.246f, 661.75f, 481.141f, 653.918f, 488.25f, 660.746f, 495.219f, +667.117f, 510.563f, 697.477f, 508.25f, 710.746f, 10, 0, 0.6f, 0.15f, 0, +0.6f, 0.15f, 0, 24, 275.246f, 487.75f, 275.246f, 487.75f, 253.219f, 508.719f, +244.246f, 509.75f, 244.246f, 509.75f, 206.578f, 514, 190.246f, 493.746f, 190.246f, 493.742f, +209.656f, 516.637f, 240.25f, 510.746f, 240.25f, 510.742f, 216.258f, 515.316f, 202.246f, 511.746f, +202.242f, 511.746f, 184.137f, 511.797f, 173.25f, 496.75f, 170.246f, 490.75f, 170.242f, 490.75f, +174.898f, 507.398f, 195.25f, 513.746f, 195.25f, 513.746f, 220.219f, 519.277f, 232.246f, 513.746f, +232.246f, 513.746f, 208.34f, 521.477f, 197.25f, 519.746f, 197.25f, 519.742f, 163.898f, 521.918f, +150.246f, 492.75f, 150.246f, 492.75f, 154.219f, 508.719f, 170.246f, 516.75f, 170.242f, 516.75f, +185.457f, 526.316f, 208.25f, 522.746f, 208.25f, 522.746f, 223.738f, 519.719f, 229.25f, 516.75f, +235.18f, 514.438f, 233.859f, 517.52f, 224.246f, 522.746f, 224.242f, 522.746f, 218.457f, 533.797f, +203.25f, 533.746f, 203.25f, 533.746f, 155.977f, 529.398f, 144.246f, 515.746f, 144.246f, 515.742f, +159.5f, 528.52f, 171.246f, 531.75f, 171.246f, 531.75f, 195.578f, 540.398f, 205.25f, 539.746f, +205.25f, 539.742f, 232.098f, 538.418f, 240.25f, 542.75f, 240.25f, 542.75f, 228.137f, 537.316f, +231.246f, 533.746f, 235.18f, 530.277f, 242.656f, 521.918f, 242.246f, 520.75f, 242.656f, 519.277f, +269.277f, 494.969f, 273.25f, 489.746f, 275.246f, 487.75f, 10, 0, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 5, 428.25f, 273.746f, 428.25f, 273.742f, 410.848f, +314.348f, 397.246f, 324.746f, 397.246f, 324.746f, 425.699f, 307.199f, 429.25f, 287.75f, 429.25f, +287.75f, 429.547f, 276.398f, 428.25f, 273.746f, 10, 0, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 7, 479.25f, 265.75f, 479.25f, 265.75f, 450.449f, 326.449f, +430.246f, 352.746f, 430.246f, 352.742f, 477.949f, 311.598f, 483.25f, 282.746f, 484.246f, 276.75f, +480.246f, 278.75f, 480.242f, 278.75f, 480.148f, 269.25f, 479.25f, 265.75f, 10, 0, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 542.246f, 319.746f, 542.246f, +319.742f, 473, 384.75f, 471.246f, 387.746f, 471.242f, 387.742f, 537.898f, 314.898f, 541.25f, +306.746f, 541.25f, 306.742f, 539, 316.547f, 542.246f, 319.746f, 10, 0, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 334.246f, 271.746f, 334.246f, 271.746f, +355.848f, 328.648f, 377.246f, 303.75f, 377.246f, 303.75f, 393.25f, 292.898f, 392.25f, 289.75f, +392.25f, 289.75f, 388.297f, 296.75f, 368.246f, 295.746f, 368.242f, 295.742f, 347.598f, 299.5f, +334.246f, 271.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 544.246f, 411.746f, 544.246f, 411.742f, 494.449f, 443.047f, 486.25f, 444.746f, 473.211f, +447.297f, 540.648f, 412.797f, 546.246f, 400.75f, 546.242f, 400.75f, 548.348f, 404, 544.246f, +411.746f, 10, 0, 0, 0, 0, 0, 0, 0, 63, +378.246f, 294.75f, 378.246f, 294.75f, 400.621f, 296.637f, 408.246f, 303.75f, 413.246f, 299.746f, +432.246f, 342.75f, 436.25f, 336.75f, 436.25f, 336.75f, 452.098f, 352.957f, 451.25f, 361.746f, +450.34f, 370.559f, 465.246f, 354.746f, 465.246f, 354.746f, 465.246f, 354.742f, 464.418f, 367.918f, +472.25f, 360.75f, 472.25f, 360.75f, 469.699f, 377.598f, 479.25f, 368.746f, 479.25f, 368.746f, +467.348f, 401.969f, 492.25f, 373.75f, 498.301f, 366.598f, 493.246f, 373.75f, 493.246f, 373.75f, +493.242f, 373.75f, 464.859f, 426.879f, 488.25f, 410.75f, 488.25f, 410.75f, 490.82f, 436.117f, +489.246f, 440.746f, 488.18f, 445.797f, 485.98f, 470.438f, 480.246f, 475.746f, 475.418f, 481.879f, +481.141f, 483.637f, 487.246f, 477.746f, 487.246f, 477.742f, 474.98f, 504.316f, 489.246f, 490.75f, +489.246f, 490.75f, 485.539f, 507.84f, 480.246f, 510.746f, 480.242f, 510.742f, 474.539f, 529.84f, +491.246f, 517.746f, 491.242f, 517.742f, 486.418f, 531.598f, 483.25f, 534.75f, 483.25f, 534.75f, +470.141f, 565.477f, 478.246f, 559.746f, 483.25f, 555.75f, 483.25f, 555.75f, 475.418f, 571.637f, +482.246f, 566.75f, 489.5f, 561.957f, 489.246f, 562.75f, 489.246f, 562.75f, 489.246f, 562.75f, +466.18f, 598.918f, 488.25f, 579.746f, 488.25f, 579.746f, 479.645f, 594.867f, 476.246f, 602.75f, +476.246f, 602.75f, 455.18f, 624.879f, 471.246f, 617.75f, 476.246f, 615.75f, 476.246f, 615.75f, +466.621f, 627.078f, 458.246f, 628.75f, 449.02f, 630.598f, 460.461f, 637.637f, 467.246f, 635.75f, +474.539f, 633.238f, 491.246f, 624.75f, 491.246f, 624.75f, 491.242f, 624.75f, 505.777f, 604.199f, +510.25f, 603.746f, 510.25f, 603.746f, 488.18f, 612.117f, 495.246f, 603.746f, 495.242f, 603.746f, +510.621f, 587.918f, 502.246f, 588.75f, 502.242f, 588.75f, 496.098f, 580.438f, 501.25f, 570.746f, +501.25f, 570.746f, 481.074f, 590.988f, 497.25f, 562.75f, 505.25f, 544.746f, 505.25f, 544.746f, +478.059f, 572.078f, 490.25f, 547.75f, 490.25f, 547.75f, 509.301f, 521.918f, 511.246f, 521.746f, +513.699f, 521.039f, 518.25f, 511.746f, 518.25f, 511.746f, 513.246f, 513.746f, 519.25f, 503.75f, +519.25f, 503.75f, 507.098f, 517.078f, 513.246f, 502.746f, 520.246f, 486.746f, 520.246f, 486.742f, +497.418f, 510.918f, 512.25f, 478.746f, 512.25f, 478.746f, 494.34f, 484.078f, 504.246f, 464.746f, +504.242f, 464.742f, 502.258f, 447.559f, 502.246f, 441.75f, 503.141f, 436.117f, 504.461f, 404.879f, +499.25f, 395.75f, 494.777f, 387.277f, 506.219f, 366.156f, 508.25f, 361.746f, 510.621f, 357.355f, +514.578f, 345.477f, 505.25f, 355.75f, 495.219f, 365.719f, 500.059f, 359.559f, 502.246f, 349.75f, +504.461f, 340.195f, 511.059f, 323.035f, 510.25f, 316.75f, 510.25f, 316.75f, 508.859f, 315.559f, +505.25f, 319.746f, 505.25f, 319.742f, 489.059f, 344.598f, 491.246f, 328.746f, 491.242f, 328.742f, +489.5f, 319.957f, 486.25f, 310.746f, 486.25f, 310.742f, 482.461f, 298.398f, 482.246f, 307.75f, +482.242f, 307.75f, 478.938f, 326.559f, 476.246f, 317.746f, 472.777f, 309.836f, 468.82f, 303.238f, +465.246f, 300.75f, 462.66f, 297.957f, 456.938f, 323.035f, 455.25f, 311.75f, 455.25f, 311.75f, +442.418f, 325.238f, 437.25f, 306.746f, 424.246f, 288.746f, 424.242f, 288.742f, 423.938f, 302.797f, +422.246f, 295.746f, 422.246f, 295.742f, 389.621f, 289.598f, 378.246f, 294.75f, 10, 0, +0, 0, 0, 0, 0, 0, 34, 340.25f, 686.746f, 340.25f, +686.742f, 327.578f, 695.719f, 323.246f, 695.746f, 318.777f, 694.84f, 353.539f, 704.957f, 399.246f, +674.75f, 399.246f, 674.75f, 404.141f, 671.52f, 408.246f, 671.746f, 408.246f, 671.742f, 411.621f, +669.316f, 408.246f, 665.75f, 408.246f, 665.75f, 398.859f, 654.797f, 411.246f, 642.746f, 411.246f, +642.742f, 431.418f, 635, 425.25f, 644.75f, 425.25f, 644.75f, 437.141f, 640.277f, 440.25f, +635.75f, 442.418f, 631.477f, 441.246f, 635.75f, 441.246f, 635.75f, 441.246f, 635.75f, 434.059f, +643.797f, 427.25f, 649.746f, 427.25f, 649.742f, 421.738f, 651.719f, 418.25f, 660.746f, 415.578f, +670.199f, 412.938f, 681.199f, 418.25f, 684.746f, 418.25f, 684.742f, 413.379f, 679.879f, 414.25f, +684.746f, 415.141f, 688.68f, 419.098f, 692.637f, 421.246f, 692.75f, 422.621f, 693.52f, 440.66f, +710.898f, 448.25f, 711.746f, 448.25f, 711.746f, 438.02f, 709.797f, 434.246f, 710.746f, 431.418f, +712, 402.16f, 724.539f, 395.25f, 725.75f, 395.25f, 725.75f, 377.078f, 733.117f, 390.246f, +730.746f, 390.242f, 730.742f, 429.66f, 726.738f, 449.25f, 711.746f, 449.25f, 711.746f, 441.758f, +721.457f, 421.246f, 728.746f, 421.246f, 728.742f, 397.098f, 743.02f, 358.25f, 737.746f, 358.25f, +737.742f, 338.801f, 734, 330.25f, 731.75f, 330.25f, 731.75f, 327.359f, 732.68f, 326.25f, +732.746f, 326.039f, 733.559f, 313.059f, 743.457f, 282.25f, 735.746f, 282.25f, 735.746f, 264, +730.699f, 254.246f, 725.75f, 254.246f, 725.75f, 237.816f, 724.098f, 234.246f, 720.75f, 234.246f, +720.75f, 213.398f, 704.52f, 211.246f, 703.75f, 209, 702.758f, 196.457f, 694.398f, 195.25f, +693.746f, 195.25f, 693.742f, 222.637f, 701.219f, 225.25f, 703.75f, 227.918f, 706.5f, 247.059f, +709.359f, 249.25f, 707.75f, 252.34f, 706.277f, 261.578f, 706.938f, 251.25f, 706.746f, 251.25f, +706.742f, 334.18f, 690, 335.246f, 687.75f, 335.938f, 685.598f, 340.25f, 686.746f, 340.25f, +686.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 13, +419.246f, 696.75f, 419.246f, 696.75f, 407.66f, 705.18f, 405.25f, 704.746f, 403.258f, 705.18f, +389.621f, 716.398f, 385.25f, 715.746f, 380.379f, 715.52f, 366.961f, 726.52f, 337.25f, 717.746f, +337.25f, 717.742f, 336.16f, 719.699f, 340.25f, 720.75f, 340.25f, 720.75f, 347.16f, 723, +347.25f, 723.75f, 347.25f, 723.75f, 369.82f, 728.277f, 377.246f, 724.746f, 377.246f, 724.746f, +387.859f, 721.457f, 394.25f, 714.75f, 394.25f, 714.75f, 407, 711.117f, 410.246f, 711.746f, +410.246f, 711.746f, 420.199f, 709.797f, 420.246f, 707.75f, 420.246f, 707.75f, 427.02f, 704.52f, +425.25f, 701.75f, 425.25f, 701.75f, 425.48f, 699.898f, 419.246f, 696.75f, 10, 0, +0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 38, 405.25f, 699.746f, 406.047f, +698.664f, 407.168f, 698.555f, 408.246f, 697.746f, 408.094f, 697.32f, 407.773f, 696.961f, 407.25f, +696.75f, 406.281f, 696.504f, 405.121f, 697.133f, 404.25f, 696.75f, 403.422f, 696.258f, 402.715f, +696.457f, 402.246f, 696.75f, 400.313f, 697.105f, 398.301f, 697.137f, 396.25f, 696.75f, 394.254f, +697.621f, 391.66f, 696.977f, 389.246f, 697.746f, 389.309f, 698.109f, 389.063f, 697.727f, 389.246f, +697.746f, 385.629f, 699.016f, 381.512f, 698.707f, 379.246f, 700.746f, 376.168f, 701.672f, 373.574f, +702.18f, 371.25f, 702.746f, 368.906f, 703.488f, 367.355f, 704.574f, 365.246f, 705.75f, 364.059f, +706.27f, 362.457f, 706.844f, 361.25f, 707.75f, 358.719f, 707.75f, 356.703f, 707.625f, 354.246f, +707.75f, 354.52f, 708.227f, 354.309f, 707.848f, 354.246f, 707.75f, 353.863f, 707.996f, 353.543f, +708.637f, 353.246f, 708.746f, 351.508f, 708.004f, 349.871f, 709.074f, 348.25f, 708.746f, 346.742f, +710.043f, 344.844f, 709.773f, 343.246f, 710.746f, 339.883f, 711.195f, 336.414f, 709.797f, 333.246f, +710.746f, 337.602f, 712.926f, 342.758f, 711.57f, 347.25f, 713.746f, 349.789f, 715.148f, 352.715f, +713.938f, 355.246f, 714.75f, 356.078f, 714.934f, 356.84f, 715.152f, 357.246f, 714.75f, 357.426f, +714.566f, 357.625f, 714.828f, 357.246f, 714.75f, 360.387f, 713.527f, 362.934f, 712.125f, 365.246f, +710.746f, 366.039f, 710.793f, 366.621f, 711.047f, 367.246f, 710.746f, 368.57f, 709.488f, 370.711f, +709.602f, 372.25f, 708.746f, 374.105f, 708.809f, 376.078f, 708.391f, 378.246f, 708.746f, 378.066f, +709.109f, 378.324f, 708.734f, 378.246f, 708.746f, 379.602f, 709.582f, 380.875f, 709.281f, 382.25f, +708.746f, 382.227f, 708.82f, 382.957f, 708.551f, 383.25f, 708.746f, 384.531f, 708.164f, 385.473f, +707.637f, 387.246f, 707.75f, 386.895f, 707.414f, 387.098f, 707.789f, 387.246f, 707.75f, 388.41f, +707.277f, 389.559f, 707.34f, 390.246f, 705.75f, 390.426f, 706.207f, 390.609f, 706.469f, 390.246f, +706.746f, 391.828f, 706.066f, 392.543f, 705.238f, 394.25f, 704.746f, 394.289f, 704.855f, 394.961f, +704.168f, 395.25f, 703.75f, 398.227f, 703.168f, 400.254f, 701.488f, 402.246f, 700.746f, 403.5f, +700.16f, 404.465f, 699.902f, 405.25f, 699.746f, 10, 0, 0.8f, 0.45f, 0.15f, +0.8f, 0.45f, 0.15f, 23, 321.246f, 714.75f, 318.094f, 716.91f, 315.488f, 718.125f, +313.246f, 719.746f, 312.605f, 720.234f, 312.207f, 720.051f, 312.246f, 719.746f, 310.879f, 720.852f, +309.902f, 721.492f, 309.246f, 722.746f, 308.227f, 722.68f, 307.324f, 722.664f, 307.25f, 722.746f, +303.969f, 724.371f, 301.074f, 724.984f, 298.246f, 726.746f, 299.066f, 727, 300.301f, 726.73f, +301.246f, 727.75f, 301.172f, 727.309f, 301.434f, 726.996f, 302.246f, 726.746f, 303.668f, 728.203f, +305.703f, 728.371f, 307.25f, 728.746f, 309.422f, 728.172f, 311.313f, 727.836f, 313.246f, 727.75f, +313.605f, 727.484f, 313.824f, 726.91f, 314.25f, 726.746f, 316.629f, 726.074f, 319.258f, 726.648f, +321.246f, 725.75f, 323.336f, 725.035f, 325.066f, 724.133f, 326.25f, 722.746f, 326.703f, 722.441f, +326.348f, 722.113f, 326.25f, 721.746f, 326.465f, 722.02f, 326.766f, 721.793f, 327.25f, 721.746f, +326.98f, 721.184f, 326.98f, 720.852f, 327.25f, 720.75f, 326.766f, 720.246f, 326.457f, 720.133f, +326.25f, 719.746f, 324.5f, 719.871f, 326.449f, 721.387f, 325.25f, 720.75f, 324.277f, 720, +325.098f, 718.453f, 324.246f, 716.75f, 323.973f, 717.27f, 323.719f, 717.512f, 324.246f, 717.746f, +324.098f, 717.363f, 323.434f, 717.043f, 323.246f, 716.75f, 322.824f, 715.898f, 321.836f, 714.344f, +321.246f, 714.75f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, +25, 285.25f, 706.746f, 281.645f, 707.801f, 278.293f, 707.676f, 275.246f, 708.746f, 274.898f, +709.109f, 274.656f, 708.727f, 274.25f, 708.746f, 273.082f, 709.41f, 272.07f, 710.473f, 271.25f, +711.746f, 269.883f, 712.602f, 268.059f, 712.176f, 266.246f, 712.75f, 266.301f, 712.848f, 266.078f, +713.41f, 266.246f, 713.746f, 264.406f, 713.625f, 263.387f, 714.672f, 262.25f, 715.746f, 264.809f, +716.172f, 267.461f, 716.137f, 270.25f, 716.75f, 270.293f, 716.582f, 270.453f, 716.227f, 270.25f, +715.746f, 270.746f, 716.227f, 270.891f, 716.469f, 271.25f, 716.75f, 271.254f, 716.309f, 271.586f, +715.953f, 272.25f, 715.746f, 272.469f, 716.824f, 273.082f, 716.617f, 273.25f, 716.75f, 273.836f, +716.563f, 273.973f, 716.227f, 274.25f, 715.746f, 274.27f, 716.227f, 274.41f, 716.57f, 274.25f, +716.75f, 274.707f, 716.57f, 274.852f, 716.227f, 275.246f, 715.746f, 275.148f, 716.227f, 275.289f, +716.469f, 275.246f, 716.75f, 276.199f, 715.758f, 277.172f, 716.367f, 278.246f, 715.746f, 279.219f, +715.922f, 279.512f, 714.656f, 280.246f, 714.75f, 285.879f, 712.895f, 290.43f, 710.535f, 295.25f, +707.75f, 295.566f, 708.078f, 295.801f, 707.805f, 295.25f, 707.75f, 295.973f, 707.379f, 296.316f, +707.477f, 296.25f, 707.75f, 297.688f, 706.523f, 298.832f, 705.922f, 299.246f, 704.746f, 299.84f, +704.34f, 299.477f, 703.895f, 299.246f, 703.75f, 294.348f, 705.047f, 289.941f, 705.715f, 285.25f, +706.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 10, +270.25f, 658.746f, 268.117f, 659.637f, 267.477f, 661.871f, 266.246f, 663.75f, 266, 664.215f, +266.301f, 664.563f, 266.246f, 664.746f, 267.266f, 664.832f, 267.863f, 664.309f, 268.246f, 663.75f, +270.234f, 663.137f, 271.922f, 661.77f, 274.25f, 661.75f, 276.309f, 659.16f, 280.992f, 658.738f, +281.25f, 654.75f, 281, 654.074f, 279.43f, 655.082f, 279.246f, 653.746f, 276.262f, 655.242f, +273.633f, 655.129f, 271.25f, 656.746f, 270.336f, 657.16f, 270.699f, 657.66f, 270.25f, 658.746f, +10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 6, 239.25f, +715.746f, 239.727f, 716.129f, 247.461f, 715.871f, 247.246f, 715.746f, 247.391f, 715.406f, 238.891f, +714.254f, 238.25f, 714.75f, 238.309f, 714.523f, 230.047f, 711.852f, 230.25f, 711.746f, 230.191f, +712.148f, 239.285f, 716.129f, 239.25f, 715.746f, 10, 0, 0, 0, 0, +0, 0, 0, 80, 256.246f, 705.75f, 256.246f, 705.75f, 240.238f, 703.418f, +235.246f, 701.75f, 230.559f, 700.777f, 210.098f, 692.418f, 207.25f, 689.746f, 207.25f, 689.746f, +192.059f, 684.277f, 173.25f, 662.746f, 173.25f, 662.742f, 181.719f, 666.02f, 184.25f, 668.75f, +184.25f, 668.75f, 199.098f, 682.957f, 199.246f, 679.75f, 199.246f, 679.75f, 212.297f, 689.559f, +211.246f, 686.746f, 211.246f, 686.742f, 238.477f, 699.457f, 236.25f, 695.746f, 236.25f, 695.742f, +260.039f, 701, 259.25f, 698.75f, 259.25f, 698.75f, 279.617f, 693.957f, 276.246f, 693.746f, +276.246f, 693.742f, 270.156f, 692.418f, 277.246f, 688.746f, 277.246f, 688.742f, 273.457f, 683.617f, +267.246f, 687.75f, 261.578f, 692.418f, 264.879f, 690, 259.25f, 688.746f, 259.25f, 688.742f, +256.52f, 688.02f, 251.25f, 692.75f, 251.25f, 692.75f, 245.297f, 697.477f, 235.246f, 693.746f, +235.242f, 693.742f, 201.957f, 679.656f, 200.246f, 678.746f, 200.246f, 678.746f, 195.797f, 675.918f, +193.25f, 671.746f, 193.25f, 671.742f, 186.777f, 667.117f, 183.25f, 665.75f, 183.25f, 665.75f, +169.398f, 652.816f, 168.25f, 651.746f, 168.25f, 651.742f, 164.34f, 645.559f, 163.25f, 644.75f, +163.25f, 644.75f, 170.5f, 649.297f, 172.25f, 651.746f, 172.25f, 651.742f, 188.098f, 662.5f, +194.25f, 663.75f, 194.25f, 663.75f, 198.879f, 666.68f, 200.246f, 667.746f, 200.246f, 667.746f, +215.598f, 678.34f, 220.246f, 678.746f, 220.246f, 678.746f, 230.34f, 672.617f, 233.246f, 680.746f, +233.246f, 680.746f, 239.359f, 682.297f, 245.246f, 680.746f, 245.246f, 680.746f, 249.039f, 683.84f, +248.25f, 686.746f, 248.25f, 686.742f, 249.918f, 688.238f, 251.25f, 683.75f, 251.25f, 683.75f, +254.758f, 680.098f, 260.25f, 682.746f, 260.25f, 682.742f, 264.437f, 682.52f, 262.25f, 679.75f, +262.25f, 679.75f, 257.398f, 675.699f, 244.246f, 675.746f, 244.246f, 675.742f, 230.777f, 674.816f, +212.246f, 666.75f, 212.246f, 666.75f, 179.957f, 655.02f, 170.246f, 643.75f, 170.242f, 643.75f, +162.797f, 633.898f, 157.25f, 632.75f, 157.25f, 632.75f, 150.477f, 631.699f, 144.246f, 623.746f, +144.246f, 623.746f, 154.656f, 629.938f, 164.246f, 629.746f, 164.242f, 629.742f, 168.957f, 632.578f, +165.246f, 628.75f, 165.242f, 628.75f, 160.816f, 620.258f, 162.25f, 614.746f, 162.25f, 614.746f, +161.918f, 608.598f, 161.246f, 606.75f, 161.246f, 606.75f, 152.457f, 592.758f, 152.25f, 589.75f, +152.457f, 587.477f, 153.777f, 576.699f, 154.246f, 575.75f, 154.656f, 575.379f, 153.117f, 577.797f, +157.25f, 574.746f, 161.477f, 572.52f, 164.559f, 570.758f, 165.246f, 567.75f, 166.316f, 564.598f, +163.238f, 573.617f, 163.25f, 575.75f, 162.797f, 577.578f, 158.18f, 585.5f, 159.246f, 587.746f, +159.242f, 587.742f, 160.156f, 587.039f, 161.246f, 585.746f, 161.246f, 585.742f, 160.379f, 586.156f, +161.246f, 589.75f, 161.246f, 589.75f, 161.918f, 595.84f, 163.25f, 599.75f, 165, 602.879f, +167.199f, 607.059f, 167.25f, 607.746f, 168.078f, 608.816f, 168.078f, 615.199f, 169.25f, 612.746f, +173.25f, 609.746f, 173.25f, 609.742f, 170.277f, 612.34f, 172.25f, 614.746f, 172.25f, 614.746f, +171.598f, 620.918f, 173.25f, 623.746f, 173.25f, 623.746f, 181.277f, 633.02f, 183.25f, 633.75f, +184.797f, 635.219f, 183.25f, 634.746f, 183.25f, 634.746f, 183.25f, 634.746f, 189.859f, 639.398f, +183.25f, 637.75f, 183.25f, 637.75f, 179.078f, 635.879f, 176.246f, 635.75f, 176.246f, 635.75f, +167.418f, 633.68f, 172.25f, 638.746f, 176.219f, 642.918f, 187.219f, 648.859f, 191.246f, 648.75f, +192.25f, 646.75f, 204.25f, 649.746f, 203.25f, 648.75f, 203.25f, 648.75f, 203.059f, 648.859f, +207.25f, 649.746f, 212.297f, 649.738f, 218.68f, 648.199f, 220.246f, 649.746f, 221.758f, 652.156f, +225.5f, 653.258f, 225.25f, 651.746f, 224.617f, 650.18f, 224.246f, 647.746f, 224.246f, 647.746f, +224.242f, 647.746f, 229.898f, 654.359f, 229.25f, 651.746f, 228.578f, 649.52f, 219.559f, 643.797f, +218.25f, 636.746f, 229.25f, 645.746f, 233.246f, 649.746f, 233.246f, 649.742f, 237.379f, 646.879f, +237.25f, 648.75f, 237.816f, 650.398f, 242.879f, 656.777f, 244.246f, 656.746f, 245.52f, 656.34f, +247.719f, 659.418f, 247.246f, 656.746f, 247.277f, 653.699f, 255.246f, 647.746f, 255.246f, 647.746f, +255.246f, 647.746f, 259.156f, 649.738f, 260.25f, 647.746f, 262.238f, 646.656f, 267.246f, 669.746f, +267.246f, 669.746f, 294.25f, 681.75f, 342.246f, 685.75f, 323.246f, 692.75f, 256.246f, 705.75f, +10, 2.2f, 0.3f, 0, 0, 0.3f, 0, 0, 3, 276.246f, +486.746f, 276.246f, 486.742f, 260.039f, 504.977f, 251.25f, 507.75f, 251.25f, 507.75f, 236.059f, +515.316f, 209.246f, 506.746f, 10, 2.2f, 0.3f, 0, 0, 0.3f, 0, +0, 3, 247.246f, 509.75f, 247.242f, 509.75f, 219.559f, 518.18f, 202.246f, 513.746f, +202.242f, 513.746f, 182.379f, 511.359f, 173.25f, 495.746f, 10, 2.2f, 0.3f, 0, +0, 0.3f, 0, 0, 4, 243.246f, 510.746f, 243.246f, 510.742f, 224.617f, +518.617f, 208.25f, 520.75f, 208.25f, 520.75f, 190.078f, 523.898f, 172.25f, 515.746f, 172.25f, +515.742f, 158.398f, 509.379f, 152.25f, 497.746f, 10, 2.2f, 0.3f, 0, 0, +0.3f, 0, 0, 4, 244.246f, 510.746f, 244.246f, 510.742f, 227.477f, 522.359f, +226.25f, 523.75f, 226.25f, 523.75f, 218.68f, 536, 204.25f, 536.75f, 204.25f, 536.75f, +180.84f, 535.559f, 162.25f, 526.746f, 10, 0, 0, 0, 0, 0, +0, 0, 169, 243.246f, 519.746f, 244.68f, 518.543f, 274.25f, 486.746f, 274.25f, +486.746f, 313.059f, 446.457f, 282.25f, 483.75f, 282.25f, 483.75f, 273.898f, 489.359f, 264.246f, +509.75f, 264.246f, 509.75f, 262.457f, 513.117f, 279.246f, 501.75f, 279.246f, 501.75f, 283.578f, +501.238f, 298.246f, 479.75f, 298.246f, 479.75f, 291.059f, 482.758f, 296.25f, 474.75f, 296.25f, +474.75f, 299.418f, 472.637f, 322.246f, 455.746f, 322.246f, 455.746f, 325.82f, 451.078f, 330.25f, +449.746f, 330.25f, 449.746f, 345.621f, 455.035f, 338.25f, 440.746f, 338.25f, 440.746f, 341.219f, +433.035f, 347.25f, 445.746f, 347.25f, 445.746f, 359.699f, 464.277f, 341.25f, 461.75f, 341.25f, +461.75f, 308.656f, 458.559f, 301.246f, 475.746f, 301.246f, 475.746f, 298.539f, 478.797f, 308.246f, +475.746f, 308.246f, 475.746f, 317.461f, 473.957f, 300.246f, 489.746f, 300.246f, 489.746f, 302.937f, +489.797f, 313.246f, 482.746f, 313.246f, 482.746f, 324.5f, 472.199f, 326.25f, 474.75f, 326.25f, +474.75f, 346.5f, 484.078f, 358.25f, 475.746f, 358.25f, 475.746f, 360.141f, 473.957f, 353.98f, +466.477f, 355.246f, 460.746f, 357.5f, 455.035f, 363.25f, 441.75f, 363.25f, 441.75f, 360.141f, +439.637f, 360.25f, 427.746f, 360.25f, 427.746f, 379.059f, 402.238f, 368.246f, 404.75f, 368.246f, +404.75f, 351.34f, 404.879f, 367.246f, 396.746f, 367.246f, 396.746f, 371.141f, 394.316f, 381.25f, +386.75f, 381.25f, 386.75f, 377.738f, 387.719f, 376.246f, 381.746f, 376.246f, 381.746f, 381.258f, +377.598f, 378.246f, 372.746f, 378.246f, 372.746f, 371.578f, 370.996f, 370.25f, 366.75f, 370.25f, +366.75f, 377.738f, 357.797f, 366.246f, 357.746f, 366.246f, 357.746f, 370.699f, 352.516f, 365.246f, +339.746f, 365.246f, 339.746f, 360.141f, 339.316f, 353.246f, 332.746f, 353.246f, 332.746f, 355.738f, +327.879f, 344.246f, 321.746f, 344.246f, 321.746f, 335.059f, 319.957f, 338.25f, 312.75f, 338.25f, +312.75f, 329.34f, 305.879f, 326.25f, 288.746f, 326.25f, 288.746f, 325.82f, 276.836f, 323.18f, +273.316f, 329.25f, 275.746f, 334.621f, 277.719f, 333.246f, 291.746f, 333.246f, 291.746f, 328.461f, +308.516f, 375.246f, 325.75f, 375.246f, 325.75f, 379.938f, 327.879f, 381.25f, 333.75f, 381.25f, +333.75f, 383.02f, 333.156f, 392.25f, 324.746f, 392.25f, 324.746f, 401.059f, 312.477f, 401.246f, +322.75f, 401.246f, 322.75f, 402.82f, 326.559f, 401.246f, 332.746f, 401.246f, 332.746f, 407.66f, +356.918f, 392.25f, 363.746f, 392.25f, 363.746f, 381.258f, 400.918f, 396.25f, 391.75f, 396.25f, +391.75f, 399.738f, 385.516f, 411.246f, 379.746f, 411.246f, 379.746f, 415.25f, 382.75f, 413.82f, +387.719f, 423.246f, 394.746f, 423.246f, 394.746f, 426.141f, 387.277f, 432.246f, 395.75f, 432.246f, +395.75f, 436.699f, 422.918f, 450.25f, 406.75f, 450.25f, 406.75f, 454.738f, 405.758f, 456.246f, +412.746f, 456.246f, 412.746f, 460.02f, 424.676f, 456.246f, 439.75f, 456.246f, 439.75f, 460.02f, +440.078f, 470.25f, 433.746f, 470.25f, 433.746f, 473.66f, 437.438f, 463.539f, 455.918f, 468.25f, +453.746f, 472.34f, 450.637f, 477.25f, 448.75f, 477.25f, 448.75f, 478.059f, 451.078f, 467.246f, +464.746f, 467.246f, 464.746f, 462.219f, 467.797f, 456.246f, 489.746f, 456.246f, 489.746f, 464.418f, +486.277f, 453.25f, 502.746f, 453.25f, 502.746f, 453.418f, 506.52f, 460.246f, 518.75f, 460.246f, +518.75f, 459.141f, 526.316f, 460.246f, 525.75f, 460.246f, 525.75f, 463.098f, 524.559f, 471.898f, +522.797f, 464.25f, 529.75f, 456.938f, 536.879f, 465.246f, 541.746f, 465.246f, 541.746f, 470.141f, +545.238f, 455.25f, 544.746f, 455.25f, 544.746f, 449.461f, 549.637f, 450.25f, 553.75f, 450.25f, +553.75f, 458.699f, 551.84f, 442.859f, 567.68f, 440.25f, 571.75f, 437.578f, 575.598f, 448.25f, +581.746f, 448.25f, 581.746f, 462.66f, 585.277f, 450.25f, 588.75f, 450.25f, 588.75f, 428.34f, +588.359f, 440.25f, 599.75f, 440.25f, 599.75f, 446.82f, 599.797f, 445.246f, 602.75f, 445.246f, +602.75f, 439.34f, 603.758f, 429.25f, 610.75f, 429.25f, 610.75f, 424.379f, 614.758f, 428.25f, +613.75f, 428.25f, 613.75f, 446.82f, 612.559f, 415.25f, 624.75f, 415.25f, 624.75f, 423.938f, +624.879f, 404.25f, 636.746f, 404.25f, 636.746f, 401.938f, 638.078f, 398.246f, 646.75f, 398.246f, +646.75f, 391.82f, 652.156f, 386.246f, 659.75f, 386.246f, 659.75f, 386.098f, 664.477f, 381.25f, +669.746f, 381.25f, 669.746f, 368.059f, 684.719f, 362.25f, 684.746f, 362.25f, 684.746f, 345.621f, +688.238f, 340.25f, 687.75f, 340.25f, 687.75f, 282.25f, 682.746f, 252.777f, 668.438f, 261.25f, +645.746f, 261.25f, 645.746f, 268.398f, 636.098f, 278.246f, 640.746f, 278.246f, 640.746f, 283.578f, +647.098f, 296.25f, 644.75f, 296.25f, 644.75f, 318.777f, 641.156f, 316.25f, 644.75f, 316.25f, +644.75f, 313.277f, 650.18f, 295.457f, 657, 295.25f, 657.75f, 295.02f, 658.316f, 285.25f, +661.75f, 285.25f, 661.75f, 282.039f, 663.379f, 277.246f, 673.746f, 277.246f, 673.746f, 273.68f, +677.238f, 291.246f, 670.75f, 291.246f, 670.75f, 289.738f, 669.758f, 298.246f, 665.75f, 298.246f, +665.75f, 317.02f, 666.457f, 328.25f, 654.75f, 328.25f, 654.75f, 340.559f, 636.316f, 341.25f, +645.746f, 341.25f, 645.746f, 343.859f, 655.68f, 331.246f, 678.746f, 331.246f, 678.746f, 331.32f, +681.199f, 340.25f, 673.746f, 340.25f, 673.746f, 341.879f, 676.137f, 343.246f, 669.746f, 343.246f, +669.746f, 342.98f, 667.117f, 347.25f, 658.746f, 347.25f, 658.746f, 350.238f, 644.02f, 354.246f, +651.746f, 354.246f, 651.746f, 359.25f, 641.75f, 360.801f, 638.957f, 354.246f, 630.75f, 354.246f, +630.75f, 353.98f, 627.52f, 354.859f, 627.738f, 348.25f, 619.75f, 342.539f, 611.02f, 346.246f, +605.746f, 346.246f, 605.746f, 344.738f, 598.918f, 354.246f, 599.75f, 354.246f, 599.75f, 357.277f, +596.938f, 361.25f, 596.746f, 361.25f, 596.746f, 363, 594.738f, 365.246f, 595.75f, 365.246f, +595.75f, 367.398f, 599.578f, 374.25f, 597.75f, 374.25f, 597.75f, 375.758f, 600.02f, 385.25f, +600.75f, 385.25f, 600.75f, 385.879f, 603.316f, 386.32f, 605.078f, 390.246f, 605.746f, 393.801f, +606.398f, 366.246f, 653.746f, 366.246f, 653.746f, 373.777f, 654.578f, 365.246f, 667.746f, 365.246f, +667.746f, 362.34f, 675.477f, 374.879f, 659.418f, 377.246f, 657.75f, 379.719f, 656.34f, 380.82f, +653.918f, 379.246f, 653.746f, 377.301f, 654.359f, 375.32f, 651.938f, 377.246f, 651.746f, 378.398f, +651.5f, 392.699f, 635, 396.25f, 623.746f, 400.18f, 612.559f, 406.777f, 608.156f, 413.246f, +601.746f, 420.418f, 594.957f, 419.246f, 568.746f, 419.246f, 568.746f, 419.098f, 558.656f, 425.25f, +546.746f, 425.25f, 546.746f, 427.898f, 542.816f, 423.246f, 522.746f, 423.246f, 522.746f, 421.078f, +520.379f, 422.246f, 519.746f, 422.246f, 519.746f, 423.719f, 518.18f, 431.246f, 503.75f, 431.246f, +503.75f, 429.219f, 503.879f, 433.246f, 499.746f, 433.246f, 499.746f, 438.898f, 493.316f, 432.246f, +496.75f, 432.246f, 496.75f, 425.258f, 498.379f, 433.246f, 487.75f, 433.246f, 487.75f, 434.277f, +485.617f, 424.246f, 490.75f, 424.246f, 490.75f, 414.258f, 491.34f, 427.25f, 483.75f, 427.25f, +483.75f, 436.48f, 475.5f, 424.246f, 480.746f, 424.246f, 480.746f, 418.879f, 482.316f, 422.246f, +474.75f, 422.246f, 474.75f, 425.918f, 473.078f, 445.246f, 465.75f, 445.246f, 465.75f, 445.277f, +461.195f, 442.246f, 455.746f, 442.246f, 455.746f, 442.418f, 451.297f, 440.25f, 447.746f, 440.25f, +447.746f, 438.68f, 438.535f, 438.25f, 437.75f, 438.25f, 437.75f, 433.398f, 437.438f, 425.25f, +422.746f, 425.25f, 422.746f, 423.277f, 419.398f, 412.246f, 405.746f, 412.246f, 405.746f, 409.859f, +398.496f, 390.246f, 406.75f, 390.246f, 406.75f, 382.801f, 402.676f, 385.25f, 406.75f, 385.25f, +406.75f, 384.559f, 408.836f, 390.246f, 415.75f, 390.246f, 415.75f, 397.539f, 418.297f, 394.25f, +429.746f, 394.25f, 429.746f, 399.078f, 431.719f, 386.758f, 434.797f, 387.246f, 435.75f, 387.199f, +437.438f, 393.25f, 438.746f, 393.25f, 438.746f, 402.379f, 441.176f, 397.246f, 443.75f, 397.246f, +443.75f, 396.879f, 448.219f, 400.246f, 454.75f, 400.246f, 454.75f, 412.938f, 455.258f, 400.246f, +472.75f, 400.246f, 472.75f, 388.301f, 481.438f, 387.246f, 487.75f, 387.246f, 487.75f, 401.059f, +496.84f, 392.039f, 510.477f, 392.25f, 514.75f, 392.48f, 518.398f, 394.25f, 541.746f, 394.25f, +541.746f, 391.598f, 548.977f, 388.246f, 563.746f, 388.246f, 563.746f, 390.719f, 569.656f, 399.246f, +583.746f, 399.246f, 583.746f, 401.938f, 588.137f, 411.621f, 593.418f, 409.246f, 596.746f, 406.777f, +600.02f, 398.246f, 597.75f, 398.246f, 597.75f, 389.621f, 599.578f, 390.246f, 593.75f, 390.246f, +593.75f, 388.52f, 592.758f, 387.246f, 587.746f, 387.246f, 587.746f, 386.848f, 578.531f, 377.246f, +571.75f, 377.246f, 571.75f, 364.758f, 564.816f, 375.246f, 560.75f, 375.246f, 560.75f, 381.48f, +553.156f, 370.25f, 552.746f, 370.25f, 552.746f, 358.598f, 554.918f, 367.246f, 543.746f, 367.246f, +543.746f, 379.5f, 529.617f, 376.246f, 526.746f, 376.246f, 526.746f, 364.98f, 525.438f, 379.246f, +515.746f, 379.246f, 515.746f, 379.242f, 515.742f, 377.961f, 517.52f, 378.246f, 515.746f, 378.398f, +513.559f, 381.699f, 508.938f, 382.25f, 506.746f, 383.461f, 504.539f, 379.246f, 504.746f, 379.246f, +504.746f, 379.719f, 493.758f, 363.25f, 498.75f, 363.25f, 498.75f, 363.25f, 498.75f, 362.777f, +498.379f, 361.25f, 497.746f, 359.258f, 497.938f, 346.938f, 498.816f, 340.25f, 500.746f, 334.18f, +503.656f, 326.25f, 503.75f, 326.25f, 503.75f, 326.25f, 503.75f, 322.301f, 501.68f, 314.25f, +501.75f, 305.578f, 502.117f, 297.246f, 498.75f, 297.246f, 498.75f, 291.937f, 499.477f, 301.398f, +504.316f, 301.246f, 503.75f, 301.84f, 503.879f, 308, 510.039f, 299.246f, 509.75f, 275.223f, +507.578f, 263.25f, 518.75f, 263.25f, 518.75f, 261.137f, 520.379f, 258.246f, 523.75f, 258.246f, +523.75f, 247.277f, 525.656f, 260.25f, 509.75f, 260.25f, 509.75f, 261.137f, 508.277f, 259.25f, +506.746f, 259.25f, 506.746f, 258.719f, 508.938f, 250.25f, 514.75f, 250.25f, 514.75f, 247.047f, +515.949f, 245.547f, 517.414f, 243.246f, 519.746f, 10, 0, 0.3f, 0, 0, +0.3f, 0, 0, 7, 216.25f, 532.746f, 216.25f, 532.742f, 229.457f, 526.758f, +232.246f, 523.75f, 235.18f, 520.598f, 250.25f, 507.75f, 250.25f, 507.75f, 250.25f, 507.75f, +244.637f, 510.258f, 242.246f, 511.746f, 238.918f, 514.219f, 227.25f, 522.746f, 227.25f, 522.746f, +227.25f, 522.746f, 222.859f, 529.84f, 216.25f, 532.746f, 10, 0, 0.6f, 0.8f, +0.2f, 0.6f, 0.8f, 0.2f, 6, 153.246f, 566.75f, 153.258f, 567.398f, 152.684f, +570.379f, 152.25f, 570.746f, 151.332f, 573.977f, 141.25f, 575.75f, 141.25f, 575.75f, 141.207f, +574.098f, 141.148f, 572.34f, 141.25f, 570.746f, 141.25f, 570.746f, 146.621f, 564.469f, 153.246f, +566.75f, 10, 0, 0.4f, 0.6f, 0, 0.4f, 0.6f, 0, 6, +153.246f, 567.75f, 152.395f, 567.281f, 152.871f, 570.461f, 152.25f, 570.746f, 151.555f, 573.977f, +141.25f, 575.75f, 141.25f, 575.75f, 141.207f, 574.207f, 141.148f, 572.449f, 141.25f, 570.746f, +141.25f, 570.746f, 145.961f, 565.02f, 153.246f, 567.75f, 10, 0, 0, 0, +0, 0, 0, 0, 6, 148.246f, 567.75f, 147.371f, 567.297f, 146.812f, +568.551f, 147.25f, 569.75f, 146.812f, 571.645f, 147.371f, 572.898f, 148.246f, 572.746f, 148.746f, +572.898f, 149.305f, 571.645f, 149.246f, 569.75f, 149.305f, 568.551f, 148.746f, 567.297f, 148.246f, +567.75f, 10, 0, 0, 0, 0, 0, 0, 0, 17, +98.2461f, 459.75f, 98.2422f, 459.75f, 91.7383f, 448.438f, 119.25f, 454.75f, 119.25f, 454.75f, +134.418f, 456.355f, 137.246f, 458.746f, 138.379f, 458.117f, 147.582f, 454.891f, 150.246f, 453.746f, +158.18f, 452.398f, 167.25f, 463.75f, 167.25f, 463.75f, 167.25f, 463.75f, 172.477f, 474.949f, +175.25f, 474.75f, 178.637f, 474.949f, 175.25f, 472.75f, 175.25f, 472.75f, 175.25f, 472.75f, +167.859f, 462.078f, 168.25f, 460.746f, 168.25f, 460.742f, 162.578f, 438.316f, 145.25f, 437.75f, +145.25f, 437.75f, 127.215f, 436.391f, 128.25f, 429.746f, 128.25f, 429.742f, 138.379f, 432.598f, +141.25f, 429.746f, 141.25f, 429.742f, 152.898f, 430.398f, 144.246f, 423.746f, 136.25f, 410.75f, +136.25f, 410.75f, 136.773f, 406.289f, 125.25f, 409.746f, 114.84f, 413.898f, 103.25f, 427.746f, +103.25f, 427.746f, 103.25f, 427.742f, 85.9648f, 444.094f, 98.2461f, 459.75f, 10, 0, +0.9f, 0.6f, 0.6f, 0.9f, 0.6f, 0.6f, 14, 96.25f, 454.75f, 96.25f, +454.75f, 94.3789f, 444.477f, 135.25f, 455.746f, 135.25f, 455.742f, 139.699f, 455.918f, 142.246f, +454.75f, 144.977f, 454.156f, 158.18f, 451.078f, 160.246f, 452.75f, 160.246f, 452.75f, 152.457f, +437.438f, 139.246f, 438.746f, 139.246f, 438.742f, 125.18f, 437.438f, 125.25f, 431.746f, 125.25f, +431.742f, 130.02f, 424.238f, 135.25f, 421.75f, 135.25f, 421.75f, 138.379f, 418.957f, 138.246f, +415.75f, 137.5f, 411.918f, 134.418f, 410.156f, 132.246f, 409.746f, 130.02f, 408.398f, 126.5f, +411.918f, 124.25f, 411.746f, 122.977f, 411.918f, 113.738f, 418.957f, 109.246f, 423.746f, 104.059f, +429.516f, 94.8164f, 442.719f, 95.25f, 445.746f, 95.6992f, 448.879f, 96.25f, 454.75f, 96.25f, +454.75f, 10, 0, 0.7f, 0.4f, 0.4f, 0.7f, 0.4f, 0.4f, 13, +100.246f, 435.75f, 102.957f, 431.496f, 106.477f, 426.879f, 109.246f, 423.746f, 113.738f, 418.957f, +122.977f, 411.918f, 124.25f, 411.746f, 126.5f, 411.918f, 130.02f, 408.398f, 132.246f, 409.746f, +134.418f, 410.156f, 137.5f, 411.918f, 138.246f, 415.75f, 138.379f, 418.957f, 135.25f, 421.75f, +135.25f, 421.75f, 131.926f, 423.285f, 128.91f, 427.125f, 127.246f, 429.746f, 127.246f, 429.742f, +127.379f, 426.879f, 121.246f, 427.746f, 115.938f, 428.637f, 110.219f, 431.719f, 108.25f, 434.746f, +106.699f, 438.758f, 104.059f, 441.398f, 106.25f, 437.75f, 107.578f, 433.477f, 110.219f, 429.516f, +112.25f, 428.75f, 113.738f, 428.637f, 113.297f, 427.316f, 110.246f, 427.746f, 108.02f, 428.195f, +104.938f, 428.637f, 100.246f, 434.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, +0.15f, 0, 20, 97.25f, 458.746f, 97.25f, 458.746f, 99.2187f, 473.957f, 100.246f, +478.746f, 100.246f, 478.746f, 99.6563f, 485.84f, 102.25f, 490.75f, 104.938f, 495.078f, 107.137f, +501.898f, 110.246f, 507.75f, 113.738f, 513.777f, 113.957f, 518.18f, 118.25f, 519.746f, 122.758f, +521.699f, 129.359f, 531.156f, 132.246f, 532.746f, 135.52f, 533.359f, 135.25f, 532.746f, 135.25f, +532.746f, 135.25f, 532.742f, 142.777f, 548.758f, 157.25f, 544.746f, 157.25f, 544.746f, 139.918f, +547.438f, 157.25f, 557.746f, 157.25f, 557.746f, 152.02f, 556.566f, 155.246f, 564.75f, 158.07f, +569.402f, 157.52f, 561.957f, 145.25f, 548.746f, 145.25f, 548.746f, 139.918f, 539.52f, 134.25f, +535.746f, 128.477f, 532.918f, 115.277f, 525.219f, 114.25f, 520.75f, 112.637f, 516.859f, 109.117f, +510.477f, 107.25f, 508.746f, 104.719f, 506.957f, 101.637f, 502.34f, 101.25f, 498.75f, 101.25f, +498.75f, 99.8789f, 494.199f, 98.2461f, 492.75f, 96.7969f, 491.559f, 96.5781f, 488.039f, 96.25f, +485.75f, 96.5781f, 483.637f, 94.3789f, 480.559f, 94.2461f, 477.746f, 94.2461f, 477.742f, 95.4766f, +457.016f, 95.25f, 454.75f, 97.25f, 458.746f, 10, 0, 1, 1, 1, +1, 1, 1, 6, 88.2461f, 453.746f, 88.2461f, 453.742f, 85.5781f, 455.477f, +80.25f, 448.75f, 80.25f, 448.75f, 88.7695f, 412.578f, 89.2461f, 410.75f, 89.2461f, 410.75f, +89.9766f, 413.348f, 88.2461f, 421.75f, 87.1172f, 429.188f, 86.25f, 442.746f, 86.25f, 442.746f, +88.2461f, 453.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, +13, 111.246f, 520.75f, 111.246f, 520.75f, 92.1797f, 517.078f, 92.2461f, 484.746f, 91.25f, +457.75f, 91.25f, 457.75f, 90.418f, 485.84f, 89.2461f, 487.75f, 87.7773f, 489.359f, 92.1797f, +501.68f, 88.2461f, 494.75f, 88.2461f, 494.75f, 73.2578f, 479.68f, 82.2461f, 456.746f, 82.2422f, +456.746f, 83.707f, 452.727f, 80.25f, 457.75f, 80.25f, 457.75f, 75.3477f, 471.648f, 76.2461f, +478.746f, 76.2422f, 478.746f, 76.7773f, 481.109f, 79.25f, 483.75f, 79.25f, 483.75f, 88.3281f, +497.059f, 91.25f, 499.746f, 91.25f, 499.742f, 93.2773f, 515.43f, 110.246f, 520.75f, 110.246f, +520.75f, 116.488f, 523.68f, 111.246f, 520.75f, 10, 0, 0, 0, 0, +0, 0, 0, 28, 265.246f, 593.75f, 265.605f, 593.809f, 265.594f, 594.875f, +266.246f, 594.746f, 267.496f, 595.441f, 267.676f, 596.617f, 268.246f, 597.75f, 269.207f, 598.93f, +269.418f, 600.617f, 270.25f, 602.75f, 270.359f, 603.027f, 270.387f, 604.078f, 270.25f, 604.75f, +268.754f, 607.531f, 267.98f, 610.227f, 266.246f, 612.746f, 266.098f, 613.391f, 265.812f, 614.262f, +265.246f, 614.746f, 265.082f, 616.441f, 263.699f, 617.535f, 263.25f, 618.746f, 262.434f, 619.469f, +263.012f, 620.488f, 262.25f, 620.746f, 261.238f, 620.695f, 259.645f, 621.332f, 259.25f, 619.75f, +258.742f, 617.359f, 259.852f, 614.586f, 261.25f, 611.75f, 260.059f, 611.137f, 260.426f, 610.125f, +260.25f, 609.746f, 261.375f, 605.313f, 260.055f, 601.625f, 259.25f, 597.75f, 259.191f, 597.691f, +259.57f, 597.473f, 259.25f, 597.75f, 258.195f, 594.449f, 256.598f, 591.762f, 254.246f, 588.75f, +253.762f, 588.051f, 252.805f, 587.043f, 252.25f, 585.746f, 251.852f, 585.008f, 251.402f, 583.945f, +251.25f, 582.75f, 247.898f, 579.801f, 245.426f, 575.57f, 242.246f, 571.75f, 242.043f, 570.594f, +242.363f, 569.262f, 243.246f, 568.746f, 243.863f, 568.527f, 244.918f, 569.656f, 245.246f, 570.746f, +245.859f, 571.352f, 246.25f, 572.066f, 247.246f, 572.746f, 246.937f, 572.969f, 246.738f, 573.43f, +247.246f, 573.75f, 249.785f, 576.145f, 251.621f, 579.375f, 254.246f, 581.746f, 256.465f, 582.34f, +258.152f, 583.438f, 260.25f, 584.75f, 260.414f, 584.75f, 260.992f, 584.477f, 261.25f, 584.75f, +263.238f, 585.984f, 263.238f, 588.223f, 263.25f, 590.746f, 263.41f, 591.297f, 263.625f, 592.746f, +265.246f, 593.75f, 10, 0, 0, 0, 0, 0, 0, 0, +19, 255.246f, 598.746f, 255.289f, 598.414f, 255.117f, 598.879f, 255.246f, 598.746f, 255.418f, +599.477f, 255.859f, 599.68f, 256.246f, 599.75f, 256.16f, 600.277f, 255.98f, 600.695f, 256.246f, +600.75f, 258.695f, 603.543f, 258.977f, 606.867f, 258.246f, 609.746f, 258.965f, 610.82f, 259.031f, +612.207f, 258.246f, 612.746f, 257.625f, 615.012f, 257.414f, 617.129f, 256.246f, 618.746f, 255.457f, +620.223f, 253.723f, 621.59f, 252.25f, 619.75f, 251.75f, 619.719f, 251.398f, 618.852f, 251.25f, +617.75f, 251.773f, 617.887f, 252.09f, 617.727f, 252.25f, 617.75f, 251.941f, 617.281f, 251.34f, +617.035f, 251.25f, 616.746f, 251.301f, 615.09f, 250.25f, 613.43f, 251.25f, 611.75f, 251.793f, +610.176f, 252.695f, 608.133f, 253.246f, 605.746f, 252.082f, 603.852f, 253.219f, 601.156f, 251.25f, +598.746f, 251.141f, 598.93f, 251.148f, 598.508f, 251.25f, 598.746f, 251.605f, 597.75f, 252.051f, +597.305f, 252.25f, 596.746f, 252.809f, 596.848f, 253.191f, 596.848f, 253.246f, 596.746f, 254.047f, +597.383f, 254.484f, 597.918f, 255.246f, 598.746f, 10, 0, 0, 0, 0, +0, 0, 0, 21, 324.246f, 609.746f, 325.773f, 607.703f, 326.094f, 604.629f, +324.246f, 602.75f, 324.445f, 599.457f, 328.129f, 601.637f, 330.25f, 601.746f, 330.32f, 602.645f, +330.57f, 603.023f, 331.246f, 602.75f, 332.043f, 603.047f, 332.789f, 604.18f, 334.246f, 603.746f, +334.438f, 605.691f, 336.238f, 606.465f, 337.25f, 607.746f, 338.844f, 612.047f, 338.195f, 616.746f, +335.246f, 620.746f, 335.129f, 620.598f, 335.371f, 621.164f, 335.246f, 621.75f, 334.402f, 623.996f, +332.125f, 624.34f, 330.25f, 624.75f, 328.703f, 629.359f, 327.977f, 633.793f, 326.25f, 637.75f, +324.266f, 638.133f, 323.496f, 640.047f, 322.246f, 640.746f, 320.559f, 641.629f, 319.934f, 639.891f, +320.246f, 638.746f, 319.988f, 638.516f, 320.484f, 638.27f, 320.246f, 637.75f, 320.215f, 637.688f, +319.926f, 637.566f, 320.246f, 637.75f, 319.93f, 637.27f, 320.172f, 637.125f, 320.246f, 636.746f, +319.309f, 636.074f, 317.742f, 635.551f, 317.25f, 634.746f, 316.371f, 630.211f, 319.199f, 626.773f, +321.246f, 623.746f, 321.684f, 622.004f, 320.875f, 620.605f, 320.246f, 619.75f, 319.559f, 618.512f, +319.676f, 617.273f, 320.246f, 616.746f, 320.809f, 613.766f, 322.559f, 611.855f, 324.246f, 609.746f, +10, 0, 0, 0, 0, 0, 0, 0, 73, 283.25f, +589.75f, 281.734f, 587.41f, 277.98f, 584.586f, 281.25f, 582.75f, 281.402f, 582.324f, 281.809f, +582.32f, 282.25f, 582.75f, 284.223f, 584.188f, 286.426f, 585.18f, 289.246f, 585.746f, 289.242f, +585.852f, 289.543f, 585.34f, 290.246f, 585.746f, 291.727f, 586.289f, 293.938f, 586.227f, 295.25f, +587.746f, 299.383f, 587.453f, 303.305f, 588.68f, 307.25f, 589.75f, 308.309f, 590.609f, 309.707f, +591.227f, 311.246f, 591.75f, 312.543f, 592.41f, 313.867f, 593.434f, 315.25f, 594.746f, 315.234f, +594.836f, 315.625f, 594.738f, 316.25f, 594.746f, 315.875f, 595.688f, 316.934f, 595.828f, 317.25f, +596.746f, 317.305f, 596.766f, 317.141f, 597.203f, 317.25f, 597.75f, 319.641f, 599.105f, 320.652f, +601.328f, 319.25f, 603.746f, 319.051f, 604.578f, 318.777f, 605.258f, 318.25f, 605.746f, 316.961f, +606.781f, 315.75f, 605.844f, 314.25f, 605.746f, 314.422f, 605.488f, 313.621f, 605.676f, 313.246f, +605.746f, 312.25f, 604.977f, 310.785f, 605.621f, 310.246f, 604.75f, 308.344f, 604.371f, 306.977f, +604.188f, 305.25f, 603.746f, 305.07f, 603.684f, 304.215f, 603.789f, 304.25f, 602.75f, 303.891f, +603.246f, 303.727f, 603.504f, 303.25f, 603.746f, 301.512f, 603.043f, 300.125f, 602.809f, 298.246f, +600.75f, 298.582f, 600.801f, 298.098f, 600.996f, 298.246f, 600.75f, 296.867f, 599.961f, 296.422f, +598.602f, 295.25f, 597.75f, 294.992f, 597.727f, 294.602f, 597.914f, 294.25f, 597.75f, 293.68f, +597.297f, 293.277f, 596.59f, 292.25f, 595.75f, 292.207f, 595.848f, 291.766f, 596.207f, 292.25f, +596.746f, 292.07f, 598.629f, 292.789f, 600.594f, 292.25f, 602.75f, 294.441f, 605.43f, 297.211f, +607.574f, 299.246f, 610.75f, 299.215f, 612.961f, 299.977f, 615.32f, 300.246f, 617.75f, 299.84f, +617.816f, 299.523f, 618.625f, 299.246f, 618.746f, 299.043f, 619.945f, 300.039f, 621.117f, 299.246f, +621.75f, 297.566f, 623.238f, 296.145f, 622.273f, 295.25f, 620.746f, 293.215f, 620.27f, 290.945f, +619.508f, 289.246f, 620.746f, 288.102f, 621.73f, 287.465f, 622.727f, 286.246f, 623.746f, 285.504f, +625.32f, 285.871f, 626.898f, 286.246f, 628.75f, 285.953f, 628.762f, 285.609f, 628.91f, 285.25f, +628.75f, 285.609f, 629.207f, 285.852f, 629.352f, 286.246f, 629.746f, 285.223f, 630.188f, 284.918f, +631.352f, 284.25f, 631.746f, 284.133f, 632.898f, 283.391f, 633.871f, 282.25f, 633.75f, 280.238f, +634.965f, 278.395f, 632.859f, 276.246f, 632.75f, 275.746f, 632.758f, 275.23f, 633.902f, 274.25f, +634.746f, 274.043f, 634.496f, 273.27f, 634.531f, 273.25f, 633.75f, 272.113f, 633.688f, 271.465f, +633.559f, 270.25f, 633.75f, 268.852f, 632.855f, 267.449f, 631.652f, 266.246f, 630.75f, 264.188f, +629.77f, 263.137f, 628.188f, 262.25f, 626.75f, 260.914f, 625.469f, 260.762f, 622.813f, 262.25f, +622.75f, 264.352f, 621.547f, 265.785f, 624.523f, 268.246f, 623.746f, 268.293f, 624.105f, 268.52f, +623.766f, 268.246f, 623.746f, 268.824f, 623.219f, 269.066f, 623.469f, 269.246f, 623.746f, 270.223f, +622.656f, 271.504f, 622.285f, 272.25f, 621.75f, 273.602f, 620.332f, 275.523f, 620.793f, 276.246f, +619.75f, 278.32f, 618.043f, 277.707f, 615.094f, 280.246f, 613.75f, 279.195f, 612.215f, 278.527f, +610.809f, 278.246f, 608.75f, 277.848f, 607.914f, 278.941f, 606.598f, 280.246f, 606.75f, 281.656f, +606.801f, 281.945f, 607.633f, 282.25f, 608.75f, 282.773f, 608.523f, 283.289f, 608.199f, 283.25f, +607.746f, 282.738f, 605.336f, 281.609f, 603.141f, 281.25f, 600.75f, 281.043f, 600.121f, 280.707f, +599.898f, 280.246f, 599.75f, 279.762f, 595.453f, 275.305f, 592.82f, 272.25f, 589.75f, 272.063f, +588.785f, 272.059f, 587.414f, 272.25f, 586.75f, 274.051f, 585.445f, 276.207f, 587.145f, 278.246f, +587.746f, 278.313f, 589.023f, 279.258f, 590.063f, 280.246f, 589.75f, 281.004f, 589.988f, 281.262f, +590.586f, 281.25f, 590.746f, 282, 590.879f, 282.555f, 590.633f, 283.25f, 590.746f, 284.77f, +592.164f, 286.32f, 593.383f, 288.246f, 594.746f, 288.441f, 594.832f, 288.82f, 594.66f, 289.246f, +594.746f, 289.414f, 594.957f, 289.621f, 595.383f, 290.246f, 595.75f, 290.359f, 595.805f, 290.625f, +595.484f, 291.246f, 594.746f, 290.129f, 594.793f, 290.125f, 593.742f, 289.246f, 593.75f, 288.629f, +593.223f, 288.012f, 592.66f, 287.246f, 591.75f, 286.949f, 591.957f, 286.227f, 592.23f, 286.246f, +591.75f, 285.453f, 590.902f, 284.152f, 590.418f, 283.25f, 589.75f, 10, 0, 0, +0, 0, 0, 0, 0, 30, 222.246f, 643.75f, 222.246f, 643.75f, +212.258f, 646.957f, 200.246f, 618.746f, 200.246f, 618.742f, 197.34f, 613, 194.25f, 610.75f, +192.059f, 608.598f, 179.738f, 604.637f, 177.246f, 599.75f, 166.246f, 582.75f, 166.246f, 582.75f, +182.379f, 600.238f, 186.25f, 602.75f, 186.25f, 602.75f, 194.699f, 612.117f, 191.246f, 604.75f, +191.242f, 604.75f, 175.777f, 592.758f, 177.246f, 582.75f, 177.246f, 582.75f, 170.938f, 566.797f, +170.246f, 564.75f, 170.242f, 564.75f, 187.656f, 599.797f, 190.246f, 600.75f, 192.938f, 602.438f, +194.258f, 602.438f, 193.25f, 598.746f, 191.617f, 594.52f, 191.18f, 576.477f, 188.246f, 574.746f, +188.246f, 574.742f, 196.898f, 596.719f, 196.25f, 599.75f, 196.25f, 599.75f, 199.539f, 604.199f, +202.246f, 598.746f, 201.246f, 580.75f, 205.25f, 567.75f, 205.25f, 567.75f, 203.059f, 580, +205.25f, 596.746f, 205.25f, 596.742f, 202.617f, 608.598f, 207.25f, 602.75f, 211.418f, 596.277f, +221.977f, 589.68f, 222.246f, 584.75f, 222.246f, 584.75f, 216.258f, 603.758f, 206.25f, 608.75f, +201.246f, 602.75f, 200.246f, 604.75f, 200.246f, 604.75f, 196.457f, 605.52f, 201.246f, 612.746f, +206.137f, 620.477f, 205.25f, 621.75f, 205.25f, 621.75f, 205.25f, 621.75f, 212.738f, 613.438f, +214.25f, 613.75f, 214.25f, 613.75f, 229.02f, 621.797f, 230.25f, 594.746f, 230.25f, 594.742f, +237.816f, 610.797f, 227.25f, 618.746f, 227.25f, 618.742f, 211.418f, 620.477f, 212.246f, 625.746f, +220.246f, 639.75f, 224.617f, 645.559f, 223.246f, 642.746f, 223.246f, 642.746f, 10, 0, +0, 0, 0, 0, 0, 0, 6, 200.246f, 625.746f, 200.246f, +625.746f, 186.34f, 625.758f, 183.25f, 619.75f, 175.25f, 609.746f, 175.25f, 609.742f, 193.816f, +620.477f, 198.246f, 621.75f, 202.617f, 623.117f, 200.246f, 625.746f, 200.246f, 625.746f, 10, +0, 0, 0, 0, 0, 0, 0, 7, 156.25f, 618.746f, +156.25f, 618.742f, 154.219f, 617.398f, 154.246f, 614.746f, 153.34f, 611.238f, 150.699f, 610.797f, +151.25f, 607.746f, 152.457f, 604.637f, 154.656f, 602, 154.246f, 606.75f, 154.656f, 610.797f, +156.418f, 613, 157.25f, 614.746f, 158.18f, 615.637f, 159.938f, 620.477f, 156.25f, 618.746f, +10, 0, 0, 0, 0, 0, 0, 0, 10, 146.25f, +551.75f, 146.25f, 551.75f, 137.5f, 555.797f, 134.25f, 559.746f, 130.457f, 563.719f, 130.957f, +558.035f, 125.25f, 558.75f, 119.187f, 558.922f, 120.246f, 576.746f, 120.246f, 576.746f, 116.246f, +567.75f, 116.246f, 567.75f, 114.617f, 552.277f, 123.25f, 554.746f, 127.715f, 556.207f, 129.137f, +554.477f, 127.246f, 553.75f, 125.617f, 552.719f, 133.539f, 552.277f, 130.25f, 550.746f, 127.379f, +548.758f, 143.219f, 554.477f, 140.25f, 542.75f, 146.25f, 551.75f, 10, 0, 0, +0, 0, 0, 0, 0, 8, 133.246f, 535.746f, 133.246f, 535.746f, +115.938f, 530.719f, 112.25f, 541.746f, 112.25f, 541.742f, 106.699f, 538.637f, 109.246f, 535.746f, +111.539f, 532.039f, 113.25f, 531.75f, 113.25f, 531.75f, 113.25f, 531.75f, 118.797f, 530.277f, +118.25f, 529.75f, 117.477f, 528.52f, 115.246f, 524.746f, 115.246f, 524.746f, 115.242f, 524.746f, +126.059f, 531.379f, 133.246f, 535.746f, 10, 0, 1, 1, 1, 1, +1, 1, 24, 384.25f, 449.746f, 383.648f, 447.191f, 381.813f, 446.309f, 379.246f, +445.746f, 377.609f, 446.629f, 374.754f, 450.047f, 372.25f, 447.746f, 372.156f, 448.305f, 371.301f, +448.371f, 371.25f, 448.75f, 370.41f, 450.086f, 370.711f, 451.238f, 370.25f, 451.746f, 369.734f, +453.516f, 368.953f, 455.016f, 369.25f, 456.746f, 371.145f, 457.359f, 371.801f, 459.457f, 371.25f, +461.75f, 371.203f, 461.68f, 370.73f, 461.895f, 371.25f, 462.746f, 371.156f, 462.633f, 371.504f, +462.883f, 372.25f, 462.746f, 371.652f, 463.031f, 371.492f, 462.773f, 371.25f, 462.746f, 370.699f, +462.91f, 370.836f, 463.613f, 371.25f, 463.75f, 371.621f, 465.957f, 373.836f, 466.25f, 375.246f, +464.746f, 375.602f, 465.559f, 376.16f, 465.348f, 376.246f, 465.75f, 376.586f, 466.016f, 377.031f, +466.594f, 377.246f, 466.746f, 377.82f, 468.266f, 379.613f, 467.047f, 380.25f, 467.746f, 381.672f, +468.629f, 382.844f, 469.398f, 384.25f, 468.75f, 386.02f, 467.621f, 387.898f, 466.285f, 389.246f, +464.746f, 389.848f, 463.453f, 390.113f, 462.047f, 390.246f, 460.746f, 390.008f, 460.281f, 388.488f, +460.668f, 388.246f, 459.75f, 387.402f, 457.723f, 389.414f, 457.152f, 390.246f, 455.746f, 390.465f, +455.297f, 390.176f, 454.961f, 390.246f, 454.75f, 389.375f, 454.711f, 388.516f, 454.922f, 388.246f, +454.75f, 389.734f, 450.91f, 386.703f, 450.164f, 384.25f, 449.746f, 10, 0, 1, +1, 1, 1, 1, 1, 11, 373.25f, 427.746f, 373.551f, 429.891f, +371.789f, 431.82f, 373.25f, 433.746f, 373.27f, 433.551f, 373.41f, 433.305f, 373.25f, 433.746f, +373.707f, 433.305f, 373.852f, 433.551f, 374.25f, 433.746f, 375.645f, 431.258f, 379.66f, 430.238f, +379.246f, 426.75f, 379.48f, 426.617f, 378.285f, 425.605f, 379.246f, 424.75f, 377.285f, 423.414f, +377.223f, 420.809f, 376.246f, 418.746f, 374.836f, 419.051f, 373.504f, 419.449f, 372.25f, 419.75f, +372.625f, 421.691f, 372.496f, 423.543f, 373.25f, 424.75f, 373.879f, 425.766f, 373.563f, 426.949f, +373.25f, 427.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +15, 190.246f, 437.75f, 190.246f, 437.75f, 172.195f, 426.727f, 187.246f, 443.75f, 197.34f, +454.156f, 208.25f, 460.746f, 208.25f, 460.746f, 208.25f, 460.742f, 219.777f, 465.156f, 223.246f, +466.746f, 227.699f, 467.797f, 244.418f, 473.52f, 248.25f, 473.746f, 251.457f, 474.398f, 262.02f, +478.797f, 269.246f, 474.75f, 276.977f, 470, 286.246f, 464.746f, 286.246f, 464.746f, 286.246f, +464.742f, 267.738f, 474.398f, 264.246f, 471.746f, 259.816f, 469.117f, 251.898f, 469.559f, 245.246f, +465.75f, 245.246f, 465.75f, 229.02f, 461.195f, 225.25f, 458.746f, 221.977f, 456.797f, 210.539f, +444.035f, 209.246f, 444.746f, 207.02f, 445.797f, 209.219f, 446.238f, 210.246f, 449.746f, 211.859f, +452.398f, 209.656f, 454.156f, 201.246f, 446.75f, 192.059f, 440.078f, 190.246f, 437.75f, 190.246f, +437.75f, 10, 0, 0, 0, 0, 0, 0, 0, 11, +199.246f, 444.746f, 199.246f, 444.742f, 200.434f, 458.785f, 210.246f, 456.746f, 210.246f, 456.746f, +218.809f, 461.539f, 222.246f, 463.75f, 222.246f, 463.75f, 230.758f, 465.578f, 232.246f, 466.746f, +252.523f, 475.824f, 268.715f, 470.855f, 269.246f, 471.746f, 269.918f, 473.316f, 291.504f, 465.488f, +295.25f, 460.746f, 295.906f, 460.508f, 284.219f, 467.152f, 273.25f, 468.75f, 264.453f, 471.008f, +240.691f, 468.961f, 228.25f, 462.746f, 225.422f, 461.211f, 215.582f, 454.848f, 213.246f, 454.75f, +210.016f, 455.094f, 199.246f, 444.746f, 199.246f, 444.746f, 10, 0, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 11, 194.25f, 416.746f, 194.25f, 416.742f, 177.977f, +418.957f, 196.25f, 420.746f, 196.25f, 420.742f, 216.258f, 422.918f, 220.246f, 428.75f, 220.246f, +428.75f, 235.617f, 438.758f, 238.25f, 438.746f, 241.777f, 439.637f, 274.777f, 447.559f, 275.246f, +449.746f, 275.656f, 452.836f, 281.816f, 452.836f, 283.25f, 451.746f, 285.34f, 451.078f, 284.457f, +449.758f, 281.25f, 448.75f, 278.297f, 447.996f, 243.977f, 429.957f, 237.25f, 428.75f, 229.898f, +427.316f, 217.137f, 418.957f, 212.246f, 417.75f, 206.578f, 416.316f, 194.25f, 416.746f, 194.25f, +416.746f, 10, 0, 0, 0, 0, 0, 0, 0, 11, +216.25f, 424.75f, 216.25f, 424.75f, 206.73f, 425.367f, 216.25f, 426.75f, 216.25f, 426.75f, +225.887f, 430.035f, 228.25f, 432.75f, 228.25f, 432.75f, 235.801f, 438.145f, 237.25f, 438.746f, +238.957f, 438.594f, 254.313f, 442.652f, 254.246f, 443.75f, 254.762f, 445.355f, 292.234f, 459.191f, +297.246f, 455.746f, 300.301f, 453.371f, 289.406f, 455.219f, 279.246f, 450.75f, 277.32f, 449.684f, +240.082f, 433.637f, 236.25f, 432.75f, 232.871f, 432.285f, 226.34f, 428.008f, 223.246f, 427.746f, +220.934f, 426.656f, 216.25f, 424.75f, 216.25f, 424.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 207.25f, 421.75f, 207.25f, 421.75f, 213.18f, +422.477f, 212.246f, 420.746f, 210.539f, 418.957f, 208.25f, 419.75f, 208.25f, 419.75f, 207.25f, +421.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +199.246f, 420.746f, 199.246f, 420.742f, 205.258f, 420.719f, 204.25f, 418.746f, 202.617f, 417.195f, +200.246f, 417.75f, 200.246f, 417.75f, 199.246f, 420.746f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 188.246f, 418.746f, 188.246f, 418.742f, 193.816f, +418.957f, 192.25f, 416.746f, 191.18f, 415.438f, 188.246f, 416.746f, 188.246f, 416.746f, 188.246f, +418.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +179.246f, 417.75f, 179.246f, 417.75f, 185.457f, 418.078f, 184.25f, 416.746f, 182.816f, 414.559f, +180.246f, 415.75f, 180.246f, 415.75f, 179.246f, 417.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 222.246f, 460.746f, 222.246f, 460.742f, 226.816f, +461.195f, 225.25f, 459.75f, 224.18f, 457.676f, 220.246f, 457.75f, 220.246f, 457.75f, 222.246f, +460.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +211.246f, 454.75f, 211.246f, 454.75f, 218.133f, 457.391f, 215.25f, 453.746f, 214.059f, 451.957f, +211.246f, 452.75f, 211.246f, 452.75f, 211.246f, 454.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 198.246f, 449.746f, 198.246f, 449.742f, 204.379f, +450.195f, 203.25f, 448.75f, 201.738f, 446.676f, 199.246f, 447.746f, 199.246f, 447.746f, 198.246f, +449.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +190.246f, 442.746f, 190.246f, 442.742f, 196.02f, 443.598f, 194.25f, 441.75f, 193.379f, 440.078f, +190.246f, 440.746f, 190.246f, 440.746f, 190.246f, 442.746f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 183.25f, 437.75f, 183.25f, 437.75f, 188.539f, +438.316f, 187.246f, 436.746f, 185.898f, 434.797f, 183.25f, 435.75f, 183.25f, 435.75f, 183.25f, +437.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +226.25f, 430.75f, 226.25f, 430.75f, 233.422f, 431.426f, 231.246f, 428.75f, 229.906f, 426.742f, +226.25f, 427.746f, 226.25f, 427.746f, 226.25f, 430.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 237.25f, 435.75f, 237.25f, 435.75f, 244.863f, +436.707f, 243.246f, 434.746f, 241.348f, 432.02f, 238.25f, 432.75f, 238.25f, 432.75f, 237.25f, +435.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +249.25f, 440.746f, 249.25f, 440.742f, 256.742f, 441.547f, 255.246f, 438.746f, 253.227f, 436.859f, +249.25f, 437.75f, 249.25f, 437.75f, 249.25f, 440.746f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 260.25f, 445.746f, 260.25f, 445.746f, 268.18f, +446.824f, 266.246f, 444.746f, 264.668f, 442.141f, 261.25f, 443.75f, 261.25f, 443.75f, 260.25f, +445.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +230.25f, 465.75f, 230.25f, 465.75f, 237.82f, 466.625f, 236.25f, 464.746f, 234.309f, 461.941f, +230.25f, 461.75f, 230.25f, 461.75f, 230.25f, 465.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 241.25f, 469.746f, 241.25f, 469.746f, 248.82f, +470.145f, 247.246f, 467.746f, 245.309f, 465.461f, 240.25f, 465.75f, 240.25f, 465.75f, 241.25f, +469.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +216.25f, 425.746f, 216.25f, 425.746f, 221.977f, 425.996f, 220.246f, 423.746f, 219.34f, 422.477f, +216.25f, 423.746f, 216.25f, 423.746f, 216.25f, 425.746f, 10, 0, 0.6f, 0.15f, +0, 0.6f, 0.15f, 0, 5, 135.25f, 534.75f, 135.25f, 534.75f, 130.898f, +525, 130.25f, 521.746f, 130.25f, 521.742f, 131.34f, 531.156f, 132.246f, 533.746f, 133.977f, +535.559f, 135.25f, 534.75f, 135.25f, 534.75f, 10, 0, 0.6f, 0.15f, 0, +0.6f, 0.15f, 0, 5, 115.246f, 519.746f, 115.242f, 519.742f, 111.977f, 503.438f, +112.25f, 500.746f, 112.25f, 500.746f, 111.098f, 513.117f, 111.246f, 514.75f, 111.977f, 515.758f, +115.246f, 519.746f, 115.246f, 519.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 6, 138.246f, 601.746f, 138.246f, 597.75f, 135.25f, 597.75f, 135.25f, +597.75f, 151.359f, 583.738f, 152.25f, 575.75f, 152.25f, 575.75f, 152.898f, 584.398f, 138.246f, +601.746f, 10, 0, 0, 0, 0, 0, 0, 0, 28, +143.246f, 599.75f, 142.285f, 600.402f, 142.527f, 601.223f, 142.246f, 601.746f, 141.188f, 602.078f, +143.508f, 602.141f, 143.246f, 602.75f, 142.836f, 604.254f, 143.039f, 604.277f, 143.246f, 605.746f, +142.844f, 606.34f, 143.488f, 608.031f, 144.246f, 608.75f, 145.504f, 610.332f, 144.047f, 613.559f, +146.25f, 615.75f, 146.188f, 615.582f, 146.598f, 616.191f, 147.25f, 616.746f, 147.637f, 617.711f, +148.941f, 618.246f, 150.246f, 618.746f, 150.336f, 619.461f, 150.113f, 620.371f, 150.246f, 620.746f, +151.523f, 620.141f, 152.891f, 620.285f, 153.246f, 619.75f, 152.715f, 617.027f, 151.254f, 615.137f, +150.246f, 613.75f, 150.344f, 612.527f, 149.84f, 611.828f, 149.246f, 610.75f, 148.059f, 608.336f, +148.266f, 605.207f, 148.246f, 601.746f, 148.07f, 601.992f, 147.73f, 601.906f, 147.25f, 601.746f, +148.129f, 599.277f, 148.77f, 596.859f, 149.246f, 594.746f, 150.141f, 593.387f, 150.66f, 592.402f, +151.25f, 591.75f, 150.945f, 590.625f, 151.059f, 589.711f, 150.246f, 588.75f, 152.848f, 585.754f, +151.41f, 582.84f, 152.25f, 578.75f, 152.922f, 578.27f, 154.785f, 576.164f, 154.246f, 576.746f, +151.512f, 577.297f, 151.387f, 577.734f, 151.25f, 578.75f, 151.031f, 579.25f, 150.668f, 580.762f, +150.246f, 581.746f, 150.336f, 581.605f, 150.148f, 583.68f, 150.246f, 583.746f, 148.398f, 586.434f, +149.895f, 586.238f, 148.246f, 588.75f, 146.816f, 589.582f, 145.754f, 590.797f, 144.246f, 591.75f, +144.301f, 592.297f, 145.559f, 593.094f, 145.25f, 593.75f, 144.156f, 594.746f, 142.887f, 595.59f, +143.246f, 596.746f, 143.43f, 597.992f, 143.578f, 599.156f, 143.246f, 599.75f, 10, 0, +0, 0, 0, 0, 0, 0, 11, 139.246f, 597.75f, 139.246f, +597.75f, 139.258f, 590.559f, 142.246f, 588.75f, 144.539f, 587.039f, 143.219f, 587.918f, 139.246f, +588.75f, 136.18f, 590.559f, 137.246f, 591.75f, 137.246f, 591.75f, 137.242f, 591.75f, 134.418f, +591, 137.246f, 588.75f, 139.699f, 586.598f, 143.656f, 583.957f, 142.246f, 583.746f, 140.137f, +583.957f, 131.777f, 588.359f, 132.246f, 591.75f, 131.777f, 594.52f, 130.25f, 598.746f, 130.25f, +598.746f, 130.25f, 598.742f, 131.887f, 599.906f, 137.246f, 599.75f, 137.242f, 599.75f, 138.707f, +599.027f, 139.246f, 597.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 138.246f, 577.75f, 138.246f, 577.75f, 128.566f, 580.648f, 108.25f, 576.746f, +108.25f, 576.742f, 118.172f, 579.203f, 139.246f, 576.746f, 150.148f, 575.324f, 138.246f, 577.75f, +138.246f, 577.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 138.246f, 577.75f, 138.246f, 577.75f, 128.566f, 580.648f, 108.25f, 576.746f, 108.25f, +576.742f, 118.172f, 579.203f, 139.246f, 576.746f, 150.148f, 575.324f, 138.246f, 577.75f, 138.246f, +577.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +140.25f, 577.75f, 140.25f, 577.75f, 131.176f, 581.527f, 110.246f, 579.746f, 110.246f, 579.746f, +120.695f, 580.984f, 141.25f, 576.746f, 152.215f, 574.355f, 140.25f, 577.75f, 140.25f, 577.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 140.25f, +577.75f, 140.25f, 577.75f, 131.176f, 581.527f, 110.246f, 579.746f, 110.246f, 579.746f, 120.695f, +580.984f, 141.25f, 576.746f, 152.215f, 574.355f, 140.25f, 577.75f, 140.25f, 577.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 142.246f, 577.75f, +142.242f, 577.75f, 133.453f, 582.086f, 113.25f, 581.746f, 113.25f, 581.746f, 122.965f, 582.328f, +143.246f, 576.746f, 153.902f, 573.371f, 142.246f, 577.75f, 142.246f, 577.75f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 142.246f, 577.75f, 142.242f, +577.75f, 133.453f, 582.086f, 113.25f, 581.746f, 113.25f, 581.746f, 122.965f, 582.328f, 143.246f, +576.746f, 153.902f, 573.371f, 142.246f, 577.75f, 142.246f, 577.75f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 143.246f, 577.75f, 143.242f, 577.75f, +136.102f, 582.047f, 117.25f, 583.746f, 117.25f, 583.742f, 126.715f, 583.066f, 144.246f, 576.746f, +153.77f, 572.66f, 143.246f, 577.75f, 143.246f, 577.75f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 143.246f, 577.75f, 143.242f, 577.75f, 136.102f, +582.047f, 117.25f, 583.746f, 117.25f, 583.742f, 126.715f, 583.066f, 144.246f, 576.746f, 153.77f, +572.66f, 143.246f, 577.75f, 143.246f, 577.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 270.25f, 565.746f, 270.25f, 565.742f, 269.398f, 565.031f, +269.246f, 566.75f, 269.871f, 567.629f, 300.898f, 582.117f, 305.25f, 581.746f, 305.25f, 581.746f, +271.602f, 567.316f, 270.25f, 565.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 270.25f, 565.746f, 270.25f, 565.742f, 269.398f, 565.031f, 269.246f, +566.75f, 269.871f, 567.629f, 300.898f, 582.117f, 305.25f, 581.746f, 305.25f, 581.746f, 271.602f, +567.316f, 270.25f, 565.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 266.246f, 564.75f, 266.246f, 564.75f, 265.727f, 564.25f, 266.246f, 565.746f, +265.992f, 566.879f, 295.785f, 583.758f, 300.246f, 583.746f, 300.246f, 583.742f, 267.742f, 566.699f, +266.246f, 564.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 266.246f, 564.75f, 266.246f, 564.75f, 265.727f, 564.25f, 266.246f, 565.746f, 265.992f, +566.879f, 295.785f, 583.758f, 300.246f, 583.746f, 300.246f, 583.742f, 267.742f, 566.699f, 266.246f, +564.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +263.25f, 563.746f, 263.25f, 563.742f, 262.164f, 562.676f, 262.25f, 563.746f, 262.254f, 565.316f, +284.055f, 582.363f, 295.25f, 584.75f, 295.25f, 584.75f, 275.016f, 575.484f, 263.25f, 563.746f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 263.25f, +563.746f, 263.25f, 563.742f, 262.164f, 562.676f, 262.25f, 563.746f, 262.254f, 565.316f, 284.055f, +582.363f, 295.25f, 584.75f, 295.25f, 584.75f, 275.016f, 575.484f, 263.25f, 563.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 260.25f, 561.746f, +260.25f, 561.742f, 259.09f, 560.711f, 259.25f, 561.746f, 259.176f, 563.086f, 278.793f, 578.43f, +288.246f, 580.75f, 288.246f, 580.75f, 270.656f, 572.238f, 260.25f, 561.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 260.25f, 561.746f, 260.25f, +561.742f, 259.09f, 560.711f, 259.25f, 561.746f, 259.176f, 563.086f, 278.793f, 578.43f, 288.246f, +580.75f, 288.246f, 580.75f, 270.656f, 572.238f, 260.25f, 561.746f, 10, 0, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 11, 225.25f, 398.746f, 225.25f, 398.742f, +208.34f, 401.355f, 227.25f, 402.75f, 227.25f, 402.75f, 246.617f, 405.316f, 251.25f, 410.75f, +251.25f, 410.75f, 265.977f, 421.156f, 269.246f, 421.75f, 272.137f, 422.035f, 290.18f, 425.996f, +290.246f, 428.75f, 291.059f, 431.277f, 297.656f, 433.918f, 299.246f, 432.75f, 301.18f, 432.156f, +301.18f, 422.035f, 298.246f, 420.746f, 295.02f, 420.277f, 274.34f, 412.355f, 267.246f, 410.75f, +260.258f, 409.719f, 247.5f, 401.355f, 242.246f, 399.75f, 236.938f, 398.719f, 225.25f, 398.746f, +225.25f, 398.746f, 10, 0, 0, 0, 0, 0, 0, 0, +11, 305.25f, 439.75f, 305.25f, 439.75f, 302.059f, 438.098f, 300.246f, 434.746f, 300.246f, +434.746f, 293.699f, 423.578f, 278.246f, 419.75f, 278.246f, 419.75f, 252.777f, 410.156f, 244.246f, +407.746f, 244.246f, 407.742f, 229.457f, 402.457f, 221.246f, 403.746f, 221.246f, 403.746f, 213.617f, +403.117f, 220.246f, 401.746f, 220.246f, 401.746f, 242.656f, 403.559f, 246.246f, 405.746f, 246.242f, +405.742f, 263.559f, 411.258f, 267.246f, 413.75f, 270.156f, 416.977f, 290.18f, 422.477f, 292.25f, +424.75f, 295.02f, 426.879f, 305.797f, 436.117f, 305.25f, 439.75f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 241.25f, 404.75f, 241.25f, 404.75f, +246.52f, 405.445f, 245.246f, 403.746f, 243.984f, 402.035f, 241.25f, 402.75f, 241.25f, 402.75f, +241.25f, 404.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 233.246f, 403.746f, 233.246f, 403.746f, 238.598f, 403.957f, 237.25f, 402.75f, 236.063f, +400.547f, 233.246f, 401.746f, 233.246f, 401.746f, 233.246f, 403.746f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 221.246f, 402.75f, 221.246f, 402.75f, +227.125f, 402.586f, 226.25f, 400.75f, 224.59f, 399.176f, 222.246f, 399.75f, 222.246f, 399.75f, +221.246f, 402.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 213.246f, 401.746f, 213.242f, 401.746f, 218.73f, 401.984f, 217.25f, 400.75f, 216.191f, +398.578f, 213.246f, 399.75f, 213.246f, 399.75f, 213.246f, 401.746f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 259.25f, 413.75f, 259.25f, 413.75f, +266.609f, 413.664f, 265.246f, 411.746f, 263.234f, 409.129f, 259.25f, 410.75f, 259.25f, 410.75f, +259.25f, 413.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 270.25f, 417.75f, 270.25f, 417.75f, 276.855f, 421.832f, 276.246f, 416.746f, 275.973f, +413.453f, 271.25f, 415.75f, 271.25f, 415.75f, 270.25f, 417.75f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 280.246f, 421.75f, 280.242f, 421.75f, +288.223f, 425.367f, 286.246f, 419.75f, 285.457f, 416.664f, 281.25f, 418.746f, 281.25f, 418.746f, +280.246f, 421.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 291.246f, 426.75f, 291.242f, 426.75f, 295.605f, 431.996f, 297.246f, 424.75f, 297.227f, +421.875f, 291.246f, 423.746f, 291.246f, 423.746f, 291.246f, 426.75f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 249.25f, 408.75f, 249.25f, 408.75f, +255.266f, 408.652f, 254.246f, 406.75f, 252.73f, 405.242f, 250.25f, 405.746f, 250.25f, 405.746f, +249.25f, 408.75f, 10, 0, 1, 1, 1, 1, 1, 1, +5, 288.246f, 541.746f, 288.246f, 541.742f, 287.875f, 541.203f, 288.246f, 542.75f, 287.875f, +543.559f, 307.109f, 558.148f, 317.25f, 559.746f, 317.25f, 559.746f, 299.125f, 552.27f, 288.246f, +541.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, +288.246f, 541.746f, 288.246f, 541.742f, 287.875f, 541.203f, 288.246f, 542.75f, 287.875f, 543.559f, +307.109f, 558.148f, 317.25f, 559.746f, 317.25f, 559.746f, 299.125f, 552.27f, 288.246f, 541.746f, +10, 0, 0, 0, 0, 0, 0, 0, 10, 292.25f, +471.746f, 292.25f, 471.742f, 316.141f, 447.117f, 326.25f, 442.746f, 326.25f, 442.742f, 336.379f, +430.836f, 332.246f, 401.746f, 332.246f, 401.746f, 328.461f, 393.879f, 325.25f, 416.746f, 325.25f, +416.742f, 328.461f, 444.477f, 316.25f, 426.75f, 316.25f, 426.75f, 306.898f, 437.766f, 314.25f, +437.75f, 314.25f, 437.75f, 317.461f, 435.238f, 318.25f, 436.746f, 318.34f, 438.758f, 309.539f, +453.719f, 290.246f, 469.746f, 271.699f, 485.398f, 292.25f, 471.746f, 292.25f, 471.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 227.25f, 428.75f, +227.25f, 428.75f, 227.477f, 431.059f, 229.25f, 429.746f, 231.438f, 429.297f, 335.059f, 422.477f, +370.25f, 395.75f, 370.25f, 395.75f, 320.098f, 421.598f, 227.25f, 428.75f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 227.25f, 428.75f, 227.25f, +428.75f, 227.477f, 431.059f, 229.25f, 429.746f, 231.438f, 429.297f, 335.059f, 422.477f, 370.25f, +395.75f, 370.25f, 395.75f, 320.098f, 421.598f, 227.25f, 428.75f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 239.25f, 433.746f, 239.25f, 433.742f, +238.918f, 435.898f, 241.25f, 434.746f, 242.879f, 434.137f, 393.141f, 435.238f, 419.246f, 399.75f, +419.246f, 399.75f, 394.898f, 427.316f, 239.25f, 433.746f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 239.25f, 433.746f, 239.25f, 433.742f, 238.918f, +435.898f, 241.25f, 434.746f, 242.879f, 434.137f, 393.141f, 435.238f, 419.246f, 399.75f, 419.246f, +399.75f, 394.898f, 427.316f, 239.25f, 433.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 252.25f, 438.746f, 252.25f, 438.742f, 251.68f, 440.297f, +253.246f, 439.75f, 255.637f, 438.535f, 446.379f, 452.836f, 472.25f, 416.746f, 472.25f, 416.742f, +461.777f, 445.355f, 252.25f, 438.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 252.25f, 438.746f, 252.25f, 438.742f, 251.68f, 440.297f, 253.246f, +439.75f, 255.637f, 438.535f, 446.379f, 452.836f, 472.25f, 416.746f, 472.25f, 416.742f, 461.777f, +445.355f, 252.25f, 438.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 263.25f, 443.75f, 263.25f, 443.75f, 262.68f, 445.578f, 264.246f, 444.746f, +266.637f, 443.816f, 401.059f, 486.277f, 427.25f, 450.75f, 427.25f, 450.75f, 412.277f, 477.699f, +263.25f, 443.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 263.25f, 443.75f, 263.25f, 443.75f, 262.68f, 445.578f, 264.246f, 444.746f, 266.637f, +443.816f, 401.059f, 486.277f, 427.25f, 450.75f, 427.25f, 450.75f, 412.277f, 477.699f, 263.25f, +443.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +199.246f, 418.746f, 199.246f, 418.742f, 198.879f, 420.496f, 201.246f, 419.75f, 202.84f, 418.738f, +222.418f, 416.316f, 224.246f, 373.75f, 224.242f, 373.75f, 216.699f, 419.836f, 199.246f, 418.746f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 199.246f, +418.746f, 199.246f, 418.742f, 198.879f, 420.496f, 201.246f, 419.75f, 202.84f, 418.738f, 222.418f, +416.316f, 224.246f, 373.75f, 224.242f, 373.75f, 216.699f, 419.836f, 199.246f, 418.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 189.246f, 416.746f, +189.246f, 416.742f, 189.199f, 418.738f, 191.246f, 417.75f, 193.156f, 416.977f, 208.777f, 422.035f, +205.25f, 379.746f, 205.25f, 379.746f, 207.02f, 418.078f, 189.246f, 416.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 189.246f, 416.746f, 189.246f, +416.742f, 189.199f, 418.738f, 191.246f, 417.75f, 193.156f, 416.977f, 208.777f, 422.035f, 205.25f, +379.746f, 205.25f, 379.746f, 207.02f, 418.078f, 189.246f, 416.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 180.246f, 416.746f, 180.242f, 416.742f, +180.398f, 418.297f, 182.25f, 417.75f, 184.359f, 416.535f, 201.297f, 415.879f, 187.246f, 390.746f, +187.246f, 390.746f, 198.219f, 417.637f, 180.246f, 416.746f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 180.246f, 416.746f, 180.242f, 416.742f, 180.398f, +418.297f, 182.25f, 417.75f, 184.359f, 416.535f, 201.297f, 415.879f, 187.246f, 390.746f, 187.246f, +390.746f, 198.219f, 417.637f, 180.246f, 416.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 232.246f, 464.746f, 232.246f, 464.742f, 232.187f, 462.887f, +234.246f, 463.75f, 251.566f, 478.113f, 287.254f, 542.906f, 348.25f, 548.746f, 348.25f, 548.746f, +306.367f, 562.426f, 232.246f, 464.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 232.246f, 464.746f, 232.246f, 464.742f, 232.187f, 462.887f, 234.246f, +463.75f, 251.566f, 478.113f, 287.254f, 542.906f, 348.25f, 548.746f, 348.25f, 548.746f, 306.367f, +562.426f, 232.246f, 464.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 245.246f, 466.746f, 245.246f, 466.742f, 243.496f, 468.379f, 245.246f, 468.75f, +247.605f, 469.754f, 371.293f, 549.508f, 414.25f, 540.75f, 414.25f, 540.75f, 384.688f, 549.004f, +245.246f, 466.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 245.246f, 466.746f, 245.246f, 466.742f, 243.496f, 468.379f, 245.246f, 468.75f, 247.605f, +469.754f, 371.293f, 549.508f, 414.25f, 540.75f, 414.25f, 540.75f, 384.688f, 549.004f, 245.246f, +466.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +222.246f, 460.746f, 222.246f, 460.742f, 221.512f, 458.594f, 223.246f, 459.75f, 233.266f, 465.301f, +237.242f, 528.234f, 285.25f, 529.75f, 285.25f, 529.75f, 249.523f, 545.801f, 222.246f, 460.746f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 222.246f, +460.746f, 222.246f, 460.742f, 221.512f, 458.594f, 223.246f, 459.75f, 233.266f, 465.301f, 237.242f, +528.234f, 285.25f, 529.75f, 285.25f, 529.75f, 249.523f, 545.801f, 222.246f, 460.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 200.246f, 447.746f, +200.246f, 447.746f, 198.973f, 446.812f, 201.246f, 446.75f, 212.391f, 448.555f, 235.937f, 493.953f, +285.25f, 488.746f, 285.25f, 488.742f, 249.656f, 504.148f, 200.246f, 447.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 200.246f, 447.746f, 200.246f, +447.746f, 198.973f, 446.812f, 201.246f, 446.75f, 212.391f, 448.555f, 235.937f, 493.953f, 285.25f, +488.746f, 285.25f, 488.742f, 249.656f, 504.148f, 200.246f, 447.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 212.246f, 454.75f, 212.246f, 454.75f, +211.625f, 453.348f, 213.246f, 453.746f, 224.461f, 457.637f, 238.852f, 506.711f, 288.246f, 510.746f, +288.246f, 510.742f, 250.363f, 519.348f, 212.246f, 454.75f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 212.246f, 454.75f, 212.246f, 454.75f, 211.625f, +453.348f, 213.246f, 453.746f, 224.461f, 457.637f, 238.852f, 506.711f, 288.246f, 510.746f, 288.246f, +510.742f, 250.363f, 519.348f, 212.246f, 454.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 192.25f, 442.746f, 192.25f, 442.742f, 191.453f, 441.449f, +193.25f, 441.75f, 202.32f, 442.863f, 221.395f, 479.633f, 261.25f, 474.75f, 261.25f, 474.75f, +232.508f, 487.891f, 192.25f, 442.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 192.25f, 442.746f, 192.25f, 442.742f, 191.453f, 441.449f, 193.25f, +441.75f, 202.32f, 442.863f, 221.395f, 479.633f, 261.25f, 474.75f, 261.25f, 474.75f, 232.508f, +487.891f, 192.25f, 442.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 184.25f, 435.75f, 184.25f, 435.75f, 182.949f, 434.945f, 184.25f, 434.746f, +189.281f, 435.414f, 222.984f, 471.801f, 243.246f, 454.75f, 243.246f, 454.75f, 230.082f, 475.344f, +184.25f, 435.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 184.25f, 435.75f, 184.25f, 435.75f, 182.949f, 434.945f, 184.25f, 434.746f, 189.281f, +435.414f, 222.984f, 471.801f, 243.246f, 454.75f, 243.246f, 454.75f, 230.082f, 475.344f, 184.25f, +435.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +260.25f, 470.75f, 260.25f, 470.75f, 259.219f, 472.699f, 261.25f, 472.75f, 263.469f, 473.547f, +396.242f, 537.031f, 438.25f, 522.746f, 438.25f, 522.746f, 409.465f, 534.84f, 260.25f, 470.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 260.25f, +470.75f, 260.25f, 470.75f, 259.219f, 472.699f, 261.25f, 472.75f, 263.469f, 473.547f, 396.242f, +537.031f, 438.25f, 522.746f, 438.25f, 522.746f, 409.465f, 534.84f, 260.25f, 470.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 223.246f, 400.75f, +223.246f, 400.75f, 223.52f, 402.457f, 225.25f, 401.746f, 227.477f, 400.695f, 244.418f, 400.035f, +231.246f, 375.75f, 231.246f, 375.75f, 241.34f, 401.797f, 223.246f, 400.75f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 223.246f, 400.75f, 223.246f, +400.75f, 223.52f, 402.457f, 225.25f, 401.746f, 227.477f, 400.695f, 244.418f, 400.035f, 231.246f, +375.75f, 231.246f, 375.75f, 241.34f, 401.797f, 223.246f, 400.75f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 235.246f, 401.746f, 235.242f, 401.746f, +234.957f, 404.219f, 237.25f, 403.746f, 238.918f, 402.457f, 258.5f, 400.035f, 260.25f, 357.746f, +260.25f, 357.746f, 252.777f, 403.559f, 235.246f, 401.746f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 235.246f, 401.746f, 235.242f, 401.746f, 234.957f, +404.219f, 237.25f, 403.746f, 238.918f, 402.457f, 258.5f, 400.035f, 260.25f, 357.746f, 260.25f, +357.746f, 252.777f, 403.559f, 235.246f, 401.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 242.246f, 403.746f, 242.246f, 403.746f, 242.437f, 405.977f, +244.246f, 404.75f, 246.398f, 404.219f, 273.457f, 400.477f, 299.246f, 364.75f, 299.246f, 364.75f, +260.258f, 405.316f, 242.246f, 403.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 242.246f, 403.746f, 242.246f, 403.746f, 242.437f, 405.977f, 244.246f, +404.75f, 246.398f, 404.219f, 273.457f, 400.477f, 299.246f, 364.75f, 299.246f, 364.75f, 260.258f, +405.316f, 242.246f, 403.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 251.25f, 405.746f, 251.25f, 405.742f, 250.566f, 408.164f, 252.25f, 407.746f, +254.723f, 406.945f, 277.199f, 409.031f, 319.25f, 371.75f, 319.25f, 371.75f, 268.316f, 409.875f, +251.25f, 405.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 251.25f, 405.746f, 251.25f, 405.742f, 250.566f, 408.164f, 252.25f, 407.746f, 254.723f, +406.945f, 277.199f, 409.031f, 319.25f, 371.75f, 319.25f, 371.75f, 268.316f, 409.875f, 251.25f, +405.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +262.25f, 410.75f, 262.25f, 410.75f, 262.004f, 413.004f, 264.246f, 412.746f, 266.164f, 411.785f, +304.48f, 406.832f, 361.25f, 368.746f, 361.25f, 368.746f, 279.754f, 414.715f, 262.25f, 410.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 262.25f, +410.75f, 262.25f, 410.75f, 262.004f, 413.004f, 264.246f, 412.746f, 266.164f, 411.785f, 304.48f, +406.832f, 361.25f, 368.746f, 361.25f, 368.746f, 279.754f, 414.715f, 262.25f, 410.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 218.25f, 423.746f, +218.25f, 423.746f, 217.797f, 425.777f, 220.246f, 424.75f, 221.758f, 424.016f, 280.5f, 421.156f, +314.25f, 391.75f, 314.25f, 391.75f, 275.547f, 418.93f, 218.25f, 423.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 218.25f, 423.746f, 218.25f, +423.746f, 217.797f, 425.777f, 220.246f, 424.75f, 221.758f, 424.016f, 280.5f, 421.156f, 314.25f, +391.75f, 314.25f, 391.75f, 275.547f, 418.93f, 218.25f, 423.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 207.25f, 419.75f, 207.25f, 419.75f, +206.797f, 421.379f, 209.246f, 420.746f, 210.758f, 419.617f, 237.816f, 415.879f, 264.246f, 379.746f, +264.246f, 379.746f, 224.617f, 420.719f, 207.25f, 419.75f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 207.25f, 419.75f, 207.25f, 419.75f, 206.797f, +421.379f, 209.246f, 420.746f, 210.758f, 419.617f, 237.816f, 415.879f, 264.246f, 379.746f, 264.246f, +379.746f, 224.617f, 420.719f, 207.25f, 419.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 274.25f, 415.75f, 274.25f, 415.75f, 273.828f, 418.031f, +276.246f, 417.75f, 278.066f, 417.125f, 316.645f, 414.992f, 376.246f, 380.75f, 376.246f, 380.75f, +290.746f, 418.625f, 274.25f, 415.75f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 274.25f, 415.75f, 274.25f, 415.75f, 273.828f, 418.031f, 276.246f, +417.75f, 278.066f, 417.125f, 316.645f, 414.992f, 376.246f, 380.75f, 376.246f, 380.75f, 290.746f, +418.625f, 274.25f, 415.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 283.25f, 418.746f, 283.25f, 418.742f, 283.07f, 420.672f, 285.25f, 419.75f, +287.309f, 419.762f, 325.883f, 417.633f, 385.25f, 383.746f, 385.25f, 383.742f, 300.648f, 421.703f, +283.25f, 418.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 283.25f, 418.746f, 283.25f, 418.742f, 283.07f, 420.672f, 285.25f, 419.75f, 287.309f, +419.762f, 325.883f, 417.633f, 385.25f, 383.746f, 385.25f, 383.742f, 300.648f, 421.703f, 283.25f, +418.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +294.25f, 424.75f, 294.25f, 424.75f, 293.629f, 426.172f, 296.25f, 425.746f, 297.867f, 425.262f, +345.242f, 420.492f, 444.246f, 382.75f, 444.246f, 382.75f, 311.207f, 427.203f, 294.25f, 424.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 294.25f, +424.75f, 294.25f, 424.75f, 293.629f, 426.172f, 296.25f, 425.746f, 297.867f, 425.262f, 345.242f, +420.492f, 444.246f, 382.75f, 444.246f, 382.75f, 311.207f, 427.203f, 294.25f, 424.75f, 10, +0, 0, 0, 0, 0, 0, 0, 4, 172.25f, 416.746f, +172.25f, 416.742f, 177.539f, 417.195f, 176.246f, 415.75f, 174.898f, 413.676f, 172.25f, 414.746f, +172.25f, 414.746f, 172.25f, 416.746f, 10, 0, 0, 0, 0, 0, +0, 0, 4, 205.25f, 401.746f, 205.25f, 401.746f, 211.418f, 401.797f, 210.246f, +399.75f, 208.777f, 398.277f, 206.25f, 398.746f, 206.25f, 398.746f, 205.25f, 401.746f, 10, +0, 0, 0, 0, 0, 0, 0, 4, 196.25f, 401.746f, +196.25f, 401.746f, 201.738f, 402.238f, 200.246f, 400.75f, 199.098f, 398.719f, 196.25f, 399.75f, +196.25f, 399.75f, 196.25f, 401.746f, 10, 0, 0, 0, 0, 0, +0, 0, 4, 91.25f, 414.746f, 91.25f, 414.746f, 96.6602f, 413.344f, 95.25f, +411.746f, 93.0156f, 410.879f, 91.25f, 412.746f, 91.25f, 412.746f, 91.25f, 414.746f, 10, +0, 0, 0, 0, 0, 0, 0, 4, 93.2461f, 425.746f, +93.2422f, 425.746f, 98.8633f, 423.902f, 97.25f, 422.746f, 95.2148f, 421.441f, 93.2461f, 422.746f, +93.2461f, 422.746f, 93.2461f, 425.746f, 10, 0, 0, 0, 0, 0, +0, 0, 4, 85.25f, 429.746f, 85.25f, 429.742f, 90.9414f, 428.742f, 89.2461f, +427.746f, 87.2969f, 426.281f, 85.25f, 427.746f, 85.25f, 427.746f, 85.25f, 429.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 173.25f, 347.75f, +173.25f, 347.75f, 171.379f, 347.676f, 167.25f, 345.75f, 164.777f, 345.477f, 152.457f, 341.516f, +146.25f, 330.746f, 146.25f, 330.742f, 159.938f, 341.078f, 173.25f, 347.75f, 10, 0, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 9, 269.246f, 245.746f, 269.781f, +245.484f, 269.84f, 245.02f, 270.25f, 244.75f, 270.887f, 244.957f, 272.242f, 244.625f, 272.25f, +245.746f, 271.172f, 250.063f, 270.211f, 255.492f, 265.246f, 257.746f, 264.961f, 257.789f, 263.375f, +257.332f, 263.25f, 256.746f, 263.156f, 254.684f, 263.027f, 253.203f, 263.25f, 251.75f, 263.695f, +250.027f, 266.07f, 250.016f, 267.246f, 251.75f, 268.109f, 249.699f, 268.582f, 247.672f, 269.246f, +245.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 9, +257.246f, 240.75f, 258.262f, 239.004f, 258.121f, 236.961f, 259.25f, 236.746f, 260.492f, 236.016f, +262.527f, 237.09f, 262.25f, 238.746f, 261.188f, 240.539f, 260.758f, 243, 259.25f, 244.75f, +259.012f, 245.281f, 259.277f, 245.867f, 259.25f, 245.746f, 258.445f, 247.57f, 257.188f, 248.379f, +255.246f, 247.746f, 254.41f, 245.594f, 255.676f, 243.25f, 257.246f, 241.75f, 257.5f, 241.203f, +257.316f, 240.793f, 257.246f, 240.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 11, 214.25f, 246.746f, 213.758f, 246.684f, 213.719f, 247.191f, 214.25f, +247.746f, 214.484f, 248.68f, 215.355f, 249.914f, 215.25f, 250.746f, 214.602f, 252.203f, 213.375f, +252, 212.246f, 251.75f, 211.41f, 250.281f, 211.355f, 248.273f, 210.246f, 246.746f, 210.379f, +246.355f, 210.438f, 245.723f, 210.246f, 245.746f, 209.43f, 244.832f, 208.945f, 243.152f, 209.246f, +242.75f, 209.109f, 242.18f, 208.91f, 231.281f, 209.246f, 231.75f, 209.836f, 232.379f, 213.188f, +243.086f, 213.246f, 243.75f, 213.328f, 244.871f, 214.133f, 245.383f, 214.25f, 246.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 7, 185.25f, 253.75f, +188.574f, 256.488f, 191.641f, 259.746f, 191.246f, 263.75f, 191.027f, 264.902f, 189.074f, 264.32f, +189.246f, 263.75f, 187.988f, 259.402f, 185.746f, 256.477f, 183.25f, 253.75f, 180.504f, 251.594f, +178.457f, 244.617f, 178.246f, 243.75f, 182.266f, 249.84f, 184.746f, 252.859f, 185.25f, 253.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 10, 170.246f, +260.746f, 171.32f, 260.707f, 170.988f, 261.246f, 171.246f, 261.746f, 172.273f, 263.215f, 173.707f, +264.586f, 173.25f, 266.746f, 173.73f, 266.805f, 173.313f, 267.145f, 173.25f, 266.746f, 172.641f, +266.695f, 172.262f, 266.551f, 172.25f, 266.746f, 169.91f, 263.715f, 168.371f, 260.777f, 167.25f, +257.746f, 166.582f, 257.289f, 165.324f, 252.352f, 165.246f, 251.75f, 165.934f, 252.133f, 167.824f, +256.734f, 168.25f, 256.746f, 169.445f, 257.613f, 169.457f, 259.391f, 170.246f, 260.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 189.246f, 238.746f, +189.641f, 239.758f, 191.371f, 241.078f, 191.246f, 241.75f, 191.117f, 243.078f, 191.633f, 244.664f, +190.246f, 243.75f, 189.246f, 242.867f, 185.453f, 241.383f, 185.25f, 234.746f, 185.129f, 234.363f, +188.398f, 237.328f, 189.246f, 238.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 19, 205.25f, 257.746f, 205.477f, 258.434f, 206.258f, 257.91f, 207.25f, +257.746f, 207.473f, 258.609f, 208.148f, 259.223f, 208.25f, 259.746f, 209.535f, 262.301f, 211.48f, +264.305f, 211.246f, 266.746f, 209.996f, 268.484f, 209.25f, 266.238f, 208.25f, 264.746f, 207.102f, +266.988f, 206.004f, 264.926f, 204.25f, 264.746f, 204.496f, 264.324f, 204.262f, 264.707f, 204.25f, +264.746f, 202.887f, 264.195f, 202.137f, 263.004f, 201.246f, 261.746f, 200.852f, 261.996f, 200.406f, +262.195f, 200.246f, 261.746f, 199.527f, 261.383f, 198.457f, 261.027f, 198.246f, 260.746f, 196.93f, +257.297f, 193.473f, 254.992f, 191.246f, 246.746f, 191.816f, 245.695f, 196.359f, 254.004f, 197.25f, +254.75f, 197.816f, 256.086f, 197.945f, 252.945f, 199.246f, 253.75f, 199.406f, 253.707f, 199.609f, +253.445f, 200.246f, 253.75f, 199.973f, 253.605f, 200.211f, 253.855f, 200.246f, 253.75f, 200.637f, +254.176f, 200.492f, 254.789f, 200.246f, 254.75f, 202.074f, 256.039f, 201.98f, 257.215f, 203.25f, +258.746f, 203.344f, 257.711f, 204.508f, 258.5f, 205.25f, 257.746f, 10, 0, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 271.25f, 197.75f, 271.25f, 197.75f, +278.957f, 220.297f, 274.25f, 232.75f, 274.25f, 232.75f, 286.656f, 208.855f, 281.25f, 196.75f, +281.25f, 196.75f, 281.156f, 207.977f, 277.246f, 213.746f, 277.246f, 213.746f, 272.359f, 199.398f, +271.25f, 197.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 254.246f, 200.75f, 254.246f, 200.75f, 260.477f, 210.398f, 251.25f, 230.75f, 251.25f, +230.75f, 250.797f, 208.195f, 243.246f, 195.746f, 243.246f, 195.742f, 258.937f, 218.316f, 254.246f, +200.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, +243.246f, 202.746f, 243.246f, 202.746f, 243.316f, 224.918f, 244.246f, 227.746f, 244.246f, 227.742f, +239.578f, 209.957f, 228.25f, 199.75f, 228.25f, 199.75f, 244.199f, 212.598f, 243.246f, 202.746f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 233.246f, +237.746f, 233.246f, 237.746f, 239.578f, 223.156f, 228.25f, 202.746f, 228.25f, 202.746f, 235.617f, +216.336f, 230.25f, 223.746f, 230.25f, 223.746f, 233.199f, 227.777f, 233.246f, 237.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, 212.246f, 203.746f, +212.246f, 203.746f, 210.758f, 220.516f, 212.246f, 222.75f, 212.246f, 222.75f, 212.957f, 229.977f, +212.246f, 230.75f, 212.246f, 230.75f, 216.918f, 237.898f, 217.25f, 229.75f, 217.25f, 229.75f, +218.68f, 221.176f, 222.246f, 215.746f, 222.246f, 215.746f, 225.719f, 210.176f, 225.25f, 202.746f, +225.25f, 202.746f, 214.5f, 236.355f, 212.246f, 203.746f, 10, 0, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 5, 208.25f, 233.75f, 208.25f, 233.75f, 200.637f, +221.836f, 198.246f, 200.75f, 198.246f, 200.75f, 197.117f, 207.758f, 201.246f, 223.746f, 201.246f, +223.746f, 205.918f, 240.535f, 208.25f, 233.75f, 10, 0, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 6, 184.25f, 211.75f, 184.25f, 211.75f, 189.418f, 217.879f, +191.246f, 223.746f, 191.242f, 223.746f, 194.918f, 240.758f, 188.246f, 231.75f, 188.246f, 231.75f, +188.098f, 222.496f, 179.246f, 214.746f, 179.246f, 214.746f, 184.359f, 216.996f, 184.25f, 211.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 177.246f, +217.746f, 177.246f, 217.742f, 181.277f, 236.578f, 182.25f, 237.746f, 182.25f, 237.746f, 184.137f, +241.195f, 181.25f, 237.746f, 181.25f, 237.746f, 171.379f, 216.559f, 167.25f, 209.75f, 167.25f, +209.75f, 175.777f, 219.418f, 177.246f, 217.746f, 10, 0, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 4, 171.246f, 235.746f, 171.246f, 235.746f, 183.918f, 260.336f, +160.246f, 231.75f, 160.246f, 231.75f, 172.039f, 242.738f, 171.246f, 235.746f, 10, 0, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 154.246f, 251.75f, 154.242f, +251.75f, 159.5f, 272.438f, 162.25f, 271.746f, 162.25f, 271.746f, 171.379f, 282.117f, 164.246f, +270.75f, 164.242f, 270.75f, 157.52f, 259.898f, 158.25f, 248.746f, 158.25f, 248.746f, 157.52f, +259.676f, 154.246f, 251.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 5, 517.246f, 264.746f, 517.242f, 264.742f, 504.348f, 275.297f, 501.25f, 278.75f, +501.25f, 278.75f, 516.449f, 258.797f, 516.25f, 250.746f, 516.25f, 250.742f, 519.199f, 259.348f, +517.246f, 264.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 523.25f, 288.746f, 523.25f, 288.742f, 500.5f, 305, 496.25f, 312.75f, 496.25f, +312.75f, 525.797f, 280.797f, 526.246f, 275.746f, 526.242f, 275.742f, 526.348f, 285.75f, 523.25f, +288.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, +542.246f, 457.75f, 542.246f, 457.75f, 529.098f, 466.699f, 527.25f, 464.746f, 527.25f, 464.742f, +539, 457.348f, 542.246f, 447.746f, 542.246f, 447.746f, 540.098f, 457.898f, 542.246f, 457.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 4, 551.25f, +369.75f, 532.25f, 382.75f, 532.25f, 382.75f, 553.297f, 363.848f, 554.25f, 359.746f, 551.25f, +369.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, +122.246f, 393.75f, 146.25f, 388.75f, 10, 1.1f, 0, 0, 0, 0, +0, 0, 2, 177.246f, 215.746f, 177.246f, 215.746f, 176.547f, 219.75f, 166.246f, +207.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, +183.25f, 210.75f, 183.25f, 210.75f, 185.348f, 217.547f, 178.246f, 212.746f, 10, 1.1f, +0, 0, 0, 0, 0, 0, 2, 242.246f, 200.75f, 242.246f, +200.75f, 244.199f, 213.148f, 231.246f, 198.75f}; + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h new file mode 100755 index 0000000..c83d3f1 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h @@ -0,0 +1,45 @@ +#ifndef __TIGER_H +#define __TIGER_H + +/*------------------------------------------------------------------------ + * + * OpenVG 1.0.1 Reference Implementation sample code + * ------------------------------------------------- + * + * Copyright (c) 2007 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + * + *//** + * \file + * \brief Header for including the Tiger image data. + * \note + *//*-------------------------------------------------------------------*/ + +extern const int tigerCommandCount; +extern const char tigerCommands[]; +extern const float tigerMinX; +extern const float tigerMaxX; +extern const float tigerMinY; +extern const float tigerMaxY; +extern const int tigerPointCount; +extern const float tigerPoints[]; + +#endif /* __TIGER_H */ diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt new file mode 100755 index 0000000..4e8128e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_triangle.bin) +set(SRCS triangle.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/Makefile b/host_applications/linux/apps/hello_pi/hello_triangle/Makefile new file mode 100755 index 0000000..45dcc0d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/Makefile @@ -0,0 +1,5 @@ +OBJS=triangle.o +BIN=hello_triangle.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h b/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h new file mode 100755 index 0000000..7dd30a9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f +}; + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c b/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c new file mode 100755 index 0000000..0956c97 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c @@ -0,0 +1,551 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "cube_texture_and_coords.h" + +#define PATH "./" + +#define IMAGE_SIZE 128 + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_ELEMENT_HANDLE_T dispman_element; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex[6]; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; +// pointers to texture buffers + char *tex_buf1; + char *tex_buf2; + char *tex_buf3; +} CUBE_STATE_T; + +static void init_ogl(CUBE_STATE_T *state); +static void init_model_proj(CUBE_STATE_T *state); +static void reset_model(CUBE_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); +static void redraw_scene(CUBE_STATE_T *state); +static void update_model(CUBE_STATE_T *state); +static void init_textures(CUBE_STATE_T *state); +static void load_tex_images(CUBE_STATE_T *state); +static void exit_func(void); +static volatile int terminate; +static CUBE_STATE_T _state, *state=&_state; + + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + + // get an appropriate EGL frame buffer configuration + result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); + assert(state->context!=EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + state->dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + state->dispman_element = vc_dispmanx_element_add ( dispman_update, state->dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = state->dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + + // Set background color and clear buffers + glClearColor(0.15f, 0.25f, 0.35f, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glMatrixMode(GL_MODELVIEW); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(CUBE_STATE_T *state) +{ + float nearp = 1.0f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_BYTE, 0, quadx ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(CUBE_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.f, 0.f, -50.f); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 40.f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void update_model(CUBE_STATE_T *state) +{ + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 120.0f) + distance = 120.f; + else if (distance <= 40.0f) + distance = 40.0f; + + return distance; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void redraw_scene(CUBE_STATE_T *state) +{ + // Start with a clear screen + glClear( GL_COLOR_BUFFER_BIT ); + + // Draw first (front) face: + // Bind texture surface to current vertices + glBindTexture(GL_TEXTURE_2D, state->tex[0]); + + // Need to rotate textures - do this by rotating each cube face + glRotatef(270.f, 0.f, 0.f, 1.f ); // front face normal along z axis + + // draw first 4 vertices + glDrawArrays( GL_TRIANGLE_STRIP, 0, 4); + + // same pattern for other 5 faces - rotation chosen to make image orientation 'nice' + glBindTexture(GL_TEXTURE_2D, state->tex[1]); + glRotatef(90.f, 0.f, 0.f, 1.f ); // back face normal along z axis + glDrawArrays( GL_TRIANGLE_STRIP, 4, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[2]); + glRotatef(90.f, 1.f, 0.f, 0.f ); // left face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 8, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[3]); + glRotatef(90.f, 1.f, 0.f, 0.f ); // right face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 12, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[4]); + glRotatef(270.f, 0.f, 1.f, 0.f ); // top face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 16, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[5]); + glRotatef(90.f, 0.f, 1.f, 0.f ); // bottom face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 20, 4); + + eglSwapBuffers(state->display, state->surface); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void init_textures(CUBE_STATE_T *state) +{ + // load three texture buffers but use them on six OGL|ES texture surfaces + load_tex_images(state); + glGenTextures(6, &state->tex[0]); + + // setup first texture + glBindTexture(GL_TEXTURE_2D, state->tex[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // setup second texture - reuse first image + glBindTexture(GL_TEXTURE_2D, state->tex[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // third texture + glBindTexture(GL_TEXTURE_2D, state->tex[2]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // fourth texture - reuse second image + glBindTexture(GL_TEXTURE_2D, state->tex[3]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + //fifth texture + glBindTexture(GL_TEXTURE_2D, state->tex[4]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // sixth texture - reuse third image + glBindTexture(GL_TEXTURE_2D, state->tex[5]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // setup overall texture environment + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); +} + +/*********************************************************** + * Name: load_tex_images + * + * Arguments: + * void + * + * Description: Loads three raw images to use as textures on faces + * + * Returns: void + * + ***********************************************************/ +static void load_tex_images(CUBE_STATE_T *state) +{ + FILE *tex_file1 = NULL, *tex_file2=NULL, *tex_file3 = NULL; + int bytes_read, image_sz = IMAGE_SIZE*IMAGE_SIZE*3; + + state->tex_buf1 = malloc(image_sz); + state->tex_buf2 = malloc(image_sz); + state->tex_buf3 = malloc(image_sz); + + tex_file1 = fopen(PATH "Lucca_128_128.raw", "rb"); + if (tex_file1 && state->tex_buf1) + { + bytes_read=fread(state->tex_buf1, 1, image_sz, tex_file1); + assert(bytes_read == image_sz); // some problem with file? + fclose(tex_file1); + } + + tex_file2 = fopen(PATH "Djenne_128_128.raw", "rb"); + if (tex_file2 && state->tex_buf2) + { + bytes_read=fread(state->tex_buf2, 1, image_sz, tex_file2); + assert(bytes_read == image_sz); // some problem with file? + fclose(tex_file2); + } + + tex_file3 = fopen(PATH "Gaudi_128_128.raw", "rb"); + if (tex_file3 && state->tex_buf3) + { + bytes_read=fread(state->tex_buf3, 1, image_sz, tex_file3); + assert(bytes_read == image_sz); // some problem with file? + fclose(tex_file3); + } +} + +//------------------------------------------------------------------------------ + +static void exit_func(void) +// Function to be passed to atexit(). +{ + DISPMANX_UPDATE_HANDLE_T dispman_update; + int s; + // clear screen + glClear( GL_COLOR_BUFFER_BIT ); + eglSwapBuffers(state->display, state->surface); + + glDeleteTextures(6, state->tex); + eglDestroySurface( state->display, state->surface ); + + dispman_update = vc_dispmanx_update_start( 0 ); + s = vc_dispmanx_element_remove(dispman_update, state->dispman_element); + assert(s == 0); + vc_dispmanx_update_submit_sync( dispman_update ); + s = vc_dispmanx_display_close(state->dispman_display); + assert (s == 0); + + // Release OpenGL resources + eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroyContext( state->display, state->context ); + eglTerminate( state->display ); + + // release texture buffers + free(state->tex_buf1); + free(state->tex_buf2); + free(state->tex_buf3); + + printf("\ncube closed\n"); +} // exit_func() + +//============================================================================== + +int main () +{ + bcm_host_init(); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + + // initialise the OGLES texture(s) + init_textures(state); + + while (!terminate) + { + update_model(state); + redraw_scene(state); + } + exit_func(); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt new file mode 100755 index 0000000..390980a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_triangle2.bin) +set(SRCS triangle2.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile b/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile new file mode 100755 index 0000000..13b1b39 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile @@ -0,0 +1,5 @@ +OBJS=triangle2.o +BIN=hello_triangle2.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c b/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c new file mode 100755 index 0000000..bc68c51 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c @@ -0,0 +1,509 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// OpenGL|ES 2 demo using shader to compute mandelbrot/julia sets +// Thanks to Peter de Rivas for original Python code + +#include +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES2/gl2.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + EGLDisplay display; + EGLSurface surface; + EGLContext context; + + GLuint verbose; + GLuint vshader; + GLuint fshader; + GLuint mshader; + GLuint program; + GLuint program2; + GLuint tex_fb; + GLuint tex; + GLuint buf; +// julia attribs + GLuint unif_color, attr_vertex, unif_scale, unif_offset, unif_tex, unif_centre; +// mandelbrot attribs + GLuint attr_vertex2, unif_scale2, unif_offset2, unif_centre2; +} CUBE_STATE_T; + +static CUBE_STATE_T _state, *state=&_state; + +#define check() assert(glGetError() == 0) + +static void showlog(GLint shader) +{ + // Prints the compile log for a shader + char log[1024]; + glGetShaderInfoLog(shader,sizeof log,NULL,log); + printf("%d:shader:\n%s\n", shader, log); +} + +static void showprogramlog(GLint shader) +{ + // Prints the information log for a program object + char log[1024]; + glGetProgramInfoLog(shader,sizeof log,NULL,log); + printf("%d:program:\n%s\n", shader, log); +} + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + check(); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + check(); + + // get an appropriate EGL frame buffer configuration + result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + check(); + + // get an appropriate EGL frame buffer configuration + result = eglBindAPI(EGL_OPENGL_ES_API); + assert(EGL_FALSE != result); + check(); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, context_attributes); + assert(state->context!=EGL_NO_CONTEXT); + check(); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + check(); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + check(); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + check(); + + // Set background color and clear buffers + glClearColor(0.15f, 0.25f, 0.35f, 1.0f); + glClear( GL_COLOR_BUFFER_BIT ); + + check(); +} + + +static void init_shaders(CUBE_STATE_T *state) +{ + static const GLfloat vertex_data[] = { + -1.0,-1.0,1.0,1.0, + 1.0,-1.0,1.0,1.0, + 1.0,1.0,1.0,1.0, + -1.0,1.0,1.0,1.0 + }; + const GLchar *vshader_source = + "attribute vec4 vertex;" + "varying vec2 tcoord;" + "void main(void) {" + " vec4 pos = vertex;" + " gl_Position = pos;" + " tcoord = vertex.xy*0.5+0.5;" + "}"; + + //Mandelbrot + const GLchar *mandelbrot_fshader_source = +"uniform vec4 color;" +"uniform vec2 scale;" +"uniform vec2 centre;" +"varying vec2 tcoord;" +"void main(void) {" +" float intensity;" +" vec4 color2;" +" float cr=(gl_FragCoord.x-centre.x)*scale.x;" +" float ci=(gl_FragCoord.y-centre.y)*scale.y;" +" float ar=cr;" +" float ai=ci;" +" float tr,ti;" +" float col=0.0;" +" float p=0.0;" +" int i=0;" +" for(int i2=1;i2<16;i2++)" +" {" +" tr=ar*ar-ai*ai+cr;" +" ti=2.0*ar*ai+ci;" +" p=tr*tr+ti*ti;" +" ar=tr;" +" ai=ti;" +" if (p>16.0)" +" {" +" i=i2;" +" break;" +" }" +" }" +" color2 = vec4(float(i)*0.0625,0,0,1);" +" gl_FragColor = color2;" +"}"; + + // Julia + const GLchar *julia_fshader_source = +"uniform vec4 color;" +"uniform vec2 scale;" +"uniform vec2 centre;" +"uniform vec2 offset;" +"varying vec2 tcoord;" +"uniform sampler2D tex;" +"void main(void) {" +" float intensity;" +" vec4 color2;" +" float ar=(gl_FragCoord.x-centre.x)*scale.x;" +" float ai=(gl_FragCoord.y-centre.y)*scale.y;" +" float cr=(offset.x-centre.x)*scale.x;" +" float ci=(offset.y-centre.y)*scale.y;" +" float tr,ti;" +" float col=0.0;" +" float p=0.0;" +" int i=0;" +" vec2 t2;" +" t2.x=tcoord.x+(offset.x-centre.x)*(0.5/centre.y);" +" t2.y=tcoord.y+(offset.y-centre.y)*(0.5/centre.x);" +" for(int i2=1;i2<16;i2++)" +" {" +" tr=ar*ar-ai*ai+cr;" +" ti=2.0*ar*ai+ci;" +" p=tr*tr+ti*ti;" +" ar=tr;" +" ai=ti;" +" if (p>16.0)" +" {" +" i=i2;" +" break;" +" }" +" }" +" color2 = vec4(0,float(i)*0.0625,0,1);" +" color2 = color2+texture2D(tex,t2);" +" gl_FragColor = color2;" +"}"; + + state->vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(state->vshader, 1, &vshader_source, 0); + glCompileShader(state->vshader); + check(); + + if (state->verbose) + showlog(state->vshader); + + state->fshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(state->fshader, 1, &julia_fshader_source, 0); + glCompileShader(state->fshader); + check(); + + if (state->verbose) + showlog(state->fshader); + + state->mshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(state->mshader, 1, &mandelbrot_fshader_source, 0); + glCompileShader(state->mshader); + check(); + + if (state->verbose) + showlog(state->mshader); + + // julia + state->program = glCreateProgram(); + glAttachShader(state->program, state->vshader); + glAttachShader(state->program, state->fshader); + glLinkProgram(state->program); + check(); + + if (state->verbose) + showprogramlog(state->program); + + state->attr_vertex = glGetAttribLocation(state->program, "vertex"); + state->unif_color = glGetUniformLocation(state->program, "color"); + state->unif_scale = glGetUniformLocation(state->program, "scale"); + state->unif_offset = glGetUniformLocation(state->program, "offset"); + state->unif_tex = glGetUniformLocation(state->program, "tex"); + state->unif_centre = glGetUniformLocation(state->program, "centre"); + + // mandelbrot + state->program2 = glCreateProgram(); + glAttachShader(state->program2, state->vshader); + glAttachShader(state->program2, state->mshader); + glLinkProgram(state->program2); + check(); + + if (state->verbose) + showprogramlog(state->program2); + + state->attr_vertex2 = glGetAttribLocation(state->program2, "vertex"); + state->unif_scale2 = glGetUniformLocation(state->program2, "scale"); + state->unif_offset2 = glGetUniformLocation(state->program2, "offset"); + state->unif_centre2 = glGetUniformLocation(state->program2, "centre"); + check(); + + glClearColor ( 0.0, 1.0, 1.0, 1.0 ); + + glGenBuffers(1, &state->buf); + + check(); + + // Prepare a texture image + glGenTextures(1, &state->tex); + check(); + glBindTexture(GL_TEXTURE_2D,state->tex); + check(); + // glActiveTexture(0) + glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,state->screen_width,state->screen_height,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,0); + check(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + check(); + // Prepare a framebuffer for rendering + glGenFramebuffers(1,&state->tex_fb); + check(); + glBindFramebuffer(GL_FRAMEBUFFER,state->tex_fb); + check(); + glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,state->tex,0); + check(); + glBindFramebuffer(GL_FRAMEBUFFER,0); + check(); + // Prepare viewport + glViewport ( 0, 0, state->screen_width, state->screen_height ); + check(); + + // Upload vertex data to a buffer + glBindBuffer(GL_ARRAY_BUFFER, state->buf); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), + vertex_data, GL_STATIC_DRAW); + glVertexAttribPointer(state->attr_vertex, 4, GL_FLOAT, 0, 16, 0); + glEnableVertexAttribArray(state->attr_vertex); + check(); +} + + +static void draw_mandelbrot_to_texture(CUBE_STATE_T *state, GLfloat cx, GLfloat cy, GLfloat scale) +{ + // Draw the mandelbrot to a texture + glBindFramebuffer(GL_FRAMEBUFFER,state->tex_fb); + check(); + glBindBuffer(GL_ARRAY_BUFFER, state->buf); + + glUseProgram ( state->program2 ); + check(); + + glUniform2f(state->unif_scale2, scale, scale); + glUniform2f(state->unif_centre2, cx, cy); + check(); + glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 ); + check(); + + glFlush(); + glFinish(); + check(); +} + +static void draw_triangles(CUBE_STATE_T *state, GLfloat cx, GLfloat cy, GLfloat scale, GLfloat x, GLfloat y) +{ + // Now render to the main frame buffer + glBindFramebuffer(GL_FRAMEBUFFER,0); + // Clear the background (not really necessary I suppose) + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + check(); + + glBindBuffer(GL_ARRAY_BUFFER, state->buf); + check(); + glUseProgram ( state->program ); + check(); + glBindTexture(GL_TEXTURE_2D,state->tex); + check(); + glUniform4f(state->unif_color, 0.5, 0.5, 0.8, 1.0); + glUniform2f(state->unif_scale, scale, scale); + glUniform2f(state->unif_offset, x, y); + glUniform2f(state->unif_centre, cx, cy); + glUniform1i(state->unif_tex, 0); // I don't really understand this part, perhaps it relates to active texture? + check(); + + glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 ); + check(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glFlush(); + glFinish(); + check(); + + eglSwapBuffers(state->display, state->surface); + check(); +} + +static int get_mouse(CUBE_STATE_T *state, int *outx, int *outy) +{ + static int fd = -1; + const int width=state->screen_width, height=state->screen_height; + static int x=800, y=400; + const int XSIGN = 1<<4, YSIGN = 1<<5; + if (fd<0) { + fd = open("/dev/input/mouse0",O_RDONLY|O_NONBLOCK); + } + if (fd>=0) { + struct {char buttons, dx, dy; } m; + while (1) { + int bytes = read(fd, &m, sizeof m); + if (bytes < (int)sizeof m) goto _exit; + if (m.buttons&8) { + break; // This bit should always be set + } + read(fd, &m, 1); // Try to sync up again + } + if (m.buttons&3) + return m.buttons&3; + x+=m.dx; + y+=m.dy; + if (m.buttons&XSIGN) + x-=256; + if (m.buttons&YSIGN) + y-=256; + if (x<0) x=0; + if (y<0) y=0; + if (x>width) x=width; + if (y>height) y=height; + } +_exit: + if (outx) *outx = x; + if (outy) *outy = y; + return 0; +} + +//============================================================================== + +int main () +{ + int terminate = 0; + GLfloat cx, cy; + bcm_host_init(); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + init_shaders(state); + cx = state->screen_width/2; + cy = state->screen_height/2; + + draw_mandelbrot_to_texture(state, cx, cy, 0.003); + while (!terminate) + { + int x, y, b; + b = get_mouse(state, &x, &y); + if (b) break; + draw_triangles(state, cx, cy, 0.003, x, y); + } + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt new file mode 100755 index 0000000..fdc1182 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt @@ -0,0 +1,10 @@ +set(EXEC hello_video.bin) +set(SRCS tsemaphore.c queue.c video.c) +#SET(pc_dependents "libtbm") + +add_executable(${EXEC} ${SRCS}) +#add_dependencies(${EXEC} ${pc_dependents}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS} -ltbm) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_video/Makefile b/host_applications/linux/apps/hello_pi/hello_video/Makefile new file mode 100755 index 0000000..c86256a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/Makefile @@ -0,0 +1,6 @@ +OBJS=tsemaphore.o queue.o video.o +BIN=hello_video.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_video/README b/host_applications/linux/apps/hello_pi/hello_video/README new file mode 100755 index 0000000..c4922d9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/README @@ -0,0 +1 @@ +The video clip test.h264 is (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org diff --git a/host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h b/host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h new file mode 100755 index 0000000..3ba1d1a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h @@ -0,0 +1,77 @@ +/** + @file src/omx_comp_debug_levels.h + + Define the level of debug prints on standard err. The different levels can + be composed with binary OR. + The debug levels defined here belong to OpenMAX components and IL core + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-07-16 09:39:31 +0200 (Wed, 16 Jul 2008) $ + Revision $Rev: 577 $ + Author $Author: gsent $ + +*/ + +#ifndef __OMX_COMP_DEBUG_LEVELS_H__ +#define __OMX_COMP_DEBUG_LEVELS_H__ + +#include + +/** Remove all debug output lines + */ +#define DEB_LEV_NO_OUTPUT 0 + +/** Messages explaing the reason of critical errors + */ +#define DEB_LEV_ERR 1 + +/** Messages showing values related to the test and the component/s used + */ +#define DEB_LEV_PARAMS 2 + +/** Messages representing steps in the execution. These are the simple messages, because + * they avoid iterations + */ +#define DEB_LEV_SIMPLE_SEQ 4 + +/** Messages representing steps in the execution. All the steps are described, + * also with iterations. With this level of output the performances are + * seriously compromised + */ +#define DEB_LEV_FULL_SEQ 8 + +/** Messages that indicates the beginning and the end of a function. + * It can be used to trace the execution + */ +#define DEB_LEV_FUNCTION_NAME 16 + +/** All the messages - max value + */ +#define DEB_ALL_MESS 255 + +/** \def DEBUG_LEVEL is the current level do debug output on standard err */ +#define DEBUG_LEVEL (DEB_LEV_ERR) +#if DEBUG_LEVEL > 0 +#define DEBUG(n, fmt, args...) do { if (DEBUG_LEVEL & (n)){fprintf(stderr, "OMX-" fmt, ##args);} } while (0) +#else +#define DEBUG(n, fmt, args...) +#endif + +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_video/queue.c b/host_applications/linux/apps/hello_pi/hello_video/queue.c new file mode 100755 index 0000000..e6ee4b7 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/queue.c @@ -0,0 +1,134 @@ +/** + @file src/queue.c + + Implements a simple LIFO structure used for queueing OMX buffers. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ +*/ + +#include +#include +#include + +#include "queue.h" +#include "omx_comp_debug_levels.h" + +/** Initialize a queue descriptor + * + * @param queue The queue descriptor to initialize. + * The user needs to allocate the queue + */ +void queue_init(queue_t* queue) { + int i; + qelem_t* newelem; + qelem_t* current; + queue->first = malloc(sizeof(qelem_t)); + memset(queue->first, 0, sizeof(qelem_t)); + current = queue->last = queue->first; + queue->nelem = 0; + for (i = 0; iq_forw = newelem; + current = newelem; + } + current->q_forw = queue->first; + + pthread_mutex_init(&queue->mutex, NULL); +} + +/** Deinitialize a queue descriptor + * flushing all of its internal data + * + * @param queue the queue descriptor to dump + */ +void queue_deinit(queue_t* queue) { + int i; + qelem_t* current; + current = queue->first; + for (i = 0; iq_forw; + free(queue->first); + queue->first = current; + } + } + if(queue->first) { + free(queue->first); + queue->first = NULL; + } + pthread_mutex_destroy(&queue->mutex); +} + +/** Enqueue an element to the given queue descriptor + * + * @param queue the queue descritpor where to queue data + * + * @param data the data to be enqueued + */ +void queue(queue_t* queue, void* data) { + if (queue->last->data != NULL) { + return; + } + pthread_mutex_lock(&queue->mutex); + queue->last->data = data; + queue->last = queue->last->q_forw; + queue->nelem++; + pthread_mutex_unlock(&queue->mutex); +} + +/** Dequeue an element from the given queue descriptor + * + * @param queue the queue descriptor from which to dequeue the element + * + * @return the element that has bee dequeued. If the queue is empty + * a NULL value is returned + */ +void* dequeue(queue_t* queue) { + void* data; + if (queue->first->data == NULL) { + return NULL; + } + pthread_mutex_lock(&queue->mutex); + data = queue->first->data; + queue->first->data = NULL; + queue->first = queue->first->q_forw; + queue->nelem--; + pthread_mutex_unlock(&queue->mutex); + + return data; +} + +/** Returns the number of elements hold in the queue + * + * @param queue the requested queue + * + * @return the number of elements in the queue + */ +int getquenelem(queue_t* queue) { + int qelem; + pthread_mutex_lock(&queue->mutex); + qelem = queue->nelem; + pthread_mutex_unlock(&queue->mutex); + return qelem; +} diff --git a/host_applications/linux/apps/hello_pi/hello_video/queue.h b/host_applications/linux/apps/hello_pi/hello_video/queue.h new file mode 100755 index 0000000..745ca70 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/queue.h @@ -0,0 +1,92 @@ +/** + @file src/queue.h + + Implements a simple LIFO structure used for queueing OMX buffers. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ +*/ + +#ifndef __TQUEUE_H__ +#define __TQUEUE_H__ + +#include +/** Maximum number of elements in a queue + */ +#define MAX_QUEUE_ELEMENTS 10 +/** Output port queue element. Contains an OMX buffer header type + */ +typedef struct qelem_t qelem_t; +struct qelem_t{ + qelem_t* q_forw; + void* data; +}; + +/** This structure contains the queue + */ +typedef struct queue_t{ + qelem_t* first; /**< Output buffer queue head */ + qelem_t* last; /**< Output buffer queue tail */ + int nelem; /**< Number of elements in the queue */ + pthread_mutex_t mutex; +} queue_t; + +/** Initialize a queue descriptor + * + * @param queue The queue descriptor to initialize. + * The user needs to allocate the queue + */ +void queue_init(queue_t* queue); + +/** Deinitialize a queue descriptor + * flushing all of its internal data + * + * @param queue the queue descriptor to dump + */ +void queue_deinit(queue_t* queue); + +/** Enqueue an element to the given queue descriptor + * + * @param queue the queue descritpor where to queue data + * + * @param data the data to be enqueued + */ +void queue(queue_t* queue, void* data); + +/** Dequeue an element from the given queue descriptor + * + * @param queue the queue descriptor from which to dequeue the element + * + * @return the element that has bee dequeued. If the queue is empty + * a NULL value is returned + */ +void* dequeue(queue_t* queue); + +/** Returns the number of elements hold in the queue + * + * @param queue the requested queue + * + * @return the number of elements in the queue + */ +int getquenelem(queue_t* queue); + +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_video/tags b/host_applications/linux/apps/hello_pi/hello_video/tags new file mode 100755 index 0000000..eb7bd70 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/tags @@ -0,0 +1,10 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +BIN Makefile /^BIN=hello_video.bin$/;" m +OBJS Makefile /^OBJS=video.o$/;" m +main video.c /^int main (int argc, char **argv)$/;" f signature:(int argc, char **argv) +video_decode_test video.c /^static int video_decode_test(char *filename)$/;" f file: signature:(char *filename) diff --git a/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c new file mode 100755 index 0000000..6f2b6ad --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c @@ -0,0 +1,111 @@ +/** + @file src/tsemaphore.c + + Implements a simple inter-thread semaphore so not to have to deal with IPC + creation and the like. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ +*/ + +#include +#include +#include +#include "tsemaphore.h" +#include "omx_comp_debug_levels.h" + +/** Initializes the semaphore at a given value + * + * @param tsem the semaphore to initialize + * @param val the initial value of the semaphore + * + */ +void tsem_init(tsem_t* tsem, unsigned int val) { + pthread_cond_init(&tsem->condition, NULL); + pthread_mutex_init(&tsem->mutex, NULL); + tsem->semval = val; +} + +/** Destroy the semaphore + * + * @param tsem the semaphore to destroy + */ +void tsem_deinit(tsem_t* tsem) { + pthread_cond_destroy(&tsem->condition); + pthread_mutex_destroy(&tsem->mutex); +} + +/** Decreases the value of the semaphore. Blocks if the semaphore + * value is zero. + * + * @param tsem the semaphore to decrease + */ +void tsem_down(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + while (tsem->semval == 0) { + pthread_cond_wait(&tsem->condition, &tsem->mutex); + } + tsem->semval--; + pthread_mutex_unlock(&tsem->mutex); +} + +/** Increases the value of the semaphore + * + * @param tsem the semaphore to increase + */ +void tsem_up(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + tsem->semval++; + pthread_cond_signal(&tsem->condition); + pthread_mutex_unlock(&tsem->mutex); +} + +/** Reset the value of the semaphore + * + * @param tsem the semaphore to reset + */ +void tsem_reset(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + tsem->semval=0; + pthread_mutex_unlock(&tsem->mutex); +} + +/** Wait on the condition. + * + * @param tsem the semaphore to wait + */ +void tsem_wait(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + pthread_cond_wait(&tsem->condition, &tsem->mutex); + pthread_mutex_unlock(&tsem->mutex); +} + +/** Signal the condition,if waiting + * + * @param tsem the semaphore to signal + */ +void tsem_signal(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + pthread_cond_signal(&tsem->condition); + pthread_mutex_unlock(&tsem->mutex); +} + diff --git a/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h new file mode 100755 index 0000000..4bc1ab2 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h @@ -0,0 +1,87 @@ +/** + @file src/tsemaphore.h + + Implements a simple inter-thread semaphore so not to have to deal with IPC + creation and the like. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ + +*/ + +#ifndef __TSEMAPHORE_H__ +#define __TSEMAPHORE_H__ + +/** The structure contains the semaphore value, mutex and green light flag + */ +typedef struct tsem_t{ + pthread_cond_t condition; + pthread_mutex_t mutex; + unsigned int semval; +}tsem_t; + +/** Initializes the semaphore at a given value + * + * @param tsem the semaphore to initialize + * + * @param val the initial value of the semaphore + */ +void tsem_init(tsem_t* tsem, unsigned int val); + +/** Destroy the semaphore + * + * @param tsem the semaphore to destroy + */ +void tsem_deinit(tsem_t* tsem); + +/** Decreases the value of the semaphore. Blocks if the semaphore + * value is zero. + * + * @param tsem the semaphore to decrease + */ +void tsem_down(tsem_t* tsem); + +/** Increases the value of the semaphore + * + * @param tsem the semaphore to increase + */ +void tsem_up(tsem_t* tsem); + +/** Reset the value of the semaphore + * + * @param tsem the semaphore to reset + */ +void tsem_reset(tsem_t* tsem); + +/** Wait on the condition. + * + * @param tsem the semaphore to wait + */ +void tsem_wait(tsem_t* tsem); + +/** Signal the condition,if waiting + * + * @param tsem the semaphore to signal + */ +void tsem_signal(tsem_t* tsem); + +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h b/host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h new file mode 100755 index 0000000..b28b07f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h @@ -0,0 +1,73 @@ +/** + @file test/components/common/user_debug_levels.h + + Define the level of debug prints on standard err. The different levels can + be composed with binary OR. + The debug levels defined here belong to the test applications + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-07-16 09:39:31 +0200 (Wed, 16 Jul 2008) $ + Revision $Rev: 577 $ + Author $Author: gsent $ + +*/ + + +/** Remove all debug output lines + */ +#define DEB_LEV_NO_OUTPUT 0 +/** Messages explaing the reason of critical errors + */ +#define DEB_LEV_ERR 1 +/** Messages showing values related to the test and the component/s used + */ +#define DEB_LEV_PARAMS 2 +/** Messages representing steps in the execution. These are the simple messages, because + * they avoid iterations + */ +#define DEB_LEV_SIMPLE_SEQ 4 +/** Messages representing steps in the execution. All the steps are described, + * also with iterations. With this level of output the performance is + * seriously compromised + */ +#define DEB_LEV_FULL_SEQ 8 +/** Messages that indicate the beginning and the end of a function. + * It can be used to trace the execution + */ +#define DEB_LEV_FUNCTION_NAME 16 + +/** Messages that are the default test application output. These message should be + * shown every time + */ +#define DEFAULT_MESSAGES 32 + +/** All the messages - max value + */ +#define DEB_ALL_MESS 255 + + +/** \def DEBUG_LEVEL is the current level do debug output on standard err */ +#define DEBUG_LEVEL (DEB_LEV_ERR | DEFAULT_MESSAGES) +#if DEBUG_LEVEL > 0 +#define DEBUG(n, args...) do { if (DEBUG_LEVEL & (n)){fprintf(stderr, args);} } while (0) +#else +#define DEBUG(n, args...) +#endif + diff --git a/host_applications/linux/apps/hello_pi/hello_video/video.c b/host_applications/linux/apps/hello_pi/hello_video/video.c new file mode 100755 index 0000000..3275d57 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/video.c @@ -0,0 +1,1055 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video deocode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include +#include "video.h" +/* +#define VERSIONMAJOR 1 +#define VERSIONMINOR 1 +#define VERSIONREVISION 0 +#define VERSIONSTEP 0 + +#define OMX_VERSION ((VERSIONSTEP<<24) | (VERSIONREVISION<<16) | (VERSIONMINOR<<8) | VERSIONMAJOR) + +#define OMX_INIT_STRUCTURE(a) \ +memset(&(a), 0, sizeof(a)); \ +(a).nSize = sizeof(a); \ +(a).nVersion.nVersion = OMX_VERSION; \ +(a).nVersion.s.nVersionMajor = VERSIONMAJOR; \ +(a).nVersion.s.nVersionMinor = VERSIONMINOR; \ +(a).nVersion.s.nRevision = VERSIONREVISION; \ +(a).nVersion.s.nStep = VERSIONSTEP + +static void setHeader(OMX_PTR header, OMX_U32 size) { + OMX_VERSIONTYPE* ver = (OMX_VERSIONTYPE*)(header + sizeof(OMX_U32)); + *((OMX_U32*)header) = size; + + ver->s.nVersionMajor = VERSIONMAJOR; + ver->s.nVersionMinor = VERSIONMINOR; + ver->s.nRevision = VERSIONREVISION; + ver->s.nStep = VERSIONSTEP; +} +*/ + +/* +void allocateBuffer(int index, int actualBuffer, int width, int height) +{ + int i, err; + tbm_bo_handle handle_bo; + int y_size, uv_size; + + y_size = ALIGN(width, 16) * ALIGN(height, 16); + uv_size = y_size /2; + + for (i = 0; i < actualBuffer; i++) { + + unsigned char *tmp; + + //tmp = (MMVideoBuffer*) malloc(sizeof(MMVideoBuffer)); + //tmp->handle.bo[0] = tbm_bo_alloc(hBufmgr, y_size, TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp->handle.bo[0], TBM_DEVICE_CPU); + tmp = handle_bo.ptr; + //handle_bo = tbm_bo_get_handle(tmp->handle.bo[0], TBM_DEVICE_MM); + //tmp->handle.dmabuf_fd[0] = handle_bo.u32; + //tmp->size[0] = y_size; + + + if (index == 1) { + tmp->handle.bo[1] = tbm_bo_alloc(hBufmgr, uv_size, TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_CPU); + tmp->data[1] = handle_bo.ptr; + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_MM); + tmp->handle.dmabuf_fd[1] = handle_bo.u32; + tmp->size[1] = uv_size; + } + + if (index == 0) { + err = OMX_UseBuffer(appPriv->videodechandle, &pInBuffer[i], 0, NULL, y_size, tmp); + printf( "index : %d -> OMX_UseBuffer :%x\n", index, err); + queue(pInBufQueue, pInBuffer[i]); + //queue(pInBufQueue, tmp); + } else if (index == 1) { + err = OMX_UseBuffer(appPriv->videodechandle, &pOutBuffer[i], 1, NULL, y_size + uv_size, tmp); + printf( "index : %d -> OMX_UseBuffer :%i\n", index, err); + queue(pOutBufQueue, pOutBuffer[i]); + } + + } + +} +*/ + +#define COMPONENT_NAME_BASE "OMX.broadcom.video_decode" +#define BASE_ROLE "video_decoder.avc" + +OMX_VIDEO_PARAM_PORTFORMATTYPE format; +OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; +COMPONENT_T *video_decode = NULL; +COMPONENT_T *list[5]; +TUNNEL_T tunnel[4]; +ILCLIENT_T *client; +FILE *in; +int status = 0; +unsigned int data_len = 0; +int err; +int actualInBufferCount; +int actualOutBufferCount; +int nInBufferSize, nOutBufferSize; +int nInBufferAlignment, nOutBufferAlignment; +OMX_BUFFERHEADERTYPE *pInBuffer[20], *pOutBuffer[16]; +OMX_STRING full_component_name; +appPrivateType* appPriv; +tbm_bufmgr hBufmgr; +int drm_fd; +queue_t* pInBufQueue; +queue_t* pOutBufQueue; + +OMX_CALLBACKTYPE videodeccallbacks = { + .EventHandler = videodecEventHandler, + .EmptyBufferDone = videodecEmptyBufferDone, + .FillBufferDone = videodecFillBufferDone + }; +/* +static void setHeader(OMX_PTR header, OMX_U32 size) { + OMX_VERSIONTYPE* ver = (OMX_VERSIONTYPE*)(header + sizeof(OMX_U32)); + *((OMX_U32*)header) = size; + + ver->s.nVersionMajor = VERSIONMAJOR; + ver->s.nVersionMinor = VERSIONMINOR; + ver->s.nRevision = VERSIONREVISION; + ver->s.nStep = VERSIONSTEP; +} +*/ +OMX_ERRORTYPE test_OMX_ComponentNameEnum() { + char * name; + int index; + + OMX_ERRORTYPE err = OMX_ErrorNone; + + printf("GENERAL TEST %s\n", __func__); + + name = malloc(OMX_MAX_STRINGNAME_SIZE); + index = 0; + while(1) { + err = OMX_ComponentNameEnum (name, OMX_MAX_STRINGNAME_SIZE, index); + if ((name != NULL) && (err == OMX_ErrorNone)) { + printf("component %i is %s\n", index, name); + } else break; + if (err != OMX_ErrorNone) break; + index++; + } + free(name); + name = NULL; + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; +} + +OMX_ERRORTYPE test_OMX_RoleEnum(OMX_STRING component_name) { + OMX_U32 no_of_roles; + OMX_U8 **string_of_roles; + OMX_ERRORTYPE err = OMX_ErrorNone; + int index; + + printf("GENERAL TEST %s\n", __func__); + printf("Getting roles of %s. Passing Null first...\n", component_name); + err = OMX_GetRolesOfComponent(component_name, &no_of_roles, NULL); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the number of roles of the given component\n"); + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; + } + printf("The number of roles for the component %s is: %i\n", component_name, (int)no_of_roles); + + if(no_of_roles == 0) { + printf("The Number or roles is 0.\nThe component selected is not correct for the purpose of this test.\nExiting...\n"); + err = OMX_ErrorInvalidComponentName; + } else { + string_of_roles = malloc(no_of_roles * sizeof(OMX_STRING)); + for (index = 0; index < no_of_roles; index++) { + *(string_of_roles + index) = malloc(no_of_roles * OMX_MAX_STRINGNAME_SIZE); + } + printf("...then buffers\n"); + + err = OMX_GetRolesOfComponent(component_name, &no_of_roles, string_of_roles); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the roles of the given component\n"); + } else if(string_of_roles != NULL) { + for (index = 0; index < no_of_roles; index++) { + printf("The role %i for the component: %s \n", (index + 1), *(string_of_roles+index)); + } + } else { + printf("role string is NULL!!! Exiting...\n"); + err = OMX_ErrorInvalidComponentName; + } + } + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; +} + +OMX_ERRORTYPE test_OMX_ComponentEnumByRole(OMX_STRING role_name) { + OMX_U32 no_of_comp_per_role; + OMX_U8 **string_of_comp_per_role; + OMX_ERRORTYPE err; + int index; + + printf("GENERAL TEST %s\n", __func__); + string_of_comp_per_role = malloc (10 * sizeof(OMX_STRING)); + for (index = 0; index < 10; index++) { + string_of_comp_per_role[index] = malloc(OMX_MAX_STRINGNAME_SIZE); + } + + printf("Getting number of components per role for %s\n", role_name); + + err = OMX_GetComponentsOfRole(role_name, &no_of_comp_per_role, NULL); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the number of components of a given role\n"); + printf( "GENERAL TEST %s result %i\n", __func__, err); + return err; + } + printf("Number of components per role for %s is %i\n", role_name, (int)no_of_comp_per_role); + + err = OMX_GetComponentsOfRole(role_name, &no_of_comp_per_role, string_of_comp_per_role); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the components of a given role\n"); + printf( "GENERAL TEST %s result %i\n",__func__, err); + return err; + } + + printf(" The components are:\n"); + for (index = 0; index < no_of_comp_per_role; index++) { + printf("%s\n", string_of_comp_per_role[index]); + } + for (index = 0; index<10; index++) { + if(string_of_comp_per_role[index]) { + free(string_of_comp_per_role[index]); + string_of_comp_per_role[index] = NULL; + } + } + + if(string_of_comp_per_role) { + free(string_of_comp_per_role); + string_of_comp_per_role = NULL; + } + printf("GENERAL TEST %s result OMX_ErrorNone\n", __func__); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE test_OpenClose(OMX_STRING component_name) { + OMX_ERRORTYPE err = OMX_ErrorNone; + + printf("GENERAL TEST %s\n",__func__); + err = OMX_GetHandle(&appPriv->videodechandle, component_name, NULL /*appPriv */, &videodeccallbacks); + if(err != OMX_ErrorNone) { + printf("No component found\n"); + } else { + err = OMX_FreeHandle(appPriv->videodechandle); + } + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; +} + +void allocateBuffer(int index, int actualBuffer, int width, int height, int aligned_size) +{ + int i, err; + tbm_bo_handle handle_bo; + int y_size, uv_size; + int size; + + y_size = ALIGN(width + 128, 16) * ALIGN(height + 128, 16); + uv_size = y_size /2; + size = y_size + uv_size; + + for (i = 0; i < actualBuffer; i++) { + + void *tmp; + unsigned char *buf; + + + + if (index == 130) { + buf = (unsigned char*) malloc (sizeof(unsigned char) * aligned_size); + err = OMX_UseBuffer(appPriv->videodechandle, &pInBuffer[i], 130, NULL, ALIGN(aligned_size, 16), buf); + printf("index : %d -> OMX_UseBuffer : %p, %x\n", index, pInBuffer[i], err); + //queue(pInBufQueue, pInBuffer[i]); + } else if (index == 131) { + tmp = tbm_bo_alloc(hBufmgr, ALIGN(aligned_size, 16), TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp, TBM_DEVICE_CPU); + buf = handle_bo.ptr; + + err = OMX_UseBuffer(appPriv->videodechandle, &pOutBuffer[i], 131, NULL, ALIGN(aligned_size, 16), buf); + printf("index : %d -> OMX_UseBuffer : %p, %x\n", index, pOutBuffer[i], err); + //queue(pOutBufQueue, pOutBuffer[i]); + } else + printf("invalid port\n"); +/* + + if (index == 1) { + tmp->handle.bo[1] = tbm_bo_alloc(hBufmgr, uv_size, TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_CPU); + tmp->data[1] = handle_bo.ptr; + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_MM); + tmp->handle.dmabuf_fd[1] = handle_bo.u32; + tmp->size[1] = uv_size; + } + + if (index == 0) { + err = OMX_UseBuffer(appPriv->videodechandle, &pInBuffer[i], 0, NULL, y_size, tmp); + DEBUG(DEFAULT_MESSAGES, "index : %d -> OMX_UseBuffer :%x\n", index, err); + queue(pInBufQueue, pInBuffer[i]); + //queue(pInBufQueue, tmp); + } else if (index == 1) { + err = OMX_UseBuffer(appPriv->videodechandle, &pOutBuffer[i], 1, NULL, y_size + uv_size, tmp); + DEBUG(DEFAULT_MESSAGES, "index : %d -> OMX_UseBuffer :%i\n", index, err); + queue(pOutBufQueue, pOutBuffer[i]); + } +*/ + } + +} +/* +void deallocateBuffer(int index, int actualBuffer) +{ + int i, err; + MMVideoBuffer *pTmp; + + for (i = 0; i < actualBuffer; i++) { + if (index == 0) { + //pTmp = (MMVideoBuffer*)dequeue(pInBufQueue); + pTmp = pInBuffer[i]; + } else if (index == 1) { + //pTmp = (MMVideoBuffer*)dequeue(pOutBufQueue); + pTmp = pOutBuffer[i]; + } + + tbm_bo_unref(pTmp->handle.bo[0]); + if (index == 1) + tbm_bo_unref(pTmp->handle.bo[1]); + + err = OMX_FreeBuffer (appPriv->videodechandle, index, pTmp); + DEBUG(DEFAULT_MESSAGES, "%d port %d buffer free : %d\n", index, i, err); + } +} +*/ +const char * omx_state_to_string(OMX_STATETYPE state) +{ + switch (state) + { + case OMX_StateInvalid: return "Invalid"; break; + case OMX_StateLoaded: return "Loaded"; break; + case OMX_StateIdle: return "Idle"; break; + case OMX_StateExecuting: return "Executing"; break; + case OMX_StatePause: return "Pause"; break; + case OMX_StateWaitForResources: return "Wait for resources"; break; + default: return "Bad state"; break; + } +} + +void wait_for(appPrivateType *app, OMX_STATETYPE state) +{ + while (app->state != state) + { + printf("wait for %s \n", omx_state_to_string(state)); + tsem_down(app->stateSem); + if (app->state != state) { + printf("we got an event, but still waiting for %s \n", omx_state_to_string(state)); + } + + } +} + +unsigned int FromByteStream2NalUnit(FILE *ByteStream, unsigned char *Nal) +{ + unsigned int NalLeng=0; + + unsigned char Val, ZeroCount, i; + ZeroCount = 0; + if(feof(ByteStream)) + return 0; + Val = fgetc(ByteStream); + while(!Val) + { + if(ZeroCount == 3) + break; + ZeroCount++; + Val = fgetc(ByteStream); + } + + if( (ZeroCount != 3) || (Val != 1) ) + { + for(i=0;i 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + while ((i % 16) != 0) { + printf(" "); + i++; + } + printf(" %s\n", buff); +} + +static int video_decode_test(char *filename) +{ + int i; + int offset; + int data_read; + int fd; + + memset(list, 0, sizeof(list)); + + if((in = fopen(filename, "rb")) == NULL) + return -2; + if ((fd = open(filename, O_RDONLY)) == -1) + return -2; +/* + if((client = ilclient_init()) == NULL) + { + fclose(in); + return -3; + } +*/ + /* Initialize application private data */ + appPriv = malloc(sizeof(appPrivateType)); + appPriv->decoderEventSem = malloc(sizeof(tsem_t)); + appPriv->stateSem = malloc(sizeof(tsem_t)); + appPriv->state = OMX_StateInvalid; + + appPriv->eofSem = malloc(sizeof(tsem_t)); + tsem_init(appPriv->decoderEventSem, 0); + tsem_init(appPriv->stateSem, 0); + tsem_init(appPriv->eofSem, 0); + + printf("Init the OMX Core\n"); + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return -4; + } + printf("Omx core is initialized \n"); + +/* + // create video_decode + if (ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; +*/ + + printf("------------------------------------\n"); + test_OMX_ComponentNameEnum(); + printf("------------------------------------\n"); + test_OMX_RoleEnum(COMPONENT_NAME_BASE); + printf("------------------------------------\n"); + test_OMX_ComponentEnumByRole(BASE_ROLE); + printf("------------------------------------\n"); + test_OpenClose(COMPONENT_NAME_BASE); + printf("------------------------------------\n"); + + full_component_name = malloc(sizeof(char*) * OMX_MAX_STRINGNAME_SIZE); + strcpy(full_component_name, COMPONENT_NAME_BASE); + printf("The component selected for decoding is %s\n", full_component_name); + + err = OMX_GetHandle(&appPriv->videodechandle, full_component_name, NULL, &videodeccallbacks); + if(err != OMX_ErrorNone){ + printf("No video decoder component found. Exiting...\n"); + exit(1); + } else { + printf("Found The component for decoding is %s\n", full_component_name); + } + + /** sending command to video decoder component to go to idle state */ + //pInBuffer1 = pInBuffer2 = NULL; + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandStateSet, OMX_StateIdle, NULL); + + //wait_for(appPriv, OMX_StateIdle); + printf("state changed to Idle :%i\n", err); + + hBufmgr = tbm_bufmgr_init(drm_fd); + if(hBufmgr == NULL){ + printf("TBM initialization failed\n"); + } + + + OMX_PARAM_PORTDEFINITIONTYPE port_def; + memset(&port_def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + port_def.nVersion.nVersion = OMX_VERSION; + port_def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + port_def.nPortIndex = 130; + + err = OMX_GetParameter (appPriv->videodechandle, OMX_IndexParamPortDefinition, &port_def); + printf( "OMX_GetParameter :%x, input buffer count : %d, nBufferSize : %d, nBufferAlignment : %d\n", err, port_def.nBufferCountActual, port_def.nBufferSize, port_def.nBufferAlignment); + actualInBufferCount = port_def.nBufferCountActual; + nInBufferSize = port_def.nBufferSize; + nInBufferAlignment = port_def.nBufferAlignment; + + port_def.nPortIndex = 131; + err = OMX_GetParameter (appPriv->videodechandle, OMX_IndexParamPortDefinition, &port_def); + printf( "OMX_GetParameter :%x, output buffer count : %d, nBufferSize : %d, nBufferAlignment : %d\n", err, port_def.nBufferCountActual, port_def.nBufferSize, port_def.nBufferAlignment); + actualOutBufferCount = port_def.nBufferCountActual; + nOutBufferSize = port_def.nBufferSize; + nOutBufferAlignment = port_def.nBufferAlignment; + + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortDisable, 130, NULL); + printf( "Input port OMX_CommandPortDisable : %x\n", err); + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortDisable, 131, NULL); + printf( "Output port OMX_CommandPortDisable : %x\n", err); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + err = OMX_SetParameter(appPriv->videodechandle, OMX_IndexParamVideoPortFormat, &format); + printf("Input OMX_IndexParamVideoPortFormat: %x\n", err); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 131; + err = OMX_SetParameter(appPriv->videodechandle, OMX_IndexParamVideoPortFormat, &format); + printf("Output OMX_IndexParamVideoPortFormat: %x\n", err); + + + pInBufQueue = calloc(1,sizeof(queue_t)); + pOutBufQueue = calloc(1,sizeof(queue_t)); + + queue_init(pInBufQueue); + queue_init(pOutBufQueue); + + //err = ilclient_change_component_state(video_decode, OMX_StateExecuting); + //printf("ilclient_change_component_state -> OMX_StateExecuting : err : %d\n", err); + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortEnable, 130, NULL); + printf( "Input port OMX_CommandPortEnable : %x\n", err); + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortEnable, 131, NULL); + printf( "Output port OMX_CommandPortEnable : %x\n", err); + + allocateBuffer(130, actualInBufferCount, 1280, 720, nInBufferSize); + allocateBuffer(131, actualOutBufferCount, 1280, 720, nOutBufferSize); + + wait_for(appPriv, OMX_StateIdle); + + /** sending command to video decoder component to go to executing state */ + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandStateSet, OMX_StateExecuting, NULL); + printf("send Excuting :%d\n", err); + + wait_for(appPriv, OMX_StateExecuting); + printf("Excuting state\n"); + + for (i = 0; i < actualOutBufferCount; i++) { + err = OMX_FillThisBuffer(appPriv->videodechandle, pOutBuffer[i]); + printf( "FillThisBuffer : %p, %x\n",pOutBuffer[i], err); + } + + for (i = 0; i < actualInBufferCount; i++) { + //pthread_mutex_lock(&mutex); + unsigned char *pTmp; + offset = 0; + + pTmp = pInBuffer[i]->pBuffer; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 1; + + data_read = FromByteStream2NalUnit(in, pTmp + offset); + + pInBuffer[i]->nFilledLen = data_read + offset; + pInBuffer[i]->nOffset = 0; + hex_dump("src0", pInBuffer[i]->pBuffer, 16); + + //pthread_mutex_unlock(&mutex); + + err = OMX_EmptyThisBuffer(appPriv->videodechandle, pInBuffer[i]); + printf("EmptyThisBuffer %x, %d\n", pInBuffer[i]->pBuffer, data_read + offset); + usleep(100000); + } + + tsem_down(appPriv->decoderEventSem); + printf("port setting changed\n"); + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortDisable, 131, NULL); + printf("Sending Port Disable Command : %d\n", err); +#if 0 + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0 && + ilclient_enable_port_buffers(video_decode, 131, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf, *outbuf; + int port_settings_changed = 0; + int first_packet = 1; + + + while ((outbuf = ilclient_get_output_buffer(video_decode, 131, 1)) != NULL) + { + unsigned char *tmp = outbuf->pBuffer; + + err = OMX_FillThisBuffer(ILC_GET_HANDLE(video_decode), outbuf); + if (err != OMX_ErrorNone) + { + printf("Failed to call OMX_FillThisBuffer\n"); + status = -6; + break; + } + printf("call OMX_FillThisBuffer : %d\n", err); + } + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + printf( "data_len :%i\n", data_len); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + //err = ilclient_change_component_state(video_decode, OMX_StateExecuting); + //printf("ilclient_change_component_state -> OMX_StateExecuting : err : %d\n", err); + printf("port setting changed \n"); + port_def.nPortIndex = 131; + err = OMX_GetParameter (ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port_def); + printf( "OMX_GetParameter :%i\n", err); + actualOutBufferCount = port_def.nBufferCountActual; + printf("outbuf count :%d\n", actualOutBufferCount); + } + + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + printf("Failed to call OMX_EmptyThisBuffer\n"); + status = -6; + break; + } + } +/* + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to video_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + ilclient_change_component_state(video_render, OMX_StateExecuting); + } + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // wait for EOS from render + ilclient_wait_for_event(video_render, OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0, + ILCLIENT_BUFFER_FLAG_EOS, -1); + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); +*/ + } +#endif + fclose(in); + + //ilclient_disable_tunnel(tunnel); + //ilclient_disable_tunnel(tunnel+1); + //ilclient_disable_tunnel(tunnel+2); + //ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + //ilclient_teardown_tunnels(tunnel); + + //ilclient_state_transition(list, OMX_StateIdle); + //ilclient_state_transition(list, OMX_StateLoaded); + + //ilclient_cleanup_components(list); + + OMX_Deinit(); + + //ilclient_destroy(client); + return status; +} + +int main (int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(1); + } + bcm_host_init(); + return video_decode_test(argv[1]); +} + + +OMX_ERRORTYPE videodecEventHandler( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_EVENTTYPE eEvent, + OMX_OUT OMX_U32 Data1, + OMX_OUT OMX_U32 Data2, + OMX_OUT OMX_PTR pEventData) { + + OMX_ERRORTYPE err = OMX_ErrorNone; + + printf("Hi there, I am in the %s callback\n", __func__); + if(eEvent == OMX_EventCmdComplete) { + if (Data1 == OMX_CommandStateSet) { + appPriv->state = (int)Data2; + printf( "State changed in "); + switch ((int)Data2) { + case OMX_StateInvalid: + printf( "OMX_StateInvalid\n"); + break; + case OMX_StateLoaded: + printf( "OMX_StateLoaded\n"); + break; + case OMX_StateIdle: + printf( "OMX_StateIdle\n"); + break; + case OMX_StateExecuting: + printf( "OMX_StateExecuting\n"); + break; + case OMX_StatePause: + printf( "OMX_StatePause\n"); + break; + case OMX_StateWaitForResources: + printf( "OMX_StateWaitForResources\n"); + break; + default: + printf( "default\n"); + break; + + } + //tsem_up(appPriv->decoderEventSem); + tsem_up(appPriv->stateSem); + printf( "tsem up decoderEventSem\n"); + } else if (OMX_CommandPortEnable || OMX_CommandPortDisable) { + printf( "In %s Received Port Enable/Disable Event\n",__func__); + tsem_up(appPriv->decoderEventSem); + printf( "tsem up decoderEventSem\n"); + } + } else if(eEvent == OMX_EventPortSettingsChanged) { + printf( "\n port settings change event handler in %s %i\n", __func__, Data2); + if(Data2 == 0) { + +/* + if(!flagSetupTunnel) { + err = setPortParameters(); + printf("set port param %i\n",err); + printf("tsem_up\n"); + tsem_up(appPriv->decoderEventSem); + if(flagIsColorConvRequested == 1) { + pOutBufferColorConv1 = pOutBufferColorConv2 = NULL; + err = OMX_SendCommand(appPriv->colorconv_handle, OMX_CommandStateSet, OMX_StateIdle, NULL); +#if 1 + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv1, 0, NULL, buffer_out_size, pOutBuffer1->pBuffer); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv2, 0, NULL, buffer_out_size, pOutBuffer2->pBuffer); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } +#else + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv1, 0, NULL, buffer_out_size, pOutBuffer1->data[0]); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv2, 0, NULL, buffer_out_size, pOutBuffer2->data[0]); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } +#endif + omx_colorconvPortDefinition.nPortIndex = 1; + setHeader(&omx_colorconvPortDefinition, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + err = OMX_GetParameter(appPriv->colorconv_handle, OMX_IndexParamPortDefinition, &omx_colorconvPortDefinition); + outbuf_colorconv_size = omx_colorconvPortDefinition.nBufferSize; + printf( " outbuf_colorconv_size : %d \n", (int)outbuf_colorconv_size); + + err = OMX_AllocateBuffer(appPriv->colorconv_handle, &pOutBufferColorConv1, 1, NULL, outbuf_colorconv_size); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to allocate buffer in color conv\n"); + exit(1); + } + err = OMX_AllocateBuffer(appPriv->colorconv_handle, &pOutBufferColorConv2, 1, NULL, outbuf_colorconv_size); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to allocate buffer in colro conv\n"); + exit(1); + } + + printf( "Before locking on idle wait semaphore\n"); + tsem_down(appPriv->colorconvEventSem); + printf( "color conv Sem free\n"); + + } + } + */ + } + } else if(eEvent == OMX_EventBufferFlag) { + printf( "In %s OMX_BUFFERFLAG_EOS\n", __func__); + if((int)Data2 == OMX_BUFFERFLAG_EOS) { + tsem_up(appPriv->eofSem); + } + } else { + printf( "Param1 is %x\n", (int)Data1); + printf( "Param2 is %x\n", (int)Data2); + } + + return err; +} + +OMX_ERRORTYPE videodecEmptyBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) { + + OMX_ERRORTYPE err; +/* + int data_read; + static int iBufferDropped=0; + unsigned char *pTmp; + int offset = 0; + + printf( "Hi there, I am in the %s callback.\n", __func__); + + pthread_mutex_lock(&mutex); + + pTmp = pBuffer->pBuffer; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 1; + + data_read = FromByteStream2NalUnit(fd, pTmp+offset); + usleep(3000); + pBuffer->nFilledLen = data_read+offset; + pBuffer->nOffset = 0; + pthread_mutex_unlock(&mutex); + + if (data_read <= 0) { + printf( "In the %s no more input data available\n", __func__); + iBufferDropped++; + if(iBufferDropped>=2) { + bEOS=OMX_TRUE; + return OMX_ErrorNone; + } + pBuffer->nFilledLen=0; + pBuffer->nFlags = OMX_BUFFERFLAG_EOS; + memset(pBuffer->pBuffer, 0x0, 640*480); + err = OMX_EmptyThisBuffer(hComponent, pBuffer); + printf( "EmptyThisBuffer for EOS %d\n", err); + hex_dump("src2", pBuffer->pBuffer, 16); + return OMX_ErrorNone; + } + + if(!bEOS) { + printf( "EmptyThisBuffer %x, %d\n", (int)pBuffer, offset); + err = OMX_EmptyThisBuffer(hComponent, pBuffer); + hex_dump("src1", pBuffer->pBuffer, 16); + } else { + printf( "In %s Dropping Empty This buffer to Audio Dec\n", __func__); + } +*/ + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE videodecFillBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) { + + OMX_ERRORTYPE err; +/* + OMX_STATETYPE eState; + + printf( "Hi there, I am in the %s callback.\n", __func__); + if(pBuffer != NULL) { + if(!bEOS) { + if(flagIsColorConvRequested && (!flagSetupTunnel)) { + OMX_GetState(appPriv->colorconv_handle,&eState); + if(eState == OMX_StateExecuting || eState == OMX_StatePause) { + if(pInBufferColorConv1->pBuffer == pBuffer->pBuffer) { + pInBufferColorConv1->nFilledLen = pBuffer->nFilledLen; + err = OMX_EmptyThisBuffer(appPriv->colorconv_handle, pInBufferColorConv1); + } else { + pInBufferColorConv2->nFilledLen = pBuffer->nFilledLen; + err = OMX_EmptyThisBuffer(appPriv->colorconv_handle, pInBufferColorConv2); + } + if(err != OMX_ErrorNone) { + printf( "In %s Error %08x Calling FillThisBuffer\n", __func__,err); + } + } else { + err = OMX_FillThisBuffer(hComponent, pBuffer); + } + } else if((pBuffer->nFilledLen > 0) && (!flagSetupTunnel)) { + printf( "nFilledLen :%d \n", pBuffer->nFilledLen); + //fwrite(pBuffer->pBuffer, 1, pBuffer->nFilledLen, outfile); + decoder_output_dump(pBuffer->pBuffer); + pBuffer->nFilledLen = 0; + } else { + printf( "In %s Empty buffer in Else\n", __func__); + printf( "nFilledLen :%d \n", pBuffer->nFilledLen); + } + if(pBuffer->nFlags == OMX_BUFFERFLAG_EOS) { + printf( "In %s: eos=%x Calling Empty This Buffer\n", __func__, (int)pBuffer->nFlags); + bEOS = OMX_TRUE; + } + if(!bEOS && !flagIsColorConvRequested && (!flagSetupTunnel)) { + err = OMX_FillThisBuffer(hComponent, pBuffer); + printf( "FillThisBuffer :%d \n", err); + } + } else { + printf( "In %s: eos=%x Dropping Empty This Buffer\n", __func__,(int)pBuffer->nFlags); + } + } else { + printf( "Ouch! In %s: had NULL buffer to output...\n", __func__); + } +*/ + return OMX_ErrorNone; +} diff --git a/host_applications/linux/apps/hello_pi/hello_video/video.h b/host_applications/linux/apps/hello_pi/hello_video/video.h new file mode 100755 index 0000000..056db03 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/video.h @@ -0,0 +1,40 @@ +#include +#include +#include + +#include "queue.h" +#include "bcm_host.h" +#include "ilclient.h" +#include +#include +#include +#include "tsemaphore.h" + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +typedef struct appPrivateType{ + tsem_t* stateSem; + tsem_t* decoderEventSem; + tsem_t* eofSem; + OMX_HANDLETYPE videodechandle; + OMX_STATETYPE state; +} appPrivateType; + +/* Callback prototypes for video decoder */ +OMX_ERRORTYPE videodecEventHandler( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_EVENTTYPE eEvent, + OMX_OUT OMX_U32 Data1, + OMX_OUT OMX_U32 Data2, + OMX_OUT OMX_PTR pEventData); + +OMX_ERRORTYPE videodecEmptyBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); + +OMX_ERRORTYPE videodecFillBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt new file mode 100755 index 0000000..d7fb059 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_videocube.bin) +set(SRCS triangle.c video.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/Makefile b/host_applications/linux/apps/hello_pi/hello_videocube/Makefile new file mode 100755 index 0000000..60d4b0a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/Makefile @@ -0,0 +1,7 @@ +OBJS=triangle.o video.o +BIN=hello_videocube.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + + diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/README.md b/host_applications/linux/apps/hello_pi/hello_videocube/README.md new file mode 100755 index 0000000..36fafcf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/README.md @@ -0,0 +1,4 @@ +hello_videocube +=============== + +Sample for Raspberry Pi that uses egl_render to display video on an animated cube. \ No newline at end of file diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h b/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h new file mode 100755 index 0000000..7dd30a9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f +}; + diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c new file mode 100755 index 0000000..42a78a4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c @@ -0,0 +1,481 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "cube_texture_and_coords.h" + +#include "triangle.h" +#include + + +#define PATH "./" + +#define IMAGE_SIZE_WIDTH 1920 +#define IMAGE_SIZE_HEIGHT 1080 + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; +} CUBE_STATE_T; + +static void init_ogl(CUBE_STATE_T *state); +static void init_model_proj(CUBE_STATE_T *state); +static void reset_model(CUBE_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); +static void redraw_scene(CUBE_STATE_T *state); +static void update_model(CUBE_STATE_T *state); +static void init_textures(CUBE_STATE_T *state); +static void exit_func(void); +static volatile int terminate; +static CUBE_STATE_T _state, *state=&_state; + +static void* eglImage = 0; +static pthread_t thread1; + + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + //EGL_SAMPLES, 4, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + + // get an appropriate EGL frame buffer configuration + // this uses a BRCM extension that gets the closest match, rather than standard which returns anything that matches + result = eglSaneChooseConfigBRCM(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); + assert(state->context!=EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + + // Set background color and clear buffers + glClearColor(0.15f, 0.25f, 0.35f, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glMatrixMode(GL_MODELVIEW); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(CUBE_STATE_T *state) +{ + float nearp = 1.0f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_BYTE, 0, quadx ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(CUBE_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.f, 0.f, -50.f); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 40.f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void update_model(CUBE_STATE_T *state) +{ + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 120.0f) + distance = 120.f; + else if (distance <= 40.0f) + distance = 40.0f; + + return distance; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void redraw_scene(CUBE_STATE_T *state) +{ + // Start with a clear screen + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Need to rotate textures - do this by rotating each cube face + glRotatef(270.f, 0.f, 0.f, 1.f ); // front face normal along z axis + + // draw first 4 vertices + glDrawArrays( GL_TRIANGLE_STRIP, 0, 4); + + // same pattern for other 5 faces - rotation chosen to make image orientation 'nice' + glRotatef(90.f, 0.f, 0.f, 1.f ); // back face normal along z axis + glDrawArrays( GL_TRIANGLE_STRIP, 4, 4); + + glRotatef(90.f, 1.f, 0.f, 0.f ); // left face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 8, 4); + + glRotatef(90.f, 1.f, 0.f, 0.f ); // right face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 12, 4); + + glRotatef(270.f, 0.f, 1.f, 0.f ); // top face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 16, 4); + + glRotatef(90.f, 0.f, 1.f, 0.f ); // bottom face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 20, 4); + + eglSwapBuffers(state->display, state->surface); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void init_textures(CUBE_STATE_T *state) +{ + //// load three texture buffers but use them on six OGL|ES texture surfaces + glGenTextures(1, &state->tex); + + glBindTexture(GL_TEXTURE_2D, state->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + + /* Create EGL Image */ + eglImage = eglCreateImageKHR( + state->display, + state->context, + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)state->tex, + 0); + + if (eglImage == EGL_NO_IMAGE_KHR) + { + printf("eglCreateImageKHR failed.\n"); + exit(1); + } + + // Start rendering + pthread_create(&thread1, NULL, video_decode_test, eglImage); + + // setup overall texture environment + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + + // Bind texture surface to current vertices + glBindTexture(GL_TEXTURE_2D, state->tex); +} +//------------------------------------------------------------------------------ + +static void exit_func(void) +// Function to be passed to atexit(). +{ + if (eglImage != 0) + { + if (!eglDestroyImageKHR(state->display, (EGLImageKHR) eglImage)) + printf("eglDestroyImageKHR failed."); + } + + // clear screen + glClear( GL_COLOR_BUFFER_BIT ); + eglSwapBuffers(state->display, state->surface); + + // Release OpenGL resources + eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroySurface( state->display, state->surface ); + eglDestroyContext( state->display, state->context ); + eglTerminate( state->display ); + + printf("\ncube closed\n"); +} // exit_func() + +//============================================================================== + +int main () +{ + bcm_host_init(); + printf("Note: ensure you have sufficient gpu_mem configured\n"); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + + // initialise the OGLES texture(s) + init_textures(state); + + while (!terminate) + { + update_model(state); + redraw_scene(state); + } + exit_func(); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h new file mode 100755 index 0000000..0f50e93 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h @@ -0,0 +1,30 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + + +void* video_decode_test(void* arg); diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/video.c b/host_applications/linux/apps/hello_pi/hello_videocube/video.c new file mode 100755 index 0000000..4baae8f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/video.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video decode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +static OMX_BUFFERHEADERTYPE* eglBuffer = NULL; +static COMPONENT_T* egl_render = NULL; + +static void* eglImage = 0; + +void my_fill_buffer_done(void* data, COMPONENT_T* comp) +{ + if (OMX_FillThisBuffer(ilclient_get_handle(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed in callback\n"); + exit(1); + } +} + + +// Modified function prototype to work with pthreads +void *video_decode_test(void* arg) +{ + const char* filename = "/opt/vc/src/hello_pi/hello_video/test.h264"; + eglImage = arg; + + if (eglImage == 0) + { + printf("eglImage is null.\n"); + exit(1); + } + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *clock = NULL; + COMPONENT_T *list[5]; + TUNNEL_T tunnel[4]; + ILCLIENT_T *client; + FILE *in; + int status = 0; + unsigned int data_len = 0; + + memset(list, 0, sizeof(list)); + memset(tunnel, 0, sizeof(tunnel)); + + if((in = fopen(filename, "rb")) == NULL) + return (void *)-2; + + if((client = ilclient_init()) == NULL) + { + fclose(in); + return (void *)-3; + } + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return (void *)-4; + } + + // callback + ilclient_set_fill_buffer_done_callback(client, my_fill_buffer_done, 0); + + // create video_decode + if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; + + // create egl_render + if(status == 0 && ilclient_create_component(client, &egl_render, "egl_render", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) + status = -14; + list[1] = egl_render; + + // create clock + if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[2] = clock; + + memset(&cstate, 0, sizeof(cstate)); + cstate.nSize = sizeof(cstate); + cstate.nVersion.nVersion = OMX_VERSION; + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = 1; + if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + status = -13; + + // create video_scheduler + if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[3] = video_scheduler; + + set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); + set_tunnel(tunnel+1, video_scheduler, 11, egl_render, 220); + set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); + + // setup clock tunnel first + if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) + status = -15; + else + ilclient_change_component_state(clock, OMX_StateExecuting); + + if(status == 0) + ilclient_change_component_state(video_decode, OMX_StateIdle); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf; + int port_settings_changed = 0; + int first_packet = 1; + + ilclient_change_component_state(video_decode, OMX_StateExecuting); + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + // loop if at end + if (feof(in)) + rewind(in); + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to egl_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + // Set egl_render to idle + ilclient_change_component_state(egl_render, OMX_StateIdle); + + // Enable the output port and tell egl_render to use the texture as a buffer + //ilclient_enable_port(egl_render, 221); THIS BLOCKS SO CAN'T BE USED + if (OMX_SendCommand(ILC_GET_HANDLE(egl_render), OMX_CommandPortEnable, 221, NULL) != OMX_ErrorNone) + { + printf("OMX_CommandPortEnable failed.\n"); + exit(1); + } + + if (OMX_UseEGLImage(ILC_GET_HANDLE(egl_render), &eglBuffer, 221, NULL, eglImage) != OMX_ErrorNone) + { + printf("OMX_UseEGLImage failed.\n"); + exit(1); + } + + // Set egl_render to executing + ilclient_change_component_state(egl_render, OMX_StateExecuting); + + + // Request egl_render to write data to the texture buffer + if(OMX_FillThisBuffer(ILC_GET_HANDLE(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed.\n"); + exit(1); + } + } + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); + + ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + } + + fclose(in); + + ilclient_disable_tunnel(tunnel); + ilclient_disable_tunnel(tunnel+1); + ilclient_disable_tunnel(tunnel+2); + ilclient_teardown_tunnels(tunnel); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return (void *)status; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt new file mode 100755 index 0000000..b0120fe --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_world.bin) +set(SRCS world.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_world/Makefile b/host_applications/linux/apps/hello_pi/hello_world/Makefile new file mode 100755 index 0000000..7bf2402 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_world/Makefile @@ -0,0 +1,5 @@ +OBJS=world.o +BIN=hello_world.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_world/world.c b/host_applications/linux/apps/hello_pi/hello_world/world.c new file mode 100755 index 0000000..0a9c4aa --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_world/world.c @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Classic Hello World + +#include + +int main(void) +{ + printf("Hello world!\n"); + return 0; +} diff --git a/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile b/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile new file mode 100755 index 0000000..82979e3 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile @@ -0,0 +1,5 @@ +OBJS=ilclient.o ilcore.o +LIB=libilclient.a + +include ../../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c b/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c new file mode 100755 index 0000000..da08ad0 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c @@ -0,0 +1,1836 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief This API defines helper functions for writing IL clients. + * + * This file defines an IL client side library. This is useful when + * writing IL clients, since there tends to be much repeated and + * common code across both single and multiple clients. This library + * seeks to remove that common code and abstract some of the + * interactions with components. There is a wrapper around a + * component and tunnel, and some operations can be done on lists of + * these. The callbacks from components are handled, and specific + * events can be checked or waited for. +*/ + +#include +#include +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_logging.h" +#include "interface/vmcs_host/vchost.h" + +#include "IL/OMX_Broadcom.h" +#include "ilclient.h" + +#define VCOS_LOG_CATEGORY (&ilclient_log_category) + +#ifndef ILCLIENT_THREAD_DEFAULT_STACK_SIZE +#define ILCLIENT_THREAD_DEFAULT_STACK_SIZE (6<<10) +#endif + +static VCOS_LOG_CAT_T ilclient_log_category; + +/****************************************************************************** +Static data and types used only in this file. +******************************************************************************/ + +struct _ILEVENT_T { + OMX_EVENTTYPE eEvent; + OMX_U32 nData1; + OMX_U32 nData2; + OMX_PTR pEventData; + struct _ILEVENT_T *next; +}; + +#define NUM_EVENTS 100 +struct _ILCLIENT_T { + ILEVENT_T *event_list; + VCOS_SEMAPHORE_T event_sema; + ILEVENT_T event_rep[NUM_EVENTS]; + + ILCLIENT_CALLBACK_T port_settings_callback; + void *port_settings_callback_data; + ILCLIENT_CALLBACK_T eos_callback; + void *eos_callback_data; + ILCLIENT_CALLBACK_T error_callback; + void *error_callback_data; + ILCLIENT_BUFFER_CALLBACK_T fill_buffer_done_callback; + void *fill_buffer_done_callback_data; + ILCLIENT_BUFFER_CALLBACK_T empty_buffer_done_callback; + void *empty_buffer_done_callback_data; + ILCLIENT_CALLBACK_T configchanged_callback; + void *configchanged_callback_data; +}; + +struct _COMPONENT_T { + OMX_HANDLETYPE comp; + ILCLIENT_CREATE_FLAGS_T flags; + VCOS_SEMAPHORE_T sema; + VCOS_EVENT_FLAGS_T event; + struct _COMPONENT_T *related; + OMX_BUFFERHEADERTYPE *out_list; + OMX_BUFFERHEADERTYPE *in_list; + char name[32]; + char bufname[32]; + unsigned int error_mask; + unsigned int private; + ILEVENT_T *list; + ILCLIENT_T *client; +}; + +#define random_wait() +static char *states[] = {"Invalid", "Loaded", "Idle", "Executing", "Pause", "WaitingForResources"}; + +typedef enum { + ILCLIENT_ERROR_UNPOPULATED = 0x1, + ILCLIENT_ERROR_SAMESTATE = 0x2, + ILCLIENT_ERROR_BADPARAMETER = 0x4 +} ILERROR_MASK_T; + +/****************************************************************************** +Static functions. +******************************************************************************/ + +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static void ilclient_lock_events(ILCLIENT_T *st); +static void ilclient_unlock_events(ILCLIENT_T *st); + +/****************************************************************************** +Global functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_init + * + * Description: Creates ilclient pointer + * + * Returns: pointer to client structure + ***********************************************************/ +ILCLIENT_T *ilclient_init() +{ + ILCLIENT_T *st = vcos_malloc(sizeof(ILCLIENT_T), "ilclient"); + int i; + + if (!st) + return NULL; + + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_WARN); + vcos_log_register("ilclient", VCOS_LOG_CATEGORY); + + memset(st, 0, sizeof(ILCLIENT_T)); + + i = vcos_semaphore_create(&st->event_sema, "il:event", 1); + vc_assert(i == VCOS_SUCCESS); + + ilclient_lock_events(st); + st->event_list = NULL; + for (i=0; ievent_rep[i].eEvent = -1; // mark as unused + st->event_rep[i].next = st->event_list; + st->event_list = st->event_rep+i; + } + ilclient_unlock_events(st); + return st; +} + +/*********************************************************** + * Name: ilclient_destroy + * + * Description: frees client state + * + * Returns: void + ***********************************************************/ +void ilclient_destroy(ILCLIENT_T *st) +{ + vcos_semaphore_delete(&st->event_sema); + vcos_free(st); + vcos_log_unregister(VCOS_LOG_CATEGORY); +} + +/*********************************************************** + * Name: ilclient_set_port_settings_callback + * + * Description: sets the callback used when receiving port settings + * changed messages. The data field in the callback function will be + * the port index reporting the message. + * + * Returns: void + ***********************************************************/ +void ilclient_set_port_settings_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->port_settings_callback = func; + st->port_settings_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_eos_callback + * + * Description: sets the callback used when receiving eos flags. The + * data parameter in the callback function will be the port index + * reporting an eos flag. + * + * Returns: void + ***********************************************************/ +void ilclient_set_eos_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->eos_callback = func; + st->eos_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_error_callback + * + * Description: sets the callback used when receiving error events. + * The data parameter in the callback function will be the error code + * being reported. + * + * Returns: void + ***********************************************************/ +void ilclient_set_error_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->error_callback = func; + st->error_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_fill_buffer_done_callback + * + * Description: sets the callback used when receiving + * fill_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_fill_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->fill_buffer_done_callback = func; + st->fill_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_empty_buffer_done_callback + * + * Description: sets the callback used when receiving + * empty_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_empty_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->empty_buffer_done_callback = func; + st->empty_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_configchanged_callback + * + * Description: sets the callback used when a config changed + * event is received + * + * Returns: void + ***********************************************************/ +void ilclient_set_configchanged_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->configchanged_callback = func; + st->configchanged_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_create_component + * + * Description: initialises a component state structure and creates + * the IL component. + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_create_component(ILCLIENT_T *client, COMPONENT_T **comp, char *name, + ILCLIENT_CREATE_FLAGS_T flags) +{ + OMX_CALLBACKTYPE callbacks; + OMX_ERRORTYPE error; + char component_name[128]; + int32_t status; + + *comp = vcos_malloc(sizeof(COMPONENT_T), "il:comp"); + if(!*comp) + return -1; + + memset(*comp, 0, sizeof(COMPONENT_T)); + +#define COMP_PREFIX "OMX.broadcom." + + status = vcos_event_flags_create(&(*comp)->event,"il:comp"); + vc_assert(status == VCOS_SUCCESS); + status = vcos_semaphore_create(&(*comp)->sema, "il:comp", 1); + vc_assert(status == VCOS_SUCCESS); + (*comp)->client = client; + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", name); + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", name); + vcos_snprintf(component_name, sizeof(component_name), "%s%s", COMP_PREFIX, name); + + (*comp)->flags = flags; + + callbacks.EventHandler = ilclient_event_handler; + callbacks.EmptyBufferDone = flags & ILCLIENT_ENABLE_INPUT_BUFFERS ? ilclient_empty_buffer_done : ilclient_empty_buffer_done_error; + callbacks.FillBufferDone = flags & ILCLIENT_ENABLE_OUTPUT_BUFFERS ? ilclient_fill_buffer_done : ilclient_fill_buffer_done_error; + + error = OMX_GetHandle(&(*comp)->comp, component_name, *comp, &callbacks); + + if (error == OMX_ErrorNone) + { + OMX_UUIDTYPE uid; + char name[128]; + OMX_VERSIONTYPE compVersion, specVersion; + + if(OMX_GetComponentVersion((*comp)->comp, name, &compVersion, &specVersion, &uid) == OMX_ErrorNone) + { + char *p = (char *) uid + strlen(COMP_PREFIX); + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", p); + (*comp)->name[sizeof((*comp)->name)-1] = 0; + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", p); + (*comp)->bufname[sizeof((*comp)->bufname)-1] = 0; + } + + if(flags & (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_OUTPUT_ZERO_BUFFERS)) + { + OMX_PORT_PARAM_TYPE ports; + OMX_INDEXTYPE types[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, OMX_IndexParamImageInit, OMX_IndexParamOtherInit}; + int i; + + ports.nSize = sizeof(OMX_PORT_PARAM_TYPE); + ports.nVersion.nVersion = OMX_VERSION; + + for(i=0; i<4; i++) + { + OMX_ERRORTYPE error = OMX_GetParameter((*comp)->comp, types[i], &ports); + if(error == OMX_ErrorNone) + { + uint32_t j; + for(j=0; jcomp, OMX_IndexParamPortDefinition, &portdef) == OMX_ErrorNone) + { + if(portdef.eDir == OMX_DirOutput && portdef.nBufferCountActual > 0) + { + portdef.nBufferCountActual = 0; + OMX_SetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef); + } + } + } + } + } + } + } + return 0; + } + else + { + vcos_event_flags_delete(&(*comp)->event); + vcos_semaphore_delete(&(*comp)->sema); + vcos_free(*comp); + *comp = NULL; + return -1; + } +} + +/*********************************************************** + * Name: ilclient_remove_event + * + * Description: Removes an event from a component event list. ignore1 + * and ignore2 are flags indicating whether to not match on nData1 and + * nData2 respectively. + * + * Returns: 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +int ilclient_remove_event(COMPONENT_T *st, OMX_EVENTTYPE eEvent, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2) +{ + ILEVENT_T *cur, *prev; + uint32_t set; + ilclient_lock_events(st->client); + + cur = st->list; + prev = NULL; + + while (cur && !(cur->eEvent == eEvent && (ignore1 || cur->nData1 == nData1) && (ignore2 || cur->nData2 == nData2))) + { + prev = cur; + cur = cur->next; + } + + if (cur == NULL) + { + ilclient_unlock_events(st->client); + return -1; + } + + if (prev == NULL) + st->list = cur->next; + else + prev->next = cur->next; + + // add back into spare list + cur->next = st->client->event_list; + st->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + // if we're removing an OMX_EventError or OMX_EventParamOrConfigChanged event, then clear the error bit from the eventgroup, + // since the user might have been notified through the error callback, and then + // can't clear the event bit - this will then cause problems the next time they + // wait for an error. + if(eEvent == OMX_EventError) + vcos_event_flags_get(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + else if(eEvent == OMX_EventParamOrConfigChanged) + vcos_event_flags_get(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR_CONSUME, 0, &set); + + ilclient_unlock_events(st->client); + return 0; +} + +/*********************************************************** + * Name: ilclient_state_transition + * + * Description: Transitions a null terminated list of IL components to + * a given state. All components are told to transition in a random + * order before any are checked for transition completion. + * + * Returns: void + ***********************************************************/ +void ilclient_state_transition(COMPONENT_T *list[], OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + int i, num; + uint32_t set; + + num=0; + while (list[num]) + num++; + + // if we transition the supplier port first, it will call freebuffer on the non + // supplier, which will correctly signal a port unpopulated error. We want to + // ignore these errors. + if (state == OMX_StateLoaded) + for (i=0; ierror_mask |= ILCLIENT_ERROR_UNPOPULATED; + for (i=0; iprivate = ((rand() >> 13) & 0xff)+1; + + for (i=0; iprivate && (min == -1 || list[min]->private > list[j]->private)) + min = j; + + list[min]->private = 0; + + random_wait(); + //Clear error event for this component + vcos_event_flags_get(&list[min]->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + error = OMX_SendCommand(list[min]->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + } + + random_wait(); + + for (i=0; ierror_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_teardown_tunnels + * + * Description: tears down a null terminated list of tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_teardown_tunnels(TUNNEL_T *tunnel) +{ + int i; + OMX_ERRORTYPE error; + + i=0;; + while (tunnel[i].source) + { + error = OMX_SetupTunnel(tunnel[i].source->comp, tunnel[i].source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SetupTunnel(tunnel[i].sink->comp, tunnel[i].sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + i++; + } +} + +/*********************************************************** + * Name: ilclient_disable_tunnel + * + * Description: disables a tunnel by disabling the ports. Allows + * ports to signal same state error if they were already disabled. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_tunnel(TUNNEL_T *tunnel) +{ + OMX_ERRORTYPE error; + + if(tunnel->source == 0 || tunnel->sink == 0) + return; + + tunnel->source->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortDisable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortDisable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortDisable, tunnel->source_port) < 0) + vc_assert(0); + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortDisable, tunnel->sink_port) < 0) + vc_assert(0); + + tunnel->source->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_enable_tunnel + * + * Description: enables a tunnel by enabling the ports + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_enable_tunnel(TUNNEL_T *tunnel) +{ + OMX_STATETYPE state; + OMX_ERRORTYPE error; + + ilclient_debug_output("ilclient: enable tunnel from %x/%d to %x/%d", + tunnel->source, tunnel->source_port, + tunnel->sink, tunnel->sink_port); + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortEnable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortEnable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + // to complete, the sink component can't be in loaded state + error = OMX_GetState(tunnel->sink->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded) + { + int ret = 0; + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0 || + OMX_SendCommand(tunnel->sink->comp, OMX_CommandStateSet, OMX_StateIdle, NULL) != OMX_ErrorNone || + (ret = ilclient_wait_for_command_complete_dual(tunnel->sink, OMX_CommandStateSet, OMX_StateIdle, tunnel->source)) < 0) + { + if(ret == -2) + { + // the error was reported fom the source component: clear this error and disable the sink component + ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port); + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + } + + ilclient_debug_output("ilclient: could not change component state to IDLE"); + ilclient_disable_port(tunnel->source, tunnel->source_port); + return -1; + } + } + else + { + if (ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0) + { + ilclient_debug_output("ilclient: could not change sink port %d to enabled", tunnel->sink_port); + + //Oops failed to enable the sink port + ilclient_disable_port(tunnel->source, tunnel->source_port); + //Clean up the port enable event from the source port. + ilclient_wait_for_event(tunnel->source, OMX_EventCmdComplete, + OMX_CommandPortEnable, 0, tunnel->source_port, 0, + ILCLIENT_PORT_ENABLED | ILCLIENT_EVENT_ERROR, VCOS_EVENT_FLAGS_SUSPEND); + return -1; + } + } + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port) != 0) + { + ilclient_debug_output("ilclient: could not change source port %d to enabled", tunnel->source_port); + + //Failed to enable the source port + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + return -1; + } + + return 0; +} + + +/*********************************************************** + * Name: ilclient_flush_tunnels + * + * Description: flushes all ports used in a null terminated list of + * tunnels. max specifies the maximum number of tunnels to flush from + * the list, where max=0 means all tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_flush_tunnels(TUNNEL_T *tunnel, int max) +{ + OMX_ERRORTYPE error; + int i; + + i=0; + while (tunnel[i].source && (max == 0 || i < max)) + { + error = OMX_SendCommand(tunnel[i].source->comp, OMX_CommandFlush, tunnel[i].source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel[i].sink->comp, OMX_CommandFlush, tunnel[i].sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + ilclient_wait_for_event(tunnel[i].source, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].source_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + ilclient_wait_for_event(tunnel[i].sink, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].sink_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + i++; + } +} + + +/*********************************************************** + * Name: ilclient_return_events + * + * Description: Returns all events from a component event list to the + * list of unused event structures. + * + * Returns: void + ***********************************************************/ +void ilclient_return_events(COMPONENT_T *comp) +{ + ilclient_lock_events(comp->client); + while (comp->list) + { + ILEVENT_T *next = comp->list->next; + comp->list->next = comp->client->event_list; + comp->client->event_list = comp->list; + comp->list = next; + } + ilclient_unlock_events(comp->client); +} + +/*********************************************************** + * Name: ilclient_cleanup_components + * + * Description: frees all components from a null terminated list and + * deletes resources used in component state structure. + * + * Returns: void + ***********************************************************/ +void ilclient_cleanup_components(COMPONENT_T *list[]) +{ + int i; + OMX_ERRORTYPE error; + + i=0; + while (list[i]) + { + ilclient_return_events(list[i]); + if (list[i]->comp) + { + error = OMX_FreeHandle(list[i]->comp); + + vc_assert(error == OMX_ErrorNone); + } + i++; + } + + i=0; + while (list[i]) + { + vcos_event_flags_delete(&list[i]->event); + vcos_semaphore_delete(&list[i]->sema); + vcos_free(list[i]); + list[i] = NULL; + i++; + } +} + +/*********************************************************** + * Name: ilclient_change_component_state + * + * Description: changes the state of a single component. Note: this + * may not be suitable if the component is tunnelled and requires + * connected components to also change state. + * + * Returns: 0 on success, -1 on failure (note - trying to change to + * the same state which causes a OMX_ErrorSameState is treated as + * success) + ***********************************************************/ +int ilclient_change_component_state(COMPONENT_T *comp, OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandStateSet, state) < 0) + { + ilclient_debug_output("ilclient: could not change component state to %d", state); + ilclient_remove_event(comp, OMX_EventError, 0, 1, 0, 1); + return -1; + } + return 0; +} + +/*********************************************************** + * Name: ilclient_disable_port + * + * Description: disables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + +/*********************************************************** + * Name: ilclient_enabled_port + * + * Description: enables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_enable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_enable_port_buffers + * + * Description: enables a port on a given component which requires + * buffers to be supplied by the client. + * + * Returns: void + ***********************************************************/ +int ilclient_enable_port_buffers(COMPONENT_T *comp, int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_BUFFERHEADERTYPE *list = NULL, **end = &list; + OMX_STATETYPE state; + int i; + + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_FALSE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return -1; + + // check component is in the right state to accept buffers + error = OMX_GetState(comp->comp, &state); + if (error != OMX_ErrorNone || !(state == OMX_StateIdle || state == OMX_StateExecuting || state == OMX_StatePause)) + return -1; + + // send the command + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + for (i=0; i != portdef.nBufferCountActual; i++) + { + unsigned char *buf; + if(ilclient_malloc) + buf = ilclient_malloc(private, portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + else + buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + + if(!buf) + break; + + error = OMX_UseBuffer(comp->comp, end, portIndex, NULL, portdef.nBufferSize, buf); + if(error != OMX_ErrorNone) + { + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + break; + } + end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate); + } + + // queue these buffers + vcos_semaphore_wait(&comp->sema); + + if(portdef.eDir == OMX_DirInput) + { + *end = comp->in_list; + comp->in_list = list; + } + else + { + *end = comp->out_list; + comp->out_list = list; + } + + vcos_semaphore_post(&comp->sema); + + if(i != portdef.nBufferCountActual || + ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + { + ilclient_disable_port_buffers(comp, portIndex, NULL, ilclient_free, private); + + // at this point the first command might have terminated with an error, which means that + // the port is disabled before the disable_port_buffers function is called, so we're left + // with the error bit set and an error event in the queue. Clear these now if they exist. + ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0); + + return -1; + } + + // success + return 0; +} + + +/*********************************************************** + * Name: ilclient_disable_port_buffers + * + * Description: disables a port on a given component which has + * buffers supplied by the client. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port_buffers(COMPONENT_T *comp, int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_BUFFERHEADERTYPE *list = bufferList; + OMX_BUFFERHEADERTYPE **head, *clist, *prev; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + int num; + + // get the buffers off the relevant queue + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_TRUE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return; + + num = portdef.nBufferCountActual; + + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + while(num > 0) + { + VCOS_UNSIGNED set; + + if(list == NULL) + { + vcos_semaphore_wait(&comp->sema); + + // take buffers for this port off the relevant queue + head = portdef.eDir == OMX_DirInput ? &comp->in_list : &comp->out_list; + clist = *head; + prev = NULL; + + while(clist) + { + if((portdef.eDir == OMX_DirInput ? clist->nInputPortIndex : clist->nOutputPortIndex) == portIndex) + { + OMX_BUFFERHEADERTYPE *pBuffer = clist; + + if(!prev) + clist = *head = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + else + clist = prev->pAppPrivate = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + + pBuffer->pAppPrivate = list; + list = pBuffer; + } + else + { + prev = clist; + clist = (OMX_BUFFERHEADERTYPE *) &(clist->pAppPrivate); + } + } + + vcos_semaphore_post(&comp->sema); + } + + while(list) + { + void *buf = list->pBuffer; + OMX_BUFFERHEADERTYPE *next = list->pAppPrivate; + + error = OMX_FreeBuffer(comp->comp, portIndex, list); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + num--; + list = next; + } + + if(num) + { + OMX_U32 mask = ILCLIENT_PORT_DISABLED | ILCLIENT_EVENT_ERROR; + mask |= (portdef.eDir == OMX_DirInput ? ILCLIENT_EMPTY_BUFFER_DONE : ILCLIENT_FILL_BUFFER_DONE); + + // also wait for command complete/error in case we didn't have all the buffers allocated + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, -1, &set); + + if((set & ILCLIENT_EVENT_ERROR) && ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0) >= 0) + return; + + if((set & ILCLIENT_PORT_DISABLED) && ilclient_remove_event(comp, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, portIndex, 0) >= 0) + return; + } + } + + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_setup_tunnel + * + * Description: creates a tunnel between components that require that + * ports be inititially disabled, then enabled after tunnel setup. If + * timeout is non-zero, it will initially wait until a port settings + * changes message has been received by the output port. If port + * streams are supported by the output port, the requested port stream + * will be selected. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout waiting for the parameter changed + * -2: an error was returned instead of parameter changed + * -3: no streams are available from this port + * -4: requested stream is not available from this port + * -5: the data format was not acceptable to the sink + ***********************************************************/ +int ilclient_setup_tunnel(TUNNEL_T *tunnel, unsigned int portStream, int timeout) +{ + OMX_ERRORTYPE error; + OMX_PARAM_U32TYPE param; + OMX_STATETYPE state; + int32_t status; + int enable_error; + + // source component must at least be idle, not loaded + error = OMX_GetState(tunnel->source->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded && ilclient_change_component_state(tunnel->source, OMX_StateIdle) < 0) + return -2; + + // wait for the port parameter changed from the source port + if(timeout) + { + status = ilclient_wait_for_event(tunnel->source, OMX_EventPortSettingsChanged, + tunnel->source_port, 0, -1, 1, + ILCLIENT_PARAMETER_CHANGED | ILCLIENT_EVENT_ERROR, timeout); + + if (status < 0) + { + ilclient_debug_output( + "ilclient: timed out waiting for port settings changed on port %d", tunnel->source_port); + return status; + } + } + + // disable ports + ilclient_disable_tunnel(tunnel); + + // if this source port uses port streams, we need to select one of them before proceeding + // if getparameter causes an error that's fine, nothing needs selecting + param.nSize = sizeof(OMX_PARAM_U32TYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = tunnel->source_port; + if (OMX_GetParameter(tunnel->source->comp, OMX_IndexParamNumAvailableStreams, ¶m) == OMX_ErrorNone) + { + if (param.nU32 == 0) + { + // no streams available + // leave the source port disabled, and return a failure + return -3; + } + if (param.nU32 <= portStream) + { + // requested stream not available + // no streams available + // leave the source port disabled, and return a failure + return -4; + } + + param.nU32 = portStream; + error = OMX_SetParameter(tunnel->source->comp, OMX_IndexParamActiveStream, ¶m); + vc_assert(error == OMX_ErrorNone); + } + + // now create the tunnel + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, tunnel->sink->comp, tunnel->sink_port); + + enable_error = 0; + + if (error != OMX_ErrorNone || (enable_error=ilclient_enable_tunnel(tunnel)) < 0) + { + // probably format not compatible + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + error = OMX_SetupTunnel(tunnel->sink->comp, tunnel->sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + if(enable_error) + { + //Clean up the errors. This does risk removing an error that was nothing to do with this tunnel :-/ + ilclient_remove_event(tunnel->sink, OMX_EventError, 0, 1, 0, 1); + ilclient_remove_event(tunnel->source, OMX_EventError, 0, 1, 0, 1); + } + + ilclient_debug_output("ilclient: could not setup/enable tunnel (setup=0x%x,enable=%d)", + error, enable_error); + return -5; + } + + return 0; +} + +/*********************************************************** + * Name: ilclient_wait_for_event + * + * Description: waits for a given event to appear on a component event + * list. If not immediately present, will wait on that components + * event group for the given event flag. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout was received. + * -2: an error event was received. + * -3: a config change event was received. + ***********************************************************/ +int ilclient_wait_for_event(COMPONENT_T *comp, OMX_EVENTTYPE event, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2, + int event_flag, int suspend) +{ + int32_t status; + uint32_t set; + + while (ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) < 0) + { + // if we want to be notified of errors, check the list for an error now + // before blocking, the event flag may have been cleared already. + if(event_flag & ILCLIENT_EVENT_ERROR) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventError) + cur = cur->next; + + if(cur) + { + // clear error flag + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + ilclient_unlock_events(comp->client); + return -2; + } + + ilclient_unlock_events(comp->client); + } + // check for config change event if we are asked to be notified of that + if(event_flag & ILCLIENT_CONFIG_CHANGED) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventParamOrConfigChanged) + cur = cur->next; + + ilclient_unlock_events(comp->client); + + if(cur) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + status = vcos_event_flags_get(&comp->event, event_flag, VCOS_OR_CONSUME, + suspend, &set); + if (status != 0) + return -1; + if (set & ILCLIENT_EVENT_ERROR) + return -2; + if (set & ILCLIENT_CONFIG_CHANGED) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + return 0; +} + + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete_dual + * + * Description: Waits for an event signalling command completion. In + * this version we may also return failure if there is an error event + * that has terminated a command on a second component. + * + * Returns: 0 on success, -1 on failure of comp, -2 on failure of other + ***********************************************************/ +int ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2, COMPONENT_T *other) +{ + OMX_U32 mask = ILCLIENT_EVENT_ERROR; + int ret = 0; + + switch(command) { + case OMX_CommandStateSet: mask |= ILCLIENT_STATE_CHANGED; break; + case OMX_CommandPortDisable: mask |= ILCLIENT_PORT_DISABLED; break; + case OMX_CommandPortEnable: mask |= ILCLIENT_PORT_ENABLED; break; + default: return -1; + } + + if(other) + other->related = comp; + + while(1) + { + ILEVENT_T *cur, *prev = NULL; + VCOS_UNSIGNED set; + + ilclient_lock_events(comp->client); + + cur = comp->list; + while(cur && + !(cur->eEvent == OMX_EventCmdComplete && cur->nData1 == command && cur->nData2 == nData2) && + !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + { + prev = cur; + cur = cur->next; + } + + if(cur) + { + if(prev == NULL) + comp->list = cur->next; + else + prev->next = cur->next; + + // work out whether this was a success or a fail event + ret = cur->eEvent == OMX_EventCmdComplete || cur->nData1 == OMX_ErrorSameState ? 0 : -1; + + if(cur->eEvent == OMX_EventError) + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + // add back into spare list + cur->next = comp->client->event_list; + comp->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + ilclient_unlock_events(comp->client); + break; + } + else if(other != NULL) + { + // check the other component for an error event that terminates a command + cur = other->list; + while(cur && !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + cur = cur->next; + + if(cur) + { + // we don't remove the event in this case, since the user + // can confirm that this event errored by calling wait_for_command on the + // other component + + ret = -2; + ilclient_unlock_events(comp->client); + break; + } + } + + ilclient_unlock_events(comp->client); + + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, VCOS_SUSPEND, &set); + } + + if(other) + other->related = NULL; + + return ret; +} + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete + * + * Description: Waits for an event signalling command completion. + * + * Returns: 0 on success, -1 on failure. + ***********************************************************/ +int ilclient_wait_for_command_complete(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2) +{ + return ilclient_wait_for_command_complete_dual(comp, command, nData2, NULL); +} + +/*********************************************************** + * Name: ilclient_get_output_buffer + * + * Description: Returns an output buffer returned from a component + * using the OMX_FillBufferDone callback from the output list for the + * given component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_output_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + VCOS_UNSIGNED set; + + do { + vcos_semaphore_wait(&comp->sema); + ret = comp->out_list; + while(ret != NULL && ret->nOutputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->out_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_get_input_buffer + * + * Description: Returns an input buffer return from a component using + * the OMX_EmptyBufferDone callback from the output list for the given + * component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_input_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + + do { + VCOS_UNSIGNED set; + + vcos_semaphore_wait(&comp->sema); + ret = comp->in_list; + while(ret != NULL && ret->nInputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->in_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_debug_output + * + * Description: prints a varg message to the log or the debug screen + * under win32 + * + * Returns: void + ***********************************************************/ +void ilclient_debug_output(char *format, ...) +{ + va_list args; + + va_start(args, format); + vcos_vlog_info(format, args); + va_end(args); +} + +/****************************************************************************** +Static functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_lock_events + * + * Description: locks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_lock_events(ILCLIENT_T *st) +{ + vcos_semaphore_wait(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_unlock_events + * + * Description: unlocks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_unlock_events(ILCLIENT_T *st) +{ + vcos_semaphore_post(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_event_handler + * + * Description: event handler passed to core to use as component + * callback + * + * Returns: success + ***********************************************************/ +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + ILEVENT_T *event; + OMX_ERRORTYPE error = OMX_ErrorNone; + + ilclient_lock_events(st->client); + + // go through the events on this component and remove any duplicates in the + // existing list, since the client probably doesn't need them. it's better + // than asserting when we run out. + event = st->list; + while(event != NULL) + { + ILEVENT_T **list = &(event->next); + while(*list != NULL) + { + if((*list)->eEvent == event->eEvent && + (*list)->nData1 == event->nData1 && + (*list)->nData2 == event->nData2) + { + // remove this duplicate + ILEVENT_T *rem = *list; + ilclient_debug_output("%s: removing %d/%d/%d", st->name, event->eEvent, event->nData1, event->nData2); + *list = rem->next; + rem->eEvent = -1; + rem->next = st->client->event_list; + st->client->event_list = rem; + } + else + list = &((*list)->next); + } + + event = event->next; + } + + vc_assert(st->client->event_list); + event = st->client->event_list; + + switch (eEvent) { + case OMX_EventCmdComplete: + switch (nData1) { + case OMX_CommandStateSet: + ilclient_debug_output("%s: callback state changed (%s)", st->name, states[nData2]); + vcos_event_flags_set(&st->event, ILCLIENT_STATE_CHANGED, VCOS_OR); + break; + case OMX_CommandPortDisable: + ilclient_debug_output("%s: callback port disable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_DISABLED, VCOS_OR); + break; + case OMX_CommandPortEnable: + ilclient_debug_output("%s: callback port enable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_ENABLED, VCOS_OR); + break; + case OMX_CommandFlush: + ilclient_debug_output("%s: callback port flush %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_FLUSH, VCOS_OR); + break; + case OMX_CommandMarkBuffer: + ilclient_debug_output("%s: callback mark buffer %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_MARKED_BUFFER, VCOS_OR); + break; + default: + vc_assert(0); + } + break; + case OMX_EventError: + { + // check if this component failed a command, and we have to notify another command + // of this failure + if(nData2 == 1 && st->related != NULL) + vcos_event_flags_set(&st->related->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + + error = nData1; + switch (error) { + case OMX_ErrorPortUnpopulated: + if (st->error_mask & ILCLIENT_ERROR_UNPOPULATED) + { + ilclient_debug_output("%s: ignore error: port unpopulated (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: port unpopulated %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorSameState: + if (st->error_mask & ILCLIENT_ERROR_SAMESTATE) + { + ilclient_debug_output("%s: ignore error: same state (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: same state %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadParameter: + if (st->error_mask & ILCLIENT_ERROR_BADPARAMETER) + { + ilclient_debug_output("%s: ignore error: bad parameter (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: bad parameter %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorIncorrectStateTransition: + ilclient_debug_output("%s: incorrect state transition %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadPortIndex: + ilclient_debug_output("%s: bad port index %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorStreamCorrupt: + ilclient_debug_output("%s: stream corrupt %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorInsufficientResources: + ilclient_debug_output("%s: insufficient resources %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorUnsupportedSetting: + ilclient_debug_output("%s: unsupported setting %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorOverflow: + ilclient_debug_output("%s: overflow %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDiskFull: + ilclient_debug_output("%s: disk full %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorMaxFileSize: + ilclient_debug_output("%s: max file size %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorDrmUnauthorised: + ilclient_debug_output("%s: drm file is unauthorised %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmExpired: + ilclient_debug_output("%s: drm file has expired %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmGeneral: + ilclient_debug_output("%s: drm library error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + default: + vc_assert(0); + ilclient_debug_output("%s: unexpected error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + } + } + break; + case OMX_EventBufferFlag: + ilclient_debug_output("%s: buffer flag %d/%x", st->name, nData1, nData2); + if (nData2 & OMX_BUFFERFLAG_EOS) + { + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_FLAG_EOS, VCOS_OR); + nData2 = OMX_BUFFERFLAG_EOS; + } + else + vc_assert(0); + break; + case OMX_EventPortSettingsChanged: + ilclient_debug_output("%s: port settings changed %d", st->name, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_PARAMETER_CHANGED, VCOS_OR); + break; + case OMX_EventMark: + ilclient_debug_output("%s: buffer mark %p", st->name, pEventData); + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_MARK, VCOS_OR); + break; + case OMX_EventParamOrConfigChanged: + ilclient_debug_output("%s: param/config 0x%X on port %d changed", st->name, nData2, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR); + break; + default: + vc_assert(0); + break; + } + + if (event) + { + // fill in details + event->eEvent = eEvent; + event->nData1 = nData1; + event->nData2 = nData2; + event->pEventData = pEventData; + + // remove from top of spare list + st->client->event_list = st->client->event_list->next; + + // put at head of component event queue + event->next = st->list; + st->list = event; + } + ilclient_unlock_events(st->client); + + // now call any callbacks without the event lock so the client can + // remove the event in context + switch(eEvent) { + case OMX_EventError: + if(event && st->client->error_callback) + st->client->error_callback(st->client->error_callback_data, st, error); + break; + case OMX_EventBufferFlag: + if ((nData2 & OMX_BUFFERFLAG_EOS) && st->client->eos_callback) + st->client->eos_callback(st->client->eos_callback_data, st, nData1); + break; + case OMX_EventPortSettingsChanged: + if (st->client->port_settings_callback) + st->client->port_settings_callback(st->client->port_settings_callback_data, st, nData1); + break; + case OMX_EventParamOrConfigChanged: + if (st->client->configchanged_callback) + st->client->configchanged_callback(st->client->configchanged_callback_data, st, nData2); + break; + default: + // ignore + break; + } + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: empty buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the same order + list = st->in_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->in_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR); + + if (st->client->empty_buffer_done_callback) + st->client->empty_buffer_done_callback(st->client->empty_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: fill buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the correct order + list = st->out_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->out_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR); + + if (st->client->fill_buffer_done_callback) + st->client->fill_buffer_done_callback(st->client->fill_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + + + +OMX_HANDLETYPE ilclient_get_handle(COMPONENT_T *comp) +{ + vcos_assert(comp); + return comp->comp; +} + + +static struct { + OMX_PORTDOMAINTYPE dom; + int param; +} port_types[] = { + { OMX_PortDomainVideo, OMX_IndexParamVideoInit }, + { OMX_PortDomainAudio, OMX_IndexParamAudioInit }, + { OMX_PortDomainImage, OMX_IndexParamImageInit }, + { OMX_PortDomainOther, OMX_IndexParamOtherInit }, +}; + +int ilclient_get_port_index(COMPONENT_T *comp, OMX_DIRTYPE dir, OMX_PORTDOMAINTYPE type, int index) +{ + uint32_t i; + // for each possible port type... + for (i=0; iILCLIENT_T structure encapsulates the state needed for the IL + * Client API. It contains a set of callback functions used to + * communicate with the user. It also includes a linked list of free + * event structures. + ***********************************************************/ +typedef struct _ILCLIENT_T ILCLIENT_T; + + +/** + * Each ILEVENT_T structure stores the result of an EventHandler + * callback from a component, storing the event message type and any + * parameters returned. + ***********************************************************/ +typedef struct _ILEVENT_T ILEVENT_T; + + + +struct _COMPONENT_T; + +/** + * The COMPONENT_T structure represents an IL component, + * together with the necessary extra information required by the IL + * Client API. This structure stores the handle to the OMX component, + * as well as the event list containing all events sent by this + * component. The component state structure also holds a pair of + * buffer queues, for input and output buffers returned to the client + * by the FillBufferDone and EmptyBufferDone + * callbacks. As some operations result in error callbacks that can + * be ignored, an error mask is maintained to allow some errors to be + * ignored. A pointer to the client state structure is also added. + ***********************************************************/ +typedef struct _COMPONENT_T COMPONENT_T; + + +/** + * The generic callback function is used for communicating events from + * a particular component to the user. + * + * @param userdata The data returned from when the callback was registered. + * + * @param comp The component structure representing the component that + * originated this event. + * + * @param data The relevant data field from the event. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_CALLBACK_T)(void *userdata, COMPONENT_T *comp, OMX_U32 data); + + +/** + * The buffer callback function is used for indicating that a + * component has returned a buffer on a port using client buffer + * communication. + * + * @param data The data returned from when the callback was registered. + * + * @param comp The component from which the buffer originated. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_BUFFER_CALLBACK_T)(void *data, COMPONENT_T *comp); + + +/** + * The malloc function is passed into + * ilclient_enable_port_buffers() and used for allocating the + * buffer payload. + * + * @param userdata Private pointer passed into + * ilclient_enable_port_buffers() call. + * + * @param size Size in bytes of the requested memory region. + * + * @param align Alignment requirement in bytes for the base memory address. + * + * @param description Text description of the memory being allocated. + * + * @return The memory address on success, NULL on failure. + ***********************************************************/ +typedef void *(*ILCLIENT_MALLOC_T)(void *userdata, VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description); + + +/** + * The free function is passed into + * ilclient_enable_port_buffers() and + * ilclient_disable_port_buffers() and used for freeing the + * buffer payload. + * + * @param userdata Private pointer passed into + * ilclient_enable_port_buffers() and + * ilclient_disable_port_buffers(). + * + * @param pointer Memory address to free, that was previously returned + * from ILCLIENT_MALLOC_T function. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_FREE_T)(void *userdata, void *pointer); + + +/** + * The event mask enumeration describes the possible events that the + * user can ask to wait for when waiting for a particular event. + ***********************************************************/ +typedef enum { + ILCLIENT_EMPTY_BUFFER_DONE = 0x1, /**< Set when a buffer is + returned from an input + port */ + + ILCLIENT_FILL_BUFFER_DONE = 0x2, /**< Set when a buffer is + returned from an output + port */ + + ILCLIENT_PORT_DISABLED = 0x4, /**< Set when a port indicates + it has completed a disable + command. */ + + ILCLIENT_PORT_ENABLED = 0x8, /**< Set when a port indicates + is has completed an enable + command. */ + + ILCLIENT_STATE_CHANGED = 0x10, /**< Set when a component + indicates it has completed + a state change command. */ + + ILCLIENT_BUFFER_FLAG_EOS = 0x20, /**< Set when a port signals + an EOS event. */ + + ILCLIENT_PARAMETER_CHANGED = 0x40, /**< Set when a port signals a + port settings changed + event. */ + + ILCLIENT_EVENT_ERROR = 0x80, /**< Set when a component + indicates an error. */ + + ILCLIENT_PORT_FLUSH = 0x100, /**< Set when a port indicates + is has completed a flush + command. */ + + ILCLIENT_MARKED_BUFFER = 0x200, /**< Set when a port indicates + it has marked a buffer. */ + + ILCLIENT_BUFFER_MARK = 0x400, /**< Set when a port indicates + it has received a buffer + mark. */ + + ILCLIENT_CONFIG_CHANGED = 0x800 /**< Set when a config parameter + changed. */ +} ILEVENT_MASK_T; + + +/** + * On component creation the user can set flags to control the + * creation of that component. + ***********************************************************/ +typedef enum { + ILCLIENT_FLAGS_NONE = 0x0, /**< Used if no flags are + set. */ + + ILCLIENT_ENABLE_INPUT_BUFFERS = 0x1, /**< If set we allow the + client to communicate with + input ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_ENABLE_OUTPUT_BUFFERS = 0x2, /**< If set we allow the + client to communicate with + output ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_DISABLE_ALL_PORTS = 0x4, /**< If set we disable all + ports on creation. */ + + ILCLIENT_HOST_COMPONENT = 0x8, /**< Create a host component. + The default host ilcore + only can create host components + by being locally hosted + so should only be used for testing + purposes. */ + + ILCLIENT_OUTPUT_ZERO_BUFFERS = 0x10 /**< All output ports will have + nBufferCountActual set to zero, + if supported by the component. */ +} ILCLIENT_CREATE_FLAGS_T; + + +/** + * \brief This structure represents a tunnel in the OpenMAX IL API. + * + * Some operations in this API act on a tunnel, so the tunnel state + * structure (TUNNEL_T) is a convenient store of the source and sink + * of the tunnel. For each, a pointer to the relevant component state + * structure and the port index is stored. + ***********************************************************/ +typedef struct { + COMPONENT_T *source; /**< The source component */ + int source_port; /**< The output port index on the source component */ + COMPONENT_T *sink; /**< The sink component */ + int sink_port; /**< The input port index on the sink component */ +} TUNNEL_T; + + +/** + * The set_tunnel macro is a useful function that initialises a + * TUNNEL_T structure. + ***********************************************************/ +#define set_tunnel(t,a,b,c,d) do {TUNNEL_T *_ilct = (t); \ + _ilct->source = (a); _ilct->source_port = (b); \ + _ilct->sink = (c); _ilct->sink_port = (d);} while(0) + +/** + * For calling OpenMAX IL methods directory, we need to access the + * OMX_HANDLETYPE corresponding to the COMPONENT_T structure. This + * macro enables this while keeping the COMPONENT_T structure opaque. + * The parameter x should be of the type *COMPONENT_T. + ***********************************************************/ +#define ILC_GET_HANDLE(x) ilclient_get_handle(x) + +/** + * An IL Client structure is created by the ilclient_init() + * method. This structure is used when creating components, but + * otherwise is not needed in other API functions as a pointer to this + * structure is maintained in the COMPONENT_T structure. + * + * @return pointer to client structure + ***********************************************************/ +VCHPRE_ ILCLIENT_T VCHPOST_ *ilclient_init(void); + +/** + * When all components have been deleted, the IL Client structure can + * be destroyed by calling the ilclient_destroy() function. + * + * @param handle The client handle. After calling this function, this + * handle should not be used. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_destroy(ILCLIENT_T *handle); + +/** + * The ilclient_set_port_settings_callback() function registers a + * callback to be used when the OMX_EventPortSettingsChanged event is + * received. When the event is received, a pointer to the component + * structure and port index is returned by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_port_settings_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_eos_callback() function registers a callback to be + * used when the OMX_EventBufferFlag is received with the + * OMX_BUFFERFLAG_EOS flag set. When the event is received, a pointer + * to the component structure and port index is returned by the + * callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_eos_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_error_callback() function registers a callback to be + * used when the OMX_EventError is received from a component. When + * the event is received, a pointer to the component structure and the + * error code are reported by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_error_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_configchanged_callback() function + * registers a callback to be used when an + * OMX_EventParamOrConfigChanged event occurs. When the + * event is received, a pointer to the component structure and the + * index value that has changed are reported by the callback. The + * user may then use an OMX_GetConfig call with the index + * as specified to retrieve the updated information. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_configchanged_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + + +/** + * The ilclient_set_fill_buffer_done_callback() function registers a + * callback to be used when a buffer passed to an output port using the + * OMX_FillBuffer call is returned with the OMX_FillBufferDone + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * ilclient_get_output_buffer() function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_fill_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_empty_buffer_done_callback() function registers a + * callback to be used when a buffer passed to an input port using the + * OMX_EmptyBuffer call is returned with the OMX_EmptyBufferDone + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * ilclient_get_input_buffer() function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_empty_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + + +/** + * Components are created using the ilclient_create_component() + * function. + * + * @param handle The client handle + * + * @param comp On successful creation, the component structure pointer + * will be written back into comp. + * + * @param name The name of the component to be created. Component + * names as provided are automatically prefixed with + * "OMX.broadcom." before passing to the IL core. The name + * provided will also be used in debugging messages added about this + * component. + * + * @param flags The client can specify some creation behaviour by using + * the flags field. The meaning of each flag is defined + * by the ILCLIENT_CREATE_FLAGS_T type. + * + * @return 0 on success, -1 on failure + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_create_component(ILCLIENT_T *handle, + COMPONENT_T **comp, + char *name, + ILCLIENT_CREATE_FLAGS_T flags); + +/** + * The ilclient_cleanup_components() function deallocates all + * state associated with components and frees the OpenMAX component + * handles. All tunnels connecting components should have been torn + * down explicitly, and all components must be in loaded state. + * + * @param list A null-terminated list of component pointers to be + * deallocated. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_cleanup_components(COMPONENT_T *list[]); + + +/** + * The ilclient_change_component_state() function changes the + * state of an individual component. This will trigger the state + * change, and also wait for that state change to be completed. It + * should not be called if this state change has dependencies on other + * components also changing states. Trying to change a component to + * its current state is treated as success. + * + * @param comp The component to change. + * + * @param state The new state to transition to. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_change_component_state(COMPONENT_T *comp, + OMX_STATETYPE state); + + +/** + * The ilclient_state_transition() function transitions a set of + * components that need to perform a simultaneous state transition; + * for example, when two components are tunnelled and the buffer + * supplier port needs to allocate and pass buffers to a non-supplier + * port. All components are sent a command to change state, then the + * function will wait for all components to signal that they have + * changed state. + * + * @param list A null-terminated list of component pointers. + * + * @param state The new state to which to transition all components. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_state_transition(COMPONENT_T *list[], + OMX_STATETYPE state); + + +/** + * The ilclient_disable_port() function disables a port on a + * given component. This function sends the disable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * success. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_port(COMPONENT_T *comp, + int portIndex); + + +/** + * The ilclient_enable_port() function enables a port on a + * given component. This function sends the enable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * success. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_enable_port(COMPONENT_T *comp, + int portIndex); + + + +/** + * The ilclient_enable_port_buffers() function enables a port + * in base profile mode on a given component. The port is not + * tunneled, so requires buffers to be allocated. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @param ilclient_malloc This function will be used to allocate + * buffer payloads. If NULL then + * vcos_malloc_aligned will be used. + * + * @param ilclient_free If an error occurs, this function is used to + * free previously allocated payloads. If NULL then + * vcos_free will be used. + * + * @param userdata The first argument to calls to + * ilclient_malloc and ilclient_free, if these + * arguments are not NULL. + * + * @return 0 indicates success. -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_port_buffers(COMPONENT_T *comp, + int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * The ilclient_disable_port_buffers() function disables a + * port in base profile mode on a given component. The port is not + * tunneled, and has been supplied with buffers by the client. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @param bufferList A list of buffers, using pAppPrivate + * as the next pointer that were allocated on this port, and currently + * held by the application. If buffers on this port are held by IL + * client or the component then these are automatically freed. + * + * @param ilclient_free This function is used to free the buffer payloads. + * If NULL then vcos_free will be used. + * + * @param userdata The first argument to calls to + * ilclient_free. + * + * @return void + */ +VCHPRE_ void VCHPOST_ ilclient_disable_port_buffers(COMPONENT_T *comp, + int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * With a populated tunnel structure, the + * ilclient_setup_tunnel() function connects the tunnel. It + * first transitions the source component to idle if currently in + * loaded state, and then optionally checks the source event list for + * a port settings changed event from the source port. If this event + * is not in the event queue then this function optionally waits for + * it to arrive. To disable this check for the port settings changed + * event, set timeout to zero. + * + * Both ports are then disabled, and the source port is inspected for + * a port streams parameter. If this is supported, then the + * portStream argument is used to select which port stream + * to use. The two ports are then tunnelled using the + * OMX_SetupTunnel function. If this is successful, then + * both ports are enabled. Note that for disabling and enabling the + * tunnelled ports, the functions ilclient_disable_tunnel() + * and ilclient_enable_tunnel() are used, so the relevant + * documentation for those functions applies here. + * + * @param tunnel The tunnel structure representing the tunnel to + * set up. + * + * @param portStream If port streams are supported on the output port + * of the tunnel, then this parameter indicates the port stream to + * select on this port. + * + * @param timeout The time duration in milliseconds to wait for the + * output port to signal a port settings changed event before + * returning a timeout failure. If this is 0, then we do not check + * for a port settings changed before setting up the tunnel. + * + * @return 0 indicates success, negative indicates failure: + * - -1: a timeout waiting for the parameter changed + * - -2: an error was returned instead of parameter changed + * - -3: no streams are available from this port + * - -4: requested stream is not available from this port + * - -5: the data format was not acceptable to the sink + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_setup_tunnel(TUNNEL_T *tunnel, + unsigned int portStream, + int timeout); + + +/** + * The ilclient_disable_tunnel() function disables both ports listed in + * the tunnel structure. It will send a port disable command to each + * port, then waits for both to indicate they have completed the + * transition. The errors OMX_ErrorPortUnpopulated and + * OMX_ErrorSameState are both ignored by this function; the former + * since the first port to disable may deallocate buffers before the + * second port has been disabled, leading to the second port reporting + * the unpopulated error. + * + * @param tunnel The tunnel to disable. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_tunnel(TUNNEL_T *tunnel); + + +/** + * The ilclient_enable_tunnel() function enables both ports listed in + * the tunnel structure. It will first send a port enable command to + * each port. It then checks whether the sink component is not in + * loaded state - if so, the function waits for both ports to complete + * the requested port enable. If the sink component was in loaded + * state, then this component is transitioned to idle to allow the + * ports to exchange buffers and enable the ports. This is since + * typically this function is used when creating a tunnel between two + * components, where the source component is processing data to enable + * it to report the port settings changed event, and the sink port has + * yet to be used. Before transitioning the sink component to idle, + * this function waits for the sink port to be enabled - since the + * component is in loaded state, this will happen quickly. If the + * transition to idle fails, the sink component is transitioned back + * to loaded and the source port disabled. If the transition + * succeeds, the function then waits for the source port to complete + * the requested port enable. + * + * @param tunnel The tunnel to enable. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_tunnel(TUNNEL_T *tunnel); + + +/** + * The ilclient_flush_tunnels() function will flush a number of tunnels + * from the list of tunnels presented. For each tunnel that is to be + * flushed, both source and sink ports are sent a flush command. The + * function then waits for both ports to report they have completed + * the flush operation. + * + * @param tunnel List of tunnels. The list must be terminated with a + * tunnel structure with NULL component entries. + * + * @param max The maximum number of tunnels to flush from the list. + * A value of 0 indicates that all tunnels in the list are flushed. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_flush_tunnels(TUNNEL_T *tunnel, + int max); + + +/** + * The ilclient_teardown_tunnels() function tears down all tunnels in + * the list of tunnels presented. For each tunnel in the list, the + * OMX_SetupTunnel is called on the source port and on the sink port, + * where for both calls the destination component is NULL and the + * destination port is zero. The VMCSX IL implementation requires + * that all tunnels are torn down in this manner before components are + * freed. + * + * @param tunnels List of tunnels to teardown. The list must be + * terminated with a tunnel structure with NULL component entries. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_teardown_tunnels(TUNNEL_T *tunnels); + + +/** + * The ilclient_get_output_buffer() function returns a buffer + * that was sent to an output port and that has been returned from a + * component using the OMX_FillBufferDone callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component that the buffer + * was returned from. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return Pointer to buffer if available, otherwise NULL. + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_output_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The ilclient_get_input_buffer() function returns a buffer + * that was sent to an input port and that has been returned from a + * component using the OMX_EmptyBufferDone callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component from which the buffer + * was returned. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return pointer to buffer if available, otherwise NULL + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_input_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The ilclient_remove_event() function queries the event list for the + * given component, matching against the given criteria. If a matching + * event is found, it is removed and added to the free event list. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The nData1 field of the matching event. + * + * @param ignore1 Whether to ignore the nData1 field when finding a + * matching event. A value of 0 indicates that nData1 must match, a + * value of 1 indicates that nData1 does not have to match. + * + * @param nData2 The nData2 field of the matching event. + * + * @param ignore2 Whether to ignore the nData2 field when finding a + * matching event. A value of 0 indicates that nData2 must match, a + * value of 1 indicates that nData2 does not have to match. + * + * @return 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_remove_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2); + + +/** + * The ilclient_return_events() function removes all events + * from a component event list and adds them to the IL client free + * event list. This function is automatically called when components + * are freed. + * + * @param comp The component from which all events should be moved to + * the free list. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_return_events(COMPONENT_T *comp); + + +/** + * The ilclient_wait_for_event() function is similar to + * ilclient_remove_event(), but allows the caller to block until that + * event arrives. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The nData1 field of the matching event. + * + * @param ignore1 Whether to ignore the nData1 field when finding a + * matching event. A value of 0 indicates that nData1 must match, a + * value of 1 indicates that nData1 does not have to match. + * + * @param nData2 The nData2 field of the matching event. + * + * @param ignore2 Whether to ignore the nData2 field when finding a + * matching event. A value of 0 indicates that nData2 must match, a + * value of 1 indicates that nData2 does not have to match. + * + * @param event_flag Specifies a bitfield of IL client events to wait + * for, given in ILEVENT_MASK_T. If any of these events + * are signalled by the component, the event list is then re-checked + * for a matching event. If the ILCLIENT_EVENT_ERROR bit + * is included, and an error is signalled by the component, then the + * function will return an error code. If the + * ILCLIENT_CONFIG_CHANGED bit is included, and this bit is + * signalled by the component, then the function will return an error + * code. + * + * @param timeout Specifies how long to block for in milliseconds + * before returning a failure. + * + * @return 0 indicates success, a matching event has been removed from + * the component's event queue. A negative return indicates failure, + * in this case no events have been removed from the component's event + * queue. + * - -1: a timeout was received. + * - -2: an error event was received. + * - -3: a config changed event was received. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2, + int event_flag, + int timeout); + + +/** + * The ilclient_wait_for_command_complete() function waits + * for a message from a component that indicates that the command + * has completed. This is either a command success message, or an + * error message that signals the completion of an event. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * OMX_CommandStateSet, OMX_CommandPortDisable + * or OMX_CommandPortEnable. + * + * @param nData2 The expected value of nData2 in the + * command complete message. + * + * @return 0 indicates success, either the command successfully completed + * or the OMX_ErrorSameState was returned. -1 indicates + * that the command terminated with a different error message. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2); + + +/** + * The ilclient_wait_for_command_complete_dual() function + * is similar to ilclient_wait_for_command_complete(). The + * difference is that while waiting for the component to complete the + * event or raise an error, we can also report if another reports an + * error that terminates a command. This is useful if the two + * components are tunneled, and we need to wait for one component to + * enable a port, or change state to OMX_StateIdle. If the + * other component is the buffer supplier and reports an error, then + * it will not allocate buffers, so our first component may stall. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * OMX_CommandStateSet, OMX_CommandPortDisable + * or OMX_CommandPortEnable. + * + * @param nData2 The expected value of nData2 in the + * command complete message. + * + * @param related Another component, where we will return if this + * component raises an error that terminates a command. + * + * @return 0 indicates success, either the command successfully + * completed or the OMX_ErrorSameState was returned. -1 + * indicates that the command terminated with a different error + * message. -2 indicates that the related component raised an error. + * In this case, the error is not cleared from the related + * component's event list. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2, + COMPONENT_T *related); + + +/** + * The ilclient_debug_output() function adds a message to a + * host-specific debug display. For a local VideoCore host the message is + * added to the internal message log. For a Win32 host the message is + * printed to the debug display. This function should be customised + * when IL client is ported to another platform. + * + * @param format A message to add, together with the variable + * argument list similar to printf and other standard C functions. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_debug_output(char *format, ...); + +/** + * The ilclient_get_handle() function returns the + * underlying OMX component held by an IL component handle. This is + * needed when calling methods such as OMX_SetParameter on + * a component created using the IL client library. + * + * @param comp IL component handle + * + * @return The OMX_HANDLETYPE value for the component. + ***********************************************************/ +VCHPRE_ OMX_HANDLETYPE VCHPOST_ ilclient_get_handle(COMPONENT_T *comp); + + +#ifndef OMX_SKIP64BIT + +/** + * Macro to return OMX_TICKS from a signed 64 bit value. + * This is the version where OMX_TICKS is a signed 64 bit + * value, an alternative definition is used when OMX_TICKS + * is a structure. + */ +#define ilclient_ticks_from_s64(s) (s) + +/** + * Macro to return signed 64 bit value from OMX_TICKS. + * This is the version where OMX_TICKS is a signed 64 bit + * value, an alternative definition is used when OMX_TICKS + * is a structure. + */ +#define ilclient_ticks_to_s64(t) (t) + +#else + +/** + * Inline function to return OMX_TICKS from a signed 64 bit + * value. This is the version where OMX_TICKS is a + * structure, an alternative definition is used when + * OMX_TICKS is a signed 64 bit value. + */ +static inline OMX_TICKS ilclient_ticks_from_s64(int64_t s) { + OMX_TICKS ret; + ret.nLowPart = s; + ret.nHighPart = s>>32; + return ret; +} + +/** + * Inline function to return signed 64 bit value from + * OMX_TICKS. This is the version where + * OMX_TICKS is a structure, an alternative definition is + * used when OMX_TICKS is a signed 64 bit value. + */ +static inline int64_t ilclient_ticks_to_s64(OMX_TICKS t) { + uint64_t u = t.nLowPart | ((uint64_t)t.nHighPart << 32); + return u; +} + + +#endif /* OMX_SKIP64BIT */ + +/** + * The ilclient_get_port_index() function returns the n'th + * port index of the specified type and direction for the given + * component. + * + * @param comp The component of interest + * @param dir The direction + * @param type The type, or -1 for any type. + * @param index Which port (counting from 0). + * + * @return The port index, or -1 if not found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_get_port_index(COMPONENT_T *comp, + OMX_DIRTYPE dir, + OMX_PORTDOMAINTYPE type, + int index); + + +/** + * The ilclient_suggest_bufsize() function gives a + * component a hint about the size of buffer it should use. This size + * is set on the component by setting the + * OMX_IndexParamBrcmOutputBufferSize index on the given + * component. + * + * @param comp IL component handle + * @param nBufSizeHint Size of buffer in bytes + * + * @return 0 indicates success, -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_suggest_bufsize(COMPONENT_T *comp, + OMX_U32 nBufSizeHint); + + +/** + * The ilclient_stack_size() function suggests a minimum + * stack size that tasks calling into with API will require. + * + * @return Suggested stack size in bytes. + ***********************************************************/ +VCHPRE_ unsigned int VCHPOST_ ilclient_stack_size(void); + +#endif /* ILCLIENT_H */ diff --git a/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c b/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c new file mode 100755 index 0000000..356733d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c @@ -0,0 +1,308 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief Host core implementation. + */ + +#include +#include + +//includes +#include +#include +#include +#include + +#include "IL/OMX_Component.h" +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vchost.h" +#include "interface/vmcs_host/vcilcs_common.h" + +static int coreInit = 0; +static int nActiveHandles = 0; +static ILCS_SERVICE_T *ilcs_service = NULL; +static VCOS_MUTEX_T lock; +static VCOS_ONCE_T once = VCOS_ONCE_INIT; + +/* Atomic creation of lock protecting shared state */ +static void initOnce(void) +{ + VCOS_STATUS_T status; + status = vcos_mutex_create(&lock, VCOS_FUNCTION); + vcos_demand(status == VCOS_SUCCESS); +} + +/* OMX_Init */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) +{ + VCOS_STATUS_T status; + OMX_ERRORTYPE err = OMX_ErrorNone; + + status = vcos_once(&once, initOnce); + vcos_demand(status == VCOS_SUCCESS); + + vcos_mutex_lock(&lock); + + if(coreInit == 0) + { + // we need to connect via an ILCS connection to VideoCore + VCHI_INSTANCE_T initialise_instance; + VCHI_CONNECTION_T *connection; + ILCS_CONFIG_T config; + + vc_host_get_vchi_state(&initialise_instance, &connection); + + vcilcs_config(&config); + + ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); + + if(ilcs_service == NULL) + { + err = OMX_ErrorHardware; + goto end; + } + + coreInit = 1; + } + else + coreInit++; + +end: + vcos_mutex_unlock(&lock); + return err; +} + +/* OMX_Deinit */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) +{ + if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) + return OMX_ErrorNotReady; + + vcos_mutex_lock(&lock); + + coreInit--; + + if(coreInit == 0) + { + // we need to teardown the ILCS connection to VideoCore + ilcs_deinit(ilcs_service); + ilcs_service = NULL; + } + + vcos_mutex_unlock(&lock); + + return OMX_ErrorNone; +} + + +/* OMX_ComponentNameEnum */ +OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); +} + + +/* OMX_GetHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE eError; + OMX_COMPONENTTYPE *pComp; + OMX_HANDLETYPE hHandle = 0; + + if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) + { + if(pHandle) + *pHandle = NULL; + return OMX_ErrorBadParameter; + } + + { + pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); + if (!pComp) + { + vcos_assert(0); + return OMX_ErrorInsufficientResources; + } + memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); + hHandle = (OMX_HANDLETYPE)pComp; + pComp->nSize = sizeof(OMX_COMPONENTTYPE); + pComp->nVersion.nVersion = OMX_VERSION; + eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); + + if (eError == OMX_ErrorNone) { + // Check that all function pointers have been filled in. + // All fields should be non-zero. + int i; + uint32_t *p = (uint32_t *) pComp; + for(i=0; i>2; i++) + if(*p++ == 0) + eError = OMX_ErrorInvalidComponent; + + if(eError != OMX_ErrorNone && pComp->ComponentDeInit) + pComp->ComponentDeInit(hHandle); + } + + if (eError == OMX_ErrorNone) { + eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); + if (eError != OMX_ErrorNone) + pComp->ComponentDeInit(hHandle); + } + if (eError == OMX_ErrorNone) { + *pHandle = hHandle; + } + else { + *pHandle = NULL; + free(pComp); + } + } + + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + nActiveHandles++; + vcos_mutex_unlock(&lock); + } + + return eError; +} + +/* OMX_FreeHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pComp; + + if (hComponent == NULL || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + pComp = (OMX_COMPONENTTYPE*)hComponent; + + if (ilcs_service == NULL) + return OMX_ErrorBadParameter; + + eError = (pComp->ComponentDeInit)(hComponent); + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + --nActiveHandles; + vcos_mutex_unlock(&lock); + free(pComp); + } + + vcos_assert(nActiveHandles >= 0); + + return eError; +} + +/* OMX_SetupTunnel */ +OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pCompIn, *pCompOut; + OMX_TUNNELSETUPTYPE oTunnelSetup; + + if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + oTunnelSetup.nTunnelFlags = 0; + oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; + + pCompOut = (OMX_COMPONENTTYPE*)hOutput; + + if (hOutput){ + eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); + } + + if (eError == OMX_ErrorNone && hInput) { + pCompIn = (OMX_COMPONENTTYPE*)hInput; + eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); + + if (eError != OMX_ErrorNone && hOutput) { + /* cancel tunnel request on output port since input port failed */ + pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); + } + } + return eError; +} + +/* OMX_GetComponentsOfRole */ +OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumComps = 0; + return eError; +} + +/* OMX_GetRolesOfComponent */ +OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumRoles = 0; + return eError; +} + +/* OMX_GetDebugInformation */ +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); +} + + + +/* File EOF */ + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile b/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile new file mode 100755 index 0000000..1e2a22b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile @@ -0,0 +1,7 @@ +OBJS=font.o vgft.o graphics.o +LIB=libvgfont.a + +INCLUDES+=-I$(SDKSTAGE)/usr/include/freetype2 -I$(SDKSTAGE)/usr/include -I$(SDKSTAGE)/usr/include/arm-linux-gnueabi + +include ../../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/font.c b/host_applications/linux/apps/hello_pi/libs/vgfont/font.c new file mode 100755 index 0000000..7da2a4e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/font.c @@ -0,0 +1,355 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +/** @file font.c + * + * Fairly primitive font handling, just enough to emulate the old API. + * + * Hinting and Font Size + * + * The old API does not create fonts explicitly, it just renders them + * as needed. That works fine for unhinted fonts, but for hinted fonts we + * care about font size. + * + * Since we now *can* do hinted fonts, we should do. Regenerating the + * fonts each time becomes quite slow, so we maintain a cache of fonts. + * + * For the typical applications which use graphics_x this is fine, but + * won't work well if lots of fonts sizes are used. + * + * Unicode + * + * This API doesn't support unicode at all at present, nor UTF-8. + */ + +#include +#include +#include +#include + +#include "graphics_x_private.h" +#include "vgft.h" + +#define VMCS_INSTALL_PREFIX "" + +/** The one and only (default) font we support for now. + */ +static struct +{ + const char *file; + void *mem; + size_t len; +} default_font = { "Vera.ttf" }; + +/** An entry in our list of fonts + */ +typedef struct gx_font_cache_entry_t +{ + struct gx_font_cache_entry_t *next; + VGFT_FONT_T font; + uint32_t ptsize; /** size in points, 26.6 */ +} gx_font_cache_entry_t; + +static char fname[128]; +static int inited; +static gx_font_cache_entry_t *fonts; + +static VGFT_FONT_T *find_font(const char *text, uint32_t text_size); + +VCOS_STATUS_T gx_priv_font_init(const char *font_dir) +{ + VCOS_STATUS_T ret; + size_t len; + int rc; + if (vgft_init()) + { + ret = VCOS_ENOMEM; + goto fail_init; + } + + int fd = -1; + // search for the font + sprintf(fname, "%s/%s", font_dir, default_font.file); + fd = open(fname, O_RDONLY); + + if (fd < 0) + { + GX_ERROR("Could not open font file '%s'", default_font.file); + ret = VCOS_ENOENT; + goto fail_open; + } + + len = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + default_font.mem = vcos_malloc(len, default_font.file); + if (!default_font.mem) + { + GX_ERROR("No memory for font %s", fname); + ret = VCOS_ENOMEM; + goto fail_mem; + } + + rc = read(fd, default_font.mem, len); + if (rc != len) + { + GX_ERROR("Could not read font %s", fname); + ret = VCOS_EINVAL; + goto fail_rd; + } + default_font.len = len; + close(fd); + + GX_TRACE("Opened font file '%s'", fname); + + inited = 1; + return VCOS_SUCCESS; + +fail_rd: + vcos_free(default_font.mem); +fail_mem: + if (fd >= 0) close(fd); +fail_open: + vgft_term(); +fail_init: + return ret; +} + +void gx_priv_font_term(void) +{ + gx_font_cache_flush(); + vgft_term(); + vcos_free(default_font.mem); +} + +/** Render text. + * + * FIXME: Not at all optimal - re-renders each time. + * FIXME: Not UTF-8 aware + * FIXME: better caching + */ +VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, + GRAPHICS_RESOURCE_HANDLE res, + int32_t x, + int32_t y, + uint32_t width, + uint32_t height, + uint32_t fg_colour, + uint32_t bg_colour, + const char *text, + uint32_t text_length, + uint32_t text_size ) +{ + VGfloat vg_colour[4]; + VGFT_FONT_T *font; + VGPaint fg; + GX_CLIENT_STATE_T save; + VCOS_STATUS_T status = VCOS_SUCCESS; + int clip = 1; + + vcos_demand(inited); // has gx_font_init() been called? + + gx_priv_save(&save, res); + + if (width == GRAPHICS_RESOURCE_WIDTH && + height == GRAPHICS_RESOURCE_HEIGHT) + { + clip = 0; + } + + width = (width == GRAPHICS_RESOURCE_WIDTH) ? res->width : width; + height = (height == GRAPHICS_RESOURCE_HEIGHT) ? res->height : height; + font = find_font(text, text_size); + if (!font) + { + status = VCOS_ENOMEM; + goto finish; + } + + // setup the clipping rectangle + if (clip) + { + VGint coords[] = {x,y,width,height}; + vgSeti(VG_SCISSORING, VG_TRUE); + vgSetiv(VG_SCISSOR_RECTS, 4, coords); + } + + // setup the background colour if needed + if (bg_colour != GRAPHICS_TRANSPARENT_COLOUR) + { + int err; + VGfloat rendered_w, rendered_h; + VGfloat vg_bg_colour[4]; + + // setup the background colour... + gx_priv_colour_to_paint(bg_colour, vg_bg_colour); + vgSetfv(VG_CLEAR_COLOR, 4, vg_bg_colour); + + // fill in a rectangle... + vgft_get_text_extents(font, text, text_length, (VGfloat)x, (VGfloat)y, &rendered_w, &rendered_h); + + if ( ( 0 < (VGint)rendered_w ) && ( 0 < (VGint)rendered_h ) ) + { + // Have to compensate for the messed up y position of multiline text. + VGfloat offset = vgft_first_line_y_offset(font); + int32_t bottom = y + offset - rendered_h; + + vgClear(x, bottom, (VGint)rendered_w, (VGint)rendered_h); + err = vgGetError(); + if (err) + { + GX_LOG("Error %d clearing bg text %d %d %g %g", + err, x, y, rendered_w, rendered_h); + vcos_assert(0); + } // if + } // if + } // if + // setup the foreground colour + fg = vgCreatePaint(); + if (!fg) + { + status = VCOS_ENOMEM; + goto finish; + } + + // draw the foreground text + vgSetParameteri(fg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + gx_priv_colour_to_paint(fg_colour, vg_colour); + vgSetParameterfv(fg, VG_PAINT_COLOR, 4, vg_colour); + vgSetPaint(fg, VG_FILL_PATH); + + vgft_font_draw(font, (VGfloat)x, (VGfloat)y, text, text_length, VG_FILL_PATH); + + vgDestroyPaint(fg); + + vcos_assert(vgGetError() == 0); + vgSeti(VG_SCISSORING, VG_FALSE); + +finish: + gx_priv_restore(&save); + + return status; +} + + +/** Find a font in our cache, or create a new entry in the cache. + * + * Very primitive at present. + */ +static VGFT_FONT_T *find_font(const char *text, uint32_t text_size) +{ + int ptsize, dpi_x = 0, dpi_y = 0; + VCOS_STATUS_T status; + gx_font_cache_entry_t *font; + + ptsize = text_size << 6; // freetype takes size in points, in 26.6 format. + + for (font = fonts; font; font = font->next) + { + if (font->ptsize == ptsize) + return &font->font; + } + + font = vcos_malloc(sizeof(*font), "font"); + if (!font) + return NULL; + + font->ptsize = ptsize; + + status = vgft_font_init(&font->font); + if (status != VCOS_SUCCESS) + { + vcos_free(font); + return NULL; + } + + // load the font + status = vgft_font_load_mem(&font->font, default_font.mem, default_font.len); + if (status != VCOS_SUCCESS) + { + GX_LOG("Could not load font from memory: %d", status); + vgft_font_term(&font->font); + vcos_free(font); + return NULL; + } + + status = vgft_font_convert_glyphs(&font->font, ptsize, dpi_x, dpi_y); + if (status != VCOS_SUCCESS) + { + GX_LOG("Could not convert font '%s' at size %d", fname, ptsize); + vgft_font_term(&font->font); + vcos_free(font); + return NULL; + } + + font->next = fonts; + fonts = font; + + return &font->font; +} + +void gx_font_cache_flush(void) +{ + while (fonts != NULL) + { + struct gx_font_cache_entry_t *next = fonts->next; + vgft_font_term(&fonts->font); + vcos_free(fonts); + fonts = next; + } +} + +int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height, + const uint32_t text_size ) +{ + GX_CLIENT_STATE_T save; + VGfloat w, h; + int ret = -1; + + gx_priv_save(&save, res); + + VGFT_FONT_T *font = find_font(text, text_size); + if (!font) + goto finish; + + + vgft_get_text_extents(font, text, text_length, 0.0, 0.0, &w, &h); + *width = w; + *height = h; + ret = 0; + +finish: + gx_priv_restore(&save); + return ret; +} + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c new file mode 100755 index 0000000..20e1d5a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c @@ -0,0 +1,1609 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Graphics library for VG + +#include +#include +#include +#include +#include "vgfont.h" +#include "graphics_x_private.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ATEXT_FONT_SIZE 12 /*< Default font size (font size can be set with *_ext functions). */ + +/****************************************************************************** +Local data +******************************************************************************/ +static GX_DISPLAY_T display; /*< Our one and only EGL display. */ + +/** + * We create one eglContext for each of the possible graphics_x resource types + * that are supported. + ***********************************************************/ +static EGLContext gx_contexts[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; + +/** Note: we have to share all our contexts, because otherwise it seems + * to be not valid to blit from one image to another if the images + * have different contexts. + * + * That means we have to use a single global lock to serialise all accesses + * to any contexts. + ***********************************************************/ +static VCOS_MUTEX_T lock; + +static EGLConfig gx_configs[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; + +static int inited; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Convert graphics_x colour formats into EGL format. */ +static int gx_egl_attrib_colours(EGLint *attribs, GRAPHICS_RESOURCE_TYPE_T res_type) +{ + int i, n; + static EGLint rgba[] = {EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE, EGL_ALPHA_SIZE}; + static uint8_t rgb565[] = {5,6,5,0}; + static uint8_t rgb888[] = {8,8,8,0}; + static uint8_t rgb32a[] = {8,8,8,8}; + + uint8_t *sizes = NULL; + + switch (res_type) + { + case GRAPHICS_RESOURCE_RGB565: + sizes = rgb565; + break; + case GRAPHICS_RESOURCE_RGB888: + sizes = rgb888; + break; + case GRAPHICS_RESOURCE_RGBA32: + sizes = rgb32a; + break; + default: + vcos_assert(0); + return -1; + } + for (n=0, i=0; imagic == RES_MAGIC)); + vcos_assert(res == NULL || !res->context_bound); + + state->context = eglGetCurrentContext(); + state->api = eglQueryAPI(); + state->read_surface = eglGetCurrentSurface(EGL_READ); + state->draw_surface = eglGetCurrentSurface(EGL_DRAW); + state->res = res; + + vcos_assert(state->api); // should never be anything other than VG or GL + + vcos_mutex_lock(&lock); + + egl_result = eglBindAPI(EGL_OPENVG_API); + vcos_assert(egl_result); + + if (res) + { + GX_TRACE("gx_priv_save: eglMakeCurrent: %s, res %x surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), + (uint32_t)res, (uint32_t)res->surface, (uint32_t)res->context); + + egl_result = eglMakeCurrent(display.disp, res->surface, + res->surface, res->context); + vcos_assert(egl_result); + + res->context_bound = 1; + } +} + +/*****************************************************************************/ +void gx_priv_restore(GX_CLIENT_STATE_T *state) +{ + EGLBoolean egl_result; + + GX_TRACE("gx_priv_restore: eglMakeCurrent: %s, res %x draw_surface %x, surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), + (uint32_t)state->res, (uint32_t)state->draw_surface, (uint32_t)state->read_surface, (uint32_t)state->context); + + // disconnect our thread from this context, so we other threads can use it via + // this API + egl_result = eglMakeCurrent(display.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + vcos_assert(egl_result); + + // now return to the client's API binding + egl_result = eglBindAPI(state->api); + vcos_assert(egl_result); + + egl_result = eglMakeCurrent(display.disp, state->draw_surface, state->read_surface, state->context); + vcos_assert(egl_result); + + if (state->res) state->res->context_bound = 0; + + vcos_mutex_unlock(&lock); +} + +/****************************************************************************** +Functions and data exported as part of the public GraphicsX API +******************************************************************************/ + +VCOS_LOG_CAT_T gx_log_cat; /*< Logging category for GraphicsX. */ + +int32_t graphics_initialise( void ) +{ + // dummy initialisation function. This is typically called + // early in the day before VLLs are available, and so cannot + // do anything useful. + return 0; +} + +/*****************************************************************************/ +int32_t graphics_uninitialise( void ) +{ + int i; + vcos_assert(inited); + + gx_priv_font_term(); + + for (i=0; iu.native_window, + &cookie); + if (rc < 0) + { + GX_LOG("%s: could not create native window", __FUNCTION__); + status = VCOS_ENOMEM; + goto fail_create_native_win; + } + + h->magic = RES_MAGIC; + h->type = GX_WINDOW; + h->alpha = 1.0; + + h->surface = eglCreateWindowSurface(display.disp, gx_configs[image_type], &h->u.native_window.egl_win, NULL); + if (!h->surface) + { + GX_LOG("Could not create window surface: 0x%x", eglGetError()); + status = VCOS_ENOMEM; + goto fail_win; + } + + egl_result = eglSurfaceAttrib(display.disp, h->surface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); + vcos_assert(egl_result); + + h->context = gx_contexts[image_type]; + h->screen_id = screen_id; + h->width = width; + h->height = height; + h->restype = image_type; + + gx_priv_save(&save, h); + + // fill it with black + status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff)); + vcos_assert(status == VCOS_SUCCESS); + + gx_priv_finish_native_window(h, cookie); + gx_priv_flush(h); + + *resource_handle = h; + gx_priv_restore(&save); + return status; + +fail_win: + gx_priv_destroy_native_window(h); +fail_create_native_win: + vcos_free(h); + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res ) +{ + EGLBoolean result; + + if (!res) + { + // let it slide - mimics old behaviour + return 0; + } + GX_TRACE("delete resource @%p", res); + + vcos_assert(res->magic == RES_MAGIC); + + if (res->type == GX_PBUFFER) + { + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + vgDestroyImage(res->u.pixmap); + vcos_assert(vgGetError() == 0); + gx_priv_restore(&save); + } + + GX_TRACE("graphics_delete_resource: calling eglDestroySurface..."); + result = eglDestroySurface(display.disp, res->surface); + vcos_assert(result); + + GX_TRACE("graphics_delete_resource: calling eglWaitClient..."); + eglWaitClient(); // wait for EGL to finish sorting out its surfaces + + if (res->type == GX_WINDOW) + { + GX_TRACE("graphics_delete_resource: calling gx_priv_destroy_native_window..."); + gx_priv_destroy_native_window(res); + } + + res->magic = ~RES_MAGIC; + vcos_free(res); + GX_TRACE("graphics_delete_resource: done"); + + return 0; +} + +/*****************************************************************************/ +int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res, + const uint32_t x_offset, + const uint32_t y_offset, + const uint32_t width, + const uint32_t height ) +{ + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + gx_priv_flush(res); + + gx_priv_restore(&save); + + return 0; +} + +/*****************************************************************************/ +int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ) +{ + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + VCOS_STATUS_T st = gx_priv_resource_fill( + res, + x, res->height-y-height, + width, height, + fill_colour); + + gx_priv_restore(&save); + + return st == VCOS_SUCCESS ? 0 : -1; +} + +/*****************************************************************************/ +int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, + const uint32_t height, + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length, + const uint32_t text_size ) +{ + + /* + * FIXME: Not at all optimal - re-renders each time. + * FIXME: Not UTF-8 safe + * FIXME: much better caching (or any caching) + */ + VCOS_STATUS_T rc = gx_priv_render_text( + &display, res, + x, res->height-y-text_size, width, height, fg_colour, bg_colour, + text, text_length, text_size); + + return (rc == VCOS_SUCCESS) ? 0 : -1; +} + +/*****************************************************************************/ +int32_t graphics_resource_render_text( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, /* this can be GRAPHICS_RESOURCE_WIDTH for no clipping */ + const uint32_t height, /* this can be GRAPHICS_RESOURCE_HEIGHT for no clipping */ + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length) +{ + return graphics_resource_render_text_ext(res, x, y, width, height, + fg_colour, bg_colour, + text, text_length, + ATEXT_FONT_SIZE); +} + +/*****************************************************************************/ +int32_t graphics_get_resource_size( + const GRAPHICS_RESOURCE_HANDLE res, + uint32_t *w, + uint32_t *h) +{ + if (w) *w = res->width; + if (h) *h = res->height; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_get_resource_type(const GRAPHICS_RESOURCE_HANDLE res, GRAPHICS_RESOURCE_TYPE_T *type) +{ + if (type) *type = res->restype; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_bitblt( const GRAPHICS_RESOURCE_HANDLE src, + const uint32_t x, // offset within source + const uint32_t y, // offset within source + const uint32_t width, + const uint32_t height, + GRAPHICS_RESOURCE_HANDLE dest, + const uint32_t x_pos, + const uint32_t y_pos ) +{ + int rc = -1; + VGfloat old[9]; + uint32_t w, h; + VGPaint paint = VG_INVALID_HANDLE; + GX_CLIENT_STATE_T save; + int is_child = 0; + VGImage img = VG_INVALID_HANDLE; + + gx_priv_save(&save, dest); + + if (src->type != GX_PBUFFER) + { + vcos_assert(0); + goto finish; + } + + // create a child image that contains just the part wanted + w = width == GRAPHICS_RESOURCE_WIDTH ? src->width : width; + h = height == GRAPHICS_RESOURCE_HEIGHT ? src->height : height; + + if (x==0 && y==0 && + w == src->width && + h == src->height) + { + img = src->u.pixmap; + } + else + { + is_child = 1; + img = vgChildImage(src->u.pixmap, x, y, w, h); + if (img == VG_INVALID_HANDLE) + { + vcos_assert(0); + goto finish; + } + } + + vcos_assert(vgGetError()==0); + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgGetMatrix(old); + vgLoadIdentity(); + vgTranslate((VGfloat)x_pos, (VGfloat)(dest->height-y_pos)); + vgScale(1.0, -1.0); + + // Do we have a translucency going on? + if (src->alpha != 1.0) + { + VGfloat colour[4] = {1.0,1.0,1.0,src->alpha}; + paint = vgCreatePaint(); + + vgSetParameterfv(paint, VG_PAINT_COLOR, 4, colour); + vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY); + vgSetPaint(paint, VG_STROKE_PATH | VG_FILL_PATH); + } + vcos_assert(vgGetError()==0); + + vgDrawImage(img); + vcos_assert(vgGetError()==0); + vgLoadMatrix(old); + + int err = vgGetError(); + + if (err) + { + GX_LOG("vg error %x blitting area", err); + vcos_assert(0); + rc = -1; + } + else + { + rc = 0; + } +finish: + if (paint != VG_INVALID_HANDLE) + vgDestroyPaint(paint); + + if (is_child) + vgDestroyImage(img); + + gx_priv_restore(&save); + return rc; +} + +void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res) +{ + EGLBoolean result; + result = eglSwapBuffers(display.disp, res->surface); + vcos_assert(result); +} + + +/** Map a colour, which the client will have supplied in RGB888. + */ + +void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba) +{ + // with OpenVG we use RGB order. + rgba[0] = ((VGfloat)((col & R_888_MASK) >> 16 )) / 0xff; + rgba[1] = ((VGfloat)((col & G_888_MASK) >> 8 )) / 0xff; + rgba[2] = ((VGfloat)((col & B_888_MASK) >> 0 )) / 0xff; + rgba[3] = ((VGfloat)((col & ALPHA_888_MASK) >> 24)) / 0xff; +} + +/** Fill an area of a surface with a fixed colour. + */ +VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ) +{ + VGfloat vg_clear_colour[4]; + + gx_priv_colour_to_paint(fill_colour, vg_clear_colour); + vgSeti(VG_SCISSORING, VG_FALSE); + + vgSetfv(VG_CLEAR_COLOR, 4, vg_clear_colour); + vgClear(x, y, width, height); + + int err = vgGetError(); + if (err) + { + GX_LOG("vg error %x filling area", err); + vcos_assert(0); + } + + return VCOS_SUCCESS; +} + +VCOS_STATUS_T gx_priv_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **p_pixels, GX_RASTER_ORDER_T raster_order) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + void *pixels, *dest; + uint32_t width, height; + int data_size, pitch; + VGImageFormat image_format; + + if (!p_pixels) + { + status = VCOS_EINVAL; + goto finish; + } + + GX_TRACE("%s: res %p", __FUNCTION__, res); + + graphics_get_resource_size(res, &width, &height); + + /* FIXME: implement e.g. gx_get_pitch */ + switch (res->restype) + { + case GRAPHICS_RESOURCE_RGB565: + pitch = ((width + 31)&(~31)) << 1; + break; + case GRAPHICS_RESOURCE_RGB888: + case GRAPHICS_RESOURCE_RGBA32: + pitch = ((width + 31)&(~31)) << 2; + break; + default: + { + GX_LOG("Unsupported pixel format"); + status = VCOS_EINVAL; + goto finish; + } + } + + data_size = pitch * height; + + /* NB: vgReadPixels requires that the data pointer is aligned, but does not + require the stride to be aligned. Most implementations probably will + require that as well though... */ + pixels = vcos_malloc(data_size, "gx_get_pixels data"); + if (!pixels) + { + GX_LOG("Could not allocate %d bytes for vgReadPixels", data_size); + status = VCOS_ENOMEM; + goto finish; + } + /* FIXME: introduce e.g. GX_COLOR_FORMAT and mapping to VGImageFormat... */ + + /* Hand out image data formatted to match OpenGL RGBA format. + */ + switch (res->restype) + { + case GRAPHICS_RESOURCE_RGB565: + image_format = VG_sBGR_565; + break; + case GRAPHICS_RESOURCE_RGB888: + image_format = VG_sXBGR_8888; + break; + case GRAPHICS_RESOURCE_RGBA32: + image_format = VG_sABGR_8888; + break; + default: + { + GX_LOG("Unsupported pixel format"); + status = VCOS_EINVAL; + goto finish; + } + } + + /* VG raster order is bottom-to-top */ + if (raster_order == GX_TOP_BOTTOM) + { + dest = ((uint8_t*)pixels)+(pitch*(height-1)); + pitch = -pitch; + } + else + { + dest = pixels; + } + + vgReadPixels(dest, pitch, image_format, 0, 0, width, height); + + vcos_assert(vgGetError() == 0); + + *p_pixels = pixels; + +finish: + return status; +} + +static VCOS_STATUS_T convert_image_type(GRAPHICS_RESOURCE_TYPE_T image_type, + VGImageFormat *vg_image_type, + int *pbytes_per_pixel) +{ + int bytes_per_pixel; + + switch (image_type) + { + case GRAPHICS_RESOURCE_RGB565: + *vg_image_type = VG_sRGB_565; + bytes_per_pixel = 2; + break; + case GRAPHICS_RESOURCE_RGB888: + *vg_image_type = VG_sRGBX_8888; + bytes_per_pixel = 3; // 24 bpp + break; + case GRAPHICS_RESOURCE_RGBA32: + *vg_image_type = VG_sARGB_8888; + bytes_per_pixel = 4; + break; + default: + vcos_assert(0); + *vg_image_type = 0; + return VCOS_EINVAL; + } + if (pbytes_per_pixel) + *pbytes_per_pixel = bytes_per_pixel; + + return VCOS_SUCCESS; +} + + +/*****************************************************************************/ +VCOS_STATUS_T gx_create_pbuffer( uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GRAPHICS_RESOURCE_HANDLE h; + VGImage image; + VGImageFormat vg_image_type; + GX_CLIENT_STATE_T save; + + h = vcos_calloc(1,sizeof(*h), "graphics_x_resource"); + if (!h) + { + GX_LOG("%s: no memory for resource", __FUNCTION__); + return VCOS_ENOMEM; + } + + status = convert_image_type(image_type, &vg_image_type, NULL); + if (status != VCOS_SUCCESS) + { + vcos_free(h); + return status; + } + + h->magic = RES_MAGIC; + h->context = gx_contexts[image_type]; + h->config = gx_configs[image_type]; + h->alpha = 1.0; + h->type = GX_PBUFFER; + h->width = width; + h->height = height; + h->restype = image_type; + + GX_TRACE("Creating pbuffer surface"); + + EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; + h->surface = eglCreatePbufferSurface(display.disp, h->config, + attribs); + if (!h->surface) + { + GX_LOG("Could not create EGL pbuffer surface: 0x%x", eglGetError()); + vcos_free(h); + return VCOS_EINVAL; + } + + gx_priv_save(&save, h); + + image = vgCreateImage(vg_image_type, width, height, VG_IMAGE_QUALITY_BETTER); + if (image == VG_INVALID_HANDLE) + { + GX_LOG("Could not create vg image type %d: vg error 0x%x", + vg_image_type, vgGetError()); + eglDestroySurface(display.disp, h->surface); + vcos_free(h); + status = VCOS_ENOMEM; + goto finish; + } + + h->u.pixmap = image; + + // fill it with black + status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff)); + vcos_assert(status == VCOS_SUCCESS); + + *resource_handle = h; +finish: + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +GX_PAINT_T *gx_create_gradient(GRAPHICS_RESOURCE_HANDLE res, + uint32_t start_colour, + uint32_t end_colour) +{ + // holds the two colour stops (offset,r,g,b,a). + VGfloat fill_stops[10]; + GX_CLIENT_STATE_T save; + VGPaint paint = VG_INVALID_HANDLE; + + gx_priv_save(&save, res); + + paint = vgCreatePaint(); + if (!paint) + { + gx_priv_restore(&save); + vcos_log("Could not create paint: vg %d\n", vgGetError()); + vcos_assert(0); + goto finish; + } + + fill_stops[0] = 0.0; + gx_priv_colour_to_paint(start_colour, fill_stops+1); + + fill_stops[5] = 1.0; + gx_priv_colour_to_paint(end_colour, fill_stops+6); + + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, 5*2, fill_stops); + +finish: + gx_priv_restore(&save); + return (GX_PAINT_T*)paint; +} + +/*****************************************************************************/ +void gx_destroy_paint(GRAPHICS_RESOURCE_HANDLE res, GX_PAINT_T *p) +{ + GX_CLIENT_STATE_T save; + VGPaint paint = (VGPaint)p; + gx_priv_save(&save, res); + vgDestroyPaint(paint); + gx_priv_restore(&save); +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_fill_gradient(GRAPHICS_RESOURCE_HANDLE dest, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + uint32_t radius, + GX_PAINT_T *p) +{ + /* Define start and end points of gradient, see OpenVG specification, + section 9.3.3. */ + VGfloat gradient[4] = {0.0, 0.0, 0.0, 0.0}; + VGPaint paint = (VGPaint)p; + VGPath path; + GX_CLIENT_STATE_T save; + VCOS_STATUS_T status = VCOS_SUCCESS; + + if (!paint) + return VCOS_EINVAL; + + gx_priv_save(&save, dest); + + if (width == GRAPHICS_RESOURCE_WIDTH) + width = dest->width; + + if (height == GRAPHICS_RESOURCE_HEIGHT) + height = dest->height; + + gradient[2] = width; + + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient); + vgSetPaint(paint, VG_FILL_PATH); + + path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32, + 1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL); + if (!path) + { + status = VCOS_ENOMEM; + goto finish; + } + + vguRoundRect(path, (VGfloat)x, (VGfloat)y, (VGfloat)width, (VGfloat)height, + (VGfloat)radius, (VGfloat)radius); + vgDrawPath(path, VG_FILL_PATH); + vgDestroyPath(path); + + vcos_assert(vgGetError() == 0); + +finish: + gx_priv_restore(&save); + + return status; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_graphics_init(const char *font_dir) +{ + GX_CLIENT_STATE_T save; + VCOS_STATUS_T rc; + + gx_priv_save(&save, NULL); + + rc = gx_priv_initialise(); + if (rc == VCOS_SUCCESS) + rc = gx_priv_font_init(font_dir); + + gx_priv_restore(&save); + + return rc; +} + +/*****************************************************************************/ +int gx_is_double_buffered(void) +{ + return 1; +} + +/*****************************************************************************/ +int32_t graphics_userblt(GRAPHICS_RESOURCE_TYPE_T src_type, + const void *src_data, + const uint32_t src_x, + const uint32_t src_y, + const uint32_t width, + const uint32_t height, + const uint32_t pitch, + GRAPHICS_RESOURCE_HANDLE dest, + const uint32_t x_pos, + const uint32_t y_pos ) +{ + VCOS_STATUS_T status; + VGImageFormat vg_src_type; + int bytes_per_pixel; + GX_CLIENT_STATE_T save; + + status = convert_image_type(src_type, &vg_src_type, &bytes_per_pixel); + if (status != VCOS_SUCCESS) + return status; + + gx_priv_save(&save, dest); + + if (dest->type == GX_PBUFFER) + { + vgImageSubData(dest->u.pixmap, + src_data, + pitch, + vg_src_type, + x_pos, y_pos, width, height); + } + else if (dest->type == GX_WINDOW) + { + // need to invert this as VG thinks zero is at the bottom + // while graphics_x thinks it is at the top. + vgWritePixels((uint8_t*)src_data + pitch*(height-1), + -pitch, + vg_src_type, + x_pos, dest->height-y_pos-height, width, height); + } + else + { + vcos_assert(0); + } + + if (vgGetError() == 0) + status = VCOS_SUCCESS; + else + { + vcos_assert(0); + status = VCOS_EINVAL; + } + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height ) +{ + return graphics_resource_text_dimensions_ext(resource_handle, text, text_length, width, height, ATEXT_FONT_SIZE); +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_render_arrowhead(GRAPHICS_RESOURCE_HANDLE res, + uint32_t tip_x, uint32_t tip_y, + int32_t w, int32_t h, + GX_PAINT_T *p) +{ + VGfloat gradient[4]; + VGPaint paint = (VGPaint)p; + VGPath path; + VCOS_STATUS_T status = VCOS_SUCCESS; + + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + if (!paint) + { + vcos_assert(0); + status = VCOS_EINVAL; + goto finish; + } + + gradient[0] = 0.0; gradient[1] = 0.0; + gradient[2] = w; gradient[2] = 0.0; + + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient); + vgSetPaint(paint, VG_FILL_PATH); + + path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32, + 1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL); + if (!path) + { + status = VCOS_ENOMEM; + goto finish; + } + VGfloat points[] = { + (VGfloat)tip_x, (VGfloat)tip_y, + (VGfloat)tip_x + w, (VGfloat)tip_y + h/2, + (VGfloat)tip_x + w, (VGfloat)tip_y - h/2, + }; + + vguPolygon(path, points, 3, 1); + + vgDrawPath(path, VG_FILL_PATH); + vgDestroyPath(path); + + vcos_assert(vgGetError()==0); + +finish: + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t gx_apply_alpha( GRAPHICS_RESOURCE_HANDLE resource_handle, + const uint8_t alpha ) +{ + vcos_assert(resource_handle); + if (resource_handle->type != GX_PBUFFER) + { + vcos_assert(0); + return -1; + } + resource_handle->alpha = 1.0*alpha/255; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_resource_set_alpha_per_colour( GRAPHICS_RESOURCE_HANDLE res, + const uint32_t colour, + const uint8_t alpha ) +{ + GX_ERROR("Not implemented yet!"); + return 0; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **pixels) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + /* Default to top-top-bottom raster scan order */ + status = gx_priv_get_pixels(res, pixels, GX_TOP_BOTTOM); + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_get_pixels_in_raster_order(const GRAPHICS_RESOURCE_HANDLE res, + void **pixels, + GX_RASTER_ORDER_T raster_order) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + status = gx_priv_get_pixels(res, pixels, raster_order); + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +void gx_free_pixels(const GRAPHICS_RESOURCE_HANDLE res, void *pixels) +{ + vcos_free(pixels); +} + +VCOS_STATUS_T gx_bind_vg( GX_CLIENT_STATE_T *save, GRAPHICS_RESOURCE_HANDLE res ) +{ + gx_priv_save(save, res); + vcos_assert(vgGetError()==0); + return VCOS_SUCCESS; +} + +/** Unbind VG */ +void gx_unbind_vg(GX_CLIENT_STATE_T *restore) +{ + gx_priv_restore(restore); +} + + +GX_CLIENT_STATE_T *gx_alloc_context(void) +{ + GX_CLIENT_STATE_T *ret = vcos_calloc(1,sizeof(*ret), "gx_client_state"); + return ret; +} + +void gx_free_context(GX_CLIENT_STATE_T *state) +{ + vcos_free(state); +} + +void gx_convert_colour(uint32_t colour, float *dest) +{ + gx_priv_colour_to_paint(colour, dest); +} + + +#define MAX_DISPLAY_HANDLES 4 + +#define CHANGE_LAYER (1<<0) +#define CHANGE_OPACITY (1<<1) +#define CHANGE_DEST (1<<2) +#define CHANGE_SRC (1<<3) +#define CHANGE_MASK (1<<4) +#define CHANGE_XFORM (1<<5) + +typedef struct +{ + /** Keep a display handle going for each connected screen (LCD, HDMI). */ + DISPMANX_DISPLAY_HANDLE_T screens[MAX_DISPLAY_HANDLES]; + int refcounts[MAX_DISPLAY_HANDLES]; + + //a flag to count the number of dispman starts that have been invoked + + uint32_t dispman_start_count; + // maintain the single global handle to the update in progress + DISPMANX_UPDATE_HANDLE_T current_update; + + VCOS_MUTEX_T lock; +} gx_priv_state_t; + +static gx_priv_state_t gx; + +void gx_priv_init(void) +{ + vcos_mutex_create(&gx.lock,NULL); +} + +void gx_priv_destroy(void) +{ + vcos_mutex_delete(&gx.lock); +} + + +static +int32_t gx_priv_open_screen(uint32_t index, DISPMANX_DISPLAY_HANDLE_T *pscreen) +{ + int ret = -1; + vcos_mutex_lock(&gx.lock); + + if (gx.refcounts[index] != 0) + { + *pscreen = gx.screens[index]; + gx.refcounts[index]++; + ret = 0; + } + else + { + DISPMANX_DISPLAY_HANDLE_T h = vc_dispmanx_display_open(index); + if (h == DISPMANX_NO_HANDLE) + { + GX_LOG("Could not open dispmanx display %d", index); + ret = -1; + goto finish; + } + gx.screens[index] = h; + gx.refcounts[index] = 1; + *pscreen = h; + ret = 0; + } +finish: + vcos_mutex_unlock(&gx.lock); + return ret; +} + +static +int32_t gx_priv_release_screen(uint32_t index) +{ + vcos_mutex_lock(&gx.lock); + gx.refcounts[index]--; + if (gx.refcounts[index] == 0) + { + vc_dispmanx_display_close(gx.screens[index]); + gx.screens[index] = DISPMANX_NO_HANDLE; + } + vcos_mutex_unlock(&gx.lock); + return 0; +} + + + + +int gx_priv_create_native_window(uint32_t screen_id, + uint32_t w, uint32_t h, + GRAPHICS_RESOURCE_TYPE_T type, + GX_NATIVE_WINDOW_T *win, + void **cookie) +{ + int rc; + DISPMANX_DISPLAY_HANDLE_T dispmanx_display; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + DISPMANX_UPDATE_HANDLE_T current_update; + *cookie = NULL; + + rc = gx_priv_open_screen(screen_id, &dispmanx_display); + if (rc < 0) + { + GX_LOG("Could not open display %d", screen_id); + goto fail_screen; + } + + current_update = vc_dispmanx_update_start(0); + if (!current_update) + { + GX_LOG("Could not start update on screen %d", screen_id); + goto fail_update; + } + + src_rect.x = src_rect.y = 0; + src_rect.width = w << 16; + src_rect.height = h << 16; + + dst_rect.x = dst_rect.y = 0; + dst_rect.width = dst_rect.height = 1; + + win->egl_win.width = w; + win->egl_win.height = h; + VC_DISPMANX_ALPHA_T alpha; + memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); + alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; + + DISPMANX_CLAMP_T clamp; + memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T)); + + win->egl_win.element = vc_dispmanx_element_add(current_update, dispmanx_display, + 0 /* layer */, &dst_rect, + 0 /* src */, &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha /* alpha */, + &clamp /* clamp */, + 0 /* transform */); + + if ( !win->egl_win.element ) + { + GX_LOG("Could not add element %dx%d",w,h); + vc_dispmanx_update_submit_sync(current_update); + rc = -1; + } + + // have to pass back the update so it can be completed *After* the + // window has been initialised (filled with background colour). + *cookie = (void*)current_update; + + return 0; + +fail_update: + gx_priv_release_screen(screen_id); +fail_screen: + return rc; +} + +void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res, + void *current_update) +{ + vc_dispmanx_update_submit_sync((DISPMANX_UPDATE_HANDLE_T)current_update); +} + +void +gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res) +{ + DISPMANX_UPDATE_HANDLE_T current_update; + + if((current_update = vc_dispmanx_update_start(0)) != 0) + { + int ret = vc_dispmanx_element_remove(current_update, res->u.native_window.egl_win.element); + vcos_assert(ret == 0); + ret = vc_dispmanx_update_submit_sync(current_update); + vcos_assert(ret == 0); + } + + gx_priv_release_screen(res->screen_id); +} + + +/*********************************************************** + * Name: graphics_get_display_size + * + * Arguments: + * void + * + * Description: Return size of display + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_get_display_size( const uint16_t display_number, + uint32_t *width, + uint32_t *height) +{ + DISPMANX_MODEINFO_T mode_info; + int32_t success = -1; + DISPMANX_DISPLAY_HANDLE_T disp; + vcos_assert(width && height); + *width = *height = 0; + + if(vcos_verify(display_number < MAX_DISPLAY_HANDLES)) + { + // TODO Shouldn't this close the display if it wasn't previously open? + if (gx_priv_open_screen(display_number, &disp) < 0) + { + vcos_assert(0); + return -1; + } + success = vc_dispmanx_display_get_info(disp, &mode_info); + + if( success >= 0 ) + { + *width = mode_info.width; + *height = mode_info.height; + vcos_assert(*height > 64); + } + else + { + vcos_assert(0); + } + } + + return success; +} + +static inline uint16_t auto_size(uint16_t arg, uint16_t actual_size) +{ + return arg == GRAPHICS_RESOURCE_WIDTH ? actual_size : arg; +} + +int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res, + const uint16_t screen_number, + const int16_t z_order, + const uint16_t offset_x, + const uint16_t offset_y, + const uint16_t dest_width, + const uint16_t dest_height, + const VC_DISPMAN_TRANSFORM_T transform, + const uint8_t display ) +{ + DISPMANX_UPDATE_HANDLE_T update; + int32_t rc; + int xform_changed; + + if (!res) + { + // mimics old behaviour. + (void)vcos_verify(0); + return 0; + } + vcos_assert(res->magic == RES_MAGIC); + + xform_changed = transform != res->transform; + res->transform = transform; + + rc = graphics_update_start(); + update = gx.current_update; + vcos_assert(rc == 0); + + if (display) + { + VC_RECT_T src_rect, dest_rect; + + int32_t src_width = res->width; + int32_t src_height = res->height; + + uint32_t change_flags = CHANGE_LAYER; + + // has the destination position changed? + uint32_t w = auto_size(dest_width, res->width); + uint32_t h = auto_size(dest_height, res->height); + + vcos_assert(screen_number == res->screen_id); + + if (gx.screens[screen_number] == 0) + { + vcos_assert(0); + DISPMANX_DISPLAY_HANDLE_T display_handle; + gx_priv_open_screen(screen_number, &display_handle); + } + + if ((offset_x != res->dest.x) || + (offset_y != res->dest.y) || + (h != res->dest.height) || + (w != res->dest.width)) + { + change_flags |= CHANGE_DEST; + res->dest.x = offset_x; + res->dest.y = offset_y; + res->dest.height = h; + res->dest.width = w; + } + + if (xform_changed) + change_flags |= CHANGE_XFORM; + + vc_dispmanx_rect_set( &src_rect, 0, 0, ((uint32_t)src_width)<<16, ((uint32_t)src_height)<<16 ); + vc_dispmanx_rect_set( &dest_rect, offset_x, offset_y, w, h); + + rc = vc_dispmanx_element_change_attributes(update, + res->u.native_window.egl_win.element, + change_flags, + z_order, /* layer */ + 0xff, /* opacity */ + &dest_rect, + &src_rect, + 0, transform); + + vcos_assert(rc==0); + gx_priv_flush(res); + + } + else + { + vgFinish(); + eglWaitClient(); + rc = vc_dispmanx_element_change_source(update, res->u.native_window.egl_win.element, 0); + vcos_assert(rc==0); + } + + rc = graphics_update_end(); + vcos_assert(rc==0); + + return rc; +} + +/*********************************************************** + * Name: graphics_update_start + * + * Arguments: + * void + * + * Description: Starts an update UNLESS and update is already in progress + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_update_start(void) +{ + int32_t success = 0; + + //check we are not already in an update + if ( 0 == gx.dispman_start_count ) + { + gx.current_update = vc_dispmanx_update_start( 10 ); + if( gx.current_update == DISPMANX_NO_HANDLE ) + { + //error + success = -1; + vc_assert( 0 ); + } + } + + if( success == 0 ) + { + //inc the counter + gx.dispman_start_count++; + } + + return success; +} + + +/*********************************************************** + * Name: graphics_update_end + * + * Arguments: + * void + * + * Description: Ends an update UNLESS more than one update is in progress + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_update_end( void ) +{ + int32_t success = -1; + + // make sure you are checking the return value of graphics_update_start + if(vcos_verify(gx.current_update != DISPMANX_NO_HANDLE)) + { + //check we are in an update + if(vcos_verify(gx.dispman_start_count > 0)) + { + //dec the counter + gx.dispman_start_count--; + + success = 0; + + //is the counter now 0? + if( 0 == gx.dispman_start_count ) + { + eglWaitClient(); + if( vc_dispmanx_update_submit_sync( gx.current_update ) != 0 ) + { + //error + success = -1; + vc_assert( 0 ); + } + } + } + } + + return success; +} + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h new file mode 100755 index 0000000..05d94b7 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Graphics library for VG + +#ifndef GRAPHICS_X_PRIVATE_H +#define GRAPHICS_X_PRIVATE_H + +#define VCOS_LOG_CATEGORY (&gx_log_cat) + +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "VG/openvg.h" +#include "VG/vgu.h" + +#include "vgfont.h" +#include "bcm_host.h" + +extern VCOS_LOG_CAT_T gx_log_cat; + +#define LOG_ERR( fmt, arg... ) vcos_log_error( "%s:%d " fmt, __func__, __LINE__, ##arg) + +#define GX_ERROR(format, arg...) if (1) {} else printf( format "\n", ##arg) +#define GX_LOG(format, arg...) if (1) {} else printf( format "\n", ##arg) +#define GX_TRACE(format, arg...) if (1) {} else printf( format "\n", ##arg) + +typedef struct +{ + EGL_DISPMANX_WINDOW_T egl_win; +} GX_NATIVE_WINDOW_T; + +typedef enum +{ + GX_TOP_BOTTOM, + GX_BOTTOM_TOP, +} GX_RASTER_ORDER_T; + +typedef struct {} GX_PAINT_T; + +typedef struct GX_CLIENT_STATE_T GX_CLIENT_STATE_T; +typedef struct { + EGLDisplay disp; +} GX_DISPLAY_T; + +struct GX_DISPLAY_T +{ + EGLDisplay disp; +}; + +typedef enum +{ + GX_WINDOW, GX_PIXMAP, GX_PBUFFER +} GX_RES_TYPE; + +#define RES_MAGIC ('G'<<24|'X'<<16|'R'<<8|'S'<<0) +#define GX_PRIV_FLAG_FLIP (1<<0) + +/** + * Structure encapsulating the per-surface state. + ***********************************************************/ +typedef struct GRAPHICS_RESOURCE_HANDLE_TABLE_T +{ + union + { + GX_NATIVE_WINDOW_T native_window; + VGImage pixmap; + } u; + GX_RES_TYPE type; + + uint32_t magic; /** To work around broken create interface */ + int context_bound; + const char *last_caller; + EGLSurface surface; + EGLContext context; + EGLConfig config; + uint32_t screen_id; /** 0-LCD, etc */ + uint16_t width; + uint16_t height; + GRAPHICS_RESOURCE_TYPE_T restype; + VC_DISPMAN_TRANSFORM_T transform; + + VC_RECT_T dest; /** destination rectangle in use, for book-keeping */ + + VGfloat alpha; +} GRAPHICS_RESOURCE_HANDLE_TABLE_T; + +/** + * Structure used to store an EGL client state. + ***********************************************************/ +struct GX_CLIENT_STATE_T +{ + EGLSurface read_surface; + EGLSurface draw_surface; + EGLContext context; + EGLenum api; + GRAPHICS_RESOURCE_HANDLE res; +}; + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_init(void); + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_destroy(void); + +/** + * \fixme add documentation + * + * @param col colour + * + * @param rgba OpenVG paint colour + * + ***********************************************************/ +void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba); + +/** + * Save current EGL client state. + * + * @param state upon return, holds the saved EGL client state. + * + * @param res handle to the surface the EGL client state belongs to (may be NULL). + * + */ +void gx_priv_save(GX_CLIENT_STATE_T *state, GRAPHICS_RESOURCE_HANDLE res); + +/** + * Restore current EGL client state. + * + * @param state the EGL client state to restore. + * + */ +void gx_priv_restore(GX_CLIENT_STATE_T *state); + +/** + * Create a native window for a surface. + * + * @param screen_id \fixme + * + * @param w width of the window + * + * @param h height of the window + * + * @param type color/raster format of the resource + * + * @param win upon successful return, holds a handle to the native window + * + * @param cookie \fixme + * + * @return VCOS_SUCCESS on success, or error code. + */ +int gx_priv_create_native_window(uint32_t screen_id, + uint32_t w, uint32_t h, + GRAPHICS_RESOURCE_TYPE_T type, + GX_NATIVE_WINDOW_T *win, + void **cookie); + +/** + * Destroy native window bound to surface. + * + * @param res Handle to surface. + * + */ +void gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res); + +/** + * Initialise font from the given directory. + * + * @param font_dir path to font + * + * \fixme only supports Vera.tff at the moment? + * + * @return VCOS_SUCCESS on success, or error code. + */ +VCOS_STATUS_T gx_priv_font_init(const char *font_dir); + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_font_term(void); + +/** + * Fill an area of a surface with a single colour. + * + * @param res Handle to surface. + * + * @param x x-offset of area to fill + * + * @param y y-offset of area to fill + * + * @param width width of area to fill + * + * @param height height of area to fill + * + * @param fill_colour fill colour + * + ***********************************************************/ +VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ); + +/** + * Render text into a surface + * + * @param disp Handle to display. + * + * @param res Handle to surface. + * + * @param x x-offset + * + * @param y y-offset + * + * @param width bounding rectangle width + * + * @param height bounding rectangle height + * + * @param fg_colour foreground color + * + * @param bg_colour background color + * + * @param text text to render + * + * @param text_length length of text + * + * @param text_size size of text + * + ***********************************************************/ +VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, + GRAPHICS_RESOURCE_HANDLE res, + int32_t x, + int32_t y, + uint32_t width, + uint32_t height, + uint32_t fg_colour, + uint32_t bg_colour, + const char *text, + uint32_t text_length, + uint32_t text_size ); + +/** + * Flush a surface. + * + * @param res Handle to surface. + * + ***********************************************************/ +void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res); + +/** + * Called after the EGL/VG initialisation of a window has completed + * following its creation. + * + * @param res ??? + * + * @param cookie ??? + * + ***********************************************************/ +void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res, + void *cookie); + +/** + * Flush font cache. + * + ***********************************************************/ +void gx_font_cache_flush(void); + +/** + * Read a bitmap (.BMP) image from the given file. + * + * @param filename filename (must not be NULL). + * + * @param width holds the width of the image upon return. + * + * @param height holds the height of the image upon return. + * + * @param pitch_bytes holds the pitch of the image data (in bytes) upon return. + * + * @param restype holds the type of the image upon return. + * + * @param vg_format holds the OpenVG image format upon return. + * + * @param flags holds flags describing image properties upon return. + * + * @param image_data_size holds size of the image data upon return. + * + * @param pimage_data holds the image data buffer upon return (must not be NULL), + * the caller is responsible for releasing the buffer afterwards. + * + * @return 0 if success, non-zero otherwise (in which case the output parameters + * may be invalid). + * + ***********************************************************/ +int gx_priv_read_bmp(const char *file_name, + uint32_t *width, uint32_t *height, uint32_t *pitch_bytes, + GRAPHICS_RESOURCE_TYPE_T *restype, + VGImageFormat *vg_format, + uint32_t *flags, + uint32_t *image_data_size, + void **pimage_data); + +/** + * Read a Targa (.TGA) image from the given file. + * + * @param filename filename (must not be NULL). + * + * @param width holds the width of the image upon return. + * + * @param height holds the height of the image upon return. + * + * @param pitch_bytes holds the pitch of the image data (in bytes) upon return. + * + * @param restype holds the type of the image upon return. + * + * @param vg_format holds the OpenVG image format upon return. + * + * @param flags holds flags describing image properties upon return. + * + * @param image_data_size holds size of the image data upon return. + * + * @param pimage_data holds the image data buffer upon return (must not be NULL), + * the caller is responsible for releasing the memory afterwards. + * + * @return 0 if success, non-zero otherwise (in which case the output parameters. + * may be invalid). + * + ***********************************************************/ +int gx_priv_read_tga(const char *file_name, + uint32_t *width, uint32_t *height, uint32_t *pitch_bytes, + GRAPHICS_RESOURCE_TYPE_T *restype, + VGImageFormat *vg_format, + uint32_t *flags, + uint32_t *image_data_size, + void **pimage_data); + +#endif diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h b/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h new file mode 100755 index 0000000..b20bb55 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h @@ -0,0 +1,136 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#ifndef VCFTLIB_H +#define VCFTLIB_H + +#include +#include "interface/vmcs_host/vc_dispservice_x_defs.h" +#include "interface/vctypes/vc_image_types.h" +#include "interface/vcos/vcos.h" + +//Definitions which in certain functions can be used to mean the actual width and height of a resource, without +//having to know the data implicitly. +#define GRAPHICS_RESOURCE_WIDTH 0xFFFF +#define GRAPHICS_RESOURCE_HEIGHT 0xFFFF + +#define R_888_MASK (0x00FF0000) +#define G_888_MASK (0x0000FF00) +#define B_888_MASK (0x000000FF) +#define ALPHA_888_MASK (0xFF000000) +#define GRAPHICS_RGBA32( r, g, b, a ) GRAPHICS_RGBA888( r, g, b, a ) +#define GRAPHICS_RGBA888( r, g, b, a ) ( (((a) << (8+8+8)) & ALPHA_888_MASK) | (((b) << (8+8)) & R_888_MASK) | (((g) << 8) & G_888_MASK) | ((r) & B_888_MASK) ) +#define GRAPHICS_TRANSPARENT_COLOUR 0x00000001UL + +//resource defs + +typedef enum +{ + GRAPHICS_RESOURCE_HANDLE_TYPE_MIN, + + GRAPHICS_RESOURCE_RGB565, + GRAPHICS_RESOURCE_RGB888, /* 888 format is ONLY used when loading bitmaps + - you can't create or delete bitmaps with this format */ + GRAPHICS_RESOURCE_RGBA32, + GRAPHICS_RESOURCE_TF_RGB32A, + GRAPHICS_RESOURCE_TF_RGB565, + GRAPHICS_RESOURCE_YUV420, + + GRAPHICS_RESOURCE_HANDLE_TYPE_MAX + +} GRAPHICS_RESOURCE_TYPE_T; + + +typedef struct GRAPHICS_RESOURCE_HANDLE_TABLE_T *GRAPHICS_RESOURCE_HANDLE; + +VCOS_STATUS_T gx_graphics_init(const char *font_dir); +int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res ); +VCOS_STATUS_T gx_create_window( uint32_t screen_id, + uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ); + +int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res, + const uint16_t screen_number, + const int16_t z_order, + const uint16_t offset_x, + const uint16_t offset_y, + const uint16_t dest_width, + const uint16_t dest_height, + const VC_DISPMAN_TRANSFORM_T transform, + const uint8_t display ); + +int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ); + +int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res, + const uint32_t x_offset, + const uint32_t y_offset, + const uint32_t width, + const uint32_t height ); + +int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, + const uint32_t height, + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length, + const uint32_t text_size ); + +int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height, + const uint32_t text_size ); + +int32_t graphics_get_resource_size( + const GRAPHICS_RESOURCE_HANDLE res, + uint32_t *w, + uint32_t *h); + +int32_t graphics_update_start(void); + +int32_t graphics_update_end( void ); + +int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height ); + +#endif diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c new file mode 100755 index 0000000..53104c3 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c @@ -0,0 +1,424 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#include +#include + +#include "graphics_x_private.h" +#include "vgft.h" + +static FT_Library lib; + +int vgft_init(void) +{ + if (FT_Init_FreeType(&lib) == 0) + return 0; + else + { + return -1; + } +} + +void vgft_term(void) +{ + FT_Done_FreeType(lib); +} + +#define SEGMENTS_COUNT_MAX 256 +#define COORDS_COUNT_MAX 1024 + +static VGuint segments_count; +static VGubyte segments[SEGMENTS_COUNT_MAX]; +static VGuint coords_count; +static VGfloat coords[COORDS_COUNT_MAX]; + +static VGfloat float_from_26_6(FT_Pos x) +{ + return (VGfloat)x / 64.0f; +} + +static void convert_contour(const FT_Vector *points, const char *tags, short points_count) +{ + int first_coords = coords_count; + + int first = 1; + char last_tag = 0; + int c = 0; + + for (; points_count != 0; ++points, ++tags, --points_count) { + ++c; + + char tag = *tags; + if (first) { + assert(tag & 0x1); + assert(c==1); c=0; + segments[segments_count++] = VG_MOVE_TO; + first = 0; + } else if (tag & 0x1) { + /* on curve */ + + if (last_tag & 0x1) { + /* last point was also on -- line */ + assert(c==1); c=0; + segments[segments_count++] = VG_LINE_TO; + } else { + /* last point was off -- quad or cubic */ + if (last_tag & 0x2) { + /* cubic */ + assert(c==3); c=0; + segments[segments_count++] = VG_CUBIC_TO; + } else { + /* quad */ + assert(c==2); c=0; + segments[segments_count++] = VG_QUAD_TO; + } + } + } else { + /* off curve */ + + if (tag & 0x2) { + /* cubic */ + + assert((last_tag & 0x1) || (last_tag & 0x2)); /* last either on or off and cubic */ + } else { + /* quad */ + + if (!(last_tag & 0x1)) { + /* last was also off curve */ + + assert(!(last_tag & 0x2)); /* must be quad */ + + /* add on point half-way between */ + assert(c==2); c=1; + segments[segments_count++] = VG_QUAD_TO; + VGfloat x = (coords[coords_count - 2] + float_from_26_6(points->x)) * 0.5f; + VGfloat y = (coords[coords_count - 1] + float_from_26_6(points->y)) * 0.5f; + coords[coords_count++] = x; + coords[coords_count++] = y; + } + } + } + last_tag = tag; + + coords[coords_count++] = float_from_26_6(points->x); + coords[coords_count++] = float_from_26_6(points->y); + } + + if (last_tag & 0x1) { + /* last point was also on -- line (implicit with close path) */ + assert(c==0); + } else { + ++c; + + /* last point was off -- quad or cubic */ + if (last_tag & 0x2) { + /* cubic */ + assert(c==3); c=0; + segments[segments_count++] = VG_CUBIC_TO; + } else { + /* quad */ + assert(c==2); c=0; + segments[segments_count++] = VG_QUAD_TO; + } + + coords[coords_count++] = coords[first_coords + 0]; + coords[coords_count++] = coords[first_coords + 1]; + } + + segments[segments_count++] = VG_CLOSE_PATH; +} + +static void convert_outline(const FT_Vector *points, const char *tags, const short *contours, short contours_count, short points_count) +{ + segments_count = 0; + coords_count = 0; + + short last_contour = 0; + for (; contours_count != 0; ++contours, --contours_count) { + short contour = *contours + 1; + convert_contour(points + last_contour, tags + last_contour, contour - last_contour); + last_contour = contour; + } + assert(last_contour == points_count); + + assert(segments_count <= SEGMENTS_COUNT_MAX); /* oops... we overwrote some memory */ + assert(coords_count <= COORDS_COUNT_MAX); +} + +VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font) +{ + font->ft_face = NULL; + font->vg_font = vgCreateFont(0); + if (font->vg_font == VG_INVALID_HANDLE) + { + return VCOS_ENOMEM; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len) +{ + if (FT_New_Memory_Face(lib, mem, len, 0, &font->ft_face)) + { + return VCOS_EINVAL; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_load_file(VGFT_FONT_T *font, const char *file) +{ + if (FT_New_Face(lib, file, 0, &font->ft_face)) { + return VCOS_EINVAL; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y) +{ + FT_UInt glyph_index; + FT_ULong ch; + + if (FT_Set_Char_Size(font->ft_face, 0, char_height, dpi_x, dpi_y)) + { + FT_Done_Face(font->ft_face); + vgDestroyFont(font->vg_font); + return VCOS_EINVAL; + } + + ch = FT_Get_First_Char(font->ft_face, &glyph_index); + + while (ch != 0) + { + if (FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT)) { + FT_Done_Face(font->ft_face); + vgDestroyFont(font->vg_font); + return VCOS_ENOMEM; + } + + VGPath vg_path; + FT_Outline *outline = &font->ft_face->glyph->outline; + if (outline->n_contours != 0) { + vg_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL); + assert(vg_path != VG_INVALID_HANDLE); + + convert_outline(outline->points, outline->tags, outline->contours, outline->n_contours, outline->n_points); + vgAppendPathData(vg_path, segments_count, segments, coords); + } else { + vg_path = VG_INVALID_HANDLE; + } + + VGfloat origin[] = { 0.0f, 0.0f }; + VGfloat escapement[] = { float_from_26_6(font->ft_face->glyph->advance.x), float_from_26_6(font->ft_face->glyph->advance.y) }; + vgSetGlyphToPath(font->vg_font, glyph_index, vg_path, VG_FALSE, origin, escapement); + + if (vg_path != VG_INVALID_HANDLE) { + vgDestroyPath(vg_path); + } + ch = FT_Get_Next_Char(font->ft_face, ch, &glyph_index); + } + + return VCOS_SUCCESS; +} + +void vgft_font_term(VGFT_FONT_T *font) +{ + if (font->ft_face) + FT_Done_Face(font->ft_face); + if (font->vg_font) + vgDestroyFont(font->vg_font); + memset(font, 0, sizeof(*font)); +} + + +#define CHAR_COUNT_MAX 200 +static VGuint glyph_indices[CHAR_COUNT_MAX]; +static VGfloat adjustments_x[CHAR_COUNT_MAX]; +static VGfloat adjustments_y[CHAR_COUNT_MAX]; + +// Draws the first char_count characters from text, with adjustments, starting +// from the current origin. The peek argument indicates whether to peek ahead +// and get a final adjustment based on the next character past char_count, or +// else just assume that this is the end of the text and add no final +// adjustment. +// +// Returns silently in some error cases. Assert fails in some error cases. + +static void draw_chars(VGFT_FONT_T *font, const char *text, int char_count, VGbitfield paint_modes, int peek) { + // Put in first character + glyph_indices[0] = FT_Get_Char_Index(font->ft_face, text[0]); + int prev_glyph_index = glyph_indices[0]; + + // Calculate glyph_indices and adjustments + int i; + FT_Vector kern; + for (i = 1; i != char_count; ++i) { + int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!glyph_index) { return; } + glyph_indices[i] = glyph_index; + + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); + adjustments_x[i - 1] = float_from_26_6(kern.x); + adjustments_y[i - 1] = float_from_26_6(kern.y); + + prev_glyph_index = glyph_index; + } + + // Get the last adjustment? + if (peek) { + int peek_glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!peek_glyph_index) { return; } + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, peek_glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); + adjustments_x[char_count - 1] = float_from_26_6(kern.x); + adjustments_y[char_count - 1] = float_from_26_6(kern.y); + } else { + adjustments_x[char_count - 1] = 0.0f; + adjustments_y[char_count - 1] = 0.0f; + } + + vgDrawGlyphs(font->vg_font, char_count, glyph_indices, adjustments_x, adjustments_y, paint_modes, VG_FALSE); +} + +// Goes to the x,y position and draws arbitrary number of characters, draws +// iteratively if the char_count exceeds the max buffer size given above. + +static void draw_line(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, int char_count, VGbitfield paint_modes) { + if (char_count == 0) return; + + // Set origin to requested x,y + VGfloat glor[] = { x, y }; + vgSetfv(VG_GLYPH_ORIGIN, 2, glor); + + // Draw the characters in blocks to reuse buffer memory + const char *curr_text = text; + int chars_left = char_count; + while (chars_left > CHAR_COUNT_MAX) { + draw_chars(font, curr_text, CHAR_COUNT_MAX, paint_modes, 1); + chars_left -= CHAR_COUNT_MAX; + curr_text += CHAR_COUNT_MAX; + } + + // Draw the last block + draw_chars(font, curr_text, chars_left, paint_modes, 0); +} + +// Draw multiple lines of text, starting from the given x and y. The x and y +// correspond to the lower left corner of the first line of text, without +// descenders. Unfortunately, for multiline text, this ends up in the middle of +// the y-extent of the block. + +void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes) +{ + VGfloat descent = float_from_26_6(font->ft_face->size->metrics.descender); + int last_draw = 0; + int i = 0; + y -= descent; + for (;;) { + int last = !text[i] || (text_length && i==text_length); + + if ((text[i] == '\n') || last) + { + draw_line(font, x, y, text + last_draw, i - last_draw, paint_modes); + last_draw = i+1; + y -= float_from_26_6(font->ft_face->size->metrics.height); + } + if (last) + { + break; + } + ++i; + } +} + +// Get text extents for a single line. Returns silently in some error cases. +// Assert fails in some error cases. + +static void line_extents(VGFT_FONT_T *font, VGfloat *x, VGfloat *y, const char *text, int chars_count) +{ + int i; + int prev_glyph_index = 0; + if (chars_count == 0) return; + + for (i=0; i < chars_count; i++) + { + int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!glyph_index) return; + + if (i != 0) + { + FT_Vector kern; + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, + FT_KERNING_DEFAULT, &kern)) + { + assert(0); + } + *x += float_from_26_6(kern.x); + *y += float_from_26_6(kern.y); + } + FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); + *x += float_from_26_6(font->ft_face->glyph->advance.x); + + prev_glyph_index = glyph_index; + } +} + +// Text extents for some ASCII text. +// +// Use text_length if non-zero, otherwise look for trailing '\0'. + +void vgft_get_text_extents(VGFT_FONT_T *font, + const char *text, + unsigned text_length, + VGfloat unused0, VGfloat unused1, + VGfloat *w, VGfloat *h) { + int last_draw = 0; + VGfloat max_x = 0; + VGfloat y = 0; + + int i, last; + for (i = 0, last = 0; !last; ++i) { + last = !text[i] || (text_length && i==text_length); + if ((text[i] == '\n') || last) { + VGfloat x = 0; + line_extents(font, &x, &y, text + last_draw, i - last_draw); + last_draw = i + 1; + y -= float_from_26_6(font->ft_face->size->metrics.height); + if (x > max_x) max_x = x; + } + } + *w = max_x; + *h = -y; +} + +// Get y offset for first line; mitigates issue of start y being middle of block +// for multiline renders by vgft_font_draw. Currently simple, may be worth +// adding y kerning? + +VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font) { + return float_from_26_6(font->ft_face->size->metrics.height); +} diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h new file mode 100755 index 0000000..4fe548d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h @@ -0,0 +1,70 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#ifndef VGFT_H +#define VGFT_H + +#include "interface/vcos/vcos.h" +#include +#include + +typedef int VGFT_BOOL; +#define VGFT_FALSE 0 +#define VGFT_TRUE (!VGFT_FALSE) + +#include FT_FREETYPE_H + +/* Returns 0 on success */ +extern int vgft_init(void); +extern void vgft_term(void); + +typedef struct { + VGFont vg_font; + FT_Face ft_face; +} VGFT_FONT_T; + +/** Initialise a FT->VG font */ +VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font); + +/** Load a font file from memory */ +VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len); + +/** Convert a font into VG glyphs */ +VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y); + +/** Release a font. */ +void vgft_font_term(VGFT_FONT_T *font); + +void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes); + +void vgft_get_text_extents(VGFT_FONT_T *font, const char *text, unsigned text_length, VGfloat start_x, VGfloat start_y, VGfloat *w, VGfloat *h); + +VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font); + +#endif diff --git a/host_applications/linux/apps/hello_pi/rebuild.sh b/host_applications/linux/apps/hello_pi/rebuild.sh new file mode 100755 index 0000000..8225dd5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/rebuild.sh @@ -0,0 +1,34 @@ +make -C libs/ilclient clean +make -C libs/vgfont clean +make -C hello_world clean +make -C hello_triangle clean +make -C hello_triangle2 clean +make -C hello_video clean +make -C hello_audio clean +make -C hello_font clean +make -C hello_dispmanx clean +make -C hello_tiger clean +make -C hello_encode clean +make -C hello_jpeg clean +make -C hello_videocube clean +make -C hello_teapot clean +make -C hello_fft clean +make -C hello_mmal_encode clean + +make -C libs/ilclient +make -C libs/vgfont +make -C hello_world +make -C hello_triangle +make -C hello_triangle2 +make -C hello_video +make -C hello_audio +make -C hello_font +make -C hello_dispmanx +make -C hello_tiger +make -C hello_encode +make -C hello_jpeg +make -C hello_videocube +make -C hello_teapot +make -C hello_fft +make -C hello_mmal_encode + diff --git a/host_applications/linux/apps/raspicam/CMakeLists.txt b/host_applications/linux/apps/raspicam/CMakeLists.txt new file mode 100755 index 0000000..e6aa6b8 --- /dev/null +++ b/host_applications/linux/apps/raspicam/CMakeLists.txt @@ -0,0 +1,36 @@ + +# raspistill/raspivid/raspiyuv + +SET(COMPILE_DEFINITIONS -Werror) + +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/apps/raspicam/) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/sm) + +set (GL_SCENE_SOURCES + gl_scenes/models.c + gl_scenes/mirror.c + gl_scenes/yuv.c + gl_scenes/sobel.c + gl_scenes/square.c + gl_scenes/teapot.c + gl_scenes/vcsm_square.c) + +set (COMMON_SOURCES + RaspiCamControl.c + RaspiCLI.c + RaspiPreview.c) + +add_executable(raspistill ${COMMON_SOURCES} RaspiStill.c RaspiTex.c RaspiTexUtil.c tga.c ${GL_SCENE_SOURCES}) +add_executable(raspiyuv ${COMMON_SOURCES} RaspiStillYUV.c) +add_executable(raspivid ${COMMON_SOURCES} RaspiVid.c) +add_executable(raspividyuv ${COMMON_SOURCES} RaspiVidYUV.c) + +set (MMAL_LIBS mmal_core mmal_util mmal_vc_client) + +target_link_libraries(raspistill ${MMAL_LIBS} vcos bcm_host brcmGLESv2 brcmEGL m) +target_link_libraries(raspiyuv ${MMAL_LIBS} vcos bcm_host) +target_link_libraries(raspivid ${MMAL_LIBS} vcos bcm_host) +target_link_libraries(raspividyuv ${MMAL_LIBS} vcos bcm_host) + +install(TARGETS raspistill raspiyuv raspivid raspividyuv RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/raspicam/Doxyfile b/host_applications/linux/apps/raspicam/Doxyfile new file mode 100755 index 0000000..94285b9 --- /dev/null +++ b/host_applications/linux/apps/raspicam/Doxyfile @@ -0,0 +1,1869 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "RaspiCam" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = Raspberry Pi Camera support applications + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/host_applications/linux/apps/raspicam/Makefile b/host_applications/linux/apps/raspicam/Makefile new file mode 100755 index 0000000..ca1853c --- /dev/null +++ b/host_applications/linux/apps/raspicam/Makefile @@ -0,0 +1,6 @@ +OBJS=RaspiCamControl.o RaspiCLI.o RaspiPreview.o RaspiStill.o +BIN=raspicam.bin +LDFLAGS+=-lmmal -lmmal_core -lmmal_util + +include ../Makefile.include + diff --git a/host_applications/linux/apps/raspicam/README.md b/host_applications/linux/apps/raspicam/README.md new file mode 100755 index 0000000..6232cab --- /dev/null +++ b/host_applications/linux/apps/raspicam/README.md @@ -0,0 +1,477 @@ +RaspiCam Documentation + +This document describes the use of the three Raspberry Pi camera applications as of October 11th 2013. + +There are three applications provided, raspistill, raspivid and raspistillyuv. raspistill and raspistillyuv are very similar and are intended for capturing images, raspivid is for capturing video. + +All the applications are command line driven, written to take advantage of the mmal API which runs over OpenMAX. The mmal API provides an easier to use system than that presented by OpenMAX. Note that mmal is a Broadcom specific API used only on Videocore 4 systems. + +The applications use up to four OpenMAX(mmal) components - camera, preview, encoder and null_sink. All applications use the camera component, raspistill uses the Image Encode component, raspivid uses the Video Encode component and raspistillyuv does not use an encoder, and sends its YUV or RGB output direct from camera component to file. + +The preview display is optional, but can be used full screen or directed to a specific rectangular area on the display. If preview is disabled, the null_sink component is used to 'absorb' the preview frames. It is necessary for the camera to produce preview frames even if not required for display, as they are used for calculating exposure and white balance settings. + +In addition it is possible to omit the filename option, in which case the preview is displayed but no file is written, or to redirect all output to stdout. + +Command line help is available by typing just the application name in on the command line. + + +Setting up the Camera hardware + + +Warning. Cameras are static sensitive. Earth yourself prior to handling the PCB, a sink tap/faucet or similar should suffice if you don't have an earthing strap. +The camera board attaches to the Raspberry Pi via a 15 way ribbon cable. There are only two connections to make, the ribbon cable need to be attached to the camera PCB and the Raspberry Pi itself. You need to get it the right way round or the camera will not work. On the camera PCB, the blue backing on the cable should be away from the PCB, and on the Raspberry Pi it should be towards the Ethernet connection (or where the Ethernet connector would be if you are using a model A). + +Although the connectors on the PCB and the Pi are different, they work in a similar way. On the Raspberry Pi, pull up the tabs on each end of the connector. It should slide up easily, and be able to pivot around slightly. Fully insert the ribbon cable into the slot, ensuring it is straight, then gently press down the tabs to clip it into place. The camera PCB itself also requires you to pull the tabs away from the board, gently insert the cable, then push the tabs back. The PCB connector is a little more awkward than the one on the Pi itself. + +Setting up the Camera software + +Execute the following instructions on the command line to download and install the latest kernel, GPU firmware and applications. You will need a internet connection for this to work correctly. + +sudo apt-get update +sudo apt-get upgrade + +Now you need to enable camera support using the raspi-config program you will have used when you first set up your Raspberry Pi. + +sudo raspi-config + +Use the cursor keys to move to the camera option and select enable. On exiting raspi-config it will ask to reboot. The enable option will ensure that on reboot the correct GPU firmware will be running (with the camera driver and tuning), and the GPU memory split is sufficient to allow the camera to acquire enough memory to run correctly. + +To test that the system is installed and working, try the following command : + +raspistill -v -o test.jpg + +The display should show a 5 second preview from the camera and then take a picture, saved to the file test.jpg, whilst display various informational messages. + +Troubleshooting + +If the camera is not working correctly, there are number of things to try. +Are the ribbon connectors all firmly seated and the right way round? They must be straight in their sockets. +Is the camera module connector firmly attached to the camera PCB? This is the connection from the smaller black camera module itself to the camera PCB. Sometimes this connection can come loose. Using a fingernail, flip up the connector on the PCB, then reseat it with gentle pressure, it engages with a very slight click. +Have sudo apt-get update, sudo apt-get upgrade been run? +Has raspi-config been run and the camera enabled? +Is your power supply sufficient? The camera adds about 200-250mA to the power requirements of the Raspberry Pi. + +So, if things are still not working, try the following: + +Error : raspistill/raspivid not found. This probably means your update/upgrade failed in some way. Try it again. + +Error : ENOMEM displayed. Camera is not starting up. Check all connections again. + +Error : ENOSPC displayed. Camera is probably running out of GPU memory. Check config.txt in the /boot/ folder. The gpu_mem option should be at least 128. + +If after all the above, the camera is still not working, it may be defective. Try posting on the Raspberry Pi forum (Camera section) to see if there is any more help available there. + +Common Command line Options +Preview Window + + + --preview, -p Preview window settings <'x,y,w,h'> + +Allows the user to define the size and location on the screen that the preview window will be placed. Note this will be superimposed over the top of any other windows/graphics. + + --fullscreen, -f Fullscreen preview mode + +Forces the preview window to use the whole screen. Note that the aspect ratio of the incoming image will be retained, so there may be bars on some edges. + + --nopreview, -n, Do not display a preview window + +Disables the preview window completely. Note that even though the preview is disabled, the camera will still be producing frames, so will be using power. + + --opacity, -op Set preview window opacity + +Sets the opacity of the preview windows. 0 = invisible, 255 = fully opaque. + +Camera Control Options + + + --sharpness, -sh Set image sharpness (-100 to 100) + +Set the sharpness of the image, 0 is the default. + + --contrast, -co Set image contrast (-100 to 100) + +Set the contrast of the image, 0 is the default + + --brightness, -br Set image brightness (0 to 100) + +Set the brightness of the image, 50 is the default. 0 is black, 100 is white. + + --saturation, -sa Set image saturation (-100 to 100) + +set the colour saturation of the image. 0 is the default. + + --ISO, -ISO Set capture ISO + +Sets the ISO to be used for captures. Range is 100 to 800. + --vstab, -vs Turn on video stabilisation + +In video mode only, turn on video stabilisation. + + --ev, -ev Set EV compensation + +Set the EV compensation of the image. Range is -10 to +10, default is 0. + + --exposure, -ex Set exposure mode + +Possible options are: + +auto Use automatic exposure mode +night Select setting for night shooting +nightpreview +backlight Select setting for back lit subject +spotlight +sports Select setting for sports (fast shutter etc) +snow Select setting optimised for snowy scenery +beach Select setting optimised for beach +verylong Select setting for long exposures +fixedfps, Constrain fps to a fixed value +antishake Antishake mode +fireworks Select settings + +Note that not all of these settings may be implemented, depending on camera tuning. + + --awb, -awb Set Automatic White Balance (AWB) mode + +off Turn off white balance calculation +auto Automatic mode (default) +sun Sunny mode +cloud Cloudy mode +shade Shaded mode +tungsten Tungsten lighting mode +fluorescent Fluorescent lighting mode +incandescent Incandescent lighting mode +flash Flash mode +horizon Horizon mode + + --imxfx, -ifx Set image effect + +Set an effect to be applied to the image + +none NO effect (default) +negative Negate the image +solarise Solarise the image +posterize Posterise the image +whiteboard Whiteboard effect +blackboard Blackboard effect +sketch Sketch style effect +denoise Denoise the image +emboss Emboss the image +oilpaint Apply an oil paint style effect +hatch Hatch sketch style +gpen +pastel A pastel style effect +watercolour A watercolour style effect +film Film grain style effect +blur Blur the image +saturation Colour saturate the image +colourswap Not fully implemented +washedout Not fully implemented +posterise Not fully implemented +colourpoint Not fully implemented +colourbalance Not fully implemented +cartoon Not fully implemented + + --colfx, -cfx Set colour effect + +The supplied U and V parameters (range 0 to 255) are applied to the U and Y channels of the image. For example, --colfx 128:128 should result in a monochrome image. + + --metering, -mm Set metering mode + +Specify the metering mode used for the preview and capture + +average Average the whole frame for metering. +spot Spot metering +backlit Assume a backlit image +matrix Matrix metering + + --rotation, -rot Set image rotation (0-359) + +Sets the rotation of the image in viewfinder and resulting image. This can take any value from 0 upwards, but due to hardware constraints only 0, 90, 180 and 270 degree rotations are supported. + + + --hflip, -hf Set horizontal flip + +Flips the preview and saved image horizontally. + + --vflip, -vf Set vertical flip + +Flips the preview and saved image vertically. + + --roi, -roi Set sensor region of interest + +Allows the specification of the area of the sensor to be used as the source for the preview and capture. This is defined as x,y for the top left corner, and a width and height, all values in normalised coordinates (0.0-1.0). So to set a ROI at half way across and down the sensor, and an width and height of a quarter of the sensor use : + + -roi 0.5,0.5,0.25,0.25 + + --shutter, -ss Set shutter speed + +Set the shutter speed to the specified value (in microseconds). There is currently an upper limit of approximately 330000us (330ms, 0.33s) past which operation is undefined. This is being investigated. + + --camselect, -cs Select + +Select camera number. Default 0 + +Application specific settings + +raspistill + +--width, -w Set image width +--height, -h Set image height +--quality, -q Set jpeg quality <0 to 100> + +Quality 100 is almost completely uncompressed. 75 is a good all round value + + --raw, -r Add raw bayer data to jpeg metadata + +This option inserts the raw Bayer data from the camera in to the JPEG metadata + + --output -o Output filename . + +Specify the output filename. If not specified, no file is saved. If the filename is '-', then all output is sent to stdout. + + --latest -l Link latest frame to filename + +Make a file system link under this name to the latest frame. + + --verbose, -v Output verbose information during run + +Outputs debugging/information messages during the program run. + + --timeout, -t Time before takes picture and shuts down. + +The program will run for this length of time, then take the capture (if output is specified). If not specified, this is set to 5 seconds. + + --timelapse, -tl Timelapse mode. + +The specific value is the time between shots in milliseconds. Note you should specify %04d at the point in the filename where you want a frame count number to appear. e.g. + + -t 30000 -tl 2000 -o image%04d.jpg + +You can also name the files with timestamp (-ts) or datetime(-dt) + + -t 30000 -tl 2000 -ts -o image%d.jpg + -t 30000 -tl 2000 -dt -o image2015%10d.jpg + +will produce a capture every 2 seconds, over a total period of 30s, named image1.jpg, image0002.jpg..image0015.jpg. Note that the %04d indicates a 4 digit number with leading zero's added to pad to the required number of digits. So, for example, %08d would result in an 8 digit number. + +If a timelapse value of 0 is entered, the application will take pictures as fast as possible. Note there is an minimum enforced pause of 30ms between captures to ensure that exposure calculations can be made. + + --thumb, -th Set thumbnail parameters (x:y:quality) + +Allows specification of the thumbnail image inserted in to the JPEG file. If not specified, defaults are a size of 64x48 at quality 35. + + --demo, -d Run a demo mode + +This options cycles through range of camera options, no capture is done, the demo will end at the end of the timeout period, irrespective of whether all the options have been cycled. The time between cycles should be specified as a millisecond value. + + --encoding, -e Encoding to use for output file + +Valid options are jpg, bmp, gif and png. Note that unaccelerated image types (gif, png, bmp) will take much longer to save than JPG which is hardware accelerated. Also note that the filename suffix is completely ignored when deciding the encoding of a file. + + --exif, -x EXIF tag to apply to captures (format as 'key=value') + +Allows the insertion of specific exif tags in to the JPEG image. You can have up to 32 exif tge entries. This is useful for things like adding GPS metadata. For example, to set the Longitude + + --exif GPS.GPSLongitude=5/1,10/1,15/100 + +would set the Longitude to 5degs, 10 minutes, 15 seconds. See exif documentation for more details on the range of tags available; the supported tags are as follows. + +IFD0.< or +IFD1.< +ImageWidth, ImageLength, BitsPerSample, Compression, PhotometricInterpretation, ImageDescription, Make, Model, StripOffsets, Orientation, SamplesPerPixel, RowsPerString, StripByteCounts, Xresolution, Yresolution, PlanarConfiguration, ResolutionUnit, TransferFunction, Software, DateTime, Artist, WhitePoint, PrimaryChromaticities, JPEGInterchangeFormat, JPEGInterchangeFormatLength, YcbCrCoefficients, YcbCrSubSampling, YcbCrPositioning, ReferenceBlackWhite, Copyright> + +EXIF.< +ExposureTime, FNumber, ExposureProgram, SpectralSensitivity, a +ISOSpeedRatings, OECF, ExifVersion, DateTimeOriginal, DateTimeDigitized, ComponentsConfiguration, CompressedBitsPerPixel, ShutterSpeedValue, ApertureValue, BrightnessValue, ExposureBiasValue, MaxApertureValue, SubjectDistance, MeteringMode, LightSource, Flash, FocalLength, SubjectArea, MakerNote, UserComment, SubSecTime, SubSecTimeOriginal, SubSecTimeDigitized, FlashpixVersion, ColorSpace, PixelXDimension, PixelYDimension, RelatedSoundFile, FlashEnergy, SpacialFrequencyResponse, FocalPlaneXResolution, FocalPlaneYResolution, FocalPlaneResolutionUnit, SubjectLocation, ExposureIndex, SensingMethod, FileSource, SceneType, CFAPattern, CustomRendered, ExposureMode, +WhiteBalance, DigitalZoomRatio, FocalLengthIn35mmFilm, SceneCaptureType, GainControl, Contrast, Saturation, Sharpness, DeviceSettingDescription, SubjectDistanceRange, ImageUniqueID> + +GPS.< +GPSVersionID, GPSLatitudeRef, GPSLatitude, GPSLongitudeRef, GPSLongitude, GPSAltitudeRef, GPSAltitude, GPSTimeStamp, GPSSatellites, GPSStatus, GPSMeasureMode, GPSDOP, GPSSpeedRef, GPSSpeed, GPSTrackRef, GPSTrack, GPSImgDirectionRef, GPSImgDirection, GPSMapDatum, GPSDestLatitudeRef, GPSDestLatitude, GPSDestLongitudeRef, GPSDestLongitude, GPSDestBearingRef, GPSDestBearing, GPSDestDistanceRef, GPSDestDistance, GPSProcessingMethod, GPSAreaInformation, GPSDateStamp, GPSDifferential> + +EINT.< +InteroperabilityIndex, InteroperabilityVersion, RelatedImageFileFormat, RelatedImageWidth, RelatedImageLength> + +Note that a small subset of these tags will be set automatically by the camera system, but will be overridden by any exif options on the command line. + + --fullpreview, -fp Full Preview mode + +This runs the preview windows using the full resolution capture mode. Maximum frames per second in this mode is 15fps and the preview will have the same field of view as the capture. Captures should happen more quickly as no mode change should be required. This feature is currently under development. + + --keypress -k Keypress mode + +The camera is run for the requested time (-t), and a captures can be initiated throughout that by pressing the Enter key. Press X then Enter will exit the application before the timeout is reached. If the timeout is set to 0, the camera will run indefinitely until X then Enter is typed. +Additional supported keys: 'i', 'o' and 'r'. Press 'i' then Enter will zoom in by 10%. Press 'o' then Enter will zoom out by 10%. Press 'r' then Enter will reset zoom. +Using the verbose option (-v) will display a prompt asking for user input, otherwise no prompt is displayed. + + --signal -s Signal mode + +The camera is run for the requested time (-t), and a captures can be initiated throughout that time by sending a USR1 signal to the camera process. If USR2 signal is received the application makes a capture and exits. This can be done using the kill command. You can find the camera process ID using the 'ps ax' command or using pidof command. + + kill -USR1 + kill -USR2 + +raspistillyuv + + +Many of the options for raspistillyuv are the same as those for raspistill. This section shows the differences. + +Unsupported Options: + --exif, --encoding, --thumb, --raw, --quality + +Extra Options : + + --rgb, -rgb Save uncompressed data as RGB888 + +This option forces the image to be saved as RGB data with 8 bits per channel, rather than YUV420. + +Note that the image buffers saved in raspistillyuv are padded to a horizontal size divisible by 16 (so there may be unused bytes at the end of each line to made the width divisible by 16). Buffers are also padded vertically to be divisible by 16, and in the YUV mode, each plane of Y,U,V is padded in this way. + + +raspivid + + + --width, -w Set image width + +Width of resulting video. This should be between 64 and 1920. + + --height, -h Set image height + +Height of resulting video. This should be between 64 and 1080. + + --bitrate, -b Set bitrate. + +Use bits per second, so 10MBits/s would be -b 10000000. For H264, 1080p a high quality bitrate would be 15Mbits/s or more. + + --output -o Output filename . + +Specify the output filename. If not specified, no file is saved. If the filename is '-', then all output is sent to stdout. + + --verbose, -v Output verbose information during run + +Outputs debugging/information messages during the program run. + + --timeout, -t Time before takes picture and shuts down. + +The program will run for this length of time, then take the capture (if output is specified). If not specified, this is set to 5seconds. Setting 0 will mean the application will run continuously until stopped with Ctrl-C. + + --demo, -d Run a demo mode + +This options cycles through range of camera options, no capture is done, the demo will end at the end of the timeout period, irrespective of whether all the options have been cycled. The time between cycles should be specified as a millisecond value. + + --framerate, -fps Specify the frames per second to record + +At present, the minimum frame rate allowed is 2fps, the maximum is 30fps. This is likely to change in the future. + + --penc, -e Display preview image *after* encoding + +Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation, the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases. + + --intra, -g Specify the intra refresh period (key frame rate/GoP) + +Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra refresh period from which subsequent frames are based. This options specifies the numbers of frames between each I-frame. Larger numbers here will reduce the size of the resulting video, smaller numbers make the stream more robust to error. Setting 0 will produce an initial I-frame and then just P-frames. + + --profile, -pf Specify H264 profile to use for encoding + +Sets the H264 profile to be used for the encoding. Options are : baseline, main, high. + +Examples + +Still captures + +By default, captures are done at the highest resolution supported by the sensor. This can be changed using the -w and -h command line options. + +Taking a default capture after 2s (note times are specified in milliseconds) on viewfinder, saving in image.jpg + + raspistill -t 2000 -o image.jpg + +Take a capture at a different resolution. + + raspistill -t 2000 -o image.jpg -w 640 -h 480 + +Now reduce the quality considerably to reduce file size + + raspistill -t 2000 -o image.jpg -q 5 + +Force the preview to appear at coordinate 100,100, with width 300 and height 200 pixels. + + raspistill -t 2000 -o image.jpg -p 100,100,300,200 + +Disable preview entirely + + raspistill -t 2000 -o image.jpg -n + +Save the image as a png file (lossless compression, but slower than JPEG). Note that the filename suffix is ignored when choosing the image encoding. + + raspistill -t 2000 -o image.png -e png + +Add some EXIF information to the JPEG. This sets the Artist tag name to Boris, and the GPS altitude to 123.5m. Note that if setting GPS tags you should set as a minimum GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef, GPSAltitude and GPSAltitudeRef. + + raspistill -t 2000 -o image.jpg -x IFDO.Artist=Boris -x GPS.GPSAltitude=1235/10 + +Set an emboss style image effect + + raspistill -t 2000 -o image.jpg -ifx emboss + +Set the U and V channels of the YUV image to specific values (128:128 produces a greyscale image) + + raspistill -t 2000 -o image.jpg -cfx 128:128 + +Run preview ONLY for 2s, no saved image. + + raspistill -t 2000 + +Take timelapse picture, one every 10 seconds for 10 minutes (10 minutes = 600000ms), named image_num_001_today.jpg, image_num_002_today.jpg onwards, with the latest picture also available under the name latest.jpg. + + raspistill -t 600000 -tl 10000 -o image_num_%03d_today.jpg -l latest.jpg + +Take a picture and send image data to stdout + + raspistill -t 2000 -o - + +Take a picture and send image data to file + + raspistill -t 2000 -o - > my_file.jpg + +Run camera forever, taking a picture when Enter is pressed + + raspistill -t 0 -k -o my_pics%02d.jpg +Video Captures + +Image size and preview settings are the same as for stills capture. Default size for video recording is 1080p (1920x1080) + +Record a 5s clip with default settings (1080p30) + + raspivid -t 5000 -o video.h264 + +Record a 5s clip at a specified bitrate (3.5MBits/s) + + raspivid -t 5000 -o video.h264 -b 3500000 + +Record a 5s clip at a specified framerate (5fps) + + raspivid -t 5000 -o video.h264 -f 5 + +Encode a 5s camera stream and send image data to stdout + + raspivid -t 5000 -o - + +Encode a 5s camera stream and send image data to file + + raspivid -t 5000 -o - > my_file.h264 + + + +Shell Error Codes + +The applications described here will return a standard error code to the shell on completion. Possible error codes are : + +ER_OK 0 Application ran successfully. +EX_USAGE 64 Bad command line parameter +ER_SOFTWARE 70 Software or camera error + 130 Application terminated by ctrl-C. + + + + + + diff --git a/host_applications/linux/apps/raspicam/RaspiCLI.c b/host_applications/linux/apps/raspicam/RaspiCLI.c new file mode 100755 index 0000000..9f858f4 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiCLI.c @@ -0,0 +1,155 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiCLI.c + * Code for handling command line parameters + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * Some functions/structures for command line parameter parsing + * + */ +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "RaspiCLI.h" + + +/** + * Convert a string from command line to a comand_id from the list + * + * @param commands Array of command to check + * @param num_command Number of commands in the array + * @param arg String to search for in the list + * @param num_parameters Returns the number of parameters used by the command + * + * @return command ID if found, -1 if not found + * + */ +int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters) +{ + int command_id = -1; + int j; + + vcos_assert(commands); + vcos_assert(num_parameters); + vcos_assert(arg); + + if (!commands || !num_parameters || !arg) + return -1; + + for (j = 0; j < num_commands; j++) + { + if (!strcmp(arg, commands[j].command) || + !strcmp(arg, commands[j].abbrev)) + { + // match + command_id = commands[j].id; + *num_parameters = commands[j].num_parameters; + break; + } + } + + return command_id; +} + + +/** + * Display the list of commands in help format + * + * @param commands Array of command to check + * @param num_command Number of commands in the arry + * + * + */ +void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands) +{ + int i; + + vcos_assert(commands); + + if (!commands) + return; + + for (i = 0; i < num_commands; i++) + { + fprintf(stdout, "-%s, -%s\t: %s\n", commands[i].abbrev, + commands[i].command, commands[i].help); + } +} + + +/** + * Function to take a string, a mapping, and return the int equivalent + * @param str Incoming string to match + * @param map Mapping data + * @param num_refs The number of items in the mapping data + * @return The integer match for the string, or -1 if no match + */ +int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs) +{ + int i; + + for (i=0;i +#include +#include + +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vc_vchi_gencmd.h" +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "RaspiCamControl.h" +#include "RaspiCLI.h" + +/// Structure to cross reference exposure strings against the MMAL parameter equivalent +static XREF_T exposure_map[] = +{ + {"off", MMAL_PARAM_EXPOSUREMODE_OFF}, + {"auto", MMAL_PARAM_EXPOSUREMODE_AUTO}, + {"night", MMAL_PARAM_EXPOSUREMODE_NIGHT}, + {"nightpreview", MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW}, + {"backlight", MMAL_PARAM_EXPOSUREMODE_BACKLIGHT}, + {"spotlight", MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT}, + {"sports", MMAL_PARAM_EXPOSUREMODE_SPORTS}, + {"snow", MMAL_PARAM_EXPOSUREMODE_SNOW}, + {"beach", MMAL_PARAM_EXPOSUREMODE_BEACH}, + {"verylong", MMAL_PARAM_EXPOSUREMODE_VERYLONG}, + {"fixedfps", MMAL_PARAM_EXPOSUREMODE_FIXEDFPS}, + {"antishake", MMAL_PARAM_EXPOSUREMODE_ANTISHAKE}, + {"fireworks", MMAL_PARAM_EXPOSUREMODE_FIREWORKS} +}; + +static const int exposure_map_size = sizeof(exposure_map) / sizeof(exposure_map[0]); + +/// Structure to cross reference flicker avoid strings against the MMAL parameter equivalent + +static XREF_T flicker_avoid_map[] = +{ + {"off", MMAL_PARAM_FLICKERAVOID_OFF}, + {"auto", MMAL_PARAM_FLICKERAVOID_AUTO}, + {"50hz", MMAL_PARAM_FLICKERAVOID_50HZ}, + {"60hz", MMAL_PARAM_FLICKERAVOID_60HZ} +}; + +static const int flicker_avoid_map_size = sizeof(flicker_avoid_map) / sizeof(flicker_avoid_map[0]); + +/// Structure to cross reference awb strings against the MMAL parameter equivalent +static XREF_T awb_map[] = +{ + {"off", MMAL_PARAM_AWBMODE_OFF}, + {"auto", MMAL_PARAM_AWBMODE_AUTO}, + {"sun", MMAL_PARAM_AWBMODE_SUNLIGHT}, + {"cloud", MMAL_PARAM_AWBMODE_CLOUDY}, + {"shade", MMAL_PARAM_AWBMODE_SHADE}, + {"tungsten", MMAL_PARAM_AWBMODE_TUNGSTEN}, + {"fluorescent", MMAL_PARAM_AWBMODE_FLUORESCENT}, + {"incandescent", MMAL_PARAM_AWBMODE_INCANDESCENT}, + {"flash", MMAL_PARAM_AWBMODE_FLASH}, + {"horizon", MMAL_PARAM_AWBMODE_HORIZON} +}; + +static const int awb_map_size = sizeof(awb_map) / sizeof(awb_map[0]); + +/// Structure to cross reference image effect against the MMAL parameter equivalent +static XREF_T imagefx_map[] = +{ + {"none", MMAL_PARAM_IMAGEFX_NONE}, + {"negative", MMAL_PARAM_IMAGEFX_NEGATIVE}, + {"solarise", MMAL_PARAM_IMAGEFX_SOLARIZE}, + {"sketch", MMAL_PARAM_IMAGEFX_SKETCH}, + {"denoise", MMAL_PARAM_IMAGEFX_DENOISE}, + {"emboss", MMAL_PARAM_IMAGEFX_EMBOSS}, + {"oilpaint", MMAL_PARAM_IMAGEFX_OILPAINT}, + {"hatch", MMAL_PARAM_IMAGEFX_HATCH}, + {"gpen", MMAL_PARAM_IMAGEFX_GPEN}, + {"pastel", MMAL_PARAM_IMAGEFX_PASTEL}, + {"watercolour", MMAL_PARAM_IMAGEFX_WATERCOLOUR}, + {"film", MMAL_PARAM_IMAGEFX_FILM}, + {"blur", MMAL_PARAM_IMAGEFX_BLUR}, + {"saturation", MMAL_PARAM_IMAGEFX_SATURATION}, + {"colourswap", MMAL_PARAM_IMAGEFX_COLOURSWAP}, + {"washedout", MMAL_PARAM_IMAGEFX_WASHEDOUT}, + {"posterise", MMAL_PARAM_IMAGEFX_POSTERISE}, + {"colourpoint", MMAL_PARAM_IMAGEFX_COLOURPOINT}, + {"colourbalance", MMAL_PARAM_IMAGEFX_COLOURBALANCE}, + {"cartoon", MMAL_PARAM_IMAGEFX_CARTOON} + }; + +static const int imagefx_map_size = sizeof(imagefx_map) / sizeof(imagefx_map[0]); + +static XREF_T metering_mode_map[] = +{ + {"average", MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE}, + {"spot", MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT}, + {"backlit", MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT}, + {"matrix", MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX} +}; + +static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]); + +static XREF_T drc_mode_map[] = +{ + {"off", MMAL_PARAMETER_DRC_STRENGTH_OFF}, + {"low", MMAL_PARAMETER_DRC_STRENGTH_LOW}, + {"med", MMAL_PARAMETER_DRC_STRENGTH_MEDIUM}, + {"high", MMAL_PARAMETER_DRC_STRENGTH_HIGH} +}; + +static const int drc_mode_map_size = sizeof(drc_mode_map)/sizeof(drc_mode_map[0]); + +static XREF_T stereo_mode_map[] = +{ + {"off", MMAL_STEREOSCOPIC_MODE_NONE}, + {"sbs", MMAL_STEREOSCOPIC_MODE_SIDE_BY_SIDE}, + {"tb", MMAL_STEREOSCOPIC_MODE_TOP_BOTTOM}, +}; + +static const int stereo_mode_map_size = sizeof(stereo_mode_map)/sizeof(stereo_mode_map[0]); + + +#define CommandSharpness 0 +#define CommandContrast 1 +#define CommandBrightness 2 +#define CommandSaturation 3 +#define CommandISO 4 +#define CommandVideoStab 5 +#define CommandEVComp 6 +#define CommandExposure 7 +#define CommandAWB 8 +#define CommandImageFX 9 +#define CommandColourFX 10 +#define CommandMeterMode 11 +#define CommandRotation 12 +#define CommandHFlip 13 +#define CommandVFlip 14 +#define CommandROI 15 +#define CommandShutterSpeed 16 +#define CommandAwbGains 17 +#define CommandDRCLevel 18 +#define CommandStatsPass 19 +#define CommandAnnotate 20 +#define CommandStereoMode 21 +#define CommandStereoDecimate 22 +#define CommandStereoSwap 23 +#define CommandAnnotateExtras 24 +#define CommandFlicker 25 +#define CommandAnalogGain 26 +#define CommandDigitalGain 27 + +static COMMAND_LIST cmdline_commands[] = +{ + {CommandSharpness, "-sharpness", "sh", "Set image sharpness (-100 to 100)", 1}, + {CommandContrast, "-contrast", "co", "Set image contrast (-100 to 100)", 1}, + {CommandBrightness, "-brightness","br", "Set image brightness (0 to 100)", 1}, + {CommandSaturation, "-saturation","sa", "Set image saturation (-100 to 100)", 1}, + {CommandISO, "-ISO", "ISO","Set capture ISO", 1}, + {CommandVideoStab, "-vstab", "vs", "Turn on video stabilisation", 0}, + {CommandEVComp, "-ev", "ev", "Set EV compensation - steps of 1/6 stop", 1}, + {CommandExposure, "-exposure", "ex", "Set exposure mode (see Notes)", 1}, + {CommandFlicker, "-flicker", "fli","Set flicker avoid mode (see Notes)", 1}, + {CommandAWB, "-awb", "awb","Set AWB mode (see Notes)", 1}, + {CommandImageFX, "-imxfx", "ifx","Set image effect (see Notes)", 1}, + {CommandColourFX, "-colfx", "cfx","Set colour effect (U:V)", 1}, + {CommandMeterMode, "-metering", "mm", "Set metering mode (see Notes)", 1}, + {CommandRotation, "-rotation", "rot","Set image rotation (0-359)", 1}, + {CommandHFlip, "-hflip", "hf", "Set horizontal flip", 0}, + {CommandVFlip, "-vflip", "vf", "Set vertical flip", 0}, + {CommandROI, "-roi", "roi","Set region of interest (x,y,w,d as normalised coordinates [0.0-1.0])", 1}, + {CommandShutterSpeed,"-shutter", "ss", "Set shutter speed in microseconds", 1}, + {CommandAwbGains, "-awbgains", "awbg", "Set AWB gains - AWB mode must be off", 1}, + {CommandDRCLevel, "-drc", "drc", "Set DRC Level (see Notes)", 1}, + {CommandStatsPass, "-stats", "st", "Force recomputation of statistics on stills capture pass"}, + {CommandAnnotate, "-annotate", "a", "Enable/Set annotate flags or text", 1}, + {CommandStereoMode, "-stereo", "3d", "Select stereoscopic mode", 1}, + {CommandStereoDecimate,"-decimate","dec", "Half width/height of stereo image"}, + {CommandStereoSwap, "-3dswap", "3dswap", "Swap camera order for stereoscopic"}, + {CommandAnnotateExtras,"-annotateex","ae", "Set extra annotation parameters (text size, text colour(hex YUV), bg colour(hex YUV))", 2}, + {CommandAnalogGain, "-analoggain", "ag", "Set the analog gain (floating point)", 1}, + {CommandDigitalGain, "-digitalgain", "dg", "Set the digtial gain (floating point)", 1}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + + +#define parameter_reset -99999 + +#define zoom_full_16P16 ((unsigned int)(65536 * 0.15)) +#define zoom_increment_16P16 (65536UL / 10) + +/** + * Update the passed in parameter according to the rest of the parameters + * passed in. + * + * + * @return 0 if reached end of cycle for this parameter, !0 otherwise + */ +static int update_cycle_parameter(int *option, int min, int max, int increment) +{ + vcos_assert(option); + if (!option) + return 0; + + if (*option == parameter_reset) + *option = min - increment; + + *option += increment; + + if (*option > max) + { + *option = parameter_reset; + return 0; + } + else + return 1; +} + + +/** + * Test/Demo code to cycle through a bunch of camera settings + * This code is pretty hacky so please don't complain!! + * It only does stuff that should have a visual impact (hence demo!) + * This will override any user supplied parameters + * + * Each call of this function will move on to the next setting + * + * @param camera Pointer to the camera to change settings on. + * @return 0 if reached end of complete sequence, !0 otherwise + */ + +int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera) +{ + static int parameter = 0; + static int parameter_option = parameter_reset; // which value the parameter currently has + + vcos_assert(camera); + + // We are going to cycle through all the relevant entries in the parameter block + // and send options to the camera. + if (parameter == 0) + { + // sharpness + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_sharpness(camera, parameter_option); + else + { + raspicamcontrol_set_sharpness(camera, 0); + parameter++; + } + } + else + if (parameter == 1) + { + // contrast + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_contrast(camera, parameter_option); + else + { + raspicamcontrol_set_contrast(camera, 0); + parameter++; + } + } + else + if (parameter == 2) + { + // brightness + if (update_cycle_parameter(¶meter_option, 0, 100, 10)) + raspicamcontrol_set_brightness(camera, parameter_option); + else + { + raspicamcontrol_set_brightness(camera, 50); + parameter++; + } + } + else + if (parameter == 3) + { + // contrast + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_saturation(camera, parameter_option); + else + { + parameter++; + raspicamcontrol_set_saturation(camera, 0); + } + } + else + if (parameter == 4) + { + // EV + if (update_cycle_parameter(¶meter_option, -10, 10, 4)) + raspicamcontrol_set_exposure_compensation(camera, parameter_option); + else + { + raspicamcontrol_set_exposure_compensation(camera, 0); + parameter++; + } + } + else + if (parameter == 5) + { + // MMAL_PARAM_EXPOSUREMODE_T + if (update_cycle_parameter(¶meter_option, 0, exposure_map_size, 1)) + raspicamcontrol_set_exposure_mode(camera, exposure_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_exposure_mode(camera, MMAL_PARAM_EXPOSUREMODE_AUTO); + parameter++; + } + } + else + if (parameter == 6) + { + // MMAL_PARAM_AWB_T + if (update_cycle_parameter(¶meter_option, 0, awb_map_size, 1)) + raspicamcontrol_set_awb_mode(camera, awb_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_awb_mode(camera, MMAL_PARAM_AWBMODE_AUTO); + parameter++; + } + } + if (parameter == 7) + { + // MMAL_PARAM_IMAGEFX_T + if (update_cycle_parameter(¶meter_option, 0, imagefx_map_size, 1)) + raspicamcontrol_set_imageFX(camera, imagefx_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_imageFX(camera, MMAL_PARAM_IMAGEFX_NONE); + parameter++; + } + } + if (parameter == 8) + { + MMAL_PARAM_COLOURFX_T colfx = {0,0,0}; + switch (parameter_option) + { + case parameter_reset : + parameter_option = 1; + colfx.u = 128; + colfx.v = 128; + break; + case 1 : + parameter_option = 2; + colfx.u = 100; + colfx.v = 200; + break; + case 2 : + parameter_option = parameter_reset; + colfx.enable = 0; + parameter++; + break; + } + raspicamcontrol_set_colourFX(camera, &colfx); + } + + // Orientation + if (parameter == 9) + { + switch (parameter_option) + { + case parameter_reset: + raspicamcontrol_set_rotation(camera, 90); + parameter_option = 1; + break; + + case 1 : + raspicamcontrol_set_rotation(camera, 180); + parameter_option = 2; + break; + + case 2 : + raspicamcontrol_set_rotation(camera, 270); + parameter_option = 3; + break; + + case 3 : + { + raspicamcontrol_set_rotation(camera, 0); + raspicamcontrol_set_flips(camera, 1,0); + parameter_option = 4; + break; + } + case 4 : + { + raspicamcontrol_set_flips(camera, 0,1); + parameter_option = 5; + break; + } + case 5 : + { + raspicamcontrol_set_flips(camera, 1, 1); + parameter_option = 6; + break; + } + case 6 : + { + raspicamcontrol_set_flips(camera, 0, 0); + parameter_option = parameter_reset; + parameter++; + break; + } + } + } + + if (parameter == 10) + { + parameter = 1; + return 0; + } + + return 1; +} + + + +/** + * Convert string to the MMAL parameter for exposure mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_EXPOSUREMODE_T exposure_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, exposure_map, exposure_map_size); + + if( i != -1) + return (MMAL_PARAM_EXPOSUREMODE_T)i; + + vcos_log_error("Unknown exposure mode: %s", str); + return MMAL_PARAM_EXPOSUREMODE_AUTO; +} + +/** + * Convert string to the MMAL parameter for flicker avoid mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_FLICKERAVOID_T flicker_avoid_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, flicker_avoid_map, flicker_avoid_map_size); + + if( i != -1) + return (MMAL_PARAM_FLICKERAVOID_T)i; + + vcos_log_error("Unknown flicker avoid mode: %s", str); + return MMAL_PARAM_FLICKERAVOID_OFF; +} + +/** + * Convert string to the MMAL parameter for AWB mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_AWBMODE_T awb_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, awb_map, awb_map_size); + + if( i != -1) + return (MMAL_PARAM_AWBMODE_T)i; + + vcos_log_error("Unknown awb mode: %s", str); + return MMAL_PARAM_AWBMODE_AUTO; +} + +/** + * Convert string to the MMAL parameter for image effects mode + * @param str Incoming string to match + * @return MMAL parameter matching the strong, or the AUTO option if no match found + */ +MMAL_PARAM_IMAGEFX_T imagefx_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, imagefx_map, imagefx_map_size); + + if( i != -1) + return (MMAL_PARAM_IMAGEFX_T)i; + + vcos_log_error("Unknown image fx: %s", str); + return MMAL_PARAM_IMAGEFX_NONE; +} + +/** + * Convert string to the MMAL parameter for exposure metering mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_EXPOSUREMETERINGMODE_T metering_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, metering_mode_map, metering_mode_map_size); + + if( i != -1) + return (MMAL_PARAM_EXPOSUREMETERINGMODE_T)i; + + vcos_log_error("Unknown metering mode: %s", str); + return MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; +} + +/** + * Convert string to the MMAL parameter for DRC level + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAMETER_DRC_STRENGTH_T drc_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, drc_mode_map, drc_mode_map_size); + + if( i != -1) + return (MMAL_PARAMETER_DRC_STRENGTH_T)i; + + vcos_log_error("Unknown DRC level: %s", str); + return MMAL_PARAMETER_DRC_STRENGTH_OFF; +} + +/** + * Convert string to the MMAL parameter for exposure metering mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_STEREOSCOPIC_MODE_T stereo_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, stereo_mode_map, stereo_mode_map_size); + + if( i != -1) + return (MMAL_STEREOSCOPIC_MODE_T)i; + + vcos_log_error("Unknown metering mode: %s", str); + return MMAL_STEREOSCOPIC_MODE_NONE; +} + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandSharpness : // sharpness - needs single number parameter + sscanf(arg2, "%d", ¶ms->sharpness); + used = 2; + break; + + case CommandContrast : // contrast - needs single number parameter + sscanf(arg2, "%d", ¶ms->contrast); + used = 2; + break; + + case CommandBrightness : // brightness - needs single number parameter + sscanf(arg2, "%d", ¶ms->brightness); + used = 2; + break; + + case CommandSaturation : // saturation - needs single number parameter + sscanf(arg2, "%d", ¶ms->saturation); + used = 2; + break; + + case CommandISO : // ISO - needs single number parameter + sscanf(arg2, "%d", ¶ms->ISO); + used = 2; + break; + + case CommandVideoStab : // video stabilisation - if here, its on + params->videoStabilisation = 1; + used = 1; + break; + + case CommandEVComp : // EV - needs single number parameter + sscanf(arg2, "%d", ¶ms->exposureCompensation); + used = 2; + break; + + case CommandExposure : // exposure mode - needs string + params->exposureMode = exposure_mode_from_string(arg2); + used = 2; + break; + + case CommandFlicker : // flicker avoid mode - needs string + params->flickerAvoidMode = flicker_avoid_mode_from_string(arg2); + used = 2; + break; + + case CommandAWB : // AWB mode - needs single number parameter + params->awbMode = awb_mode_from_string(arg2); + used = 2; + break; + + case CommandImageFX : // Image FX - needs string + params->imageEffect = imagefx_mode_from_string(arg2); + used = 2; + break; + + case CommandColourFX : // Colour FX - needs string "u:v" + sscanf(arg2, "%d:%d", ¶ms->colourEffects.u, ¶ms->colourEffects.v); + params->colourEffects.enable = 1; + used = 2; + break; + + case CommandMeterMode: + params->exposureMeterMode = metering_mode_from_string(arg2); + used = 2; + break; + + case CommandRotation : // Rotation - degree + sscanf(arg2, "%d", ¶ms->rotation); + used = 2; + break; + + case CommandHFlip : + params->hflip = 1; + used = 1; + break; + + case CommandVFlip : + params->vflip = 1; + used = 1; + break; + + case CommandROI : + { + double x,y,w,h; + int args; + + args = sscanf(arg2, "%lf,%lf,%lf,%lf", &x,&y,&w,&h); + + if (args != 4 || x > 1.0 || y > 1.0 || w > 1.0 || h > 1.0) + { + return 0; + } + + // Make sure we stay within bounds + if (x + w > 1.0) + w = 1 - x; + + if (y + h > 1.0) + h = 1 - y; + + params->roi.x = x; + params->roi.y = y; + params->roi.w = w; + params->roi.h = h; + + used = 2; + break; + } + + case CommandShutterSpeed : // Shutter speed needs single number parameter + { + sscanf(arg2, "%d", ¶ms->shutter_speed); + used = 2; + break; + } + + case CommandAwbGains : + { + double r,b; + int args; + + args = sscanf(arg2, "%lf,%lf", &r,&b); + + if (args != 2 || r > 8.0 || b > 8.0) + { + return 0; + } + + params->awb_gains_r = r; + params->awb_gains_b = b; + + used = 2; + break; + } + + case CommandDRCLevel: + { + params->drc_level = drc_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStatsPass: + { + params->stats_pass = MMAL_TRUE; + used = 1; + break; + } + + case CommandAnnotate: + { + char dummy; + unsigned int bitmask; + // If parameter is a number, assume its a bitmask, otherwise a string + if (sscanf(arg2, "%u%c", &bitmask, &dummy) == 1) + { + params->enable_annotate |= bitmask; + } + else + { + params->enable_annotate |= ANNOTATE_USER_TEXT; + //copy string char by char and replace "\n" with newline character + unsigned char c; + char const *s = arg2; + char *t = ¶ms->annotate_string[0]; + int n=0; + while ((c = *s++) && n < MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1) + { + if (c == '\\' && *s) + { + switch (c = *s++) + { + case 'n': + c = '\n'; + break; + + default: + c = '\\'; + s--; + break; + } + } + *(t++) = c; + n++; + } + *t='\0'; + + //params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; + } + used=2; + break; + } + + case CommandAnnotateExtras: + { + // 3 parameters - text size (6-80), text colour (Hex VVUUYY) and background colour (Hex VVUUYY) + sscanf(arg2, "%u,%X,%X", ¶ms->annotate_text_size, + ¶ms->annotate_text_colour, + ¶ms->annotate_bg_colour); + used=2; + break; + } + + case CommandStereoMode: + { + params->stereo_mode.mode = stereo_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStereoDecimate: + { + params->stereo_mode.decimate = MMAL_TRUE; + used = 1; + break; + } + + case CommandStereoSwap: + { + params->stereo_mode.swap_eyes = MMAL_TRUE; + used = 1; + break; + } + + case CommandAnalogGain: + { + double gain; + int args; + + args = sscanf(arg2, "%lf", &gain); + + if (args != 1 || gain > 16.0) + { + return 0; + } + + params->analog_gain = gain; + + used = 2; + break; + } + case CommandDigitalGain: + { + double gain; + int args; + + args = sscanf(arg2, "%lf", &gain); + + if (args != 1 || gain > 64.0) + { + return 0; + } + + params->digital_gain = gain; + + used = 2; + break; + } + + + } + + return used; +} + +/** + * Display help for command line options + */ +void raspicamcontrol_display_help() +{ + int i; + + fprintf(stdout, "\nImage parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + fprintf(stdout, "\n\nNotes\n\nExposure mode options :\n%s", exposure_map[0].mode ); + + for (i=1;iexposureMode, exposure_map, exposure_map_size); + const char *fl_mode = raspicli_unmap_xref(params->flickerAvoidMode, flicker_avoid_map, flicker_avoid_map_size); + const char *awb_mode = raspicli_unmap_xref(params->awbMode, awb_map, awb_map_size); + const char *image_effect = raspicli_unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); + const char *metering_mode = raspicli_unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); + + fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); + fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); + fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); + fprintf(stderr, "Flicker Avoid Mode '%s'\n", fl_mode); + fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); + fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); + fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h); +} + +/** + * Convert a MMAL status return value to a simple boolean of success + * ALso displays a fault if code is not success + * + * @param status The error code to convert + * @return 0 if status is success, 1 otherwise + */ +int mmal_status_to_int(MMAL_STATUS_T status) +{ + if (status == MMAL_SUCCESS) + return 0; + else + { + switch (status) + { + case MMAL_ENOMEM : vcos_log_error("Out of memory"); break; + case MMAL_ENOSPC : vcos_log_error("Out of resources (other than memory)"); break; + case MMAL_EINVAL: vcos_log_error("Argument is invalid"); break; + case MMAL_ENOSYS : vcos_log_error("Function not implemented"); break; + case MMAL_ENOENT : vcos_log_error("No such file or directory"); break; + case MMAL_ENXIO : vcos_log_error("No such device or address"); break; + case MMAL_EIO : vcos_log_error("I/O error"); break; + case MMAL_ESPIPE : vcos_log_error("Illegal seek"); break; + case MMAL_ECORRUPT : vcos_log_error("Data is corrupt \attention FIXME: not POSIX"); break; + case MMAL_ENOTREADY :vcos_log_error("Component is not ready \attention FIXME: not POSIX"); break; + case MMAL_ECONFIG : vcos_log_error("Component is not configured \attention FIXME: not POSIX"); break; + case MMAL_EISCONN : vcos_log_error("Port is already connected "); break; + case MMAL_ENOTCONN : vcos_log_error("Port is disconnected"); break; + case MMAL_EAGAIN : vcos_log_error("Resource temporarily unavailable. Try again later"); break; + case MMAL_EFAULT : vcos_log_error("Bad address"); break; + default : vcos_log_error("Unknown status error"); break; + } + + return 1; + } +} + +/** + * Give the supplied parameter block a set of default values + * @params Pointer to parameter block + */ +void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params) +{ + vcos_assert(params); + + params->sharpness = 0; + params->contrast = 0; + params->brightness = 50; + params->saturation = 0; + params->ISO = 0; // 0 = auto + params->videoStabilisation = 0; + params->exposureCompensation = 0; + params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; + params->flickerAvoidMode = MMAL_PARAM_FLICKERAVOID_OFF; + params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; + params->awbMode = MMAL_PARAM_AWBMODE_AUTO; + params->imageEffect = MMAL_PARAM_IMAGEFX_NONE; + params->colourEffects.enable = 0; + params->colourEffects.u = 128; + params->colourEffects.v = 128; + params->rotation = 0; + params->hflip = params->vflip = 0; + params->roi.x = params->roi.y = 0.0; + params->roi.w = params->roi.h = 1.0; + params->shutter_speed = 0; // 0 = auto + params->awb_gains_r = 0; // Only have any function if AWB OFF is used. + params->awb_gains_b = 0; + params->drc_level = MMAL_PARAMETER_DRC_STRENGTH_OFF; + params->stats_pass = MMAL_FALSE; + params->enable_annotate = 0; + params->annotate_string[0] = '\0'; + params->annotate_text_size = 0; //Use firmware default + params->annotate_text_colour = -1; //Use firmware default + params->annotate_bg_colour = -1; //Use firmware default + params->stereo_mode.mode = MMAL_STEREOSCOPIC_MODE_NONE; + params->stereo_mode.decimate = MMAL_FALSE; + params->stereo_mode.swap_eyes = MMAL_FALSE; +} + +/** + * Get all the current camera parameters from specified camera component + * @param camera Pointer to camera component + * @param params Pointer to parameter block to accept settings + * @return 0 if successful, non-zero if unsuccessful + */ +int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params) +{ + vcos_assert(camera); + vcos_assert(params); + + if (!camera || !params) + return 1; + +/* TODO : Write these get functions + params->sharpness = raspicamcontrol_get_sharpness(camera); + params->contrast = raspicamcontrol_get_contrast(camera); + params->brightness = raspicamcontrol_get_brightness(camera); + params->saturation = raspicamcontrol_get_saturation(camera); + params->ISO = raspicamcontrol_get_ISO(camera); + params->videoStabilisation = raspicamcontrol_get_video_stabilisation(camera); + params->exposureCompensation = raspicamcontrol_get_exposure_compensation(camera); + params->exposureMode = raspicamcontrol_get_exposure_mode(camera); + params->flickerAvoidMode = raspicamcontrol_get_flicker_avoid_mode(camera); + params->awbMode = raspicamcontrol_get_awb_mode(camera); + params->imageEffect = raspicamcontrol_get_image_effect(camera); + params->colourEffects = raspicamcontrol_get_colour_effect(camera); + params->thumbnailConfig = raspicamcontrol_get_thumbnail_config(camera); +*/ + return 0; +} + +/** + * Set the specified camera to all the specified settings + * @param camera Pointer to camera component + * @param params Pointer to parameter block containing parameters + * @return 0 if successful, none-zero if unsuccessful. + */ +int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params) +{ + int result; + + result = raspicamcontrol_set_saturation(camera, params->saturation); + result += raspicamcontrol_set_sharpness(camera, params->sharpness); + result += raspicamcontrol_set_contrast(camera, params->contrast); + result += raspicamcontrol_set_brightness(camera, params->brightness); + result += raspicamcontrol_set_ISO(camera, params->ISO); + result += raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation); + result += raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation); + result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); + result += raspicamcontrol_set_flicker_avoid_mode(camera, params->flickerAvoidMode); + result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); + result += raspicamcontrol_set_awb_mode(camera, params->awbMode); + result += raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b); + result += raspicamcontrol_set_imageFX(camera, params->imageEffect); + result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); + //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason + result += raspicamcontrol_set_rotation(camera, params->rotation); + result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); + result += raspicamcontrol_set_ROI(camera, params->roi); + result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); + result += raspicamcontrol_set_DRC(camera, params->drc_level); + result += raspicamcontrol_set_stats_pass(camera, params->stats_pass); + result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string, + params->annotate_text_size, + params->annotate_text_colour, + params->annotate_bg_colour); + result += raspicamcontrol_set_gains(camera, params->analog_gain, params->digital_gain); + + return result; +} + +/** + * Adjust the saturation level for images + * @param camera Pointer to camera component + * @param saturation Value to adjust, -100 to 100 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation) +{ + int ret = 0; + + if (!camera) + return 1; + + if (saturation >= -100 && saturation <= 100) + { + MMAL_RATIONAL_T value = {saturation, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SATURATION, value)); + } + else + { + vcos_log_error("Invalid saturation value"); + ret = 1; + } + + return ret; +} + +/** + * Set the sharpness of the image + * @param camera Pointer to camera component + * @param sharpness Sharpness adjustment -100 to 100 + */ +int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness) +{ + int ret = 0; + + if (!camera) + return 1; + + if (sharpness >= -100 && sharpness <= 100) + { + MMAL_RATIONAL_T value = {sharpness, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SHARPNESS, value)); + } + else + { + vcos_log_error("Invalid sharpness value"); + ret = 1; + } + + return ret; +} + +/** + * Set the contrast adjustment for the image + * @param camera Pointer to camera component + * @param contrast Contrast adjustment -100 to 100 + * @return + */ +int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast) +{ + int ret = 0; + + if (!camera) + return 1; + + if (contrast >= -100 && contrast <= 100) + { + MMAL_RATIONAL_T value = {contrast, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_CONTRAST, value)); + } + else + { + vcos_log_error("Invalid contrast value"); + ret = 1; + } + + return ret; +} + +/** + * Adjust the brightness level for images + * @param camera Pointer to camera component + * @param brightness Value to adjust, 0 to 100 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness) +{ + int ret = 0; + + if (!camera) + return 1; + + if (brightness >= 0 && brightness <= 100) + { + MMAL_RATIONAL_T value = {brightness, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_BRIGHTNESS, value)); + } + else + { + vcos_log_error("Invalid brightness value"); + ret = 1; + } + + return ret; +} + +/** + * Adjust the ISO used for images + * @param camera Pointer to camera component + * @param ISO Value to set TODO : + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_ISO, ISO)); +} + +/** + * Adjust the metering mode for images + * @param camera Pointer to camera component + * @param saturation Value from following + * - MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T m_mode ) +{ + MMAL_PARAMETER_EXPOSUREMETERINGMODE_T meter_mode = {{MMAL_PARAMETER_EXP_METERING_MODE,sizeof(meter_mode)}, + m_mode}; + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &meter_mode.hdr)); +} + + +/** + * Set the video stabilisation flag. Only used in video mode + * @param camera Pointer to camera component + * @param saturation Flag 0 off 1 on + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_VIDEO_STABILISATION, vstabilisation)); +} + +/** + * Adjust the exposure compensation for images (EV) + * @param camera Pointer to camera component + * @param exp_comp Value to adjust, -10 to +10 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_int32(camera->control, MMAL_PARAMETER_EXPOSURE_COMP , exp_comp)); +} + + +/** + * Set exposure mode for images + * @param camera Pointer to camera component + * @param mode Exposure mode to set from + * - MMAL_PARAM_EXPOSUREMODE_OFF, + * - MMAL_PARAM_EXPOSUREMODE_AUTO, + * - MMAL_PARAM_EXPOSUREMODE_NIGHT, + * - MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + * - MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + * - MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + * - MMAL_PARAM_EXPOSUREMODE_SPORTS, + * - MMAL_PARAM_EXPOSUREMODE_SNOW, + * - MMAL_PARAM_EXPOSUREMODE_BEACH, + * - MMAL_PARAM_EXPOSUREMODE_VERYLONG, + * - MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + * - MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + * - MMAL_PARAM_EXPOSUREMODE_FIREWORKS, + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode) +{ + MMAL_PARAMETER_EXPOSUREMODE_T exp_mode = {{MMAL_PARAMETER_EXPOSURE_MODE,sizeof(exp_mode)}, mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &exp_mode.hdr)); +} + + +/** + * Set flicker avoid mode for images + * @param camera Pointer to camera component + * @param mode Exposure mode to set from + * - MMAL_PARAM_FLICKERAVOID_OFF, + * - MMAL_PARAM_FLICKERAVOID_AUTO, + * - MMAL_PARAM_FLICKERAVOID_50HZ, + * - MMAL_PARAM_FLICKERAVOID_60HZ, + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_flicker_avoid_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_FLICKERAVOID_T mode) +{ + MMAL_PARAMETER_FLICKERAVOID_T fl_mode = {{MMAL_PARAMETER_FLICKER_AVOID,sizeof(fl_mode)}, mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &fl_mode.hdr)); +} + + +/** + * Set the aWB (auto white balance) mode for images + * @param camera Pointer to camera component + * @param awb_mode Value to set from + * - MMAL_PARAM_AWBMODE_OFF, + * - MMAL_PARAM_AWBMODE_AUTO, + * - MMAL_PARAM_AWBMODE_SUNLIGHT, + * - MMAL_PARAM_AWBMODE_CLOUDY, + * - MMAL_PARAM_AWBMODE_SHADE, + * - MMAL_PARAM_AWBMODE_TUNGSTEN, + * - MMAL_PARAM_AWBMODE_FLUORESCENT, + * - MMAL_PARAM_AWBMODE_INCANDESCENT, + * - MMAL_PARAM_AWBMODE_FLASH, + * - MMAL_PARAM_AWBMODE_HORIZON, + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode) +{ + MMAL_PARAMETER_AWBMODE_T param = {{MMAL_PARAMETER_AWB_MODE,sizeof(param)}, awb_mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain) +{ + MMAL_PARAMETER_AWB_GAINS_T param = {{MMAL_PARAMETER_CUSTOM_AWB_GAINS,sizeof(param)}, {0,0}, {0,0}}; + + if (!camera) + return 1; + + if (!r_gain || !b_gain) + return 0; + + param.r_gain.num = (unsigned int)(r_gain * 65536); + param.b_gain.num = (unsigned int)(b_gain * 65536); + param.r_gain.den = param.b_gain.den = 65536; + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + +/** + * Set the image effect for the images + * @param camera Pointer to camera component + * @param imageFX Value from + * - MMAL_PARAM_IMAGEFX_NONE, + * - MMAL_PARAM_IMAGEFX_NEGATIVE, + * - MMAL_PARAM_IMAGEFX_SOLARIZE, + * - MMAL_PARAM_IMAGEFX_POSTERIZE, + * - MMAL_PARAM_IMAGEFX_WHITEBOARD, + * - MMAL_PARAM_IMAGEFX_BLACKBOARD, + * - MMAL_PARAM_IMAGEFX_SKETCH, + * - MMAL_PARAM_IMAGEFX_DENOISE, + * - MMAL_PARAM_IMAGEFX_EMBOSS, + * - MMAL_PARAM_IMAGEFX_OILPAINT, + * - MMAL_PARAM_IMAGEFX_HATCH, + * - MMAL_PARAM_IMAGEFX_GPEN, + * - MMAL_PARAM_IMAGEFX_PASTEL, + * - MMAL_PARAM_IMAGEFX_WATERCOLOUR, + * - MMAL_PARAM_IMAGEFX_FILM, + * - MMAL_PARAM_IMAGEFX_BLUR, + * - MMAL_PARAM_IMAGEFX_SATURATION, + * - MMAL_PARAM_IMAGEFX_COLOURSWAP, + * - MMAL_PARAM_IMAGEFX_WASHEDOUT, + * - MMAL_PARAM_IMAGEFX_POSTERISE, + * - MMAL_PARAM_IMAGEFX_COLOURPOINT, + * - MMAL_PARAM_IMAGEFX_COLOURBALANCE, + * - MMAL_PARAM_IMAGEFX_CARTOON, + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX) +{ + MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr)); +} + +/* TODO :what to do with the image effects parameters? + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)}, + imageFX, 0, {0}}; +mmal_port_parameter_set(camera->control, &imfx_param.hdr); + */ + +/** + * Set the colour effect for images (Set UV component) + * @param camera Pointer to camera component + * @param colourFX Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white) + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX) +{ + MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0}; + + if (!camera) + return 1; + + colfx.enable = colourFX->enable; + colfx.u = colourFX->u; + colfx.v = colourFX->v; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr)); + +} + + +/** + * Set the rotation of the image + * @param camera Pointer to camera component + * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only) + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation) +{ + int ret; + int my_rotation = ((rotation % 360 ) / 90) * 90; + + ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation); + mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation); + mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation); + + return ret; +} + +/** + * Set the flips state of the image + * @param camera Pointer to camera component + * @param hflip If true, horizontally flip the image + * @param vflip If true, vertically flip the image + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip) +{ + MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE}; + + if (hflip && vflip) + mirror.value = MMAL_PARAM_MIRROR_BOTH; + else + if (hflip) + mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL; + else + if (vflip) + mirror.value = MMAL_PARAM_MIRROR_VERTICAL; + + mmal_port_parameter_set(camera->output[0], &mirror.hdr); + mmal_port_parameter_set(camera->output[1], &mirror.hdr); + return mmal_port_parameter_set(camera->output[2], &mirror.hdr); +} + +/** + * Set the ROI of the sensor to use for captures/preview + * @param camera Pointer to camera component + * @param rect Normalised coordinates of ROI rectangle + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) +{ + MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}}; + + crop.rect.x = (65536 * rect.x); + crop.rect.y = (65536 * rect.y); + crop.rect.width = (65536 * rect.w); + crop.rect.height = (65536 * rect.h); + + return mmal_port_parameter_set(camera->control, &crop.hdr); +} + +/** + * Zoom in and Zoom out by changing ROI + * @param camera Pointer to camera component + * @param zoom_command zoom command enum + * @return 0 if successful, non-zero otherwise + */ +int raspicamcontrol_zoom_in_zoom_out(MMAL_COMPONENT_T *camera, ZOOM_COMMAND_T zoom_command, PARAM_FLOAT_RECT_T *roi) { + MMAL_PARAMETER_INPUT_CROP_T crop; + crop.hdr.id = MMAL_PARAMETER_INPUT_CROP; + crop.hdr.size = sizeof(crop); + + if (mmal_port_parameter_get(camera->control, &crop.hdr) != MMAL_SUCCESS) + { + vcos_log_error("mmal_port_parameter_get(camera->control, &crop.hdr) failed, skip it"); + return 0; + } + + if (zoom_command == ZOOM_IN) + { + if (crop.rect.width <= (zoom_full_16P16 + zoom_increment_16P16)) + { + crop.rect.width = zoom_full_16P16; + crop.rect.height = zoom_full_16P16; + } + else + { + crop.rect.width -= zoom_increment_16P16; + crop.rect.height -= zoom_increment_16P16; + } + } + else if (zoom_command == ZOOM_OUT) + { + unsigned int increased_size = crop.rect.width + zoom_increment_16P16; + if (increased_size < crop.rect.width) //overflow + { + crop.rect.width = 65536; + crop.rect.height = 65536; + } + else + { + crop.rect.width = increased_size; + crop.rect.height = increased_size; + } + } + + if (zoom_command == ZOOM_RESET) + { + crop.rect.x = 0; + crop.rect.y = 0; + crop.rect.width = 65536; + crop.rect.height = 65536; + } + else + { + unsigned int centered_top_coordinate = (65536 - crop.rect.width) / 2; + crop.rect.x = centered_top_coordinate; + crop.rect.y = centered_top_coordinate; + } + + int ret = mmal_status_to_int(mmal_port_parameter_set(camera->control, &crop.hdr)); + + if (ret == 0) { + roi->x = roi->y = (double)crop.rect.x/65536; + roi->w = roi->h = (double)crop.rect.width/65536; + } + else + { + vcos_log_error("Failed to set crop values, x/y: %u, w/h: %u", crop.rect.x, crop.rect.width); + ret = 1; + } + + return ret; +} + +/** + * Adjust the exposure time used for images + * @param camera Pointer to camera component + * @param shutter speed in microseconds + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_SHUTTER_SPEED, speed)); +} + +/** + * Adjust the Dynamic range compression level + * @param camera Pointer to camera component + * @param strength Strength of DRC to apply + * MMAL_PARAMETER_DRC_STRENGTH_OFF + * MMAL_PARAMETER_DRC_STRENGTH_LOW + * MMAL_PARAMETER_DRC_STRENGTH_MEDIUM + * MMAL_PARAMETER_DRC_STRENGTH_HIGH + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength) +{ + MMAL_PARAMETER_DRC_T drc = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, sizeof(MMAL_PARAMETER_DRC_T)}, strength}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &drc.hdr)); +} + +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_CAPTURE_STATS_PASS, stats_pass)); +} + + +/** + * Set the annotate data + * @param camera Pointer to camera component + * @param Bitmask of required annotation data. 0 for off. + * @param If set, a pointer to text string to use instead of bitmask, max length 32 characters + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string, + const int text_size, const int text_colour, const int bg_colour) +{ + MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T annotate = + {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T)}}; + + if (settings) + { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3]; + int process_datetime = 1; + + annotate.enable = 1; + + if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT)) + { + if ((settings & (ANNOTATE_TIME_TEXT | ANNOTATE_DATE_TEXT)) && strchr(string,'%') != NULL) + { //string contains strftime parameter? + strftime(annotate.text, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3, string, &tm ); + process_datetime = 0; + }else{ + strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3); + } + annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; + } + + if (process_datetime && (settings & ANNOTATE_TIME_TEXT)) + { + if(strlen(annotate.text)){ + strftime(tmp, 32, " %X", &tm ); + }else{ + strftime(tmp, 32, "%X", &tm ); + } + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); + } + + if (process_datetime && (settings & ANNOTATE_DATE_TEXT)) + { + if(strlen(annotate.text)){ + strftime(tmp, 32, " %x", &tm ); + }else{ + strftime(tmp, 32, "%x", &tm ); + } + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); + } + + if (settings & ANNOTATE_SHUTTER_SETTINGS) + annotate.show_shutter = MMAL_TRUE; + + if (settings & ANNOTATE_GAIN_SETTINGS) + annotate.show_analog_gain = MMAL_TRUE; + + if (settings & ANNOTATE_LENS_SETTINGS) + annotate.show_lens = MMAL_TRUE; + + if (settings & ANNOTATE_CAF_SETTINGS) + annotate.show_caf = MMAL_TRUE; + + if (settings & ANNOTATE_MOTION_SETTINGS) + annotate.show_motion = MMAL_TRUE; + + if (settings & ANNOTATE_FRAME_NUMBER) + annotate.show_frame_num = MMAL_TRUE; + + if (settings & ANNOTATE_BLACK_BACKGROUND) + annotate.enable_text_background = MMAL_TRUE; + + annotate.text_size = text_size; + + if (text_colour != -1) + { + annotate.custom_text_colour = MMAL_TRUE; + annotate.custom_text_Y = text_colour&0xff; + annotate.custom_text_U = (text_colour>>8)&0xff; + annotate.custom_text_V = (text_colour>>16)&0xff; + } + else + annotate.custom_text_colour = MMAL_FALSE; + + if (bg_colour != -1) + { + annotate.custom_background_colour = MMAL_TRUE; + annotate.custom_background_Y = bg_colour&0xff; + annotate.custom_background_U = (bg_colour>>8)&0xff; + annotate.custom_background_V = (bg_colour>>16)&0xff; + } + else + annotate.custom_background_colour = MMAL_FALSE; + } + else + annotate.enable = 0; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &annotate.hdr)); +} + +int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode) +{ + MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo = { {MMAL_PARAMETER_STEREOSCOPIC_MODE, sizeof(stereo)}, + MMAL_STEREOSCOPIC_MODE_NONE, MMAL_FALSE, MMAL_FALSE }; + if (stereo_mode->mode != MMAL_STEREOSCOPIC_MODE_NONE) + { + stereo.mode = stereo_mode->mode; + stereo.decimate = stereo_mode->decimate; + stereo.swap_eyes = stereo_mode->swap_eyes; + } + return mmal_status_to_int(mmal_port_parameter_set(port, &stereo.hdr)); +} + +int raspicamcontrol_set_gains(MMAL_COMPONENT_T *camera, float analog, float digital) +{ + MMAL_RATIONAL_T rational = {0,65536}; + MMAL_STATUS_T status; + + if (!camera) + return 1; + + rational.num = (unsigned int)(analog * 65536); + status = mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_ANALOG_GAIN, rational); + if (status != MMAL_SUCCESS) + return mmal_status_to_int(status); + + rational.num = (unsigned int)(digital * 65536); + status = mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_DIGITAL_GAIN, rational); + return mmal_status_to_int(status); +} + +/** + * Asked GPU how much memory it has allocated + * + * @return amount of memory in MB + */ +static int raspicamcontrol_get_mem_gpu(void) +{ + char response[80] = ""; + int gpu_mem = 0; + if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0) + vc_gencmd_number_property(response, "gpu", &gpu_mem); + return gpu_mem; +} + +/** + * Ask GPU about its camera abilities + * @param supported None-zero if software supports the camera + * @param detected None-zero if a camera has been detected + */ +static void raspicamcontrol_get_camera(int *supported, int *detected) +{ + char response[80] = ""; + if (vc_gencmd(response, sizeof response, "get_camera") == 0) + { + if (supported) + vc_gencmd_number_property(response, "supported", supported); + if (detected) + vc_gencmd_number_property(response, "detected", detected); + } +} + +/** + * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities + * @param supported None-zero if software supports the camera + * @param detected None-zero if a camera has been detected + */ +void raspicamcontrol_check_configuration(int min_gpu_mem) +{ + int gpu_mem = raspicamcontrol_get_mem_gpu(); + int supported = 0, detected = 0; + raspicamcontrol_get_camera(&supported, &detected); + if (!supported) + vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n"); + else if (gpu_mem < min_gpu_mem) + vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem); + else if (!detected) + vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n"); + else + vcos_log_error("Failed to run camera app. Please check for firmware updates\n"); +} + diff --git a/host_applications/linux/apps/raspicam/RaspiCamControl.h b/host_applications/linux/apps/raspicam/RaspiCamControl.h new file mode 100755 index 0000000..0286d9f --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiCamControl.h @@ -0,0 +1,236 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPICAMCONTROL_H_ +#define RASPICAMCONTROL_H_ + +/* Various parameters + * + * Exposure Mode + * MMAL_PARAM_EXPOSUREMODE_OFF, + MMAL_PARAM_EXPOSUREMODE_AUTO, + MMAL_PARAM_EXPOSUREMODE_NIGHT, + MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + MMAL_PARAM_EXPOSUREMODE_SPORTS, + MMAL_PARAM_EXPOSUREMODE_SNOW, + MMAL_PARAM_EXPOSUREMODE_BEACH, + MMAL_PARAM_EXPOSUREMODE_VERYLONG, + MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + MMAL_PARAM_EXPOSUREMODE_FIREWORKS, + * + * Flicker Avoid Mode + * MMAL_PARAM_FLICKERAVOID_OFF, + MMAL_PARAM_FLICKERAVOID_AUTO, + MMAL_PARAM_FLICKERAVOID_50HZ, + MMAL_PARAM_FLICKERAVOID_60HZ, + * + * AWB Mode + * MMAL_PARAM_AWBMODE_OFF, + MMAL_PARAM_AWBMODE_AUTO, + MMAL_PARAM_AWBMODE_SUNLIGHT, + MMAL_PARAM_AWBMODE_CLOUDY, + MMAL_PARAM_AWBMODE_SHADE, + MMAL_PARAM_AWBMODE_TUNGSTEN, + MMAL_PARAM_AWBMODE_FLUORESCENT, + MMAL_PARAM_AWBMODE_INCANDESCENT, + MMAL_PARAM_AWBMODE_FLASH, + MMAL_PARAM_AWBMODE_HORIZON, + * + * Image FX + MMAL_PARAM_IMAGEFX_NONE, + MMAL_PARAM_IMAGEFX_NEGATIVE, + MMAL_PARAM_IMAGEFX_SOLARIZE, + MMAL_PARAM_IMAGEFX_POSTERIZE, + MMAL_PARAM_IMAGEFX_WHITEBOARD, + MMAL_PARAM_IMAGEFX_BLACKBOARD, + MMAL_PARAM_IMAGEFX_SKETCH, + MMAL_PARAM_IMAGEFX_DENOISE, + MMAL_PARAM_IMAGEFX_EMBOSS, + MMAL_PARAM_IMAGEFX_OILPAINT, + MMAL_PARAM_IMAGEFX_HATCH, + MMAL_PARAM_IMAGEFX_GPEN, + MMAL_PARAM_IMAGEFX_PASTEL, + MMAL_PARAM_IMAGEFX_WATERCOLOUR, + MMAL_PARAM_IMAGEFX_FILM, + MMAL_PARAM_IMAGEFX_BLUR, + MMAL_PARAM_IMAGEFX_SATURATION, + MMAL_PARAM_IMAGEFX_COLOURSWAP, + MMAL_PARAM_IMAGEFX_WASHEDOUT, + MMAL_PARAM_IMAGEFX_POSTERISE, + MMAL_PARAM_IMAGEFX_COLOURPOINT, + MMAL_PARAM_IMAGEFX_COLOURBALANCE, + MMAL_PARAM_IMAGEFX_CARTOON, + + */ + +/// Annotate bitmask options +/// Supplied by user on command line +#define ANNOTATE_USER_TEXT 1 +/// Supplied by app using this module +#define ANNOTATE_APP_TEXT 2 +/// Insert current date +#define ANNOTATE_DATE_TEXT 4 +// Insert current time +#define ANNOTATE_TIME_TEXT 8 + +#define ANNOTATE_SHUTTER_SETTINGS 16 +#define ANNOTATE_CAF_SETTINGS 32 +#define ANNOTATE_GAIN_SETTINGS 64 +#define ANNOTATE_LENS_SETTINGS 128 +#define ANNOTATE_MOTION_SETTINGS 256 +#define ANNOTATE_FRAME_NUMBER 512 +#define ANNOTATE_BLACK_BACKGROUND 1024 + + +// There isn't actually a MMAL structure for the following, so make one +typedef struct mmal_param_colourfx_s +{ + int enable; /// Turn colourFX on or off + int u,v; /// U and V to use +} MMAL_PARAM_COLOURFX_T; + +typedef struct mmal_param_thumbnail_config_s +{ + int enable; + int width,height; + int quality; +} MMAL_PARAM_THUMBNAIL_CONFIG_T; + +typedef struct param_float_rect_s +{ + double x; + double y; + double w; + double h; +} PARAM_FLOAT_RECT_T; + +/// struct contain camera settings +typedef struct raspicam_camera_parameters_s +{ + int sharpness; /// -100 to 100 + int contrast; /// -100 to 100 + int brightness; /// 0 to 100 + int saturation; /// -100 to 100 + int ISO; /// TODO : what range? + int videoStabilisation; /// 0 or 1 (false or true) + int exposureCompensation; /// -10 to +10 ? + MMAL_PARAM_EXPOSUREMODE_T exposureMode; + MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode; + MMAL_PARAM_AWBMODE_T awbMode; + MMAL_PARAM_IMAGEFX_T imageEffect; + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters; + MMAL_PARAM_COLOURFX_T colourEffects; + MMAL_PARAM_FLICKERAVOID_T flickerAvoidMode; + int rotation; /// 0-359 + int hflip; /// 0 or 1 + int vflip; /// 0 or 1 + PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect + int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms + float awb_gains_r; /// AWB red gain + float awb_gains_b; /// AWB blue gain + MMAL_PARAMETER_DRC_STRENGTH_T drc_level; // Strength of Dynamic Range compression to apply + MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off + int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed + char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings + int annotate_text_size; // Text size for annotation + int annotate_text_colour; // Text colour for annotation + int annotate_bg_colour; // Background colour for annotation + MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo_mode; + float analog_gain; // Analog gain + float digital_gain; // Digital gain +} RASPICAM_CAMERA_PARAMETERS; + +typedef enum { + ZOOM_IN, ZOOM_OUT, ZOOM_RESET +} ZOOM_COMMAND_T; + + +void raspicamcontrol_check_configuration(int min_gpu_mem); + +int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2); +void raspicamcontrol_display_help(); +int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera); + +int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params); +int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params); +void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params); + +void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params); + +void raspicamcontrol_check_configuration(int min_gpu_mem); + +// Individual setting functions +int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation); +int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness); +int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast); +int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness); +int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO); +int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode); +int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation); +int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); +int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); +int raspicamcontrol_set_flicker_avoid_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_FLICKERAVOID_T mode); +int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain); +int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); +int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); +int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); +int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); +int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); +int raspicamcontrol_zoom_in_zoom_out(MMAL_COMPONENT_T *camera, ZOOM_COMMAND_T zoom_command, PARAM_FLOAT_RECT_T *roi); +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength); +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass); +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string, + const int text_size, const int text_colour, const int bg_colour); +int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode); +int raspicamcontrol_set_gains(MMAL_COMPONENT_T *camera, float analog, float digital); + +//Individual getting functions +int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera); +MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera); +MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera); +MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_FLICKERAVOID_T raspicamcontrol_get_flicker_avoid_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera); +MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera); + + + + +#endif /* RASPICAMCONTROL_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiPreview.c b/host_applications/linux/apps/raspicam/RaspiPreview.c new file mode 100755 index 0000000..7656c87 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiPreview.c @@ -0,0 +1,281 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#define CommandPreview 1 +#define CommandFullScreen 2 +#define CommandOpacity 3 +#define CommandDisablePreview 4 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandPreview, "-preview", "p", "Preview window settings <'x,y,w,h'>", 1 }, + { CommandFullScreen, "-fullscreen", "f", "Fullscreen preview mode", 0 }, + { CommandOpacity, "-opacity", "op", "Preview window opacity (0-255)", 1}, + { CommandDisablePreview,"-nopreview", "n", "Do not display a preview window", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +/** + * Create the preview component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) +{ + MMAL_COMPONENT_T *preview = 0; + MMAL_PORT_T *preview_port = NULL; + MMAL_STATUS_T status; + + if (!state->wantPreview) + { + // No preview required, so create a null sink component to take its place + status = mmal_component_create("vc.null_sink", &preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create null sink component"); + goto error; + } + } + else + { + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, + &preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create preview component"); + goto error; + } + + if (!preview->input_num) + { + status = MMAL_ENOSYS; + vcos_log_error("No input ports found on component"); + goto error; + } + + preview_port = preview->input[0]; + + MMAL_DISPLAYREGION_T param; + param.hdr.id = MMAL_PARAMETER_DISPLAYREGION; + param.hdr.size = sizeof(MMAL_DISPLAYREGION_T); + + param.set = MMAL_DISPLAY_SET_LAYER; + param.layer = PREVIEW_LAYER; + + param.set |= MMAL_DISPLAY_SET_ALPHA; + param.alpha = state->opacity; + + if (state->wantFullScreenPreview) + { + param.set |= MMAL_DISPLAY_SET_FULLSCREEN; + param.fullscreen = 1; + } + else + { + param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN); + param.fullscreen = 0; + param.dest_rect = state->previewWindow; + } + + status = mmal_port_parameter_set(preview_port, ¶m.hdr); + + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + vcos_log_error("unable to set preview port parameters (%u)", status); + goto error; + } + } + + /* Enable component */ + status = mmal_component_enable(preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable preview/null sink component (%u)", status); + goto error; + } + + state->preview_component = preview; + + return status; + +error: + + if (preview) + mmal_component_destroy(preview); + + return status; +} + + +/** + * Destroy the preview component + * + * @param state Pointer to state control struct + * + */ +void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state) +{ + if (state->preview_component) + { + mmal_component_destroy(state->preview_component); + state->preview_component = NULL; + } +} + +/** + * Assign set of default parameters to the passed in parameter block + * + * @param state Pointer to parameter block + * + */ +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state) +{ + state->wantPreview = 1; + state->wantFullScreenPreview = 1; + state->opacity = 255; + state->previewWindow.x = 0; + state->previewWindow.y = 0; + state->previewWindow.width = 1024; + state->previewWindow.height = 768; + state->preview_component = NULL; +} + +/** + * Dump parameters as human readable to stdout + * + * @param state Pointer to parameter block + * + */ +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state) +{ + fprintf(stderr, "Preview %s, Full screen %s\n", state->wantPreview ? "Yes" : "No", + state->wantFullScreenPreview ? "Yes" : "No"); + + fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", state->previewWindow.x, + state->previewWindow.y, state->previewWindow.width, + state->previewWindow.height, state->opacity); +}; + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandPreview: // Preview window + { + int tmp; + + params->wantPreview = 1; + + tmp = sscanf(arg2, "%d,%d,%d,%d", + ¶ms->previewWindow.x, ¶ms->previewWindow.y, + ¶ms->previewWindow.width, ¶ms->previewWindow.height); + + // Failed to get any window parameters, so revert to full screen + if (tmp == 0) + params->wantFullScreenPreview = 1; + else + params->wantFullScreenPreview = 0; + + used = 2; + + break; + } + + case CommandFullScreen: // Want full screen preview mode (overrides display rect) + params->wantPreview = 1; + params->wantFullScreenPreview = 1; + + used = 1; + break; + + case CommandOpacity: // Define preview window opacity + if (sscanf(arg2, "%u", ¶ms->opacity) != 1) + params->opacity = 255; + else + used = 2; + break; + + case CommandDisablePreview: // Turn off preview output + params->wantPreview = 0; + used = 1; + break; + } + + return used; +} + +/** + * Display help for command line options + */ +void raspipreview_display_help() +{ + fprintf(stdout, "\nPreview parameter commands\n\n"); + raspicli_display_help(cmdline_commands, cmdline_commands_size); +} diff --git a/host_applications/linux/apps/raspicam/RaspiPreview.h b/host_applications/linux/apps/raspicam/RaspiPreview.h new file mode 100755 index 0000000..409dbff --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiPreview.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPIPREVIEW_H_ +#define RASPIPREVIEW_H_ + +/// Layer that preview window should be displayed on +#define PREVIEW_LAYER 2 + +// Frames rates of 0 implies variable, but denominator needs to be 1 to prevent div by 0 +#define PREVIEW_FRAME_RATE_NUM 0 +#define PREVIEW_FRAME_RATE_DEN 1 + +#define FULL_RES_PREVIEW_FRAME_RATE_NUM 0 +#define FULL_RES_PREVIEW_FRAME_RATE_DEN 1 + +#define FULL_FOV_PREVIEW_16x9_X 1280 +#define FULL_FOV_PREVIEW_16x9_Y 720 + +#define FULL_FOV_PREVIEW_4x3_X 1296 +#define FULL_FOV_PREVIEW_4x3_Y 972 + +#define FULL_FOV_PREVIEW_FRAME_RATE_NUM 0 +#define FULL_FOV_PREVIEW_FRAME_RATE_DEN 1 + +typedef struct +{ + int wantPreview; /// Display a preview + int wantFullScreenPreview; /// 0 is use previewRect, non-zero to use full screen + int opacity; /// Opacity of window - 0 = transparent, 255 = opaque + MMAL_RECT_T previewWindow; /// Destination rectangle for the preview window. + MMAL_COMPONENT_T *preview_component; /// Pointer to the created preview display component +} RASPIPREVIEW_PARAMETERS; + +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state); +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2); +void raspipreview_display_help(); + +#endif /* RASPIPREVIEW_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiStill.c b/host_applications/linux/apps/raspicam/RaspiStill.c new file mode 100755 index 0000000..0c3cf3f --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiStill.c @@ -0,0 +1,2169 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiStill.c + * Command line program to capture a still frame and encode it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 31 Jan 2013 + * \Author: James Hughes + * + * Description + * + * 3 components are created; camera, preview and JPG encoder. + * Camera component has three ports, preview, video and stills. + * This program connects preview and stills to the preview and jpg + * encoder. Using mmal we don't need to worry about buffers between these + * components, but we do need to handle buffers from the encoder, which + * are simply written straight to the file in the requisite buffer callback. + * + * We use the RaspiCamControl code to handle the specific camera settings. + */ + +// We use some GNU extensions (asprintf, basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.11" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" +#include "interface/mmal/mmal_parameters_camera.h" + + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" +#include "RaspiTex.h" + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + + +// Stills format information +// 0 implies variable +#define STILLS_FRAME_RATE_NUM 0 +#define STILLS_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +#define MAX_USER_EXIF_TAGS 32 +#define MAX_EXIF_PAYLOAD_LENGTH 128 + +/// Frame advance method +#define FRAME_NEXT_SINGLE 0 +#define FRAME_NEXT_TIMELAPSE 1 +#define FRAME_NEXT_KEYPRESS 2 +#define FRAME_NEXT_FOREVER 3 +#define FRAME_NEXT_GPIO 4 +#define FRAME_NEXT_SIGNAL 5 +#define FRAME_NEXT_IMMEDIATELY 6 + + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + char camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN]; // Name of the camera sensor + int quality; /// JPEG quality setting (1-100) + int wantRAW; /// Flag for whether the JPEG metadata also contains the RAW bayer image + char *filename; /// filename of output file + char *linkname; /// filename of output file + int frameStart; /// First number of frame output counter + MMAL_PARAM_THUMBNAIL_CONFIG_T thumbnailConfig; + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + MMAL_FOURCC_T encoding; /// Encoding to use for the output file. + const char *exifTags[MAX_USER_EXIF_TAGS]; /// Array of pointers to tags supplied from the command line + int numExifTags; /// Number of supplied tags + int enableExifTags; /// Enable/Disable EXIF tags in output + int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse + int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps. + int frameNextMethod; /// Which method to use to advance to next frame + int useGL; /// Render preview using OpenGL + int glCapture; /// Save the GL frame-buffer instead of camera output + int settings; /// Request settings from the camera + int cameraNum; /// Camera number + int burstCaptureMode; /// Enable burst mode + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + int datetime; /// Use DateTime instead of frame# + int timestamp; /// Use timestamp instead of frame# + int restart_interval; /// JPEG restart interval. 0 for none. + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component + MMAL_COMPONENT_T *null_sink_component; /// Pointer to the null sink component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + + RASPITEX_STATE raspitex_state; /// GL renderer state and parameters + +} RASPISTILL_STATE; + +/** Struct used to pass information in encoder port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) + RASPISTILL_STATE *pstate; /// pointer to our state in case required in callback +} PORT_USERDATA; + +static void display_valid_parameters(char *app_name); +static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag); + +/// Comamnd ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandQuality 3 +#define CommandRaw 4 +#define CommandOutput 5 +#define CommandVerbose 6 +#define CommandTimeout 7 +#define CommandThumbnail 8 +#define CommandDemoMode 9 +#define CommandEncoding 10 +#define CommandExifTag 11 +#define CommandTimelapse 12 +#define CommandFullResPreview 13 +#define CommandLink 14 +#define CommandKeypress 15 +#define CommandSignal 16 +#define CommandGL 17 +#define CommandGLCapture 18 +#define CommandSettings 19 +#define CommandCamSelect 20 +#define CommandBurstMode 21 +#define CommandSensorMode 22 +#define CommandDateTime 23 +#define CommandTimeStamp 24 +#define CommandFrameStart 25 +#define CommandRestartInterval 26 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width ", 1 }, + { CommandHeight, "-height", "h", "Set image height ", 1 }, + { CommandQuality, "-quality", "q", "Set jpeg quality <0 to 100>", 1 }, + { CommandRaw, "-raw", "r", "Add raw bayer data to jpeg metadata", 0 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -'). If not specified, no file is saved", 1 }, + { CommandLink, "-latest", "l", "Link latest complete image to filename ", 1}, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down (if not specified, set to 5s)", 1 }, + { CommandThumbnail,"-thumb", "th", "Set thumbnail parameters (x:y:quality) or none", 1}, + { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 0}, + { CommandEncoding,"-encoding", "e", "Encoding to use for output file (jpg, bmp, gif, png)", 1}, + { CommandExifTag, "-exif", "x", "EXIF tag to apply to captures (format as 'key=value') or none", 1}, + { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every ms. %d == frame number (Try: -o img_%04d.jpg)", 1}, + { CommandFullResPreview,"-fullpreview","fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0}, + { CommandKeypress,"-keypress", "k", "Wait between captures for a ENTER, X then ENTER to exit", 0}, + { CommandSignal, "-signal", "s", "Wait between captures for a SIGUSR1 or SIGUSR2 from another process", 0}, + { CommandGL, "-gl", "g", "Draw preview to texture instead of using video render component", 0}, + { CommandGLCapture, "-glcapture","gc", "Capture the GL frame-buffer instead of the camera image", 0}, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandCamSelect, "-camselect","cs", "Select camera . Default 0", 1 }, + { CommandBurstMode, "-burst", "bm", "Enable 'burst capture mode'", 0}, + { CommandSensorMode,"-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandDateTime, "-datetime", "dt", "Replace output pattern (%d) with DateTime (MonthDayHourMinSec)", 0}, + { CommandTimeStamp, "-timestamp", "ts", "Replace output pattern (%d) with unix timestamp (seconds since 1970)", 0}, + { CommandFrameStart,"-framestart","fs", "Starting frame number in output pattern(%d)", 1}, + { CommandRestartInterval, "-restart","rs","JPEG Restart interval (default of 0 for none)", 1}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +static struct +{ + char *format; + MMAL_FOURCC_T encoding; +} encoding_xref[] = +{ + {"jpg", MMAL_ENCODING_JPEG}, + {"bmp", MMAL_ENCODING_BMP}, + {"gif", MMAL_ENCODING_GIF}, + {"png", MMAL_ENCODING_PNG} +}; + +static int encoding_xref_size = sizeof(encoding_xref) / sizeof(encoding_xref[0]); + + +static struct +{ + char *description; + int nextFrameMethod; +} next_frame_description[] = +{ + {"Single capture", FRAME_NEXT_SINGLE}, + {"Capture on timelapse", FRAME_NEXT_TIMELAPSE}, + {"Capture on keypress", FRAME_NEXT_KEYPRESS}, + {"Run forever", FRAME_NEXT_FOREVER}, + {"Capture on GPIO", FRAME_NEXT_GPIO}, + {"Capture on signal", FRAME_NEXT_SIGNAL}, +}; + +static int next_frame_description_size = sizeof(next_frame_description) / sizeof(next_frame_description[0]); + +static void set_sensor_defaults(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *camera_info; + MMAL_STATUS_T status; + + // Default to the OV5647 setup + strncpy(state->camera_name, "OV5647", MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN); + + // Try to get the camera name and maximum supported resolution + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA_INFO, &camera_info); + if (status == MMAL_SUCCESS) + { + MMAL_PARAMETER_CAMERA_INFO_T param; + param.hdr.id = MMAL_PARAMETER_CAMERA_INFO; + param.hdr.size = sizeof(param)-4; // Deliberately undersize to check firmware veresion + status = mmal_port_parameter_get(camera_info->control, ¶m.hdr); + + if (status != MMAL_SUCCESS) + { + // Running on newer firmware + param.hdr.size = sizeof(param); + status = mmal_port_parameter_get(camera_info->control, ¶m.hdr); + if (status == MMAL_SUCCESS && param.num_cameras > state->cameraNum) + { + // Take the parameters from the first camera listed. + if (state->width == 0) + state->width = param.cameras[state->cameraNum].max_width; + if (state->height == 0) + state->height = param.cameras[state->cameraNum].max_height; + strncpy(state->camera_name, param.cameras[state->cameraNum].camera_name, MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN); + state->camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN-1] = 0; + } + else + vcos_log_error("Cannot read camera info, keeping the defaults for OV5647"); + } + else + { + // Older firmware + // Nothing to do here, keep the defaults for OV5647 + } + + mmal_component_destroy(camera_info); + } + else + { + vcos_log_error("Failed to create camera_info component"); + } + //Command line hasn't specified a resolution, and we failed to + //get a default resolution from camera_info. Assume OV5647 full res + if (state->width == 0) + state->width = 2592; + if (state->height == 0) + state->height = 1944; +} + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPISTILL_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + state->timeout = 5000; // 5s delay before take image + state->quality = 85; + state->wantRAW = 0; + state->filename = NULL; + state->linkname = NULL; + state->frameStart = 0; + state->verbose = 0; + state->thumbnailConfig.enable = 1; + state->thumbnailConfig.width = 64; + state->thumbnailConfig.height = 48; + state->thumbnailConfig.quality = 35; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->camera_component = NULL; + state->encoder_component = NULL; + state->preview_connection = NULL; + state->encoder_connection = NULL; + state->encoder_pool = NULL; + state->encoding = MMAL_ENCODING_JPEG; + state->numExifTags = 0; + state->enableExifTags = 1; + state->timelapse = 0; + state->fullResPreview = 0; + state->frameNextMethod = FRAME_NEXT_SINGLE; + state->useGL = 0; + state->glCapture = 0; + state->settings = 0; + state->cameraNum = 0; + state->burstCaptureMode=0; + state->sensor_mode = 0; + state->datetime = 0; + state->timestamp = 0; + state->restart_interval = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); + + // Set initial GL preview state + raspitex_set_defaults(&state->raspitex_state); +} + +/** + * Dump image state parameters to stderr. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPISTILL_STATE *state) +{ + int i; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, quality %d, filename %s\n", state->width, + state->height, state->quality, state->filename); + fprintf(stderr, "Time delay %d, Raw %s\n", state->timeout, + state->wantRAW ? "yes" : "no"); + fprintf(stderr, "Thumbnail enabled %s, width %d, height %d, quality %d\n", + state->thumbnailConfig.enable ? "Yes":"No", state->thumbnailConfig.width, + state->thumbnailConfig.height, state->thumbnailConfig.quality); + fprintf(stderr, "Link to latest frame enabled "); + if (state->linkname) + { + fprintf(stderr, " yes, -> %s\n", state->linkname); + } + else + { + fprintf(stderr, " no\n"); + } + fprintf(stderr, "Full resolution preview %s\n", state->fullResPreview ? "Yes": "No"); + + fprintf(stderr, "Capture method : "); + for (i=0;iframeNextMethod == next_frame_description[i].nextFrameMethod) + fprintf(stderr, "%s", next_frame_description[i].description); + } + fprintf(stderr, "\n\n"); + + if (state->enableExifTags) + { + if (state->numExifTags) + { + fprintf(stderr, "User supplied EXIF tags :\n"); + + for (i=0;inumExifTags;i++) + { + fprintf(stderr, "%s", state->exifTags[i]); + if (i != state->numExifTags-1) + fprintf(stderr, ","); + } + fprintf(stderr, "\n\n"); + } + } + else + fprintf(stderr, "EXIF tags disabled\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + // exit straight away if help requested + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandQuality: // Quality = 1-100 + if (sscanf(argv[i + 1], "%u", &state->quality) == 1) + { + if (state->quality > 100) + { + fprintf(stderr, "Setting max quality = 100\n"); + state->quality = 100; + } + i++; + } + else + valid = 0; + + break; + + case CommandRaw: // Add raw bayer data in metadata + state->wantRAW = 1; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + //We use sprintf to append the frame number for timelapse mode + //Ensure that any % is either %% or %d. + const char *percent = argv[i+1]; + while(valid && *percent && (percent=strchr(percent, '%')) != NULL) + { + int digits=0; + percent++; + while(isdigit(*percent)) + { + percent++; + digits++; + } + if(!((*percent == '%' && !digits) || *percent == 'd')) + { + valid = 0; + fprintf(stderr, "Filename contains %% characters, but not %%d or %%%% - sorry, will fail\n"); + } + percent++; + } + state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandLink : + { + int len = strlen(argv[i+1]); + if (len) + { + state->linkname = malloc(len + 10); + vcos_assert(state->linkname); + if (state->linkname) + strncpy(state->linkname, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + + } + + case CommandFrameStart: // use a staring value != 0 + { + if (sscanf(argv[i + 1], "%d", &state->frameStart) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + case CommandDateTime: // use datetime + state->datetime = 1; + break; + case CommandTimeStamp: // use timestamp + state->timestamp = 1; + break; + + case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected CommandKeypress we don't overwrite it + if (state->timeout == 0 && state->frameNextMethod == FRAME_NEXT_SINGLE) + state->frameNextMethod = FRAME_NEXT_FOREVER; + + i++; + } + else + valid = 0; + break; + } + case CommandThumbnail : // thumbnail parameters - needs string "x:y:quality" + if ( strcmp( argv[ i + 1 ], "none" ) == 0 ) + { + state->thumbnailConfig.enable = 0; + } + else + { + sscanf(argv[i + 1], "%d:%d:%d", + &state->thumbnailConfig.width, + &state->thumbnailConfig.height, + &state->thumbnailConfig.quality); + } + i++; + break; + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandEncoding : + { + int len = strlen(argv[i + 1]); + valid = 0; + + if (len) + { + int j; + for (j=0;jencoding = encoding_xref[j].encoding; + valid = 1; + i++; + break; + } + } + } + break; + } + + case CommandExifTag: + if ( strcmp( argv[ i + 1 ], "none" ) == 0 ) + { + state->enableExifTags = 0; + } + else + { + store_exif_tag(state, argv[i+1]); + } + i++; + break; + + case CommandTimelapse: + if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1) + valid = 0; + else + { + if (state->timelapse) + state->frameNextMethod = FRAME_NEXT_TIMELAPSE; + else + state->frameNextMethod = FRAME_NEXT_IMMEDIATELY; + + + i++; + } + break; + + case CommandFullResPreview: + state->fullResPreview = 1; + break; + + case CommandKeypress: // Set keypress between capture mode + state->frameNextMethod = FRAME_NEXT_KEYPRESS; + break; + + case CommandSignal: // Set SIGUSR1 & SIGUSR2 between capture mode + state->frameNextMethod = FRAME_NEXT_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + signal(SIGUSR2, signal_handler); + break; + + case CommandGL: + state->useGL = 1; + break; + + case CommandGLCapture: + state->glCapture = 1; + break; + + case CommandSettings: + state->settings = 1; + break; + + + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandBurstMode: + state->burstCaptureMode=1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandRestartInterval: + { + if (sscanf(argv[i + 1], "%u", &state->restart_interval) == 1) + { + i++; + } + else + valid = 0; + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + // Still unused, try GL preview options + if (!parms_used) + parms_used = raspitex_parse_cmdline(&state->raspitex_state, &argv[i][1], second_arg); + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + /* GL preview parameters use preview parameters as defaults unless overriden */ + if (! state->raspitex_state.gl_win_defined) + { + state->raspitex_state.x = state->preview_parameters.previewWindow.x; + state->raspitex_state.y = state->preview_parameters.previewWindow.y; + state->raspitex_state.width = state->preview_parameters.previewWindow.width; + state->raspitex_state.height = state->preview_parameters.previewWindow.height; + } + /* Also pass the preview information through so GL renderer can determine + * the real resolution of the multi-media image */ + state->raspitex_state.preview_x = state->preview_parameters.previewWindow.x; + state->raspitex_state.preview_y = state->preview_parameters.previewWindow.y; + state->raspitex_state.preview_width = state->preview_parameters.previewWindow.width; + state->raspitex_state.preview_height = state->preview_parameters.previewWindow.height; + state->raspitex_state.opacity = state->preview_parameters.opacity; + state->raspitex_state.verbose = state->verbose; + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stdout, "Runs camera for specific time, and take JPG capture at end if requested\n\n"); + fprintf(stdout, "usage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + // Now display GL preview help + raspitex_display_help(); + + fprintf(stdout, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * No actions taken in current version + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for encoder + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + int complete = 0; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + + if (buffer->length && pData->file_handle) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + // We need to check we wrote what we wanted - it's possible we have run out of storage. + if (bytes_written != buffer->length) + { + vcos_log_error("Unable to write buffer to file - aborting"); + complete = 1; + } + + // Now flag if we have completed + if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) + complete = 1; + } + else + { + vcos_log_error("Received a encoder buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_BUFFER_HEADER_T *new_buffer; + + new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + + if (new_buffer) + { + status = mmal_port_send_buffer(port, new_buffer); + } + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the encoder port"); + } + + if (complete) + vcos_semaphore_post(&(pData->complete_semaphore)); +} + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct. camera_component member set to the created camera_component if successful. + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + status = raspicamcontrol_set_stereo_mode(camera->output[0], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[1], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[2], &state->camera_parameters.stereo_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set stereo mode : error %d", status); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 1, + .max_preview_video_w = state->preview_parameters.previewWindow.width, + .max_preview_video_h = state->preview_parameters.previewWindow.height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + + if (state->fullResPreview) + { + cam_config.max_preview_video_w = state->width; + cam_config.max_preview_video_h = state->height; + } + + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + // Now set up the port formats + + format = preview_port->format; + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + if (state->fullResPreview) + { + // In this mode we are forcing the preview to be generated from the full capture resolution. + // This runs at a max of 15fps with the OV5647 sensor. + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN; + } + else + { + // Use a full FOV 4:3 mode + format->es->video.width = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->preview_parameters.previewWindow.width; + format->es->video.crop.height = state->preview_parameters.previewWindow.height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + } + + status = mmal_port_format_commit(preview_port); + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the same format on the video port (which we don't use here) + mmal_format_full_copy(video_port->format, format); + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + format = still_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + // Set our stills format on the stills (for encoder) port + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; + format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; + + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + if (state->useGL) + { + status = raspitex_configure_preview_port(&state->raspitex_state, preview_port); + if (status != MMAL_SUCCESS) + { + fprintf(stderr, "Failed to configure preview port for GL rendering"); + goto error; + } + } + + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPISTILL_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Create the encoder component, set up its ports + * + * @param state Pointer to state control struct. encoder_component member set to the created camera_component if successful. + * + * @return a MMAL_STATUS, MMAL_SUCCESS if all OK, something else otherwise + */ +static MMAL_STATUS_T create_encoder_component(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *encoder = 0; + MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create JPEG encoder component"); + goto error; + } + + if (!encoder->input_num || !encoder->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("JPEG encoder doesn't have input/output ports"); + goto error; + } + + encoder_input = encoder->input[0]; + encoder_output = encoder->output[0]; + + // We want same format on input and output + mmal_format_copy(encoder_output->format, encoder_input->format); + + // Specify out output format + encoder_output->format->encoding = state->encoding; + + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + + if (encoder_output->buffer_size < encoder_output->buffer_size_min) + encoder_output->buffer_size = encoder_output->buffer_size_min; + + encoder_output->buffer_num = encoder_output->buffer_num_recommended; + + if (encoder_output->buffer_num < encoder_output->buffer_num_min) + encoder_output->buffer_num = encoder_output->buffer_num_min; + + // Commit the port changes to the output port + status = mmal_port_format_commit(encoder_output); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on video encoder output port"); + goto error; + } + + // Set the JPEG quality level + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, state->quality); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set JPEG quality"); + goto error; + } + + // Set the JPEG restart interval + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, state->restart_interval); + + if (state->restart_interval && status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set JPEG restart interval"); + goto error; + } + + // Set up any required thumbnail + { + MMAL_PARAMETER_THUMBNAIL_CONFIG_T param_thumb = {{MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, sizeof(MMAL_PARAMETER_THUMBNAIL_CONFIG_T)}, 0, 0, 0, 0}; + + if ( state->thumbnailConfig.enable && + state->thumbnailConfig.width > 0 && state->thumbnailConfig.height > 0 ) + { + // Have a valid thumbnail defined + param_thumb.enable = 1; + param_thumb.width = state->thumbnailConfig.width; + param_thumb.height = state->thumbnailConfig.height; + param_thumb.quality = state->thumbnailConfig.quality; + } + status = mmal_port_parameter_set(encoder->control, ¶m_thumb.hdr); + } + + // Enable component + status = mmal_component_enable(encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable video encoder component"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); + } + + state->encoder_pool = pool; + state->encoder_component = encoder; + + if (state->verbose) + fprintf(stderr, "Encoder component done\n"); + + return status; + + error: + + if (encoder) + mmal_component_destroy(encoder); + + return status; +} + +/** + * Destroy the encoder component + * + * @param state Pointer to state control struct + * + */ +static void destroy_encoder_component(RASPISTILL_STATE *state) +{ + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + } + + if (state->encoder_component) + { + mmal_component_destroy(state->encoder_component); + state->encoder_component = NULL; + } +} + + +/** + * Add an exif tag to the capture + * + * @param state Pointer to state control struct + * @param exif_tag String containing a "key=value" pair. + * @return Returns a MMAL_STATUS_T giving result of operation + */ +static MMAL_STATUS_T add_exif_tag(RASPISTILL_STATE *state, const char *exif_tag) +{ + MMAL_STATUS_T status; + MMAL_PARAMETER_EXIF_T *exif_param = (MMAL_PARAMETER_EXIF_T*)calloc(sizeof(MMAL_PARAMETER_EXIF_T) + MAX_EXIF_PAYLOAD_LENGTH, 1); + + vcos_assert(state); + vcos_assert(state->encoder_component); + + // Check to see if the tag is present or is indeed a key=value pair. + if (!exif_tag || strchr(exif_tag, '=') == NULL || strlen(exif_tag) > MAX_EXIF_PAYLOAD_LENGTH-1) + return MMAL_EINVAL; + + exif_param->hdr.id = MMAL_PARAMETER_EXIF; + + strncpy((char*)exif_param->data, exif_tag, MAX_EXIF_PAYLOAD_LENGTH-1); + + exif_param->hdr.size = sizeof(MMAL_PARAMETER_EXIF_T) + strlen((char*)exif_param->data); + + status = mmal_port_parameter_set(state->encoder_component->output[0], &exif_param->hdr); + + free(exif_param); + + return status; +} + +/** + * Add a basic set of EXIF tags to the capture + * Make, Time etc + * + * @param state Pointer to state control struct + * + */ +static void add_exif_tags(RASPISTILL_STATE *state) +{ + time_t rawtime; + struct tm *timeinfo; + char model_buf[32]; + char time_buf[32]; + char exif_buf[128]; + int i; + + + snprintf(model_buf, 32, "IFD0.Model=RP_%s", state->camera_name); + add_exif_tag(state, model_buf); + add_exif_tag(state, "IFD0.Make=RaspberryPi"); + + time(&rawtime); + timeinfo = localtime(&rawtime); + + snprintf(time_buf, sizeof(time_buf), + "%04d:%02d:%02d %02d:%02d:%02d", + timeinfo->tm_year+1900, + timeinfo->tm_mon+1, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec); + + snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeDigitized=%s", time_buf); + add_exif_tag(state, exif_buf); + + snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeOriginal=%s", time_buf); + add_exif_tag(state, exif_buf); + + snprintf(exif_buf, sizeof(exif_buf), "IFD0.DateTime=%s", time_buf); + add_exif_tag(state, exif_buf); + + // Now send any user supplied tags + + for (i=0;inumExifTags && i < MAX_USER_EXIF_TAGS;i++) + { + if (state->exifTags[i]) + { + add_exif_tag(state, state->exifTags[i]); + } + } +} + +/** + * Stores an EXIF tag in the state, incrementing various pointers as necessary. + * Any tags stored in this way will be added to the image file when add_exif_tags + * is called + * + * Will not store if run out of storage space + * + * @param state Pointer to state control struct + * @param exif_tag EXIF tag string + * + */ +static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag) +{ + if (state->numExifTags < MAX_USER_EXIF_TAGS) + { + state->exifTags[state->numExifTags] = exif_tag; + state->numExifTags++; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + + +/** + * Allocates and generates a filename based on the + * user-supplied pattern and the frame number. + * On successful return, finalName and tempName point to malloc()ed strings + * which must be freed externally. (On failure, returns nulls that + * don't need free()ing.) + * + * @param finalName pointer receives an + * @param pattern sprintf pattern with %d to be replaced by frame + * @param frame for timelapse, the frame number + * @return Returns a MMAL_STATUS_T giving result of operation +*/ + +MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame) +{ + *finalName = NULL; + *tempName = NULL; + if (0 > asprintf(finalName, pattern, frame) || + 0 > asprintf(tempName, "%s~", *finalName)) + { + if (*finalName != NULL) + { + free(*finalName); + } + return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right + } + return MMAL_SUCCESS; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1 || signal_number == SIGUSR2) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 or USR2 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } +} + + +/** + * Function to wait in various ways (depending on settings) for the next frame + * + * @param state Pointer to the state data + * @param [in][out] frame The last frame number, adjusted to next frame number on output + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_frame(RASPISTILL_STATE *state, int *frame) +{ + static int64_t complete_time = -1; + int keep_running = 1; + + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + // If timeout = 0 then always continue + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->frameNextMethod) + { + case FRAME_NEXT_SINGLE : + // simple timeout for a single capture + vcos_sleep(state->timeout); + return 0; + + case FRAME_NEXT_FOREVER : + { + *frame+=1; + + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + // Run forever so never indicate end of loop + return 1; + } + + case FRAME_NEXT_TIMELAPSE : + { + static int64_t next_frame_ms = -1; + + // Always need to increment by at least one, may add a skip later + *frame += 1; + + if (next_frame_ms == -1) + { + vcos_sleep(state->timelapse); + + // Update our current time after the sleep + current_time = vcos_getmicrosecs64()/1000; + + // Set our initial 'next frame time' + next_frame_ms = current_time + state->timelapse; + } + else + { + int64_t this_delay_ms = next_frame_ms - current_time; + + if (this_delay_ms < 0) + { + // We are already past the next exposure time + if (-this_delay_ms < state->timelapse/2) + { + // Less than a half frame late, take a frame and hope to catch up next time + next_frame_ms += state->timelapse; + vcos_log_error("Frame %d is %d ms late", *frame, (int)(-this_delay_ms)); + } + else + { + int nskip = 1 + (-this_delay_ms)/state->timelapse; + vcos_log_error("Skipping frame %d to restart at frame %d", *frame, *frame+nskip); + *frame += nskip; + this_delay_ms += nskip * state->timelapse; + vcos_sleep(this_delay_ms); + next_frame_ms += (nskip + 1) * state->timelapse; + } + } + else + { + vcos_sleep(this_delay_ms); + next_frame_ms += state->timelapse; + } + } + + return keep_running; + } + + case FRAME_NEXT_KEYPRESS : + { + int ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to capture, X then ENTER to exit\n"); + + ch = getchar(); + *frame+=1; + if (ch == 'x' || ch == 'X') + return 0; + else + { + return keep_running; + } + } + + case FRAME_NEXT_IMMEDIATELY : + { + // Not waiting, just go to next frame. + // Actually, we do need a slight delay here otherwise exposure goes + // badly wrong since we never allow it frames to work it out + // This could probably be tuned down. + // First frame has a much longer delay to ensure we get exposure to a steady state + if (*frame == 0) + vcos_sleep(1000); + else + vcos_sleep(30); + + *frame+=1; + + return keep_running; + } + + case FRAME_NEXT_GPIO : + { + // Intended for GPIO firing of a capture + return 0; + } + + case FRAME_NEXT_SIGNAL : + { + // Need to wait for a SIGUSR1 or SIGUSR2 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + sigaddset( &waitset, SIGUSR2 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block until a SIGUSR1 or SIGUSR2 signal appears + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to initiate capture and continue or SIGUSR2 to capture and exit\n"); + } + + result = sigwait( &waitset, &sig ); + + if (result == 0) + { + if (sig == SIGUSR1) + { + if (state->verbose) + fprintf(stderr, "Received SIGUSR1\n"); + } + else if (sig == SIGUSR2) + { + if (state->verbose) + fprintf(stderr, "Received SIGUSR2\n"); + keep_running = 0; + } + } + else + { + if (state->verbose) + fprintf(stderr, "Bad signal received - error %d\n", errno); + } + + *frame+=1; + + return keep_running; + } + } // end of switch + + // Should have returned by now, but default to timeout + return keep_running; +} + +static void rename_file(RASPISTILL_STATE *state, FILE *output_file, + const char *final_filename, const char *use_filename, int frame) +{ + MMAL_STATUS_T status; + + fclose(output_file); + vcos_assert(use_filename != NULL && final_filename != NULL); + if (0 != rename(use_filename, final_filename)) + { + vcos_log_error("Could not rename temp file to: %s; %s", + final_filename,strerror(errno)); + } + if (state->linkname) + { + char *use_link; + char *final_link; + status = create_filenames(&final_link, &use_link, state->linkname, frame); + + // Create hard link if possible, symlink otherwise + if (status != MMAL_SUCCESS + || (0 != link(final_filename, use_link) + && 0 != symlink(final_filename, use_link)) + || 0 != rename(use_link, final_link)) + { + vcos_log_error("Could not link as filename: %s; %s", + state->linkname,strerror(errno)); + } + if (use_link) free(use_link); + if (final_link) free(final_link); + } +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPISTILL_STATE state = {0}; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + MMAL_PORT_T *encoder_output_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 and USR2 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + exit(EX_USAGE); + } + + // Setup for sensor specific parameters + set_sensor_defaults(&state); + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + dump_status(&state); + } + + if (state.useGL) + raspitex_init(&state.raspitex_state); + + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. + // Camera and encoder are different in stills/video, but preview + // is the same so handed off to a separate module + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((!state.useGL) && (status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + encoder_input_port = state.encoder_component->input[0]; + encoder_output_port = state.encoder_component->output[0]; + + if (! state.useGL) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to video render.\n"); + + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + } + + if (status == MMAL_SUCCESS) + { + VCOS_STATUS_T vcos_status; + + if (state.verbose) + fprintf(stderr, "Connecting camera stills port to encoder input port\n"); + + // Now connect the camera to the encoder + status = connect_ports(camera_still_port, encoder_input_port, &state.encoder_connection); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } + + // Set up our userdata - this is passed though to the callback where we need the information. + // Null until we open our filename + callback_data.file_handle = NULL; + callback_data.pstate = &state; + vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0); + + vcos_assert(vcos_status == VCOS_SUCCESS); + + /* If GL preview is requested then start the GL threads */ + if (state.useGL && (raspitex_start(&state.raspitex_state) != 0)) + goto error; + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + for (i=0;itm_mon+1; + frame *= 100; + frame += timeinfo->tm_mday; + frame *= 100; + frame += timeinfo->tm_hour; + frame *= 100; + frame += timeinfo->tm_min; + frame *= 100; + frame += timeinfo->tm_sec; + } + if (state.timestamp) + { + frame = (int)time(NULL); + } + + // Open the file + if (state.filename) + { + if (state.filename[0] == '-') + { + output_file = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + vcos_assert(use_filename == NULL && final_filename == NULL); + status = create_filenames(&final_filename, &use_filename, state.filename, frame); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create filenames"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Opening output file %s\n", final_filename); + // Technically it is opening the temp~ filename which will be ranamed to the final filename + + output_file = fopen(use_filename, "wb"); + + if (!output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename); + } + } + + callback_data.file_handle = output_file; + } + + // We only capture if a filename was specified and it opened + if (state.useGL && state.glCapture && output_file) + { + /* Save the next GL framebuffer as the next camera still */ + int rc = raspitex_capture(&state.raspitex_state, output_file); + if (rc != 0) + vcos_log_error("Failed to capture GL preview"); + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else if (output_file) + { + int num, q; + + // Must do this before the encoder output port is enabled since + // once enabled no further exif data is accepted + if ( state.enableExifTags ) + { + add_exif_tags(&state); + } + else + { + mmal_port_parameter_set_boolean( + state.encoder_component->output[0], MMAL_PARAMETER_EXIF_DISABLE, 1); + } + + // Same with raw, apparently need to set it for each capture, whilst port + // is not enabled + if (state.wantRAW) + { + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_ENABLE_RAW_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("RAW was requested, but failed to enable"); + } + } + + // There is a possibility that shutter needs to be set each loop. + if (mmal_status_to_int(mmal_port_parameter_set_uint32(state.camera_component->control, MMAL_PARAMETER_SHUTTER_SPEED, state.camera_parameters.shutter_speed)) != MMAL_SUCCESS) + vcos_log_error("Unable to set shutter speed"); + + + // Enable the encoder output port + encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling encoder output port\n"); + + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + + // Send all the buffers to the encoder output port + num = mmal_queue_length(state.encoder_pool->queue); + + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + } + + if (state.burstCaptureMode && frame==1) + { + mmal_port_parameter_set_boolean(state.camera_component->control, MMAL_PARAMETER_CAMERA_BURST_CAPTURE, 1); + } + + if(state.camera_parameters.enable_annotate) + raspicamcontrol_set_annotate(state.camera_component, state.camera_parameters.enable_annotate, + state.camera_parameters.annotate_string, + state.camera_parameters.annotate_text_size, + state.camera_parameters.annotate_text_colour, + state.camera_parameters.annotate_bg_colour); + + if (state.verbose) + fprintf(stderr, "Starting capture %d\n", frame); + + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to start capture", __func__); + } + else + { + // Wait for capture to complete + // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error + // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic + vcos_semaphore_wait(&callback_data.complete_semaphore); + if (state.verbose) + fprintf(stderr, "Finished capture %d\n", frame); + } + + // Ensure we don't die if get callback with no open file + callback_data.file_handle = NULL; + + if (output_file != stdout) + { + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else + { + fflush(output_file); + } + // Disable encoder output port + status = mmal_port_disable(encoder_output_port); + } + + if (use_filename) + { + free(use_filename); + use_filename = NULL; + } + if (final_filename) + { + free(final_filename); + final_filename = NULL; + } + } // end for (frame) + + vcos_semaphore_delete(&callback_data.complete_semaphore); + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + if (state.useGL) + { + raspitex_stop(&state.raspitex_state); + raspitex_destroy(&state.raspitex_state); + } + + // Disable all our ports that are not handled by connections + check_disable_port(camera_video_port); + check_disable_port(encoder_output_port); + + if (state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.encoder_connection) + mmal_connection_destroy(state.encoder_connection); + + + /* Disable components */ + if (state.encoder_component) + mmal_component_disable(state.encoder_component); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + destroy_encoder_component(&state); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + diff --git a/host_applications/linux/apps/raspicam/RaspiStillYUV.c b/host_applications/linux/apps/raspicam/RaspiStillYUV.c new file mode 100755 index 0000000..ac178a3 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiStillYUV.c @@ -0,0 +1,1472 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file RaspiStillYUV.c + * Command line program to capture a still frame and dump uncompressed it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * 2 components are created; camera and preview. + * Camera component has three ports, preview, video and stills. + * Preview is connected using standard mmal connections, the stills output + * is written straight to the file in YUV 420 format via the requisite buffer + * callback. video port is not used + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the generic preview + */ + +// We use some GNU extensions (basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.7" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + + +// Stills format information +// 0 implies variable +#define STILLS_FRAME_RATE_NUM 0 +#define STILLS_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +/// Frame advance method +#define FRAME_NEXT_SINGLE 0 +#define FRAME_NEXT_TIMELAPSE 1 +#define FRAME_NEXT_KEYPRESS 2 +#define FRAME_NEXT_FOREVER 3 +#define FRAME_NEXT_GPIO 4 +#define FRAME_NEXT_SIGNAL 5 +#define FRAME_NEXT_IMMEDIATELY 6 + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + char *filename; /// filename of output file + char *linkname; /// filename of output file + int verbose; /// !0 if want detailed run information + int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse + int useRGB; /// Output RGB data rather than YUV + int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps. + int frameNextMethod; /// Which method to use to advance to next frame + int settings; /// Request settings from the camera + int cameraNum; /// Camera number + int burstCaptureMode; /// Enable burst mode + int onlyLuma; /// Only output the luma / Y plane of the YUV data + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *null_sink_component; /// Pointer to the camera component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera stills port +} RASPISTILLYUV_STATE; + + +/** Struct used to pass information in camera still port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) + RASPISTILLYUV_STATE *pstate; /// pointer to our state in case required in callback +} PORT_USERDATA; + +static void display_valid_parameters(char *app_name); + +/// Comamnd ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandOutput 3 +#define CommandVerbose 4 +#define CommandTimeout 5 +#define CommandTimelapse 6 +#define CommandUseRGB 7 +#define CommandCamSelect 8 +#define CommandFullResPreview 9 +#define CommandLink 10 +#define CommandKeypress 11 +#define CommandSignal 12 +#define CommandSettings 13 +#define CommandBurstMode 14 +#define CommandOnlyLuma 15 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width ", 1 }, + { CommandHeight, "-height", "h", "Set image height ", 1 }, + { CommandOutput, "-output", "o", "Output filename . If not specifed, no image is saved", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down. If not specified set to 5s", 1 }, + { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every ms", 1}, + { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0}, + { CommandCamSelect,"-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandFullResPreview,"-fullpreview","fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0}, + { CommandLink, "-latest", "l", "Link latest complete image to filename ", 1}, + { CommandKeypress,"-keypress", "k", "Wait between captures for a ENTER, X then ENTER to exit", 0}, + { CommandSignal, "-signal", "s", "Wait between captures for a SIGUSR1 from another process", 0}, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandBurstMode, "-burst", "bm", "Enable 'burst capture mode'", 0}, + { CommandOnlyLuma, "-luma", "y", "Only output the luma / Y of the YUV data'", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +static struct +{ + char *description; + int nextFrameMethod; +} next_frame_description[] = +{ + {"Single capture", FRAME_NEXT_SINGLE}, + {"Capture on timelapse", FRAME_NEXT_TIMELAPSE}, + {"Capture on keypress", FRAME_NEXT_KEYPRESS}, + {"Run forever", FRAME_NEXT_FOREVER}, + {"Capture on GPIO", FRAME_NEXT_GPIO}, + {"Capture on signal", FRAME_NEXT_SIGNAL}, +}; + +static int next_frame_description_size = sizeof(next_frame_description) / sizeof(next_frame_description[0]); + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPISTILLYUV_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPISTILLYUV_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 2592; + state->height = 1944; + state->timelapse = 0; + state->filename = NULL; + state->linkname = NULL; + state->verbose = 0; + state->fullResPreview = 0; + state->frameNextMethod = FRAME_NEXT_SINGLE; + state->settings = 0; + state->burstCaptureMode=0; + state->onlyLuma = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); + + // Set default camera + state->cameraNum = 0; +} + +/** + * Dump image state parameters to stderr. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPISTILLYUV_STATE *state) +{ + int i; + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, + state->height, state->filename); + fprintf(stderr, "Time delay %d, Timelapse %d\n", state->timeout, state->timelapse); + fprintf(stderr, "Link to latest frame enabled "); + if (state->linkname) + { + fprintf(stderr, " yes, -> %s\n", state->linkname); + } + else + { + fprintf(stderr, " no\n"); + } + fprintf(stderr, "Full resolution preview %s\n", state->fullResPreview ? "Yes": "No"); + + fprintf(stderr, "Capture method : "); + for (i=0;iframeNextMethod == next_frame_description[i].nextFrameMethod) + fprintf(stderr, "%s", next_frame_description[i].description); + } + fprintf(stderr, "\n\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; // set 0 if we have a bad parameter + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandLink : + { + int len = strlen(argv[i+1]); + if (len) + { + state->linkname = malloc(len + 10); + vcos_assert(state->linkname); + if (state->linkname) + strncpy(state->linkname, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected CommandKeypress we don't overwrite it + if (state->timeout == 0 && state->frameNextMethod == FRAME_NEXT_SINGLE) + state->frameNextMethod = FRAME_NEXT_FOREVER; + + i++; + } + else + valid = 0; + break; + } + + case CommandTimelapse: + if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1) + valid = 0; + else + { + if (state->timelapse) + state->frameNextMethod = FRAME_NEXT_TIMELAPSE; + else + state->frameNextMethod = FRAME_NEXT_IMMEDIATELY; + + + i++; + } + break; + + case CommandUseRGB: // display lots of data during run + if (state->onlyLuma) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->useRGB = 1; + break; + + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandFullResPreview: + state->fullResPreview = 1; + break; + + case CommandKeypress: // Set keypress between capture mode + state->frameNextMethod = FRAME_NEXT_KEYPRESS; + break; + + case CommandSignal: // Set SIGUSR1 between capture mode + state->frameNextMethod = FRAME_NEXT_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandSettings: + state->settings = 1; + break; + + case CommandBurstMode: + state->burstCaptureMode=1; + break; + + case CommandOnlyLuma: + if (state->useRGB) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->onlyLuma = 1; + break; + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + * + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stdout, "Runs camera for specific time, and take uncompressed YUV capture at end if requested\n\n"); + fprintf(stdout, "usage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + fprintf(stdout, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + fprintf(stderr, "Camera control callback cmd=0x%08x", buffer->cmd); + + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for camera output port + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + int complete = 0; + // We pass our file handle and other stuff in via the userdata field. + + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = 0; + int bytes_to_write = buffer->length; + + if (pData->pstate->onlyLuma) + bytes_to_write = vcos_min(buffer->length, port->format->es->video.width * port->format->es->video.height); + + if (bytes_to_write && pData->file_handle) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + // We need to check we wrote what we wanted - it's possible we have run out of storage. + if (buffer->length && bytes_written != bytes_to_write) + { + vcos_log_error("Unable to write buffer to file - aborting %d vs %d", bytes_written, bytes_to_write); + complete = 1; + } + + // Check end of frame or error + if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) + complete = 1; + } + else + { + vcos_log_error("Received a camera still buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue); + + // and back to the port from there. + if (new_buffer) + { + status = mmal_port_send_buffer(port, new_buffer); + } + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return the buffer to the camera still port"); + } + + if (complete) + { + vcos_semaphore_post(&(pData->complete_semaphore)); + } +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return 0 if failed, pointer to component if successful + * + */ +static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 1, + .max_preview_video_w = state->preview_parameters.previewWindow.width, + .max_preview_video_h = state->preview_parameters.previewWindow.height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + + if (state->fullResPreview) + { + cam_config.max_preview_video_w = state->width; + cam_config.max_preview_video_h = state->height; + } + + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + // Now set up the port formats + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + if (state->fullResPreview) + { + // In this mode we are forcing the preview to be generated from the full capture resolution. + // This runs at a max of 15fps with the OV5647 sensor. + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN; + } + else + { + // Use a full FOV 4:3 mode + format->es->video.width = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->preview_parameters.previewWindow.width; + format->es->video.crop.height = state->preview_parameters.previewWindow.height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + } + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the same format on the video port (which we don't use here) + mmal_format_full_copy(video_port->format, format); + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + format = still_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + // Set our stills format on the stills port + if (state->useRGB) + { + format->encoding = mmal_util_rgb_order_fixed(still_port) ? MMAL_ENCODING_RGB24 : MMAL_ENCODING_BGR24; + format->encoding_variant = 0; //Irrelevant when not in opaque mode + } + else + { + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + } + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; + format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; + + if (still_port->buffer_size < still_port->buffer_size_min) + still_port->buffer_size = still_port->buffer_size_min; + + still_port->buffer_num = still_port->buffer_num_recommended; + + status = mmal_port_parameter_set_boolean(video_port, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to select zero copy"); + goto error; + } + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(still_port, still_port->buffer_num, still_port->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name); + } + + state->camera_pool = pool; + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPISTILLYUV_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Allocates and generates a filename based on the + * user-supplied pattern and the frame number. + * On successful return, finalName and tempName point to malloc()ed strings + * which must be freed externally. (On failure, returns nulls that + * don't need free()ing.) + * + * @param finalName pointer receives an + * @param pattern sprintf pattern with %d to be replaced by frame + * @param frame for timelapse, the frame number + * @return Returns a MMAL_STATUS_T giving result of operation +*/ + +MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame) +{ + *finalName = NULL; + *tempName = NULL; + if (0 > asprintf(finalName, pattern, frame) || + 0 > asprintf(tempName, "%s~", *finalName)) + { + if (*finalName != NULL) + { + free(*finalName); + } + return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right + } + return MMAL_SUCCESS; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } +} + +/** + * Function to wait in various ways (depending on settings) for the next frame + * + * @param state Pointer to the state data + * @param [in][out] frame The last frame number, adjusted to next frame number on output + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_frame(RASPISTILLYUV_STATE *state, int *frame) +{ + static int64_t complete_time = -1; + int keep_running = 1; + + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + // If timeout = 0 then always continue + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->frameNextMethod) + { + case FRAME_NEXT_SINGLE : + // simple timeout for a single capture + vcos_sleep(state->timeout); + return 0; + + case FRAME_NEXT_FOREVER : + { + *frame+=1; + + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + // Run forever so never indicate end of loop + return 1; + } + + case FRAME_NEXT_TIMELAPSE : + { + static int64_t next_frame_ms = -1; + + // Always need to increment by at least one, may add a skip later + *frame += 1; + + if (next_frame_ms == -1) + { + vcos_sleep(state->timelapse); + + // Update our current time after the sleep + current_time = vcos_getmicrosecs64()/1000; + + // Set our initial 'next frame time' + next_frame_ms = current_time + state->timelapse; + } + else + { + int64_t this_delay_ms = next_frame_ms - current_time; + + if (this_delay_ms < 0) + { + // We are already past the next exposure time + if (-this_delay_ms < -state->timelapse/2) + { + // Less than a half frame late, take a frame and hope to catch up next time + next_frame_ms += state->timelapse; + vcos_log_error("Frame %d is %d ms late", *frame, (int)(-this_delay_ms)); + } + else + { + int nskip = 1 + (-this_delay_ms)/state->timelapse; + vcos_log_error("Skipping frame %d to restart at frame %d", *frame, *frame+nskip); + *frame += nskip; + this_delay_ms += nskip * state->timelapse; + vcos_sleep(this_delay_ms); + next_frame_ms += (nskip + 1) * state->timelapse; + } + } + else + { + vcos_sleep(this_delay_ms); + next_frame_ms += state->timelapse; + } + } + + return keep_running; + } + + case FRAME_NEXT_KEYPRESS : + { + int ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to capture, X then ENTER to exit\n"); + + ch = getchar(); + *frame+=1; + if (ch == 'x' || ch == 'X') + return 0; + else + { + return keep_running; + } + } + + case FRAME_NEXT_IMMEDIATELY : + { + // Not waiting, just go to next frame. + // Actually, we do need a slight delay here otherwise exposure goes + // badly wrong since we never allow it frames to work it out + // This could probably be tuned down. + // First frame has a much longer delay to ensure we get exposure to a steady state + if (*frame == 0) + vcos_sleep(1000); + else + vcos_sleep(30); + + *frame+=1; + + return keep_running; + } + + case FRAME_NEXT_GPIO : + { + // Intended for GPIO firing of a capture + return 0; + } + + case FRAME_NEXT_SIGNAL : + { + // Need to wait for a SIGUSR1 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block SIGUSR1 so we can wait on it. + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to initiate capture\n"); + } + + result = sigwait( &waitset, &sig ); + + if (state->verbose) + { + if( result == 0) + { + fprintf(stderr, "Received SIGUSR1\n"); + } + else + { + fprintf(stderr, "Bad signal received - error %d\n", errno); + } + } + + *frame+=1; + + return keep_running; + } + } // end of switch + + // Should have returned by now, but default to timeout + return keep_running; +} + +static void rename_file(RASPISTILLYUV_STATE *state, FILE *output_file, + const char *final_filename, const char *use_filename, int frame) +{ + MMAL_STATUS_T status; + + fclose(output_file); + vcos_assert(use_filename != NULL && final_filename != NULL); + if (0 != rename(use_filename, final_filename)) + { + vcos_log_error("Could not rename temp file to: %s; %s", + final_filename,strerror(errno)); + } + if (state->linkname) + { + char *use_link; + char *final_link; + status = create_filenames(&final_link, &use_link, state->linkname, frame); + + // Create hard link if possible, symlink otherwise + if (status != MMAL_SUCCESS + || (0 != link(final_filename, use_link) + && 0 != symlink(final_filename, use_link)) + || 0 != rename(use_link, final_link)) + { + vcos_log_error("Could not link as filename: %s; %s", + state->linkname,strerror(errno)); + } + if (use_link) free(use_link); + if (final_link) free(final_link); + } +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPISTILLYUV_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + FILE *output_file = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + default_status(&state); + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have two components. Camera and Preview + // Camera is different in stills/video, but preview + // is the same so handed off to a separate module + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + + if (status == MMAL_SUCCESS) + { + VCOS_STATUS_T vcos_status; + + // Set up our userdata - this is passed though to the callback where we need the information. + // Null until we open our filename + callback_data.file_handle = NULL; + callback_data.pstate = &state; + + vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0); + vcos_assert(vcos_status == VCOS_SUCCESS); + + camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling camera still output port\n"); + + // Enable the camera still output port and tell it its callback function + status = mmal_port_enable(camera_still_port, camera_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup camera output"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Starting video preview\n"); + + + int frame, keep_looping = 1; + FILE *output_file = NULL; + char *use_filename = NULL; // Temporary filename while image being written + char *final_filename = NULL; // Name that file gets once writing complete + + frame = 0; + + while (keep_looping) + { + keep_looping = wait_for_next_frame(&state, &frame); + + // Open the file + if (state.filename) + { + if (state.filename[0] == '-') + { + output_file = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + vcos_assert(use_filename == NULL && final_filename == NULL); + status = create_filenames(&final_filename, &use_filename, state.filename, frame); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create filenames"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Opening output file %s\n", final_filename); + // Technically it is opening the temp~ filename which will be ranamed to the final filename + + output_file = fopen(use_filename, "wb"); + + if (!output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename); + } + } + + callback_data.file_handle = output_file; + } + + if (output_file) + { + int num, q; + + // There is a possibility that shutter needs to be set each loop. + if (mmal_status_to_int(mmal_port_parameter_set_uint32(state.camera_component->control, MMAL_PARAMETER_SHUTTER_SPEED, state.camera_parameters.shutter_speed) != MMAL_SUCCESS)) + vcos_log_error("Unable to set shutter speed"); + + + // Send all the buffers to the camera output port + num = mmal_queue_length(state.camera_pool->queue); + + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(camera_still_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to camera output port (%d)", q); + } + + if (state.burstCaptureMode && frame==1) + { + mmal_port_parameter_set_boolean(state.camera_component->control, MMAL_PARAMETER_CAMERA_BURST_CAPTURE, 1); + } + + if (state.verbose) + fprintf(stderr, "Starting capture %d\n", frame); + + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to start capture", __func__); + } + else + { + // Wait for capture to complete + // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error + // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic + vcos_semaphore_wait(&callback_data.complete_semaphore); + if (state.verbose) + fprintf(stderr, "Finished capture %d\n", frame); + } + + // Ensure we don't die if get callback with no open file + callback_data.file_handle = NULL; + + if (output_file != stdout) + { + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else + { + fflush(output_file); + } + } + + if (use_filename) + { + free(use_filename); + use_filename = NULL; + } + if (final_filename) + { + free(final_filename); + final_filename = NULL; + } + } // end for (frame) + + vcos_semaphore_delete(&callback_data.complete_semaphore); + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + if (output_file) + fclose(output_file); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_video_port); + + if (state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + /* Disable components */ + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + + + diff --git a/host_applications/linux/apps/raspicam/RaspiTex.c b/host_applications/linux/apps/raspicam/RaspiTex.c new file mode 100755 index 0000000..f7cc08f --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTex.c @@ -0,0 +1,754 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "RaspiTex.h" +#include "RaspiCLI.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RaspiTexUtil.h" +#include "interface/vcos/vcos.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "tga.h" + +#include "gl_scenes/mirror.h" +#include "gl_scenes/sobel.h" +#include "gl_scenes/square.h" +#include "gl_scenes/teapot.h" +#include "gl_scenes/vcsm_square.h" +#include "gl_scenes/yuv.h" + +/** + * \file RaspiTex.c + * + * A simple framework for extending a MMAL application to render buffers via + * OpenGL. + * + * MMAL buffers are often in YUV colour space and in either a planar or + * tile format which is not supported directly by V3D. Instead of copying + * the buffer from the GPU and doing a colour space / pixel format conversion + * the GL_OES_EGL_image_external is used. This allows an EGL image to be + * created from GPU buffer handle (MMAL opaque buffer handle). The EGL image + * may then be used to create a texture (glEGLImageTargetTexture2DOES) and + * drawn by either OpenGL ES 1.0 or 2.0 contexts. + * + * Notes: + * 1) GL_OES_EGL_image_external textures always return pixels in RGBA format. + * This is also the case when used from a fragment shader. + * + * 2) The driver implementation creates a new RGB_565 buffer and does the color + * space conversion from YUV. This happens in GPU memory using the vector + * processor. + * + * 3) Each EGL external image in use will consume GPU memory for the RGB 565 + * buffer. In addition, the GL pipeline might require more than one EGL image + * to be retained in GPU memory until the drawing commands are flushed. + * + * Typically 128 MB of GPU memory is sufficient for 720p viewfinder and 720p + * GL surface. If both the viewfinder and the GL surface are 1080p then + * 256MB of GPU memory is recommended, otherwise, for non-trivial scenes + * the system can run out of GPU memory whilst the camera is running. + * + * 4) It is important to make sure that the MMAL opaque buffer is not returned + * to MMAL before the GL driver has completed the asynchronous call to + * glEGLImageTargetTexture2DOES. Deferring destruction of the EGL image and + * the buffer return to MMAL until after eglSwapBuffers is the recommended. + * + * See also: http://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external.txt + */ + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +#define CommandGLScene 1 +#define CommandGLWin 2 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandGLScene, "-glscene", "gs", "GL scene square,teapot,mirror,yuv,sobel,vcsm_square", 1 }, + { CommandGLWin, "-glwin", "gw", "GL window settings <'x,y,w,h'>", 1 }, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspitex_parse_cmdline(RASPITEX_STATE *state, + const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, + cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandGLWin: // Allows a GL window to be different to preview-res + { + int tmp; + tmp = sscanf(arg2, "%d,%d,%d,%d", + &state->x, &state->y, &state->width, &state->height); + if (tmp != 4) + { + // Default to safe size on parse error + state->x = state->y = 0; + state->width = DEFAULT_WIDTH; + state->height = DEFAULT_HEIGHT; + } + else + { + state->gl_win_defined = 1; + } + + used = 2; + break; + } + + case CommandGLScene: // Selects the GL scene + { + if (strcmp(arg2, "square") == 0) + state->scene_id = RASPITEX_SCENE_SQUARE; + else if (strcmp(arg2, "teapot") == 0) + state->scene_id = RASPITEX_SCENE_TEAPOT; + else if (strcmp(arg2, "mirror") == 0) + state->scene_id = RASPITEX_SCENE_MIRROR; + else if (strcmp(arg2, "yuv") == 0) + state->scene_id = RASPITEX_SCENE_YUV; + else if (strcmp(arg2, "sobel") == 0) + state->scene_id = RASPITEX_SCENE_SOBEL; + else if (strcmp(arg2, "vcsm_square") == 0) + state->scene_id = RASPITEX_SCENE_VCSM_SQUARE; + else + vcos_log_error("Unknown scene %s", arg2); + + used = 2; + break; + } + } + return used; +} + +/** + * Display help for command line options + */ +void raspitex_display_help() +{ + fprintf(stdout, "\nPreview parameter commands\n\n"); + raspicli_display_help(cmdline_commands, cmdline_commands_size); +} + +static void update_fps() +{ + static int frame_count = 0; + static long long time_start = 0; + long long time_now; + struct timeval te; + float fps; + + frame_count++; + + gettimeofday(&te, NULL); + time_now = te.tv_sec * 1000LL + te.tv_usec / 1000; + + if (time_start == 0) + { + time_start = time_now; + } + else if (time_now - time_start > 5000) + { + fps = (float) frame_count / ((time_now - time_start) / 1000.0); + frame_count = 0; + time_start = time_now; + vcos_log_error("%3.2f FPS", fps); + } +} + +/** + * Captures the frame-buffer if requested. + * @param state RASPITEX STATE + * @return Zero if successful. + */ +static void raspitex_do_capture(RASPITEX_STATE *state) +{ + uint8_t *buffer = NULL; + size_t size = 0; + + if (state->capture.request) + { + if (state->ops.capture(state, &buffer, &size) == 0) + { + /* Pass ownership of buffer to main thread via capture state */ + state->capture.buffer = buffer; + state->capture.size = size; + } + else + { + state->capture.buffer = NULL; // Null indicates an error + state->capture.size = 0; + } + + state->capture.request = 0; // Always clear request and post sem + vcos_semaphore_post(&state->capture.completed_sem); + } +} + +/** + * Checks if there is at least one valid EGL image. + * @param state RASPITEX STATE + * @return Zero if successful. + */ +static int check_egl_image(RASPITEX_STATE *state) +{ + if (state->egl_image == EGL_NO_IMAGE_KHR && + state->y_egl_image == EGL_NO_IMAGE_KHR && + state->u_egl_image == EGL_NO_IMAGE_KHR && + state->v_egl_image == EGL_NO_IMAGE_KHR) + return -1; + else + return 0; +} + +/** + * Draws the next preview frame. If a new preview buffer is available then the + * preview texture is updated first. + * + * @param state RASPITEX STATE + * @param video_frame MMAL buffer header containing the opaque buffer handle. + * @return Zero if successful. + */ +static int raspitex_draw(RASPITEX_STATE *state, MMAL_BUFFER_HEADER_T *buf) +{ + int rc = 0; + + /* If buf is non-NULL then there is a new viewfinder frame available + * from the camera so the texture should be updated. + * + * Although it's possible to have multiple textures mapped to different + * viewfinder frames this can consume a lot of GPU memory for high-resolution + * viewfinders. + */ + if (buf) + { + /* Update the texture to the new viewfinder image which should */ + if (state->ops.update_texture) + { + rc = state->ops.update_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update RGBX texture %d", + VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_y_texture) + { + rc = state->ops.update_y_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update Y' plane texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_u_texture) + { + rc = state->ops.update_u_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update U plane texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_v_texture) + { + rc = state->ops.update_v_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update V texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + /* Now return the PREVIOUS MMAL buffer header back to the camera preview. */ + if (state->preview_buf) + mmal_buffer_header_release(state->preview_buf); + + state->preview_buf = buf; + } + + /* Do the drawing */ + if (check_egl_image(state) == 0) + { + rc = state->ops.update_model(state); + if (rc != 0) + goto end; + + rc = state->ops.redraw(state); + if (rc != 0) + goto end; + + raspitex_do_capture(state); + + eglSwapBuffers(state->display, state->surface); + update_fps(); + } + else + { + // vcos_log_trace("%s: No preview image", VCOS_FUNCTION); + } + +end: + return rc; +} + +/** + * Process preview buffers. + * + * Dequeue each available preview buffer in order and call current redraw + * function. If no new buffers are available then the render function is + * invoked anyway. + * @param state The GL preview window state. + * @return Zero if successful. + */ +static int preview_process_returned_bufs(RASPITEX_STATE* state) +{ + MMAL_BUFFER_HEADER_T *buf; + int new_frame = 0; + int rc = 0; + + while ((buf = mmal_queue_get(state->preview_queue)) != NULL) + { + if (state->preview_stop == 0) + { + new_frame = 1; + rc = raspitex_draw(state, buf); + if (rc != 0) + { + vcos_log_error("%s: Error drawing frame. Stopping.", VCOS_FUNCTION); + state->preview_stop = 1; + return rc; + } + } + } + + /* If there were no new frames then redraw the scene again with the previous + * texture. Otherwise, go round the loop again to see if any new buffers + * are returned. + */ + if (! new_frame) + rc = raspitex_draw(state, NULL); + return rc; +} + +/** Preview worker thread. + * Ensures camera preview is supplied with buffers and sends preview frames to GL. + * @param arg Pointer to state. + * @return NULL always. + */ +static void *preview_worker(void *arg) +{ + RASPITEX_STATE* state = arg; + MMAL_PORT_T *preview_port = state->preview_port; + MMAL_BUFFER_HEADER_T *buf; + MMAL_STATUS_T st; + int rc; + + vcos_log_trace("%s: port %p", VCOS_FUNCTION, preview_port); + + rc = state->ops.create_native_window(state); + if (rc != 0) + goto end; + + rc = state->ops.gl_init(state); + if (rc != 0) + goto end; + + while (state->preview_stop == 0) + { + /* Send empty buffers to camera preview port */ + while ((buf = mmal_queue_get(state->preview_pool->queue)) != NULL) + { + st = mmal_port_send_buffer(preview_port, buf); + if (st != MMAL_SUCCESS) + { + vcos_log_error("Failed to send buffer to %s", preview_port->name); + } + } + /* Process returned buffers */ + if (preview_process_returned_bufs(state) != 0) + { + vcos_log_error("Preview error. Exiting."); + state->preview_stop = 1; + } + } + +end: + /* Make sure all buffers are returned on exit */ + while ((buf = mmal_queue_get(state->preview_queue)) != NULL) + mmal_buffer_header_release(buf); + + /* Tear down GL */ + state->ops.gl_term(state); + vcos_log_trace("Exiting preview worker"); + return NULL; +} + +/** + * MMAL Callback from camera preview output port. + * @param port The camera preview port. + * @param buf The new preview buffer. + **/ +static void preview_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + RASPITEX_STATE *state = (RASPITEX_STATE*) port->userdata; + + if (buf->length == 0) + { + vcos_log_trace("%s: zero-length buffer => EOS", port->name); + state->preview_stop = 1; + mmal_buffer_header_release(buf); + } + else if (buf->data == NULL) + { + vcos_log_trace("%s: zero buffer handle", port->name); + mmal_buffer_header_release(buf); + } + else + { + /* Enqueue the preview frame for rendering and return to + * avoid blocking MMAL core. + */ + mmal_queue_put(state->preview_queue, buf); + } +} + +/* Registers a callback on the camera preview port to receive + * notifications of new frames. + * This must be called before rapitex_start and may not be called again + * without calling raspitex_destroy first. + * + * @param state Pointer to the GL preview state. + * @param port Pointer to the camera preview port + * @return Zero if successful. + */ +int raspitex_configure_preview_port(RASPITEX_STATE *state, + MMAL_PORT_T *preview_port) +{ + MMAL_STATUS_T status; + vcos_log_trace("%s port %p", VCOS_FUNCTION, preview_port); + + /* Enable ZERO_COPY mode on the preview port which instructs MMAL to only + * pass the 4-byte opaque buffer handle instead of the contents of the opaque + * buffer. + * The opaque handle is resolved on VideoCore by the GL driver when the EGL + * image is created. + */ + status = mmal_port_parameter_set_boolean(preview_port, + MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to enable zero copy on camera preview port"); + goto end; + } + + status = mmal_port_format_commit(preview_port); + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto end; + } + + /* For GL a pool of opaque buffer handles must be allocated in the client. + * These buffers are used to create the EGL images. + */ + state->preview_port = preview_port; + preview_port->buffer_num = preview_port->buffer_num_recommended; + preview_port->buffer_size = preview_port->buffer_size_recommended; + + vcos_log_trace("Creating buffer pool for GL renderer num %d size %d", + preview_port->buffer_num, preview_port->buffer_size); + + /* Pool + queue to hold preview frames */ + state->preview_pool = mmal_port_pool_create(preview_port, + preview_port->buffer_num, preview_port->buffer_size); + + if (! state->preview_pool) + { + vcos_log_error("Error allocating pool"); + status = MMAL_ENOMEM; + goto end; + } + + /* Place filled buffers from the preview port in a queue to render */ + state->preview_queue = mmal_queue_create(); + if (! state->preview_queue) + { + vcos_log_error("Error allocating queue"); + status = MMAL_ENOMEM; + goto end; + } + + /* Enable preview port callback */ + preview_port->userdata = (struct MMAL_PORT_USERDATA_T*) state; + status = mmal_port_enable(preview_port, preview_output_cb); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to camera preview port"); + goto end; + } +end: + return (status == MMAL_SUCCESS ? 0 : -1); +} + +/* Initialises GL preview state and creates the dispmanx native window. + * @param state Pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitex_init(RASPITEX_STATE *state) +{ + VCOS_STATUS_T status; + int rc; + vcos_init(); + + vcos_log_register("RaspiTex", VCOS_LOG_CATEGORY); + vcos_log_set_level(VCOS_LOG_CATEGORY, + state->verbose ? VCOS_LOG_INFO : VCOS_LOG_WARN); + vcos_log_trace("%s", VCOS_FUNCTION); + + status = vcos_semaphore_create(&state->capture.start_sem, + "glcap_start_sem", 1); + if (status != VCOS_SUCCESS) + goto error; + + status = vcos_semaphore_create(&state->capture.completed_sem, + "glcap_completed_sem", 0); + if (status != VCOS_SUCCESS) + goto error; + + switch (state->scene_id) + { + case RASPITEX_SCENE_SQUARE: + rc = square_open(state); + break; + case RASPITEX_SCENE_MIRROR: + rc = mirror_open(state); + break; + case RASPITEX_SCENE_TEAPOT: + rc = teapot_open(state); + break; + case RASPITEX_SCENE_YUV: + rc = yuv_open(state); + break; + case RASPITEX_SCENE_SOBEL: + rc = sobel_open(state); + break; + case RASPITEX_SCENE_VCSM_SQUARE: + rc = vcsm_square_open(state); + break; + default: + rc = -1; + break; + } + if (rc != 0) + goto error; + + return 0; + +error: + vcos_log_error("%s: failed", VCOS_FUNCTION); + return -1; +} + +/* Destroys the pools of buffers used by the GL renderer. + * @param state Pointer to the GL preview state. + */ +void raspitex_destroy(RASPITEX_STATE *state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + if (state->preview_pool) + { + mmal_pool_destroy(state->preview_pool); + state->preview_pool = NULL; + } + + if (state->preview_queue) + { + mmal_queue_destroy(state->preview_queue); + state->preview_queue = NULL; + } + + if (state->ops.destroy_native_window) + state->ops.destroy_native_window(state); + + if (state->ops.close) + state->ops.close(state); + + vcos_semaphore_delete(&state->capture.start_sem); + vcos_semaphore_delete(&state->capture.completed_sem); +} + +/* Initialise the GL / window state to sensible defaults. + * Also initialise any rendering parameters e.g. the scene + * + * @param state Pointer to the GL preview state. + * @return Zero if successful. + */ +void raspitex_set_defaults(RASPITEX_STATE *state) +{ + memset(state, 0, sizeof(*state)); + state->version_major = RASPITEX_VERSION_MAJOR; + state->version_minor = RASPITEX_VERSION_MINOR; + state->display = EGL_NO_DISPLAY; + state->surface = EGL_NO_SURFACE; + state->context = EGL_NO_CONTEXT; + state->egl_image = EGL_NO_IMAGE_KHR; + state->y_egl_image = EGL_NO_IMAGE_KHR; + state->u_egl_image = EGL_NO_IMAGE_KHR; + state->v_egl_image = EGL_NO_IMAGE_KHR; + state->opacity = 255; + state->width = DEFAULT_WIDTH; + state->height = DEFAULT_HEIGHT; + state->scene_id = RASPITEX_SCENE_SQUARE; + + state->ops.create_native_window = raspitexutil_create_native_window; + state->ops.gl_init = raspitexutil_gl_init_1_0; + state->ops.update_model = raspitexutil_update_model; + state->ops.redraw = raspitexutil_redraw; + state->ops.capture = raspitexutil_capture_bgra; + state->ops.gl_term = raspitexutil_gl_term; + state->ops.destroy_native_window = raspitexutil_destroy_native_window; + state->ops.close = raspitexutil_close; +} + +/* Stops the rendering loop and destroys MMAL resources + * @param state Pointer to the GL preview state. + */ +void raspitex_stop(RASPITEX_STATE *state) +{ + if (! state->preview_stop) + { + vcos_log_trace("Stopping GL preview"); + state->preview_stop = 1; + vcos_thread_join(&state->preview_thread, NULL); + } +} + +/** + * Starts the worker / GL renderer thread. + * @pre raspitex_init was successful + * @pre raspitex_configure_preview_port was successful + * @param state Pointer to the GL preview state. + * @return Zero on success, otherwise, -1 is returned + * */ +int raspitex_start(RASPITEX_STATE *state) +{ + VCOS_STATUS_T status; + + vcos_log_trace("%s", VCOS_FUNCTION); + status = vcos_thread_create(&state->preview_thread, "preview-worker", + NULL, preview_worker, state); + + if (status != VCOS_SUCCESS) + vcos_log_error("%s: Failed to start worker thread %d", + VCOS_FUNCTION, status); + + return (status == VCOS_SUCCESS ? 0 : -1); +} + +/** + * Writes the next GL frame-buffer to a RAW .ppm formatted file + * using the specified file-handle. + * @param state Pointer to the GL preview state. + * @param outpt_file Output file handle for the ppm image. + * @return Zero on success. + */ +int raspitex_capture(RASPITEX_STATE *state, FILE *output_file) +{ + int rc = 0; + uint8_t *buffer = NULL; + size_t size = 0; + + vcos_log_trace("%s: state %p file %p", VCOS_FUNCTION, + state, output_file); + + if (state && output_file) + { + /* Only request one capture at a time */ + vcos_semaphore_wait(&state->capture.start_sem); + state->capture.request = 1; + + /* Wait for capture to start */ + vcos_semaphore_wait(&state->capture.completed_sem); + + /* Take ownership of the captured buffer */ + buffer = state->capture.buffer; + size = state->capture.size; + + state->capture.request = 0; + state->capture.buffer = 0; + state->capture.size = 0; + + /* Allow another capture to be requested */ + vcos_semaphore_post(&state->capture.start_sem); + } + if (size == 0 || ! buffer) + { + vcos_log_error("%s: capture failed", VCOS_FUNCTION); + rc = -1; + goto end; + } + + raspitexutil_brga_to_rgba(buffer, size); + rc = write_tga(output_file, state->width, state->height, buffer, size); + fflush(output_file); + +end: + free(buffer); + return rc; +} diff --git a/host_applications/linux/apps/raspicam/RaspiTex.h b/host_applications/linux/apps/raspicam/RaspiTex.h new file mode 100755 index 0000000..712a5ba --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTex.h @@ -0,0 +1,192 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPITEX_H_ +#define RASPITEX_H_ + +#include +#include +#include +#include +#include +#include "interface/khronos/include/EGL/eglext_brcm.h" +#include "interface/mmal/mmal.h" + +#define RASPITEX_VERSION_MAJOR 1 +#define RASPITEX_VERSION_MINOR 0 + +typedef enum { + RASPITEX_SCENE_SQUARE = 0, + RASPITEX_SCENE_MIRROR, + RASPITEX_SCENE_TEAPOT, + RASPITEX_SCENE_YUV, + RASPITEX_SCENE_SOBEL, + RASPITEX_SCENE_VCSM_SQUARE, + +} RASPITEX_SCENE_T; + +struct RASPITEX_STATE; + +typedef struct RASPITEX_SCENE_OPS +{ + /// Creates a native window that will be used by egl_init + /// to create a window surface. + int (*create_native_window)(struct RASPITEX_STATE *state); + + /// Creates EGL surface for native window + int (*gl_init)(struct RASPITEX_STATE *state); + + /// Updates the RGBX texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the Y' plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_y_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the U plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_u_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the V plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_v_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Advance to the next animation step + int (*update_model)(struct RASPITEX_STATE *state); + + /// Draw the scene - called after update_model + int (*redraw)(struct RASPITEX_STATE *state); + + /// Allocates a buffer and copies the pixels from the current + /// frame-buffer into it. + int (*capture)(struct RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size); + + /// Creates EGL surface for native window + void (*gl_term)(struct RASPITEX_STATE *state); + + /// Destroys the native window + void (*destroy_native_window)(struct RASPITEX_STATE *state); + + /// Called when the scene is unloaded + void (*close)(struct RASPITEX_STATE *state); +} RASPITEX_SCENE_OPS; + +typedef struct RASPITEX_CAPTURE +{ + /// Wait for previous capture to complete + VCOS_SEMAPHORE_T start_sem; + + /// Posted once the capture is complete + VCOS_SEMAPHORE_T completed_sem; + + /// The RGB capture buffer + uint8_t *buffer; + + /// Size of the captured buffer in bytes + size_t size; + + /// Frame-buffer capture has been requested. Could use + /// a queue instead here to allow multiple capture requests. + int request; +} RASPITEX_CAPTURE; + +/** + * Contains the internal state and configuration for the GL rendered + * preview window. + */ +typedef struct RASPITEX_STATE +{ + int version_major; /// For binary compatibility + int version_minor; /// Incremented for new features + MMAL_PORT_T *preview_port; /// Source port for preview opaque buffers + MMAL_POOL_T *preview_pool; /// Pool for storing opaque buffer handles + MMAL_QUEUE_T *preview_queue; /// Queue preview buffers to display in order + VCOS_THREAD_T preview_thread; /// Preview worker / GL rendering thread + uint32_t preview_stop; /// If zero the worker can continue + + /* Copy of preview window params */ + int32_t preview_x; /// x-offset of preview window + int32_t preview_y; /// y-offset of preview window + int32_t preview_width; /// preview y-plane width in pixels + int32_t preview_height; /// preview y-plane height in pixels + + /* Display rectangle for the native window */ + int32_t x; /// x-offset in pixels + int32_t y; /// y-offset in pixels + int32_t width; /// width in pixels + int32_t height; /// height in pixels + int opacity; /// Alpha value for display element + int gl_win_defined; /// Use rect from --glwin instead of preview + + /* DispmanX info. This might be unused if a custom create_native_window + * does something else. */ + DISPMANX_DISPLAY_HANDLE_T disp; /// Dispmanx display for GL preview + EGL_DISPMANX_WINDOW_T win; /// Dispmanx handle for preview surface + + EGLNativeWindowType* native_window; /// Native window used for EGL surface + EGLDisplay display; /// The current EGL display + EGLSurface surface; /// The current EGL surface + EGLContext context; /// The current EGL context + const EGLint *egl_config_attribs; /// GL scenes preferred EGL configuration + + GLuint texture; /// Name for the preview texture + EGLImageKHR egl_image; /// The current preview EGL image + + GLuint y_texture; /// The Y plane texture + EGLImageKHR y_egl_image; /// EGL image for Y plane texture + GLuint u_texture; /// The U plane texture + EGLImageKHR u_egl_image; /// EGL image for U plane texture + GLuint v_texture; /// The V plane texture + EGLImageKHR v_egl_image; /// EGL image for V plane texture + + MMAL_BUFFER_HEADER_T *preview_buf; /// MMAL buffer currently bound to texture(s) + + RASPITEX_SCENE_T scene_id; /// Id of the scene to load + RASPITEX_SCENE_OPS ops; /// The interface for the current scene + void *scene_state; /// Pointer to scene specific data + int verbose; /// Log FPS + + RASPITEX_CAPTURE capture; /// Frame-buffer capture state + +} RASPITEX_STATE; + +int raspitex_init(RASPITEX_STATE *state); +void raspitex_destroy(RASPITEX_STATE *state); +int raspitex_start(RASPITEX_STATE *state); +void raspitex_stop(RASPITEX_STATE *state); +void raspitex_set_defaults(RASPITEX_STATE *state); +int raspitex_configure_preview_port(RASPITEX_STATE *state, + MMAL_PORT_T *preview_port); +void raspitex_display_help(); +int raspitex_parse_cmdline(RASPITEX_STATE *state, + const char *arg1, const char *arg2); +int raspitex_capture(RASPITEX_STATE *state, FILE* output_file); + +#endif /* RASPITEX_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.c b/host_applications/linux/apps/raspicam/RaspiTexUtil.c new file mode 100755 index 0000000..d5b5954 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.c @@ -0,0 +1,597 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "RaspiTexUtil.h" +#include "RaspiTex.h" +#include +#include + +VCOS_LOG_CAT_T raspitex_log_category; + +/** + * \file RaspiTexUtil.c + * + * Provides default implementations for the raspitex_scene_ops functions + * and general utility functions. + */ + +/** + * Deletes textures and EGL surfaces and context. + * @param raspitex_state Pointer to the Raspi + */ +void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + /* Delete OES textures */ + glDeleteTextures(1, &raspitex_state->texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->egl_image); + raspitex_state->egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->y_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->y_egl_image); + raspitex_state->y_egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->u_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->u_egl_image); + raspitex_state->u_egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->v_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->v_egl_image); + raspitex_state->v_egl_image = EGL_NO_IMAGE_KHR; + + /* Terminate EGL */ + eglMakeCurrent(raspitex_state->display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(raspitex_state->display, raspitex_state->context); + eglDestroySurface(raspitex_state->display, raspitex_state->surface); + eglTerminate(raspitex_state->display); +} + +/** Creates a native window for the GL surface using dispmanx + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful, otherwise, -1 is returned. + */ +int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state) +{ + VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; + VC_RECT_T src_rect = {0}; + VC_RECT_T dest_rect = {0}; + uint32_t disp_num = 0; // Primary + uint32_t layer_num = 0; + DISPMANX_ELEMENT_HANDLE_T elem; + DISPMANX_UPDATE_HANDLE_T update; + + alpha.opacity = raspitex_state->opacity; + dest_rect.x = raspitex_state->x; + dest_rect.y = raspitex_state->y; + dest_rect.width = raspitex_state->width; + dest_rect.height = raspitex_state->height; + + vcos_log_trace("%s: %d,%d,%d,%d %d,%d,0x%x,0x%x", VCOS_FUNCTION, + src_rect.x, src_rect.y, src_rect.width, src_rect.height, + dest_rect.x, dest_rect.y, dest_rect.width, dest_rect.height); + + src_rect.width = dest_rect.width << 16; + src_rect.height = dest_rect.height << 16; + + raspitex_state->disp = vc_dispmanx_display_open(disp_num); + if (raspitex_state->disp == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to open display handle"); + goto error; + } + + update = vc_dispmanx_update_start(0); + if (update == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to open update handle"); + goto error; + } + + elem = vc_dispmanx_element_add(update, raspitex_state->disp, layer_num, + &dest_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, + DISPMANX_NO_ROTATE); + if (elem == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to create element handle"); + goto error; + } + + raspitex_state->win.element = elem; + raspitex_state->win.width = raspitex_state->width; + raspitex_state->win.height = raspitex_state->height; + vc_dispmanx_update_submit_sync(update); + + raspitex_state->native_window = (EGLNativeWindowType*) &raspitex_state->win; + + return 0; +error: + return -1; +} + +/** Destroys the pools of buffers used by the GL renderer. + * @param raspitex_state A pointer to the GL preview state. + */ +void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + if (raspitex_state->disp != DISPMANX_NO_HANDLE) + { + vc_dispmanx_display_close(raspitex_state->disp); + raspitex_state->disp = DISPMANX_NO_HANDLE; + } +} + +/** Creates the EGL context and window surface for the native window + * using specified arguments. + * @param raspitex_state A pointer to the GL preview state. This contains + * the native_window pointer. + * @param attribs The config attributes. + * @param context_attribs The context attributes. + * @return Zero if successful. + */ +static int raspitexutil_gl_common(RASPITEX_STATE *raspitex_state, + const EGLint attribs[], const EGLint context_attribs[]) +{ + EGLConfig config; + EGLint num_configs; + + vcos_log_trace("%s", VCOS_FUNCTION); + + if (raspitex_state->native_window == NULL) + { + vcos_log_error("%s: No native window", VCOS_FUNCTION); + goto error; + } + + raspitex_state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (raspitex_state->display == EGL_NO_DISPLAY) + { + vcos_log_error("%s: Failed to get EGL display", VCOS_FUNCTION); + goto error; + } + + if (! eglInitialize(raspitex_state->display, 0, 0)) + { + vcos_log_error("%s: eglInitialize failed", VCOS_FUNCTION); + goto error; + } + + if (! eglChooseConfig(raspitex_state->display, attribs, &config, + 1, &num_configs)) + { + vcos_log_error("%s: eglChooseConfig failed", VCOS_FUNCTION); + goto error; + } + + raspitex_state->surface = eglCreateWindowSurface(raspitex_state->display, + config, raspitex_state->native_window, NULL); + if (raspitex_state->surface == EGL_NO_SURFACE) + { + vcos_log_error("%s: eglCreateWindowSurface failed", VCOS_FUNCTION); + goto error; + } + + raspitex_state->context = eglCreateContext(raspitex_state->display, + config, EGL_NO_CONTEXT, context_attribs); + if (raspitex_state->context == EGL_NO_CONTEXT) + { + vcos_log_error("%s: eglCreateContext failed", VCOS_FUNCTION); + goto error; + } + + if (!eglMakeCurrent(raspitex_state->display, raspitex_state->surface, + raspitex_state->surface, raspitex_state->context)) + { + vcos_log_error("%s: Failed to activate EGL context", VCOS_FUNCTION); + goto error; + } + + return 0; + +error: + vcos_log_error("%s: EGL error 0x%08x", VCOS_FUNCTION, eglGetError()); + raspitex_state->ops.gl_term(raspitex_state); + return -1; +} + +/* Creates the RGBA and luma textures with some default parameters + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state) +{ + GLCHK(glGenTextures(1, &raspitex_state->texture)); + GLCHK(glGenTextures(1, &raspitex_state->y_texture)); + GLCHK(glGenTextures(1, &raspitex_state->u_texture)); + GLCHK(glGenTextures(1, &raspitex_state->v_texture)); + return 0; +} + +/** + * Creates an OpenGL ES 1.X context. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state) +{ + int rc; + const EGLint* attribs = raspitex_state->egl_config_attribs; + + const EGLint default_attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE + }; + + const EGLint context_attribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + if (! attribs) + attribs = default_attribs; + + rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs); + if (rc != 0) + goto end; + + GLCHK(glEnable(GL_TEXTURE_EXTERNAL_OES)); + rc = raspitexutil_create_textures(raspitex_state); + +end: + return rc; +} + +/** + * Creates an OpenGL ES 2.X context. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state) +{ + int rc; + const EGLint* attribs = raspitex_state->egl_config_attribs;; + + const EGLint default_attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + const EGLint context_attribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + if (! attribs) + attribs = default_attribs; + + vcos_log_trace("%s", VCOS_FUNCTION); + rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs); + if (rc != 0) + goto end; + + rc = raspitexutil_create_textures(raspitex_state); +end: + return rc; +} + +/** + * Advances the texture and EGL image to the next MMAL buffer. + * + * @param display The EGL display. + * @param target The EGL image target e.g. EGL_IMAGE_BRCM_MULTIMEDIA + * @param mm_buf The EGL client buffer (mmal opaque buffer) that is used to + * create the EGL Image for the preview texture. + * @param egl_image Pointer to the EGL image to update with mm_buf. + * @param texture Pointer to the texture to update from EGL image. + * @return Zero if successful. + */ +int raspitexutil_do_update_texture(EGLDisplay display, EGLenum target, + EGLClientBuffer mm_buf, GLuint *texture, EGLImageKHR *egl_image) +{ + vcos_log_trace("%s: mm_buf %u", VCOS_FUNCTION, (unsigned) mm_buf); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, *texture)); + if (*egl_image != EGL_NO_IMAGE_KHR) + { + /* Discard the EGL image for the preview frame */ + eglDestroyImageKHR(display, *egl_image); + *egl_image = EGL_NO_IMAGE_KHR; + } + + *egl_image = eglCreateImageKHR(display, EGL_NO_CONTEXT, target, mm_buf, NULL); + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, *egl_image)); + + return 0; +} + +/** + * Updates the RGBX texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA, mm_buf, + &raspitex_state->texture, &raspitex_state->egl_image); +} + +/** + * Updates the Y plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_Y, mm_buf, + &raspitex_state->y_texture, &raspitex_state->y_egl_image); +} + +/** + * Updates the U plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_U, mm_buf, + &raspitex_state->u_texture, &raspitex_state->u_egl_image); +} + +/** + * Updates the V plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_V, mm_buf, + &raspitex_state->v_texture, &raspitex_state->v_egl_image); +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + * @return Zero. + */ +int raspitexutil_update_model(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + * @return Zero. + */ +int raspitexutil_redraw(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + */ +void raspitexutil_close(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; +} + +/** + * Performs an in-place byte swap from BGRA to RGBA. + * @param buffer The buffer to modify. + * @param size Size of the buffer in bytes. + */ +void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size) +{ + uint8_t* out = buffer; + uint8_t* end = buffer + size; + + while (out < end) + { + uint8_t tmp = out[0]; + out[0] = out[2]; + out[2] = tmp; + out += 4; + } +} + +/** + * Uses glReadPixels to grab the current frame-buffer contents + * and returns the result in a newly allocate buffer along with + * the its size. + * Data is returned in BGRA format for TGA output. PPM output doesn't + * require the channel order swap but would require a vflip. The TGA + * format also supports alpha. The byte swap is not done in this function + * to avoid blocking the GL rendering thread. + * @param state Pointer to the GL preview state. + * @param buffer Address of pointer to set to pointer to new buffer. + * @param buffer_size The size of the new buffer in bytes (out param) + * @return Zero if successful. + */ +int raspitexutil_capture_bgra(RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size) +{ + const int bytes_per_pixel = 4; + + vcos_log_trace("%s: %dx%d %d", VCOS_FUNCTION, + state->width, state->height, bytes_per_pixel); + + *buffer_size = state->width * state->height * bytes_per_pixel; + *buffer = calloc(*buffer_size, 1); + if (! *buffer) + goto error; + + glReadPixels(0, 0, state->width, state->height, GL_RGBA, + GL_UNSIGNED_BYTE, *buffer); + if (glGetError() != GL_NO_ERROR) + goto error; + + return 0; + +error: + *buffer_size = 0; + if (*buffer) + free(*buffer); + *buffer = NULL; + return -1; +} + + +/** + * Takes a description of shader program, compiles it and gets the locations + * of uniforms and attributes. + * + * @param p The shader program state. + * @return Zero if successful. + */ +int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p) +{ + GLint status; + int i = 0; + char log[1024]; + int logLen = 0; + vcos_assert(p); + vcos_assert(p->vertex_source); + vcos_assert(p->fragment_source); + + if (! (p && p->vertex_source && p->fragment_source)) + goto fail; + + p->vs = p->fs = 0; + + p->vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(p->vs, 1, &p->vertex_source, NULL); + glCompileShader(p->vs); + glGetShaderiv(p->vs, GL_COMPILE_STATUS, &status); + if (! status) { + glGetShaderInfoLog(p->vs, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + p->fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(p->fs, 1, &p->fragment_source, NULL); + glCompileShader(p->fs); + + glGetShaderiv(p->fs, GL_COMPILE_STATUS, &status); + if (! status) { + glGetShaderInfoLog(p->fs, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + p->program = glCreateProgram(); + glAttachShader(p->program, p->vs); + glAttachShader(p->program, p->fs); + glLinkProgram(p->program); + glGetProgramiv(p->program, GL_LINK_STATUS, &status); + if (! status) + { + vcos_log_error("Failed to link shader program"); + glGetProgramInfoLog(p->program, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + for (i = 0; i < SHADER_MAX_ATTRIBUTES; ++i) + { + if (! p->attribute_names[i]) + break; + p->attribute_locations[i] = glGetAttribLocation(p->program, p->attribute_names[i]); + if (p->attribute_locations[i] == -1) + { + vcos_log_error("Failed to get location for attribute %s", + p->attribute_names[i]); + goto fail; + } + else { + vcos_log_trace("Attribute for %s is %d", + p->attribute_names[i], p->attribute_locations[i]); + } + } + + for (i = 0; i < SHADER_MAX_UNIFORMS; ++i) + { + if (! p->uniform_names[i]) + break; + p->uniform_locations[i] = glGetUniformLocation(p->program, p->uniform_names[i]); + if (p->uniform_locations[i] == -1) + { + vcos_log_error("Failed to get location for uniform %s", + p->uniform_names[i]); + goto fail; + } + else { + vcos_log_trace("Uniform for %s is %d", + p->uniform_names[i], p->uniform_locations[i]); + } + } + + return 0; + +fail: + vcos_log_error("%s: Failed to build shader program", VCOS_FUNCTION); + if (p) + { + glDeleteProgram(p->program); + glDeleteShader(p->fs); + glDeleteShader(p->vs); + } + return -1; +} + diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.h b/host_applications/linux/apps/raspicam/RaspiTexUtil.h new file mode 100755 index 0000000..cf5dfb3 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPITEX_UTIL_H_ +#define RASPITEX_UTIL_H_ + +#define VCOS_LOG_CATEGORY (&raspitex_log_category) +#include +#include +#include +#include +#include +#include +#include "RaspiTex.h" +#include "interface/vcos/vcos.h" + +extern VCOS_LOG_CAT_T raspitex_log_category; + +#define SHADER_MAX_ATTRIBUTES 16 +#define SHADER_MAX_UNIFORMS 16 +/** + * Container for a simple shader program. The uniform and attribute locations + * are automatically setup by raspitex_build_shader_program. + */ +typedef struct RASPITEXUTIL_SHADER_PROGRAM_T +{ + const char *vertex_source; /// Pointer to vertex shader source + const char *fragment_source; /// Pointer to fragment shader source + + /// Array of uniform names for raspitex_build_shader_program to process + const char *uniform_names[SHADER_MAX_UNIFORMS]; + /// Array of attribute names for raspitex_build_shader_program to process + const char *attribute_names[SHADER_MAX_ATTRIBUTES]; + + GLint vs; /// Vertex shader handle + GLint fs; /// Fragment shader handle + GLint program; /// Shader program handle + + /// The locations for uniforms defined in uniform_names + GLint uniform_locations[SHADER_MAX_UNIFORMS]; + + /// The locations for attributes defined in attribute_names + GLint attribute_locations[SHADER_MAX_ATTRIBUTES]; +} RASPITEXUTIL_SHADER_PROGRAM_T; + + +/* Uncomment to enable extra GL error checking */ +//#define CHECK_GL_ERRORS +#if defined(CHECK_GL_ERRORS) +#define GLCHK(X) \ +do { \ + GLenum err = GL_NO_ERROR; \ + X; \ + while ((err = glGetError())) \ + { \ + vcos_log_error("GL error 0x%x in " #X "file %s line %d", err, __FILE__,__LINE__); \ + vcos_assert(err == GL_NO_ERROR); \ + exit(err); \ + } \ +} \ +while(0) +#else +#define GLCHK(X) X +#endif /* CHECK_GL_ERRORS */ + +/* Default GL scene ops functions */ +int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state); +int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state); +int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state); +int raspitexutil_update_model(RASPITEX_STATE* raspitex_state); +int raspitexutil_redraw(RASPITEX_STATE* raspitex_state); +void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state); +void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state); +int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state); +int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_capture_bgra(struct RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size); +void raspitexutil_close(RASPITEX_STATE* raspitex_state); + +/* Utility functions */ +int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p); +void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size); + +#endif /* RASPITEX_UTIL_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiVid.c b/host_applications/linux/apps/raspicam/RaspiVid.c new file mode 100755 index 0000000..20b0ad5 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiVid.c @@ -0,0 +1,3018 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiVid.c + * Command line program to capture a camera video stream and encode it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 28th Feb 2013 + * \Author: James Hughes + * + * Description + * + * 3 components are created; camera, preview and video encoder. + * Camera component has three ports, preview, video and stills. + * This program connects preview and video to the preview and video + * encoder. Using mmal we don't need to worry about buffers between these + * components, but we do need to handle buffers from the encoder, which + * are simply written straight to the file in the requisite buffer callback. + * + * If raw option is selected, a video splitter component is connected between + * camera and preview. This allows us to set up callback for raw camera data + * (in YUV420 or RGB format) which might be useful for further image processing. + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the (generic) preview window + */ + +// We use some GNU extensions (basename) +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.12" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" +#include "interface/mmal/mmal_parameters_camera.h" + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + +// Port configuration for the splitter component +#define SPLITTER_OUTPUT_PORT 0 +#define SPLITTER_PREVIEW_PORT 1 + +// Video format information +// 0 implies variable +#define VIDEO_FRAME_RATE_NUM 30 +#define VIDEO_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +// Max bitrate we allow for recording +const int MAX_BITRATE_MJPEG = 25000000; // 25Mbits/s +const int MAX_BITRATE_LEVEL4 = 25000000; // 25Mbits/s +const int MAX_BITRATE_LEVEL42 = 62500000; // 62.5Mbits/s + +/// Interval at which we check for an failure abort during capture +const int ABORT_INTERVAL = 100; // ms + + +/// Capture/Pause switch method +/// Simply capture for time specified +#define WAIT_METHOD_NONE 0 +/// Cycle between capture and pause for times specified +#define WAIT_METHOD_TIMED 1 +/// Switch between capture and pause on keypress +#define WAIT_METHOD_KEYPRESS 2 +/// Switch between capture and pause on signal +#define WAIT_METHOD_SIGNAL 3 +/// Run/record forever +#define WAIT_METHOD_FOREVER 4 + + + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + +// Forward +typedef struct RASPIVID_STATE_S RASPIVID_STATE; + +/** Struct used to pass information in encoder port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + RASPIVID_STATE *pstate; /// pointer to our state in case required in callback + int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture + char *cb_buff; /// Circular buffer + int cb_len; /// Length of buffer + int cb_wptr; /// Current write pointer + int cb_wrap; /// Has buffer wrapped at least once? + int cb_data; /// Valid bytes in buffer +#define IFRAME_BUFSIZE (60*1000) + int iframe_buff[IFRAME_BUFSIZE]; /// buffer of iframe pointers + int iframe_buff_wpos; + int iframe_buff_rpos; + char header_bytes[29]; + int header_wptr; + FILE *imv_file_handle; /// File handle to write inline motion vectors to. + FILE *raw_file_handle; /// File handle to write raw data to. + int flush_buffers; + FILE *pts_file_handle; /// File timestamps +} PORT_USERDATA; + +/** Possible raw output formats + */ +typedef enum { + RAW_OUTPUT_FMT_YUV = 1, + RAW_OUTPUT_FMT_RGB, + RAW_OUTPUT_FMT_GRAY, +} RAW_OUTPUT_FMT; + +/** Structure containing all state information for the current run + */ +struct RASPIVID_STATE_S +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + MMAL_FOURCC_T encoding; /// Requested codec video encoding (MJPEG or H264) + int bitrate; /// Requested bitrate + int framerate; /// Requested frame rate (fps) + int intraperiod; /// Intra-refresh period (key frame rate) + int quantisationParameter; /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate + int bInlineHeaders; /// Insert inline headers to stream (SPS, PPS) + char *filename; /// filename of output file + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either + /// the camera output or the encoder output (with compression artifacts) + int profile; /// H264 profile to use for encoding + int level; /// H264 level to use for encoding + int waitMethod; /// Method for switching between pause and capture + + int onTime; /// In timed cycle mode, the amount of time the capture is on per cycle + int offTime; /// In timed cycle mode, the amount of time the capture is off per cycle + + int segmentSize; /// Segment mode In timed cycle mode, the amount of time the capture is off per cycle + int segmentWrap; /// Point at which to wrap segment counter + int segmentNumber; /// Current segment counter + int splitNow; /// Split at next possible i-frame if set to 1. + int splitWait; /// Switch if user wants splited files + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *splitter_component; /// Pointer to the splitter component + MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera or splitter to preview + MMAL_CONNECTION_T *splitter_connection;/// Pointer to the connection from camera to splitter + MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + + MMAL_POOL_T *splitter_pool; /// Pointer to the pool of buffers used by splitter output port 0 + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + + PORT_USERDATA callback_data; /// Used to move data to the encoder callback + + int bCapturing; /// State of capture/pause + int bCircularBuffer; /// Whether we are writing to a circular buffer + + int inlineMotionVectors; /// Encoder outputs inline Motion Vectors + char *imv_filename; /// filename of inline Motion Vectors output + int raw_output; /// Output raw video from camera as well + RAW_OUTPUT_FMT raw_output_fmt; /// The raw video format + char *raw_filename; /// Filename for raw video output + int cameraNum; /// Camera number + int settings; /// Request settings from the camera + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + int intra_refresh_type; /// What intra refresh type to use. -1 to not set. + int frame; + char *pts_filename; + int save_pts; + int64_t starttime; + int64_t lasttime; + + bool netListen; +}; + + +/// Structure to cross reference H264 profile strings against the MMAL parameter equivalent +static XREF_T profile_map[] = +{ + {"baseline", MMAL_VIDEO_PROFILE_H264_BASELINE}, + {"main", MMAL_VIDEO_PROFILE_H264_MAIN}, + {"high", MMAL_VIDEO_PROFILE_H264_HIGH}, +// {"constrained", MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this? +}; + +static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]); + +/// Structure to cross reference H264 level strings against the MMAL parameter equivalent +static XREF_T level_map[] = +{ + {"4", MMAL_VIDEO_LEVEL_H264_4}, + {"4.1", MMAL_VIDEO_LEVEL_H264_41}, + {"4.2", MMAL_VIDEO_LEVEL_H264_42}, +}; + +static int level_map_size = sizeof(level_map) / sizeof(level_map[0]); + +static XREF_T initial_map[] = +{ + {"record", 0}, + {"pause", 1}, +}; + +static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]); + +static XREF_T intra_refresh_map[] = +{ + {"cyclic", MMAL_VIDEO_INTRA_REFRESH_CYCLIC}, + {"adaptive", MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE}, + {"both", MMAL_VIDEO_INTRA_REFRESH_BOTH}, + {"cyclicrows", MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS}, +// {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why. +}; + +static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]); + +static XREF_T raw_output_fmt_map[] = +{ + {"yuv", RAW_OUTPUT_FMT_YUV}, + {"rgb", RAW_OUTPUT_FMT_RGB}, + {"gray", RAW_OUTPUT_FMT_GRAY}, +}; + +static int raw_output_fmt_map_size = sizeof(raw_output_fmt_map) / sizeof(raw_output_fmt_map[0]); + +static void display_valid_parameters(char *app_name); + +/// Command ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandBitrate 3 +#define CommandOutput 4 +#define CommandVerbose 5 +#define CommandTimeout 6 +#define CommandDemoMode 7 +#define CommandFramerate 8 +#define CommandPreviewEnc 9 +#define CommandIntraPeriod 10 +#define CommandProfile 11 +#define CommandTimed 12 +#define CommandSignal 13 +#define CommandKeypress 14 +#define CommandInitialState 15 +#define CommandQP 16 +#define CommandInlineHeaders 17 +#define CommandSegmentFile 18 +#define CommandSegmentWrap 19 +#define CommandSegmentStart 20 +#define CommandSplitWait 21 +#define CommandCircular 22 +#define CommandIMV 23 +#define CommandCamSelect 24 +#define CommandSettings 25 +#define CommandSensorMode 26 +#define CommandIntraRefreshType 27 +#define CommandFlush 28 +#define CommandSavePTS 29 +#define CommandCodec 30 +#define CommandLevel 31 +#define CommandRaw 32 +#define CommandRawFormat 33 +#define CommandNetListen 34 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -').\n" + "\t\t Connect to a remote IPv4 host (e.g. tcp://192.168.1.2:1234, udp://192.168.1.2:1234)\n" + "\t\t To listen on a TCP port (IPv4) and wait for an incoming connection use -l\n" + "\t\t (e.g. raspivid -l -o tcp://0.0.0.0:3333 -> bind to all network interfaces, raspivid -l -o tcp://192.168.1.1:3333 -> bind to a certain local IPv4)", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1}, + { CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, + { CommandIntraPeriod, "-intra", "g", "Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames.", 1}, + { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, + { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0}, + { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0}, + { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0}, + { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1}, + { CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1}, + { CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0}, + { CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval ", 1}, + { CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1}, + { CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1}, + { CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0}, + { CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0}, + { CommandIMV, "-vectors", "x", "Output filename for inline motion vectors", 1 }, + { CommandCamSelect, "-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1}, + { CommandFlush, "-flush", "fl", "Flush buffers in order to decrease latency", 0 }, + { CommandSavePTS, "-save-pts", "pts","Save Timestamps to file for mkvmerge", 1 }, + { CommandCodec, "-codec", "cd", "Specify the codec to use - H264 (default) or MJPEG", 1 }, + { CommandLevel, "-level", "lev","Specify H264 level to use for encoding", 1}, + { CommandRaw, "-raw", "r", "Output filename for raw video", 1 }, + { CommandRawFormat, "-raw-format", "rf", "Specify output format for raw video. Default is yuv", 1}, + { CommandNetListen, "-listen", "l", "Listen on a TCP socket", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + + +static struct +{ + char *description; + int nextWaitMethod; +} wait_method_description[] = +{ + {"Simple capture", WAIT_METHOD_NONE}, + {"Capture forever", WAIT_METHOD_FOREVER}, + {"Cycle on time", WAIT_METHOD_TIMED}, + {"Cycle on keypress", WAIT_METHOD_KEYPRESS}, + {"Cycle on signal", WAIT_METHOD_SIGNAL}, +}; + +static int wait_method_description_size = sizeof(wait_method_description) / sizeof(wait_method_description[0]); + + + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPIVID_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPIVID_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 1920; // Default to 1080p + state->height = 1080; + state->encoding = MMAL_ENCODING_H264; + state->bitrate = 17000000; // This is a decent default bitrate for 1080p + state->framerate = VIDEO_FRAME_RATE_NUM; + state->intraperiod = -1; // Not set + state->quantisationParameter = 0; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->immutableInput = 1; + state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + state->level = MMAL_VIDEO_LEVEL_H264_4; + state->waitMethod = WAIT_METHOD_NONE; + state->onTime = 5000; + state->offTime = 5000; + + state->bCapturing = 0; + state->bInlineHeaders = 0; + + state->segmentSize = 0; // 0 = not segmenting the file. + state->segmentNumber = 1; + state->segmentWrap = 0; // Point at which to wrap segment number back to 1. 0 = no wrap + state->splitNow = 0; + state->splitWait = 0; + + state->inlineMotionVectors = 0; + state->cameraNum = 0; + state->settings = 0; + state->sensor_mode = 0; + + state->intra_refresh_type = -1; + + state->frame = 0; + state->save_pts = 0; + + state->netListen = false; + + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); +} + + +/** + * Dump image state parameters to stderr. + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPIVID_STATE *state) +{ + int i; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); + fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->bitrate, state->framerate, state->timeout); + fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->profile, profile_map, profile_map_size)); + fprintf(stderr, "H264 Level %s\n", raspicli_unmap_xref(state->level, level_map, level_map_size)); + fprintf(stderr, "H264 Quantisation level %d, Inline headers %s\n", state->quantisationParameter, state->bInlineHeaders ? "Yes" : "No"); + fprintf(stderr, "H264 Intra refresh type %s, period %d\n", raspicli_unmap_xref(state->intra_refresh_type, intra_refresh_map, intra_refresh_map_size), state->intraperiod); + + // Not going to display segment data unless asked for it. + if (state->segmentSize) + fprintf(stderr, "Segment size %d, segment wrap value %d, initial segment number %d\n", state->segmentSize, state->segmentWrap, state->segmentNumber); + + if (state->raw_output) + fprintf(stderr, "Raw output enabled, format %s\n", raspicli_unmap_xref(state->raw_output_fmt, raw_output_fmt_map, raw_output_fmt_map_size)); + + fprintf(stderr, "Wait method : "); + for (i=0;iwaitMethod == wait_method_description[i].nextWaitMethod) + fprintf(stderr, "%s", wait_method_description[i].description); + } + fprintf(stderr, "\nInitial state '%s'\n", raspicli_unmap_xref(state->bCapturing, initial_map, initial_map_size)); + fprintf(stderr, "\n\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return Non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandBitrate: // 1-100 + if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1) + { + i++; + } + else + valid = 0; + + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 1); + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder/capture + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected a waitMethod we don't overwrite it + if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE) + state->waitMethod = WAIT_METHOD_FOREVER; + + i++; + } + else + valid = 0; + break; + } + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + if (state->demoInterval == 0) + state->demoInterval = 250; // ms + + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandFramerate: // fps to record + { + if (sscanf(argv[i + 1], "%u", &state->framerate) == 1) + { + // TODO : What limits do we need for fps 1 - 30 - 120?? + i++; + } + else + valid = 0; + break; + } + + case CommandPreviewEnc: + state->immutableInput = 0; + break; + + case CommandIntraPeriod: // key frame rate + { + if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1) + i++; + else + valid = 0; + break; + } + + case CommandQP: // quantisation parameter + { + if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1) + i++; + else + valid = 0; + break; + } + + case CommandProfile: // H264 profile + { + state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size); + + if( state->profile == -1) + state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + + i++; + break; + } + + case CommandInlineHeaders: // H264 inline headers + { + state->bInlineHeaders = 1; + break; + } + + case CommandTimed: + { + if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2) + { + i++; + + if (state->onTime < 1000) + state->onTime = 1000; + + if (state->offTime < 1000) + state->offTime = 1000; + + state->waitMethod = WAIT_METHOD_TIMED; + } + else + valid = 0; + break; + } + + case CommandKeypress: + state->waitMethod = WAIT_METHOD_KEYPRESS; + break; + + case CommandSignal: + state->waitMethod = WAIT_METHOD_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandInitialState: + { + state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size); + + if( state->bCapturing == -1) + state->bCapturing = 0; + + i++; + break; + } + + case CommandSegmentFile: // Segment file in to chunks of specified time + { + if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1) + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + i++; + } + else + valid = 0; + break; + } + + case CommandSegmentWrap: // segment wrap value + { + if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1) + i++; + else + valid = 0; + break; + } + + case CommandSegmentStart: // initial segment number + { + if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap))) + i++; + else + valid = 0; + break; + } + + case CommandSplitWait: // split files on restart + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + state->splitWait = 1; + break; + } + + case CommandCircular: + { + state->bCircularBuffer = 1; + break; + } + + case CommandIMV: // output filename + { + state->inlineMotionVectors = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->imv_filename = malloc(len + 1); + vcos_assert(state->imv_filename); + if (state->imv_filename) + strncpy(state->imv_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandSettings: + state->settings = 1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandIntraRefreshType: + { + state->intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size); + i++; + break; + } + + case CommandFlush: + { + state->callback_data.flush_buffers = 1; + break; + } + case CommandSavePTS: // output filename + { + state->save_pts = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->pts_filename = malloc(len + 1); + vcos_assert(state->pts_filename); + if (state->pts_filename) + strncpy(state->pts_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + case CommandCodec: // codec type + { + int len = strlen(argv[i + 1]); + if (len) + { + if (len==4 && !strncmp("H264", argv[i+1], 4)) + state->encoding = MMAL_ENCODING_H264; + else if (len==5 && !strncmp("MJPEG", argv[i+1], 5)) + state->encoding = MMAL_ENCODING_MJPEG; + else + valid = 0; + i++; + } + else + valid = 0; + break; + } + + case CommandLevel: // H264 level + { + state->level = raspicli_map_xref(argv[i + 1], level_map, level_map_size); + + if( state->level == -1) + state->level = MMAL_VIDEO_LEVEL_H264_4; + + i++; + break; + } + + case CommandRaw: // output filename + { + state->raw_output = 1; + state->raw_output_fmt = RAW_OUTPUT_FMT_YUV; + int len = strlen(argv[i + 1]); + if (len) + { + state->raw_filename = malloc(len + 1); + vcos_assert(state->raw_filename); + if (state->raw_filename) + strncpy(state->raw_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandRawFormat: + { + state->raw_output_fmt = raspicli_map_xref(argv[i + 1], raw_output_fmt_map, raw_output_fmt_map_size); + + if (state->raw_output_fmt == -1) + valid = 0; + + i++; + break; + } + + case CommandNetListen: + { + state->netListen = true; + + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + int i; + + fprintf(stdout, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n"); + fprintf(stdout, "\nusage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Profile options + fprintf(stdout, "\n\nH264 Profile options :\n%s", profile_map[0].mode ); + + for (i=1;icmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + + +/** + * Open a file based on the settings in state + * + * @param state Pointer to state + */ +static FILE *open_filename(RASPIVID_STATE *pState, char *filename) +{ + FILE *new_handle = NULL; + char *tempname = NULL; + + if (pState->segmentSize || pState->splitWait) + { + // Create a new filename string + asprintf(&tempname, filename, pState->segmentNumber); + filename = tempname; + } + + if (filename) + { + bool bNetwork = false; + int sfd = -1, socktype; + + if(!strncmp("tcp://", filename, 6)) + { + bNetwork = true; + socktype = SOCK_STREAM; + } + else if(!strncmp("udp://", filename, 6)) + { + if (pState->netListen) + { + fprintf(stderr, "No support for listening in UDP mode\n"); + exit(131); + } + bNetwork = true; + socktype = SOCK_DGRAM; + } + + if(bNetwork) + { + unsigned short port; + filename += 6; + char *colon; + if(NULL == (colon = strchr(filename, ':'))) + { + fprintf(stderr, "%s is not a valid IPv4:port, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(132); + } + if(1 != sscanf(colon + 1, "%hu", &port)) + { + fprintf(stderr, + "Port parse failed. %s is not a valid network file name, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(133); + } + char chTmp = *colon; + *colon = 0; + + struct sockaddr_in saddr={}; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + if(0 == inet_aton(filename, &saddr.sin_addr)) + { + fprintf(stderr, "inet_aton failed. %s is not a valid IPv4 address\n", + filename); + exit(134); + } + *colon = chTmp; + + if (pState->netListen) + { + int sockListen = socket(AF_INET, SOCK_STREAM, 0); + if (sockListen >= 0) + { + int iTmp = 1; + setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &iTmp, sizeof(int));//no error handling, just go on + if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + while ((-1 == (iTmp = listen(sockListen, 0))) && (EINTR == errno)) + ; + if (-1 != iTmp) + { + fprintf(stderr, "Waiting for a TCP connection on %s:%"SCNu16"...", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + while ((-1 == (sfd = accept(sockListen, (struct sockaddr *) &cli_addr, &clilen))) && (EINTR == errno)) + ; + if (sfd >= 0) + fprintf(stderr, "Client connected from %s:%"SCNu16"\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); + else + fprintf(stderr, "Error on accept: %s\n", strerror(errno)); + } + else//if (-1 != iTmp) + { + fprintf(stderr, "Error trying to listen on a socket: %s\n", strerror(errno)); + } + } + else//if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + fprintf(stderr, "Error on binding socket: %s\n", strerror(errno)); + } + } + else//if (sockListen >= 0) + { + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sockListen >= 0)//regardless success or error + close(sockListen);//do not listen on a given port anymore + } + else//if (pState->netListen) + { + if(0 <= (sfd = socket(AF_INET, socktype, 0))) + { + fprintf(stderr, "Connecting to %s:%hu...", inet_ntoa(saddr.sin_addr), port); + + int iTmp = 1; + while ((-1 == (iTmp = connect(sfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)))) && (EINTR == errno)) + ; + if (iTmp < 0) + fprintf(stderr, "error: %s\n", strerror(errno)); + else + fprintf(stderr, "connected, sending video...\n"); + } + else + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sfd >= 0) + new_handle = fdopen(sfd, "w"); + } + else + { + new_handle = fopen(filename, "wb"); + } + } + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new file \"%s\"\n", filename); + } + + if (tempname) + free(tempname); + + return new_handle; +} + +/** + * Update any annotation data specific to the video. + * This simply passes on the setting from cli, or + * if application defined annotate requested, updates + * with the H264 parameters + * + * @param state Pointer to state control struct + * + */ +static void update_annotation_data(RASPIVID_STATE *state) +{ + // So, if we have asked for a application supplied string, set it to the H264 parameters + if (state->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) + { + char *text; + const char *refresh = raspicli_unmap_xref(state->intra_refresh_type, intra_refresh_map, intra_refresh_map_size); + + asprintf(&text, "%dk,%df,%s,%d,%s,%s", + state->bitrate / 1000, state->framerate, + refresh ? refresh : "(none)", + state->intraperiod, + raspicli_unmap_xref(state->profile, profile_map, profile_map_size), + raspicli_unmap_xref(state->level, level_map, level_map_size)); + + raspicamcontrol_set_annotate(state->camera_component, state->camera_parameters.enable_annotate, text, + state->camera_parameters.annotate_text_size, + state->camera_parameters.annotate_text_colour, + state->camera_parameters.annotate_bg_colour); + + free(text); + } + else + { + raspicamcontrol_set_annotate(state->camera_component, state->camera_parameters.enable_annotate, state->camera_parameters.annotate_string, + state->camera_parameters.annotate_text_size, + state->camera_parameters.annotate_text_colour, + state->camera_parameters.annotate_bg_colour); + } +} + + + +/** + * buffer header callback function for encoder + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + static int64_t base_time = -1; + static int64_t last_second = -1; + + // All our segment times based on the receipt of the first encoder callback + if (base_time == -1) + base_time = vcos_getmicrosecs64()/1000; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + int64_t current_time = vcos_getmicrosecs64()/1000; + + vcos_assert(pData->file_handle); + if(pData->pstate->inlineMotionVectors) vcos_assert(pData->imv_file_handle); + + if (pData->cb_buff) + { + int space_in_buff = pData->cb_len - pData->cb_wptr; + int copy_to_end = space_in_buff > buffer->length ? buffer->length : space_in_buff; + int copy_to_start = buffer->length - copy_to_end; + + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) + { + if(pData->header_wptr + buffer->length > sizeof(pData->header_bytes)) + { + vcos_log_error("Error in header bytes\n"); + } + else + { + // These are the header bytes, save them for final output + mmal_buffer_header_mem_lock(buffer); + memcpy(pData->header_bytes + pData->header_wptr, buffer->data, buffer->length); + mmal_buffer_header_mem_unlock(buffer); + pData->header_wptr += buffer->length; + } + } + else if((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO)) + { + // Do something with the inline motion vectors... + } + else + { + static int frame_start = -1; + int i; + + if(frame_start == -1) + frame_start = pData->cb_wptr; + + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) + { + pData->iframe_buff[pData->iframe_buff_wpos] = frame_start; + pData->iframe_buff_wpos = (pData->iframe_buff_wpos + 1) % IFRAME_BUFSIZE; + } + + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) + frame_start = -1; + + // If we overtake the iframe rptr then move the rptr along + if((pData->iframe_buff_rpos + 1) % IFRAME_BUFSIZE != pData->iframe_buff_wpos) + { + while( + ( + pData->cb_wptr <= pData->iframe_buff[pData->iframe_buff_rpos] && + (pData->cb_wptr + buffer->length) > pData->iframe_buff[pData->iframe_buff_rpos] + ) || + ( + (pData->cb_wptr > pData->iframe_buff[pData->iframe_buff_rpos]) && + (pData->cb_wptr + buffer->length) > (pData->iframe_buff[pData->iframe_buff_rpos] + pData->cb_len) + ) + ) + pData->iframe_buff_rpos = (pData->iframe_buff_rpos + 1) % IFRAME_BUFSIZE; + } + + mmal_buffer_header_mem_lock(buffer); + // We are pushing data into a circular buffer + memcpy(pData->cb_buff + pData->cb_wptr, buffer->data, copy_to_end); + memcpy(pData->cb_buff, buffer->data + copy_to_end, copy_to_start); + mmal_buffer_header_mem_unlock(buffer); + + if((pData->cb_wptr + buffer->length) > pData->cb_len) + pData->cb_wrap = 1; + + pData->cb_wptr = (pData->cb_wptr + buffer->length) % pData->cb_len; + + for(i = pData->iframe_buff_rpos; i != pData->iframe_buff_wpos; i = (i + 1) % IFRAME_BUFSIZE) + { + int p = pData->iframe_buff[i]; + if(pData->cb_buff[p] != 0 || pData->cb_buff[p+1] != 0 || pData->cb_buff[p+2] != 0 || pData->cb_buff[p+3] != 1) + { + vcos_log_error("Error in iframe list\n"); + } + } + } + } + else + { + // For segmented record mode, we need to see if we have exceeded our time/size, + // but also since we have inline headers turned on we need to break when we get one to + // ensure that the new stream has the header in it. If we break on an I-frame, the + // SPS/PPS header is actually in the previous chunk. + if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) && + ((pData->pstate->segmentSize && current_time > base_time + pData->pstate->segmentSize) || + (pData->pstate->splitWait && pData->pstate->splitNow))) + { + FILE *new_handle; + + base_time = current_time; + + pData->pstate->splitNow = 0; + pData->pstate->segmentNumber++; + + // Only wrap if we have a wrap point set + if (pData->pstate->segmentWrap && pData->pstate->segmentNumber > pData->pstate->segmentWrap) + pData->pstate->segmentNumber = 1; + + if (pData->pstate->filename && pData->pstate->filename[0] != '-') + { + new_handle = open_filename(pData->pstate, pData->pstate->filename); + + if (new_handle) + { + fclose(pData->file_handle); + pData->file_handle = new_handle; + } + } + + if (pData->pstate->imv_filename && pData->pstate->imv_filename[0] != '-') + { + new_handle = open_filename(pData->pstate, pData->pstate->imv_filename); + + if (new_handle) + { + fclose(pData->imv_file_handle); + pData->imv_file_handle = new_handle; + } + } + + if (pData->pstate->pts_filename && pData->pstate->pts_filename[0] != '-') + { + new_handle = open_filename(pData->pstate, pData->pstate->pts_filename); + + if (new_handle) + { + fclose(pData->pts_file_handle); + pData->pts_file_handle = new_handle; + } + } + } + if (buffer->length) + { + mmal_buffer_header_mem_lock(buffer); + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO) + { + if(pData->pstate->inlineMotionVectors) + { + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->imv_file_handle); + if(pData->flush_buffers) fflush(pData->imv_file_handle); + } + else + { + //We do not want to save inlineMotionVectors... + bytes_written = buffer->length; + } + } + else + { + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + if(pData->flush_buffers) fflush(pData->file_handle); + + if(pData->pstate->save_pts && + (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END || + buffer->flags == 0 || + buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) && + !(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)) + { + if(buffer->pts != MMAL_TIME_UNKNOWN && buffer->pts != pData->pstate->lasttime) + { + int64_t pts; + if(pData->pstate->frame==0)pData->pstate->starttime=buffer->pts; + pData->pstate->lasttime=buffer->pts; + pts = buffer->pts - pData->pstate->starttime; + fprintf(pData->pts_file_handle,"%lld.%03lld\n", pts/1000, pts%1000); + pData->pstate->frame++; + } + } + } + + mmal_buffer_header_mem_unlock(buffer); + + if (bytes_written != buffer->length) + { + vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); + pData->abort = 1; + } + } + } + + // See if the second count has changed and we need to update any annotation + if (current_time/1000 != last_second) + { + update_annotation_data(pData->pstate); + last_second = current_time/1000; + } + } + else + { + vcos_log_error("Received a encoder buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the encoder port"); + } +} + +/** + * buffer header callback function for splitter + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void splitter_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = 0; + int bytes_to_write = buffer->length; + + /* Write only luma component to get grayscale image: */ + if (buffer->length && pData->pstate->raw_output_fmt == RAW_OUTPUT_FMT_GRAY) + bytes_to_write = port->format->es->video.width * port->format->es->video.height; + + vcos_assert(pData->raw_file_handle); + + if (bytes_to_write) + { + mmal_buffer_header_mem_lock(buffer); + bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->raw_file_handle); + mmal_buffer_header_mem_unlock(buffer); + + if (bytes_written != bytes_to_write) + { + vcos_log_error("Failed to write raw buffer data (%d from %d)- aborting", bytes_written, bytes_to_write); + pData->abort = 1; + } + } + } + else + { + vcos_log_error("Received a camera buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->splitter_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the splitter port"); + } +} + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + status = raspicamcontrol_set_stereo_mode(camera->output[0], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[1], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[2], &state->camera_parameters.stereo_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set stereo mode : error %d", status); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 0, + .max_preview_video_w = state->width, + .max_preview_video_h = state->height, + .num_preview_video_frames = 3 + vcos_max(0, (state->framerate-30)/10), + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC + }; + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + // Now set up the port formats + + // Set the encode format on the Preview port + // HW limitations mean we need the preview to be the same size as the required recorded output + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + + //enable dynamic framerate if necessary + if (state->camera_parameters.shutter_speed) + { + if (state->framerate > 1000000./state->camera_parameters.shutter_speed) + { + state->framerate=0; + if (state->verbose) + fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + } + } + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the encode format on the video port + + format = video_port->format; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = state->framerate; + format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + + // Set the encode format on the still port + + format = still_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = 0; + format->es->video.frame_rate.den = 1; + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + // Note: this sets lots of parameters that were not individually addressed before. + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + state->camera_component = camera; + + update_annotation_data(state); + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPIVID_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Create the splitter component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_splitter_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *splitter = 0; + MMAL_PORT_T *splitter_output = NULL; + MMAL_ES_FORMAT_T *format; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + int i; + + if (state->camera_component == NULL) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera component must be created before splitter"); + goto error; + } + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER, &splitter); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create splitter component"); + goto error; + } + + if (!splitter->input_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Splitter doesn't have any input port"); + goto error; + } + + if (splitter->output_num < 2) + { + status = MMAL_ENOSYS; + vcos_log_error("Splitter doesn't have enough output ports"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames: */ + mmal_format_copy(splitter->input[0]->format, state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]->format); + + if (splitter->input[0]->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + splitter->input[0]->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + status = mmal_port_format_commit(splitter->input[0]); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on splitter input port"); + goto error; + } + + /* Splitter can do format conversions, configure format for its output port: */ + for (i = 0; i < splitter->output_num; i++) + { + mmal_format_copy(splitter->output[i]->format, splitter->input[0]->format); + + if (i == SPLITTER_OUTPUT_PORT) + { + format = splitter->output[i]->format; + + switch (state->raw_output_fmt) + { + case RAW_OUTPUT_FMT_YUV: + case RAW_OUTPUT_FMT_GRAY: /* Grayscale image contains only luma (Y) component */ + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + break; + case RAW_OUTPUT_FMT_RGB: + if (mmal_util_rgb_order_fixed(state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT])) + format->encoding = MMAL_ENCODING_RGB24; + else + format->encoding = MMAL_ENCODING_BGR24; + format->encoding_variant = 0; /* Irrelevant when not in opaque mode */ + break; + default: + status = MMAL_EINVAL; + vcos_log_error("unknown raw output format"); + goto error; + } + } + + status = mmal_port_format_commit(splitter->output[i]); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on splitter output port %d", i); + goto error; + } + } + + /* Enable component */ + status = mmal_component_enable(splitter); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("splitter component couldn't be enabled"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + splitter_output = splitter->output[SPLITTER_OUTPUT_PORT]; + pool = mmal_port_pool_create(splitter_output, splitter_output->buffer_num, splitter_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for splitter output port %s", splitter_output->name); + } + + state->splitter_pool = pool; + state->splitter_component = splitter; + + if (state->verbose) + fprintf(stderr, "Splitter component done\n"); + + return status; + +error: + + if (splitter) + mmal_component_destroy(splitter); + + return status; +} + +/** + * Destroy the splitter component + * + * @param state Pointer to state control struct + * + */ +static void destroy_splitter_component(RASPIVID_STATE *state) +{ + // Get rid of any port buffers first + if (state->splitter_pool) + { + mmal_port_pool_destroy(state->splitter_component->output[SPLITTER_OUTPUT_PORT], state->splitter_pool); + } + + if (state->splitter_component) + { + mmal_component_destroy(state->splitter_component); + state->splitter_component = NULL; + } +} + +/** + * Create the encoder component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *encoder = 0; + MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create video encoder component"); + goto error; + } + + if (!encoder->input_num || !encoder->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Video encoder doesn't have input/output ports"); + goto error; + } + + encoder_input = encoder->input[0]; + encoder_output = encoder->output[0]; + + // We want same format on input and output + mmal_format_copy(encoder_output->format, encoder_input->format); + + // Only supporting H264 at the moment + encoder_output->format->encoding = state->encoding; + + if(state->encoding == MMAL_ENCODING_H264) + { + if(state->level == MMAL_VIDEO_LEVEL_H264_4) + { + if(state->bitrate > MAX_BITRATE_LEVEL4) + { + fprintf(stderr, "Bitrate too high: Reducing to 25MBit/s\n"); + state->bitrate = MAX_BITRATE_LEVEL4; + } + } + else + { + if(state->bitrate > MAX_BITRATE_LEVEL42) + { + fprintf(stderr, "Bitrate too high: Reducing to 62.5MBit/s\n"); + state->bitrate = MAX_BITRATE_LEVEL42; + } + } + } + else if(state->encoding == MMAL_ENCODING_MJPEG) + { + if(state->bitrate > MAX_BITRATE_MJPEG) + { + fprintf(stderr, "Bitrate too high: Reducing to 25MBit/s\n"); + state->bitrate = MAX_BITRATE_MJPEG; + } + } + + encoder_output->format->bitrate = state->bitrate; + + if (state->encoding == MMAL_ENCODING_H264) + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + else + encoder_output->buffer_size = 256<<10; + + + if (encoder_output->buffer_size < encoder_output->buffer_size_min) + encoder_output->buffer_size = encoder_output->buffer_size_min; + + encoder_output->buffer_num = encoder_output->buffer_num_recommended; + + if (encoder_output->buffer_num < encoder_output->buffer_num_min) + encoder_output->buffer_num = encoder_output->buffer_num_min; + + // We need to set the frame rate on output to 0, to ensure it gets + // updated correctly from the input framerate when port connected + encoder_output->format->es->video.frame_rate.num = 0; + encoder_output->format->es->video.frame_rate.den = 1; + + // Commit the port changes to the output port + status = mmal_port_format_commit(encoder_output); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on video encoder output port"); + goto error; + } + + // Set the rate control parameter + if (0) + { + MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set ratecontrol"); + goto error; + } + + } + + if (state->encoding == MMAL_ENCODING_H264 && + state->intraperiod != -1) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set intraperiod"); + goto error; + } + } + + if (state->encoding == MMAL_ENCODING_H264 && + state->quantisationParameter) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set initial QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, state->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m2.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set min QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, state->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m3.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set max QP"); + goto error; + } + + } + + if (state->encoding == MMAL_ENCODING_H264) + { + MMAL_PARAMETER_VIDEO_PROFILE_T param; + param.hdr.id = MMAL_PARAMETER_PROFILE; + param.hdr.size = sizeof(param); + + param.profile[0].profile = state->profile; + + if((VCOS_ALIGN_UP(state->width,16) >> 4) * (VCOS_ALIGN_UP(state->height,16) >> 4) * state->framerate > 245760) + { + if((VCOS_ALIGN_UP(state->width,16) >> 4) * (VCOS_ALIGN_UP(state->height,16) >> 4) * state->framerate <= 522240) + { + fprintf(stderr, "Too many macroblocks/s: Increasing H264 Level to 4.2\n"); + state->level=MMAL_VIDEO_LEVEL_H264_42; + } + else + { + vcos_log_error("Too many macroblocks/s requested"); + goto error; + } + } + + param.profile[0].level = state->level; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 profile"); + goto error; + } + } + + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS) + { + vcos_log_error("Unable to set immutable input flag"); + // Continue rather than abort.. + } + + //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, state->bInlineHeaders) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE HEADER FLAG parameters"); + // Continue rather than abort.. + } + + //set INLINE VECTORS flag to request motion vector estimates + if (state->encoding == MMAL_ENCODING_H264 && + mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, state->inlineMotionVectors) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE VECTORS parameters"); + // Continue rather than abort.. + } + + // Adaptive intra refresh settings + if (state->encoding == MMAL_ENCODING_H264 && + state->intra_refresh_type != -1) + { + MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; + param.hdr.size = sizeof(param); + + // Get first so we don't overwrite anything unexpectedly + status = mmal_port_parameter_get(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_warn("Unable to get existing H264 intra-refresh values. Please update your firmware"); + // Set some defaults, don't just pass random stack data + param.air_mbs = param.air_ref = param.cir_mbs = param.pir_mbs = 0; + } + + param.refresh_mode = state->intra_refresh_type; + + //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS) + // param.cir_mbs = 10; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 intra-refresh values"); + goto error; + } + } + + // Enable component + status = mmal_component_enable(encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable video encoder component"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); + } + + state->encoder_pool = pool; + state->encoder_component = encoder; + + if (state->verbose) + fprintf(stderr, "Encoder component done\n"); + + return status; + + error: + if (encoder) + mmal_component_destroy(encoder); + + state->encoder_component = NULL; + + return status; +} + +/** + * Destroy the encoder component + * + * @param state Pointer to state control struct + * + */ +static void destroy_encoder_component(RASPIVID_STATE *state) +{ + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + } + + if (state->encoder_component) + { + mmal_component_destroy(state->encoder_component); + state->encoder_component = NULL; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } + +} + +/** + * Pause for specified time, but return early if detect an abort request + * + * @param state Pointer to state control struct + * @param pause Time in ms to pause + * @param callback Struct contain an abort flag tested for early termination + * + */ +static int pause_and_test_abort(RASPIVID_STATE *state, int pause) +{ + int wait; + + if (!pause) + return 0; + + // Going to check every ABORT_INTERVAL milliseconds + for (wait = 0; wait < pause; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (state->callback_data.abort) + return 1; + } + + return 0; +} + + +/** + * Function to wait in various ways (depending on settings) + * + * @param state Pointer to the state data + * + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_change(RASPIVID_STATE *state) +{ + int keep_running = 1; + static int64_t complete_time = -1; + + // Have we actually exceeded our timeout? + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->waitMethod) + { + case WAIT_METHOD_NONE: + (void)pause_and_test_abort(state, state->timeout); + return 0; + + case WAIT_METHOD_FOREVER: + { + // We never return from this. Expect a ctrl-c to exit. + while (1) + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + return 0; + } + + case WAIT_METHOD_TIMED: + { + int abort; + + if (state->bCapturing) + abort = pause_and_test_abort(state, state->onTime); + else + abort = pause_and_test_abort(state, state->offTime); + + if (abort) + return 0; + else + return keep_running; + } + + case WAIT_METHOD_KEYPRESS: + { + char ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to %s, X then ENTER to exit, [i,o,r] then ENTER to change zoom\n", state->bCapturing ? "pause" : "capture"); + + ch = getchar(); + if (ch == 'x' || ch == 'X') + return 0; + else if (ch == 'i' || ch == 'I') + { + if (state->verbose) + fprintf(stderr, "Starting zoom in\n"); + + raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_IN, &(state->camera_parameters).roi); + + if (state->verbose) + dump_status(state); + } + else if (ch == 'o' || ch == 'O') + { + if (state->verbose) + fprintf(stderr, "Starting zoom out\n"); + + raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_OUT, &(state->camera_parameters).roi); + + if (state->verbose) + dump_status(state); + } + else if (ch == 'r' || ch == 'R') + { + if (state->verbose) + fprintf(stderr, "starting reset zoom\n"); + + raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_RESET, &(state->camera_parameters).roi); + + if (state->verbose) + dump_status(state); + } + + return keep_running; + } + + + case WAIT_METHOD_SIGNAL: + { + // Need to wait for a SIGUSR1 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block SIGUSR1 so we can wait on it. + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to %s\n", state->bCapturing ? "pause" : "capture"); + } + + result = sigwait( &waitset, &sig ); + + if (state->verbose && result != 0) + fprintf(stderr, "Bad signal received - error %d\n", errno); + + return keep_running; + } + + } // switch + + return keep_running; +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPIVID_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + MMAL_PORT_T *encoder_output_port = NULL; + MMAL_PORT_T *splitter_input_port = NULL; + MMAL_PORT_T *splitter_output_port = NULL; + MMAL_PORT_T *splitter_preview_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if (state.raw_output && (status = create_splitter_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create splitter component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + destroy_encoder_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + preview_input_port = state.preview_parameters.preview_component->input[0]; + encoder_input_port = state.encoder_component->input[0]; + encoder_output_port = state.encoder_component->output[0]; + + if (state.raw_output) + { + splitter_input_port = state.splitter_component->input[0]; + splitter_output_port = state.splitter_component->output[SPLITTER_OUTPUT_PORT]; + splitter_preview_port = state.splitter_component->output[SPLITTER_PREVIEW_PORT]; + } + + if (state.preview_parameters.wantPreview ) + { + if (state.raw_output) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to splitter input port\n"); + + // Connect camera to splitter + status = connect_ports(camera_preview_port, splitter_input_port, &state.splitter_connection); + + if (status != MMAL_SUCCESS) + { + state.splitter_connection = NULL; + vcos_log_error("%s: Failed to connect camera preview port to splitter input", __func__); + goto error; + } + + if (state.verbose) + { + fprintf(stderr, "Connecting splitter preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect splitter to preview + status = connect_ports(splitter_preview_port, preview_input_port, &state.preview_connection); + } + else + { + if (state.verbose) + { + fprintf(stderr, "Connecting camera preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect camera to preview + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + } + + if (status != MMAL_SUCCESS) + state.preview_connection = NULL; + } + else + { + if (state.raw_output) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to splitter input port\n"); + + // Connect camera to splitter + status = connect_ports(camera_preview_port, splitter_input_port, &state.splitter_connection); + + if (status != MMAL_SUCCESS) + { + state.splitter_connection = NULL; + vcos_log_error("%s: Failed to connect camera preview port to splitter input", __func__); + goto error; + } + } + else + { + status = MMAL_SUCCESS; + } + } + + if (status == MMAL_SUCCESS) + { + if (state.verbose) + fprintf(stderr, "Connecting camera video port to encoder input port\n"); + + // Now connect the camera to the encoder + status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection); + + if (status != MMAL_SUCCESS) + { + state.encoder_connection = NULL; + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } + } + + if (status == MMAL_SUCCESS) + { + // Set up our userdata - this is passed though to the callback where we need the information. + state.callback_data.pstate = &state; + state.callback_data.abort = 0; + + if (state.raw_output) + { + splitter_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling splitter output port\n"); + + // Enable the splitter output port and tell it its callback function + status = mmal_port_enable(splitter_output_port, splitter_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to setup splitter output port", __func__); + goto error; + } + } + + state.callback_data.file_handle = NULL; + + if (state.filename) + { + if (state.filename[0] == '-') + { + state.callback_data.file_handle = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + state.callback_data.file_handle = open_filename(&state, state.filename); + } + + if (!state.callback_data.file_handle) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); + } + } + + state.callback_data.imv_file_handle = NULL; + + if (state.imv_filename) + { + if (state.imv_filename[0] == '-') + { + state.callback_data.imv_file_handle = stdout; + } + else + { + state.callback_data.imv_file_handle = open_filename(&state, state.imv_filename); + } + + if (!state.callback_data.imv_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.imv_filename); + state.inlineMotionVectors=0; + } + } + + state.callback_data.pts_file_handle = NULL; + + if (state.pts_filename) + { + if (state.pts_filename[0] == '-') + { + state.callback_data.pts_file_handle = stdout; + } + else + { + state.callback_data.pts_file_handle = open_filename(&state, state.pts_filename); + if (state.callback_data.pts_file_handle) /* save header for mkvmerge */ + fprintf(state.callback_data.pts_file_handle, "# timecode format v2\n"); + } + + if (!state.callback_data.pts_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.pts_filename); + state.save_pts=0; + } + } + + state.callback_data.raw_file_handle = NULL; + + if (state.raw_filename) + { + if (state.raw_filename[0] == '-') + { + state.callback_data.raw_file_handle = stdout; + } + else + { + state.callback_data.raw_file_handle = open_filename(&state, state.raw_filename); + } + + if (!state.callback_data.raw_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n", state.raw_filename); + state.raw_output = 0; + } + } + + if(state.bCircularBuffer) + { + if(state.bitrate == 0) + { + vcos_log_error("%s: Error circular buffer requires constant bitrate and small intra period\n", __func__); + goto error; + } + else if(state.timeout == 0) + { + vcos_log_error("%s: Error, circular buffer size is based on timeout must be greater than zero\n", __func__); + goto error; + } + else if(state.waitMethod != WAIT_METHOD_KEYPRESS && state.waitMethod != WAIT_METHOD_SIGNAL) + { + vcos_log_error("%s: Error, Circular buffer mode requires either keypress (-k) or signal (-s) triggering\n", __func__); + goto error; + } + else if(!state.callback_data.file_handle) + { + vcos_log_error("%s: Error require output file (or stdout) for Circular buffer mode\n", __func__); + goto error; + } + else + { + int count = state.bitrate * (state.timeout / 1000) / 8; + + state.callback_data.cb_buff = (char *) malloc(count); + if(state.callback_data.cb_buff == NULL) + { + vcos_log_error("%s: Unable to allocate circular buffer for %d seconds at %.1f Mbits\n", __func__, state.timeout / 1000, (double)state.bitrate/1000000.0); + goto error; + } + else + { + state.callback_data.cb_len = count; + state.callback_data.cb_wptr = 0; + state.callback_data.cb_wrap = 0; + state.callback_data.cb_data = 0; + state.callback_data.iframe_buff_wpos = 0; + state.callback_data.iframe_buff_rpos = 0; + state.callback_data.header_wptr = 0; + } + } + } + + // Set up our userdata - this is passed though to the callback where we need the information. + encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling encoder output port\n"); + + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + + if (state.verbose) + fprintf(stderr, "Running in demo mode\n"); + + for (i=0;state.timeout == 0 || iqueue); + int q; + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + } + } + + // Send all the buffers to the splitter output port + if (state.raw_output) { + int num = mmal_queue_length(state.splitter_pool->queue); + int q; + for (q = 0; q < num; q++) + { + MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.splitter_pool->queue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(splitter_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to splitter output port (%d)", q); + } + } + + int initialCapturing=state.bCapturing; + while (running) + { + // Change state + + state.bCapturing = !state.bCapturing; + + if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, state.bCapturing) != MMAL_SUCCESS) + { + // How to handle? + } + + // In circular buffer mode, exit and save the buffer (make sure we do this after having paused the capture + if(state.bCircularBuffer && !state.bCapturing) + { + break; + } + + if (state.verbose) + { + if (state.bCapturing) + fprintf(stderr, "Starting video capture\n"); + else + fprintf(stderr, "Pausing video capture\n"); + } + + if(state.splitWait) + { + if(state.bCapturing) + { + if (mmal_port_parameter_set_boolean(encoder_output_port, MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, 1) != MMAL_SUCCESS) + { + vcos_log_error("failed to request I-FRAME"); + } + } + else + { + if(!initialCapturing) + state.splitNow=1; + } + initialCapturing=0; + } + running = wait_for_next_change(&state); + } + + if (state.verbose) + fprintf(stderr, "Finished capture\n"); + } + else + { + if (state.timeout) + vcos_sleep(state.timeout); + else + { + // timeout = 0 so run forever + while(1) + vcos_sleep(ABORT_INTERVAL); + } + } + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + + if(state.bCircularBuffer) + { + int copy_from_end, copy_from_start; + + copy_from_end = state.callback_data.cb_len - state.callback_data.iframe_buff[state.callback_data.iframe_buff_rpos]; + copy_from_start = state.callback_data.cb_len - copy_from_end; + copy_from_start = state.callback_data.cb_wptr < copy_from_start ? state.callback_data.cb_wptr : copy_from_start; + if(!state.callback_data.cb_wrap) + { + copy_from_start = state.callback_data.cb_wptr; + copy_from_end = 0; + } + + fwrite(state.callback_data.header_bytes, 1, state.callback_data.header_wptr, state.callback_data.file_handle); + // Save circular buffer + fwrite(state.callback_data.cb_buff + state.callback_data.iframe_buff[state.callback_data.iframe_buff_rpos], 1, copy_from_end, state.callback_data.file_handle); + fwrite(state.callback_data.cb_buff, 1, copy_from_start, state.callback_data.file_handle); + if(state.callback_data.flush_buffers) fflush(state.callback_data.file_handle); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_still_port); + check_disable_port(encoder_output_port); + check_disable_port(splitter_output_port); + + if (state.preview_parameters.wantPreview && state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.encoder_connection) + mmal_connection_destroy(state.encoder_connection); + + if (state.splitter_connection) + mmal_connection_destroy(state.splitter_connection); + + // Can now close our file. Note disabling ports may flush buffers which causes + // problems if we have already closed the file! + if (state.callback_data.file_handle && state.callback_data.file_handle != stdout) + fclose(state.callback_data.file_handle); + if (state.callback_data.imv_file_handle && state.callback_data.imv_file_handle != stdout) + fclose(state.callback_data.imv_file_handle); + if (state.callback_data.pts_file_handle && state.callback_data.pts_file_handle != stdout) + fclose(state.callback_data.pts_file_handle); + if (state.callback_data.raw_file_handle && state.callback_data.raw_file_handle != stdout) + fclose(state.callback_data.raw_file_handle); + + /* Disable components */ + if (state.encoder_component) + mmal_component_disable(state.encoder_component); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.splitter_component) + mmal_component_disable(state.splitter_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + destroy_encoder_component(&state); + raspipreview_destroy(&state.preview_parameters); + destroy_splitter_component(&state); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} diff --git a/host_applications/linux/apps/raspicam/RaspiVidYUV.c b/host_applications/linux/apps/raspicam/RaspiVidYUV.c new file mode 100755 index 0000000..7034b40 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiVidYUV.c @@ -0,0 +1,1651 @@ +/* +Copyright (c) 2014, DSP Group Ltd +Copyright (c) 2014, James Hughes +Copyright (c) 2013, Broadcom Europe Ltd + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiVidYUV.c + * Command line program to capture a camera video stream and save file + * as uncompressed YUV420 data + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 7th Jan 2014 + * \Author: James Hughes + * + * Description + * + * 2 components are created; camera and preview. + * Camera component has three ports, preview, video and stills. + * Preview is connected using standard mmal connections, the video output + * is written straight to the file in YUV 420 format via the requisite buffer + * callback. Still port is not used + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the generic preview +*/ + +// We use some GNU extensions (basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.15" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + +// Video format information +// 0 implies variable +#define VIDEO_FRAME_RATE_NUM 30 +#define VIDEO_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +/// Interval at which we check for an failure abort during capture +const int ABORT_INTERVAL = 100; // ms + + +/// Capture/Pause switch method +/// Simply capture for time specified +#define WAIT_METHOD_NONE 0 +/// Cycle between capture and pause for times specified +#define WAIT_METHOD_TIMED 1 +/// Switch between capture and pause on keypress +#define WAIT_METHOD_KEYPRESS 2 +/// Switch between capture and pause on signal +#define WAIT_METHOD_SIGNAL 3 +/// Run/record forever +#define WAIT_METHOD_FOREVER 4 + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + +// Forward +typedef struct RASPIVIDYUV_STATE_S RASPIVIDYUV_STATE; + +/** Struct used to pass information in camera video port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + RASPIVIDYUV_STATE *pstate; /// pointer to our state in case required in callback + int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture + FILE *pts_file_handle; /// File timestamps +} PORT_USERDATA; + +/** Structure containing all state information for the current run + */ +struct RASPIVIDYUV_STATE_S +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + int framerate; /// Requested frame rate (fps) + char *filename; /// filename of output file + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + int waitMethod; /// Method for switching between pause and capture + + int onTime; /// In timed cycle mode, the amount of time the capture is on per cycle + int offTime; /// In timed cycle mode, the amount of time the capture is off per cycle + + int onlyLuma; /// Only output the luma / Y plane of the YUV data + int useRGB; /// Output RGB data rather than YUV + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + + MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera video port + + PORT_USERDATA callback_data; /// Used to move data to the camera callback + + int bCapturing; /// State of capture/pause + + int cameraNum; /// Camera number + int settings; /// Request settings from the camera + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + + int frame; + char *pts_filename; + int save_pts; + int64_t starttime; + int64_t lasttime; + + bool netListen; +}; + +static XREF_T initial_map[] = +{ + {"record", 0}, + {"pause", 1}, +}; + +static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]); + + +static void display_valid_parameters(char *app_name); + +/// Command ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandOutput 3 +#define CommandVerbose 4 +#define CommandTimeout 5 +#define CommandDemoMode 6 +#define CommandFramerate 7 +#define CommandTimed 8 +#define CommandSignal 9 +#define CommandKeypress 10 +#define CommandInitialState 11 +#define CommandCamSelect 12 +#define CommandSettings 13 +#define CommandSensorMode 14 +#define CommandOnlyLuma 15 +#define CommandUseRGB 16 +#define CommandSavePTS 17 +#define CommandNetListen 18 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -')", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1}, + { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0}, + { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0}, + { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0}, + { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1}, + { CommandCamSelect, "-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandOnlyLuma, "-luma", "y", "Only output the luma / Y of the YUV data'", 0}, + { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0}, + { CommandSavePTS, "-save-pts", "pts","Save Timestamps to file", 1 }, + { CommandNetListen, "-listen", "l", "Listen on a TCP socket", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + + +static struct +{ + char *description; + int nextWaitMethod; +} wait_method_description[] = +{ + {"Simple capture", WAIT_METHOD_NONE}, + {"Capture forever", WAIT_METHOD_FOREVER}, + {"Cycle on time", WAIT_METHOD_TIMED}, + {"Cycle on keypress", WAIT_METHOD_KEYPRESS}, + {"Cycle on signal", WAIT_METHOD_SIGNAL}, +}; + +static int wait_method_description_size = sizeof(wait_method_description) / sizeof(wait_method_description[0]); + + + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPIVIDYUV_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPIVIDYUV_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 1920; // Default to 1080p + state->height = 1080; + state->framerate = VIDEO_FRAME_RATE_NUM; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->waitMethod = WAIT_METHOD_NONE; + state->onTime = 5000; + state->offTime = 5000; + + state->bCapturing = 0; + + state->cameraNum = 0; + state->settings = 0; + state->sensor_mode = 0; + state->onlyLuma = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); +} + + +/** + * Dump image state parameters to stderr. + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPIVIDYUV_STATE *state) +{ + int i, size, ystride, yheight; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); + fprintf(stderr, "framerate %d, time delay %d\n", state->framerate, state->timeout); + + // Calculate the individual image size + // Y stride rounded to multiple of 32. U&V stride is Y stride/2 (ie multiple of 16). + // Y height is padded to a 16. U/V height is Y height/2 (ie multiple of 8). + + // Y plane + ystride = ((state->width + 31) & ~31); + yheight = ((state->height + 15) & ~15); + + size = ystride * yheight; + + // U and V plane + size += 2 * ystride/2 * yheight/2; + + fprintf(stderr, "Sub-image size %d bytes in total.\n Y pitch %d, Y height %d, UV pitch %d, UV Height %d\n", size, ystride, yheight, ystride/2,yheight/2); + + fprintf(stderr, "Wait method : "); + for (i=0;iwaitMethod == wait_method_description[i].nextWaitMethod) + fprintf(stderr, "%s", wait_method_description[i].description); + } + fprintf(stderr, "\nInitial state '%s'\n", raspicli_unmap_xref(state->bCapturing, initial_map, initial_map_size)); + fprintf(stderr, "\n\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return Non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPIVIDYUV_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 1); + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder/capture + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected a waitMethod we don't overwrite it + if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE) + state->waitMethod = WAIT_METHOD_FOREVER; + + i++; + } + else + valid = 0; + break; + } + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + if (state->demoInterval == 0) + state->demoInterval = 250; // ms + + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandFramerate: // fps to record + { + if (sscanf(argv[i + 1], "%u", &state->framerate) == 1) + { + // TODO : What limits do we need for fps 1 - 30 - 120?? + i++; + } + else + valid = 0; + break; + } + + case CommandTimed: + { + if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2) + { + i++; + + if (state->onTime < 1000) + state->onTime = 1000; + + if (state->offTime < 1000) + state->offTime = 1000; + + state->waitMethod = WAIT_METHOD_TIMED; + } + else + valid = 0; + break; + } + + case CommandKeypress: + state->waitMethod = WAIT_METHOD_KEYPRESS; + break; + + case CommandSignal: + state->waitMethod = WAIT_METHOD_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandInitialState: + { + state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size); + + if( state->bCapturing == -1) + state->bCapturing = 0; + + i++; + break; + } + + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandSettings: + state->settings = 1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandOnlyLuma: + if (state->useRGB) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->onlyLuma = 1; + break; + + case CommandUseRGB: // display lots of data during run + if (state->onlyLuma) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->useRGB = 1; + break; + + case CommandSavePTS: // output filename + { + state->save_pts = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->pts_filename = malloc(len + 1); + vcos_assert(state->pts_filename); + if (state->pts_filename) + strncpy(state->pts_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandNetListen: + { + state->netListen = true; + + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stdout, "Display camera output to display, and optionally saves an uncompressed YUV420 file \n\n"); + fprintf(stdout, "NOTE: High resolutions and/or frame rates may exceed the bandwidth of the system due\n"); + fprintf(stdout, "to the large amounts of data being moved to the SD card. This will result in undefined\n"); + fprintf(stdout, "results in the subsequent file.\n"); + fprintf(stdout, "The raw file produced contains all the files. Each image in the files will be of size\n"); + fprintf(stdout, "width*height*1.5, unless width and/or height are not divisible by 16. Use the image size\n"); + fprintf(stdout, "displayed during the run (in verbose mode) for an accurate value\n"); + + fprintf(stdout, "The Linux split command can be used to split up the file to individual frames\n"); + + fprintf(stdout, "\nusage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + fprintf(stdout, "\n"); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + fprintf(stdout, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + + +/** + * Open a file based on the settings in state + * + * @param state Pointer to state + */ +static FILE *open_filename(RASPIVIDYUV_STATE *pState, char *filename) +{ + FILE *new_handle = NULL; + + if (filename) + { + bool bNetwork = false; + int sfd = -1, socktype; + + if(!strncmp("tcp://", filename, 6)) + { + bNetwork = true; + socktype = SOCK_STREAM; + } + else if(!strncmp("udp://", filename, 6)) + { + if (pState->netListen) + { + fprintf(stderr, "No support for listening in UDP mode\n"); + exit(131); + } + bNetwork = true; + socktype = SOCK_DGRAM; + } + + if(bNetwork) + { + unsigned short port; + filename += 6; + char *colon; + if(NULL == (colon = strchr(filename, ':'))) + { + fprintf(stderr, "%s is not a valid IPv4:port, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(132); + } + if(1 != sscanf(colon + 1, "%hu", &port)) + { + fprintf(stderr, + "Port parse failed. %s is not a valid network file name, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(133); + } + char chTmp = *colon; + *colon = 0; + + struct sockaddr_in saddr={}; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + if(0 == inet_aton(filename, &saddr.sin_addr)) + { + fprintf(stderr, "inet_aton failed. %s is not a valid IPv4 address\n", + filename); + exit(134); + } + *colon = chTmp; + + if (pState->netListen) + { + int sockListen = socket(AF_INET, SOCK_STREAM, 0); + if (sockListen >= 0) + { + int iTmp = 1; + setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &iTmp, sizeof(int));//no error handling, just go on + if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + while ((-1 == (iTmp = listen(sockListen, 0))) && (EINTR == errno)) + ; + if (-1 != iTmp) + { + fprintf(stderr, "Waiting for a TCP connection on %s:%"SCNu16"...", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + while ((-1 == (sfd = accept(sockListen, (struct sockaddr *) &cli_addr, &clilen))) && (EINTR == errno)) + ; + if (sfd >= 0) + fprintf(stderr, "Client connected from %s:%"SCNu16"\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); + else + fprintf(stderr, "Error on accept: %s\n", strerror(errno)); + } + else//if (-1 != iTmp) + { + fprintf(stderr, "Error trying to listen on a socket: %s\n", strerror(errno)); + } + } + else//if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + fprintf(stderr, "Error on binding socket: %s\n", strerror(errno)); + } + } + else//if (sockListen >= 0) + { + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sockListen >= 0)//regardless success or error + close(sockListen);//do not listen on a given port anymore + } + else//if (pState->netListen) + { + if(0 <= (sfd = socket(AF_INET, socktype, 0))) + { + fprintf(stderr, "Connecting to %s:%hu...", inet_ntoa(saddr.sin_addr), port); + + int iTmp = 1; + while ((-1 == (iTmp = connect(sfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)))) && (EINTR == errno)) + ; + if (iTmp < 0) + fprintf(stderr, "error: %s\n", strerror(errno)); + else + fprintf(stderr, "connected, sending video...\n"); + } + else + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sfd >= 0) + new_handle = fdopen(sfd, "w"); + } + else + { + new_handle = fopen(filename, "wb"); + } + } + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new file \"%s\"\n", filename); + } + + return new_handle; +} + +/** + * buffer header callback function for camera + * + * Callback will dump buffer data to internal buffer + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + static int64_t base_time = -1; + + // All our times based on the receipt of the first callback + if (base_time == -1) + base_time = vcos_getmicrosecs64()/1000; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = 0; + int bytes_to_write = buffer->length; + + if (pData->pstate->onlyLuma) + bytes_to_write = vcos_min(buffer->length, port->format->es->video.width * port->format->es->video.height); + + vcos_assert(pData->file_handle); + + if (bytes_to_write) + { + mmal_buffer_header_mem_lock(buffer); + bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->file_handle); + mmal_buffer_header_mem_unlock(buffer); + + if (bytes_written != bytes_to_write) + { + vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, bytes_to_write); + pData->abort = 1; + } + } + } + else + { + vcos_log_error("Received a camera buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the camera port"); + } +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPIVIDYUV_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 0, + .max_preview_video_w = state->width, + .max_preview_video_h = state->height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + // Now set up the port formats + + // Set the encode format on the Preview port + // HW limitations mean we need the preview to be the same size as the required recorded output + + format = preview_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + + //enable dynamic framerate if necessary + if (state->camera_parameters.shutter_speed) + { + if (state->framerate > 1000000./state->camera_parameters.shutter_speed) + { + state->framerate=0; + if (state->verbose) + fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + } + } + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the encode format on the video port + + format = video_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + + if (state->useRGB) + { + format->encoding = mmal_util_rgb_order_fixed(still_port) ? MMAL_ENCODING_RGB24 : MMAL_ENCODING_BGR24; + format->encoding_variant = 0; //Irrelevant when not in opaque mode + } + else + { + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + } + + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = state->framerate; + format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + status = mmal_port_parameter_set_boolean(video_port, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to select zero copy"); + goto error; + } + + // Set the encode format on the still port + + format = still_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = 0; + format->es->video.frame_rate.den = 1; + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(video_port, video_port->buffer_num, video_port->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name); + } + + state->camera_pool = pool; + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPIVIDYUV_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } + +} + +/** + * Pause for specified time, but return early if detect an abort request + * + * @param state Pointer to state control struct + * @param pause Time in ms to pause + * @param callback Struct contain an abort flag tested for early termination + * + */ +static int pause_and_test_abort(RASPIVIDYUV_STATE *state, int pause) +{ + int wait; + + if (!pause) + return 0; + + // Going to check every ABORT_INTERVAL milliseconds + for (wait = 0; wait < pause; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (state->callback_data.abort) + return 1; + } + + return 0; +} + + +/** + * Function to wait in various ways (depending on settings) + * + * @param state Pointer to the state data + * + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_change(RASPIVIDYUV_STATE *state) +{ + int keep_running = 1; + static int64_t complete_time = -1; + + // Have we actually exceeded our timeout? + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->waitMethod) + { + case WAIT_METHOD_NONE: + (void)pause_and_test_abort(state, state->timeout); + return 0; + + case WAIT_METHOD_FOREVER: + { + // We never return from this. Expect a ctrl-c to exit. + while (1) + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + return 0; + } + + case WAIT_METHOD_TIMED: + { + int abort; + + if (state->bCapturing) + abort = pause_and_test_abort(state, state->onTime); + else + abort = pause_and_test_abort(state, state->offTime); + + if (abort) + return 0; + else + return keep_running; + } + + case WAIT_METHOD_KEYPRESS: + { + char ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to %s, X then ENTER to exit\n", state->bCapturing ? "pause" : "capture"); + + ch = getchar(); + if (ch == 'x' || ch == 'X') + return 0; + else + return keep_running; + } + + case WAIT_METHOD_SIGNAL: + { + // Need to wait for a SIGUSR1 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block SIGUSR1 so we can wait on it. + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to %s\n", state->bCapturing ? "pause" : "capture"); + } + + result = sigwait( &waitset, &sig ); + + if (state->verbose && result != 0) + fprintf(stderr, "Bad signal received - error %d\n", errno); + + return keep_running; + } + + } // switch + + return keep_running; +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPIVIDYUV_STATE state = {0}; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have two components. Camera, Preview + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + preview_input_port = state.preview_parameters.preview_component->input[0]; + + if (state.preview_parameters.wantPreview ) + { + if (state.verbose) + { + fprintf(stderr, "Connecting camera preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect camera to preview + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + + if (status != MMAL_SUCCESS) + state.preview_connection = NULL; + } + else + { + status = MMAL_SUCCESS; + } + + if (status == MMAL_SUCCESS) + { + state.callback_data.file_handle = NULL; + + if (state.filename) + { + if (state.filename[0] == '-') + { + state.callback_data.file_handle = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + state.callback_data.file_handle = open_filename(&state, state.filename); + } + + if (!state.callback_data.file_handle) + { + // Notify user, carry on but discarding output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); + } + } + + state.callback_data.pts_file_handle = NULL; + + if (state.pts_filename) + { + if (state.pts_filename[0] == '-') + { + state.callback_data.pts_file_handle = stdout; + } + else + { + state.callback_data.pts_file_handle = open_filename(&state, state.pts_filename); + if (state.callback_data.pts_file_handle) /* save header for mkvmerge */ + fprintf(state.callback_data.pts_file_handle, "# timecode format v2\n"); + } + + if (!state.callback_data.pts_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.pts_filename); + state.save_pts=0; + } + } + + // Set up our userdata - this is passed though to the callback where we need the information. + state.callback_data.pstate = &state; + state.callback_data.abort = 0; + + camera_video_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling camera video port\n"); + + // Enable the camera video port and tell it its callback function + status = mmal_port_enable(camera_video_port, camera_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup camera output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + + if (state.verbose) + fprintf(stderr, "Running in demo mode\n"); + + for (i=0;state.timeout == 0 || iqueue); + int q; + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(camera_video_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to camera video port (%d)", q); + } + } + + while (running) + { + // Change state + + state.bCapturing = !state.bCapturing; + + if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, state.bCapturing) != MMAL_SUCCESS) + { + // How to handle? + } + + if (state.verbose) + { + if (state.bCapturing) + fprintf(stderr, "Starting video capture\n"); + else + fprintf(stderr, "Pausing video capture\n"); + } + + running = wait_for_next_change(&state); + } + + if (state.verbose) + fprintf(stderr, "Finished capture\n"); + } + else + { + if (state.timeout) + vcos_sleep(state.timeout); + else + { + // timeout = 0 so run forever + while(1) + vcos_sleep(ABORT_INTERVAL); + } + } + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_still_port); + + if (state.preview_parameters.wantPreview && state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + // Can now close our file. Note disabling ports may flush buffers which causes + // problems if we have already closed the file! + if (state.callback_data.file_handle && state.callback_data.file_handle != stdout) + fclose(state.callback_data.file_handle); + if (state.callback_data.pts_file_handle && state.callback_data.pts_file_handle != stdout) + fclose(state.callback_data.pts_file_handle); + + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h b/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h new file mode 100755 index 0000000..663e23b --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, +}; + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/mirror.c b/host_applications/linux/apps/raspicam/gl_scenes/mirror.c new file mode 100755 index 0000000..fe14096 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/mirror.c @@ -0,0 +1,123 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mirror.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/** + * Draws an external EGL image and applies a sine wave distortion to create + * a hall of mirrors effect. + */ +static RASPITEXUTIL_SHADER_PROGRAM_T mirror_shader = { + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "uniform float offset;\n" + "const float waves = 2.0;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " float x = texcoord.x + 0.05 * sin(offset + (texcoord.y * waves * 2.0 * 3.141592));\n" + " float y = texcoord.y + 0.05 * sin(offset + (texcoord.x * waves * 2.0 * 3.141592));\n" + " if (y < 1.0 && y > 0.0 && x < 1.0 && x > 0.0) {\n" + " vec2 pos = vec2(x, y);\n" + " gl_FragColor = texture2D(tex, pos);\n" + " }\n" + " else {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n", + .uniform_names = {"tex", "offset"}, + .attribute_names = {"vertex"}, +}; + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int mirror_init(RASPITEX_STATE *state) +{ + int rc = raspitexutil_gl_init_2_0(state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&mirror_shader); +end: + return rc; +} + +static int mirror_redraw(RASPITEX_STATE *raspitex_state) { + static float offset = 0.0; + + // Start with a clear screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Bind the OES texture which is used to render the camera preview + glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture); + + offset += 0.05; + GLCHK(glUseProgram(mirror_shader.program)); + GLCHK(glEnableVertexAttribArray(mirror_shader.attribute_locations[0])); + GLfloat varray[] = { + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f, + + -1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + }; + GLCHK(glVertexAttribPointer(mirror_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, varray)); + GLCHK(glUniform1f(mirror_shader.uniform_locations[1], offset)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(mirror_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + return 0; +} + +int mirror_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = mirror_init; + state->ops.redraw = mirror_redraw; + state->ops.update_texture = raspitexutil_update_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/mirror.h b/host_applications/linux/apps/raspicam/gl_scenes/mirror.h new file mode 100755 index 0000000..8437728 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/mirror.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MIRROR_H +#define MIRROR_H + +#include "RaspiTex.h" + +int mirror_open(RASPITEX_STATE *state); + +#endif /* MIRROR_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/models.c b/host_applications/linux/apps/raspicam/gl_scenes/models.c new file mode 100755 index 0000000..bbb5a30 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/models.c @@ -0,0 +1,521 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "GLES/gl.h" +#include +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "models.h" + +#define VMCS_RESOURCE(a,b) (b) + +/****************************************************************************** +Private typedefs, macros and constants +******************************************************************************/ + +enum {VBO_VERTEX, VBO_NORMAL, VBO_TEXTURE, VBO_MAX}; +#define MAX_MATERIALS 4 +#define MAX_MATERIAL_NAME 32 + +typedef struct wavefront_material_s { + GLuint vbo[VBO_MAX]; + int numverts; + char name[MAX_MATERIAL_NAME]; + GLuint texture; +} WAVEFRONT_MATERIAL_T; + +typedef struct wavefront_model_s { + WAVEFRONT_MATERIAL_T material[MAX_MATERIALS]; + int num_materials; + GLuint texture; +} WAVEFRONT_MODEL_T; + + +/****************************************************************************** +Static Data +******************************************************************************/ + +/****************************************************************************** +Static Function Declarations +******************************************************************************/ + +/****************************************************************************** +Static Function Definitions +******************************************************************************/ + +static void create_vbo(GLenum type, GLuint *vbo, int size, void *data) +{ + glGenBuffers(1, vbo); + vc_assert(*vbo); + glBindBuffer(type, *vbo); + glBufferData(type, size, data, GL_STATIC_DRAW); + glBindBuffer(type, 0); +} + + +static void destroy_vbo(GLuint *vbo) +{ + glDeleteBuffers(1, vbo); + *vbo = 0; +} + +#define MAX_VERTICES 100000 +static void *allocbuffer(int size) +{ + return malloc(size); +} + +static void freebuffer(void *p) +{ + free (p); +} + +static void centre_and_rescale(float *verts, int numvertices) +{ + float cx=0.0f, cy=0.0f, cz=0.0f, scale=0.0f; + float minx=0.0f, miny=0.0f, minz=0.0f; + float maxx=0.0f, maxy=0.0f, maxz=0.0f; + int i; + float *v = verts; + minx = maxx = verts[0]; + miny = maxy = verts[1]; + minz = maxz = verts[2]; + for (i=0; i= 3) *dst++ = src[ind + 2]; + indexes += 3; + } +} + +int draw_wavefront(MODEL_T m, GLuint texture) +{ + int i; + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->texture == -1) continue; + + if (mat->texture) + glBindTexture(GL_TEXTURE_2D, mat->texture); + else + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); + + if (mat->vbo[VBO_VERTEX]) { + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_VERTEX]); + glVertexPointer(3, GL_FLOAT, 0, NULL); + } + if (mat->vbo[VBO_NORMAL]) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_NORMAL]); + glNormalPointer(GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + if (mat->vbo[VBO_TEXTURE]) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_TEXTURE]); + glTexCoordPointer(2, GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + glDrawArrays(GL_TRIANGLES, 0, mat->numverts); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + return 0; +} + +struct wavefront_model_loading_s { + unsigned short material_index[MAX_MATERIALS]; + int num_materials; + int numv, numt, numn, numf; + unsigned int data[0]; +}; + +static int load_wavefront_obj(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + char line[256+1]; + unsigned short pp[54+1]; + FILE *fp; + int i, valid; + float *qv = (float *)m->data; + float *qt = (float *)m->data + 3 * MAX_VERTICES; + float *qn = (float *)m->data + (3+2) * MAX_VERTICES; + unsigned short *qf = (unsigned short *)((float *)m->data + (3+2+3) * MAX_VERTICES); + float *pv = qv, *pt = qt, *pn = qn; + unsigned short *pf = qf; + fp = fopen(modelname, "r"); + if (!fp) return -1; + + m->num_materials = 0; + m->material_index[0] = 0; + + valid = fread(line, 1, sizeof(line)-1, fp); + + while (valid > 0) { + char *s, *end = line; + + while((end-line < valid) && *end != '\n' && *end != '\r') + end++; + *end++ = 0; + + if((end-line < valid) && *end != '\n' && *end != '\r') + *end++ = 0; + + s = line; + + if (s[strlen(s)-1] == 10) s[strlen(s)-1]=0; + switch (s[0]) { + case '#': break; // comment + case '\r': case '\n': case '\0': break; // blank line + case 'm': vc_assert(strncmp(s, "mtllib", sizeof "mtllib"-1)==0); break; + case 'o': break; + case 'u': + if (sscanf(s, "usemtl %s", /*MAX_MATERIAL_NAME-1, */model->material[m->num_materials].name) == 1) { + if (m->num_materials < MAX_MATERIALS) { + if (m->num_materials > 0 && ((pf-qf)/3 == m->material_index[m->num_materials-1] || strcmp(model->material[m->num_materials-1].name, model->material[m->num_materials].name)==0)) { + strcpy(model->material[m->num_materials-1].name, model->material[m->num_materials].name); + m->num_materials--; + } else + m->material_index[m->num_materials] = (pf-qf)/3; + m->num_materials++; + } + } else { printf("%s", s); vc_assert(0); } + break; + case 'g': vc_assert(strncmp(s, "g ", sizeof "g "-1)==0); break; + case 's': vc_assert(strncmp(s, "s ", sizeof "s "-1)==0); break; + case 'v': case 'f': + if (sscanf(s, "v %f %f %f", pv+0, pv+1, pv+2) == 3) { + pv += 3; + } else if (sscanf(s, "vt %f %f", pt+0, pt+1) == 2) { + pt += 2; + } else if (sscanf(s, "vn %f %f %f", pn+0, pn+1, pn+2) == 3) { + pn += 3; + } else if (i = sscanf(s, "f"" %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu", + pp+ 0, pp+ 1, pp+ 2, pp+ 3, pp+ 4, pp+ 5, pp+ 6, pp+ 7, pp+ 8, pp+ 9, pp+10, pp+11, + pp+12, pp+13, pp+14, pp+15, pp+16, pp+17, pp+18, pp+19, pp+20, pp+21, pp+22, pp+23, + pp+24, pp+25, pp+26, pp+27, pp+28, pp+29, pp+30, pp+32, pp+32, pp+33, pp+34, pp+35, pp+36), i >= 6) { + int poly = i/2; + //vc_assert(i < countof(pp)); // may need to increment poly count and pp array + for (i=1; i= 6) { + int poly = i/2; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i= 9) { + int poly = i/3; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i valid ? valid : end-line; + memmove(line, end, valid - i); + valid -= i; + valid += fread(line+valid, 1, sizeof(line)-1-valid, fp); + } + fclose(fp); + + if (m->num_materials==0) m->material_index[m->num_materials++] = 0; + + centre_and_rescale(qv, (pv-qv)/3); + renormalise(qn, (pn-qn)/3); + //centre_and_rescale2(qt, (pt-qt)/2); + + m->numv = pv-qv; + m->numt = pt-qt; + m->numn = pn-qn; + m->numf = pf-qf; + + // compress array + //memcpy((float *)m->data, (float *)m->data, m->numv * sizeof *qv); - nop + memcpy((float *)m->data + m->numv, (float *)m->data + 3 * MAX_VERTICES, m->numt * sizeof *qt); + memcpy((float *)m->data + m->numv + m->numt,(float *) m->data + (3 + 2) * MAX_VERTICES, m->numn * sizeof *qn); + memcpy((float *)m->data + m->numv + m->numt + m->numn, (float *)m->data + (3 + 2 + 3) * MAX_VERTICES, m->numf * sizeof *qf); + + return 0; +} + +static int load_wavefront_dat(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + FILE *fp; + int s; + const int size = sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES; //each face has 9 vertices + + fp = fopen(modelname, "r"); + if (!fp) return -1; + s = fread(m, 1, size, fp); + if (s < 0) return -1; + fclose(fp); + return 0; +} + +MODEL_T load_wavefront(const char *modelname, const char *texturename) +{ + WAVEFRONT_MODEL_T *model; + float *temp, *qv, *qt, *qn; + unsigned short *qf; + int i; + int numverts = 0, offset = 0; + struct wavefront_model_loading_s *m; + int s=-1; + char modelname_obj[128]; + model = malloc(sizeof *model); + if (!model || !modelname) return NULL; + memset (model, 0, sizeof *model); + model->texture = 0; //load_texture(texturename); + m = allocbuffer(sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES); //each face has 9 vertices + if (!m) return 0; + + if (strlen(modelname) + 5 <= sizeof modelname_obj) { + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + s = load_wavefront_dat(modelname_obj, model, m); + } + if (s==0) {} + else if (strncmp(modelname + strlen(modelname) - 4, ".obj", 4) == 0) { + #ifdef DUMP_OBJ_DAT + int size; + FILE *fp; + #endif + s = load_wavefront_obj(modelname, model, m); + #ifdef DUMP_OBJ_DAT + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + size = sizeof *m + + sizeof(float)*(3*m->numv+2*m->numt+3*m->numn) + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*m->numf; //each face has 9 vertices + fp = host_file_open(modelname_obj, "w"); + fwrite(m, 1, size, fp); + fclose(fp); + #endif + } else if (strncmp(modelname + strlen(modelname) - 4, ".dat", 4) == 0) { + s = load_wavefront_dat(modelname, model, m); + } + if (s != 0) return 0; + + qv = (float *)(m->data); + qt = (float *)(m->data + m->numv); + qn = (float *)(m->data + m->numv + m->numt); + qf = (unsigned short *)(m->data + m->numv + m->numt + m->numn); + + numverts = m->numf/3; + vc_assert(numverts <= MAX_VERTICES); + + temp = allocbuffer(3*numverts*sizeof *temp); + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + mat->numverts = i < m->num_materials-1 ? m->material_index[i+1]-m->material_index[i] : numverts - m->material_index[i]; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + offset += mat->numverts; + mat->texture = model->texture; + } + model->num_materials = m->num_materials; + vc_assert(offset == numverts); + freebuffer(temp); + freebuffer(m); + return (MODEL_T)model; +} + +void unload_wavefront(MODEL_T m) +{ + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + int i; + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->vbo[VBO_VERTEX]) + destroy_vbo(mat->vbo+VBO_VERTEX); + if (mat->vbo[VBO_TEXTURE]) + destroy_vbo(mat->vbo+VBO_TEXTURE); + if (mat->vbo[VBO_NORMAL]) + destroy_vbo(mat->vbo+VBO_NORMAL); + } +} + +// create a cube model that looks like a wavefront model, +MODEL_T cube_wavefront(void) +{ + static const float qv[] = { + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + }; + + static const float qn[] = { + 0.0f, -1.0f, -0.0f, + 0.0f, 1.0f, -0.0f, + 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, -0.0f, + 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, -0.0f, + }; + + static const float qt[] = { + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }; + + static const unsigned short qf[] = { + 1,1,1, 2,2,1, 3,3,1, + 3,3,1, 4,4,1, 1,1,1, + 5,4,2, 6,1,2, 7,2,2, + 7,2,2, 8,3,2, 5,4,2, + 1,4,3, 4,1,3, 6,2,3, + 6,2,3, 5,3,3, 1,4,3, + 4,4,4, 3,1,4, 7,2,4, + 7,2,4, 6,3,4, 4,4,4, + 3,4,5, 2,1,5, 8,2,5, + 8,2,5, 7,3,5, 3,4,5, + 2,4,6, 1,1,6, 5,2,6, + 5,2,6, 8,3,6, 2,4,6, + }; + WAVEFRONT_MODEL_T *model = malloc(sizeof *model); + if (model) { + WAVEFRONT_MATERIAL_T *mat = model->material; + float *temp; + const int offset = 0; + memset(model, 0, sizeof *model); + + temp = allocbuffer(3*MAX_VERTICES*sizeof *temp); + mat->numverts = countof(qf)/3; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + + freebuffer(temp); + model->num_materials = 1; + } + return (MODEL_T)model; +} + + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/models.h b/host_applications/linux/apps/raspicam/gl_scenes/models.h new file mode 100755 index 0000000..4a6cbd0 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/models.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MODELS_T +#define MODELS_T +typedef struct opqaue_model_s * MODEL_T; + +MODEL_T load_wavefront(const char *modelname, const char *texturename); +MODEL_T cube_wavefront(void); +void unload_wavefront(MODEL_T m); +int draw_wavefront(MODEL_T m, GLuint texture); +#endif diff --git a/host_applications/linux/apps/raspicam/gl_scenes/sobel.c b/host_applications/linux/apps/raspicam/gl_scenes/sobel.c new file mode 100755 index 0000000..ad2c8d9 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/sobel.c @@ -0,0 +1,192 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "sobel.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/* \file sobel.c + * Example code for implementing Sobel filter as GLSL shaders. + * The input image is a greyscale texture from the MMAL buffer Y plane. + */ + +#define SOBEL_VSHADER_SOURCE \ + "attribute vec2 vertex;\n" \ + "varying vec2 texcoord;\n" \ + "void main(void) {\n" \ + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" \ + "}\n" + +/* Example Sobel edge detct shader. The texture format for + * EGL_IMAGE_BRCM_MULTIMEDIA_Y is a one byte per pixel greyscale GL_LUMINANCE. + * If the output is to be fed into another image processing shader then it may + * be worth changing this code to take 4 input Y pixels and pack the result + * into a 32bpp RGBA pixel. + */ +#define SOBEL_FSHADER_SOURCE \ + "#extension GL_OES_EGL_image_external : require\n" \ + "uniform samplerExternalOES tex;\n" \ + "varying vec2 texcoord;\n" \ + "uniform vec2 tex_unit;\n" \ + "void main(void) {\n" \ + " float x = texcoord.x;\n" \ + " float y = texcoord.y;\n" \ + " float x1 = x - tex_unit.x;\n" \ + " float y1 = y - tex_unit.y;\n" \ + " float x2 = x + tex_unit.x;\n" \ + " float y2 = y + tex_unit.y;\n" \ + " vec4 p0 = texture2D(tex, vec2(x1, y1));\n" \ + " vec4 p1 = texture2D(tex, vec2(x, y1));\n" \ + " vec4 p2 = texture2D(tex, vec2(x2, y1));\n" \ + " vec4 p3 = texture2D(tex, vec2(x1, y));\n" \ + " /* vec4 p4 = texture2D(tex, vec2(x, y)); */\n" \ + " vec4 p5 = texture2D(tex, vec2(x2, y));\n" \ + " vec4 p6 = texture2D(tex, vec2(x1, y2));\n" \ + " vec4 p7 = texture2D(tex, vec2(x, y2));\n" \ + " vec4 p8 = texture2D(tex, vec2(x2, y2));\n" \ + "\n" \ + " vec4 v = p0 + (2.0 * p1) + p3 -p6 + (-2.0 * p7) + -p8;\n" \ + " vec4 h = p0 + (2.0 * p3) + p7 -p2 + (-2.0 * p5) + -p8;\n" \ + " gl_FragColor = sqrt(h*h + v*v);\n" \ + " gl_FragColor.a = 1.0;\n" \ + "}\n" + +static GLfloat quad_varray[] = { + -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, +}; + +static GLuint quad_vbo; + +static RASPITEXUTIL_SHADER_PROGRAM_T sobel_shader = +{ + .vertex_source = SOBEL_VSHADER_SOURCE, + .fragment_source = SOBEL_FSHADER_SOURCE, + .uniform_names = {"tex", "tex_unit"}, + .attribute_names = {"vertex"}, +}; + +static const EGLint sobel_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + + +/** + * Initialisation of shader uniforms. + * + * @param width Width of the EGL image. + * @param width Height of the EGL image. + */ +static int shader_set_uniforms(RASPITEXUTIL_SHADER_PROGRAM_T *shader, + int width, int height) +{ + GLCHK(glUseProgram(shader->program)); + GLCHK(glUniform1i(shader->uniform_locations[0], 0)); // Texture unit + + /* Dimensions of a single pixel in texture co-ordinates */ + GLCHK(glUniform2f(shader->uniform_locations[1], + 1.0 / (float) width, 1.0 / (float) height)); + + /* Enable attrib 0 as vertex array */ + GLCHK(glEnableVertexAttribArray(shader->attribute_locations[0])); + return 0; +} + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int sobel_init(RASPITEX_STATE *raspitex_state) +{ + int rc = 0; + int width = raspitex_state->width; + int height = raspitex_state->height; + + vcos_log_trace("%s", VCOS_FUNCTION); + raspitex_state->egl_config_attribs = sobel_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(raspitex_state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&sobel_shader); + if (rc != 0) + goto end; + + rc = shader_set_uniforms(&sobel_shader, width, height); + if (rc != 0) + goto end; + + GLCHK(glGenBuffers(1, &quad_vbo)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW)); + GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + +end: + return rc; +} + +/* Redraws the scene with the latest luma buffer. + * + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int sobel_redraw(RASPITEX_STATE* state) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLCHK(glUseProgram(sobel_shader.program)); + + /* Bind the Y plane texture */ + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(sobel_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(sobel_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + return 0; +} + +int sobel_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = sobel_init; + state->ops.redraw = sobel_redraw; + state->ops.update_y_texture = raspitexutil_update_y_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/sobel.h b/host_applications/linux/apps/raspicam/gl_scenes/sobel.h new file mode 100755 index 0000000..14ab13a --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/sobel.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SOBEL_H +#define SOBEL_H + +#include "RaspiTex.h" + +int sobel_open(RASPITEX_STATE *state); + +#endif /* SOBEL_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/square.c b/host_applications/linux/apps/raspicam/gl_scenes/square.c new file mode 100755 index 0000000..a1028a1 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/square.c @@ -0,0 +1,121 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "RaspiTexUtil.h" + +/* Vertex co-ordinates: + * + * v0----v1 + * | | + * | | + * | | + * v3----v2 + */ + +static const GLfloat vertices[] = +{ +#define V0 -0.8, 0.8, 0.8, +#define V1 0.8, 0.8, 0.8, +#define V2 0.8, -0.8, 0.8, +#define V3 -0.8, -0.8, 0.8, + V0 V3 V2 V2 V1 V0 +}; + +/* Texture co-ordinates: + * + * (0,0) b--c + * | | + * a--d + * + * b,a,d d,c,b + */ +static const GLfloat tex_coords[] = +{ + 0, 0, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0 +}; + +static GLfloat angle; +static uint32_t anim_step; + +static int square_init(RASPITEX_STATE *state) +{ + int rc = raspitexutil_gl_init_1_0(state); + + if (rc != 0) + goto end; + + angle = 0.0f; + anim_step = 0; + + glClearColor(0, 0, 0, 0); + glClearDepthf(1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + +end: + return rc; +} + +static int square_update_model(RASPITEX_STATE *state) +{ + int frames_per_rev = 30 * 15; + (void) state; + angle = (anim_step * 360) / (GLfloat) frames_per_rev; + anim_step = (anim_step + 1) % frames_per_rev; + + return 0; +} + +static int square_redraw(RASPITEX_STATE *state) +{ + /* Bind the OES texture which is used to render the camera preview */ + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, state->texture)); + glLoadIdentity(); + glRotatef(angle, 0.0, 0.0, 1.0); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices); + glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex_coords); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, vcos_countof(tex_coords) / 2)); + return 0; +} + +int square_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = square_init; + state->ops.update_model = square_update_model; + state->ops.redraw = square_redraw; + state->ops.update_texture = raspitexutil_update_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/square.h b/host_applications/linux/apps/raspicam/gl_scenes/square.h new file mode 100755 index 0000000..ef4741a --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/square.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SQUARE_H +#define SQUARE_H + +#include "RaspiTex.h" + +int square_open(RASPITEX_STATE *state); + +#endif /* SQUARE_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/teapot.c b/host_applications/linux/apps/raspicam/gl_scenes/teapot.c new file mode 100755 index 0000000..eac4bcb --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/teapot.c @@ -0,0 +1,332 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cube_texture_and_coords.h" +#include "models.h" +#include "teapot.h" + +#include "RaspiTex.h" +#include "RaspiTexUtil.h" + +#define PATH "./" + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; + MODEL_T model; +} TEAPOT_STATE_T; + +static void init_ogl(TEAPOT_STATE_T *state); +static void init_model_proj(TEAPOT_STATE_T *state); +static void reset_model(TEAPOT_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(TEAPOT_STATE_T *state) +{ + // Set background color and clear buffers + glClearColor((0.3922f+7*0.5f)/8, (0.1176f+7*0.5f)/8, (0.5882f+7*0.5f)/8, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glClearDepthf(1.0); + glDepthFunc(GL_LEQUAL); + + float noAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(TEAPOT_STATE_T *state) +{ + float nearp = 0.1f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(TEAPOT_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 0.8f*1.5f; +} + +/*********************************************************** + * Name: teapot_update_model + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static int teapot_update_model(RASPITEX_STATE *raspitex_state) +{ + TEAPOT_STATE_T *state = (TEAPOT_STATE_T *) raspitex_state->scene_state; + + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); + + return 0; +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 10.0f) + distance = 10.f; + else if (distance <= 1.0f) + distance = 1.0f; + + return distance; +} + +/*********************************************************** + * Name: teapot_redraw + * + * Arguments: + * RASPITEX_STATE_T *state - holds OGLES model info + * + * Description: Draws the model + * + * Returns: void + * + ***********************************************************/ +static int teapot_redraw(RASPITEX_STATE *raspitex_state) +{ + TEAPOT_STATE_T *state = (TEAPOT_STATE_T *) raspitex_state->scene_state; + + // Start with a clear screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* Bind the OES texture which is used to render the camera preview */ + glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture); + draw_wavefront(state->model, raspitex_state->texture); + return 0; +} + +//============================================================================== + +static int teapot_gl_init(RASPITEX_STATE *raspitex_state) +{ + const char *model_path = "/opt/vc/src/hello_pi/hello_teapot/teapot.obj.dat"; + TEAPOT_STATE_T *state = NULL; + int rc = 0; + + // Clear scene state + state = calloc(1, sizeof(TEAPOT_STATE_T)); + raspitex_state->scene_state = state; + state->screen_width = raspitex_state->width; + state->screen_height = raspitex_state->height; + + rc = raspitexutil_gl_init_1_0(raspitex_state); + if (rc != 0) + goto end; + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + state->model = load_wavefront(model_path, NULL); + + if (! state->model) + { + vcos_log_error("Failed to load model from %s\n", model_path); + rc = -1; + } + +end: + return rc; +} + +static void teapot_gl_term(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s:", VCOS_FUNCTION); + + TEAPOT_STATE_T *state = raspitex_state->scene_state; + if (state) + { + if (state->model) + unload_wavefront(state->model); + raspitexutil_gl_term(raspitex_state); + free(raspitex_state->scene_state); + raspitex_state->scene_state = NULL; + } +} + +int teapot_open(RASPITEX_STATE *raspitex_state) +{ + raspitex_state->ops.gl_init = teapot_gl_init; + raspitex_state->ops.update_model = teapot_update_model; + raspitex_state->ops.redraw = teapot_redraw; + raspitex_state->ops.gl_term = teapot_gl_term; + raspitex_state->ops.update_texture = raspitexutil_update_texture; + return 0; +} + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/teapot.h b/host_applications/linux/apps/raspicam/gl_scenes/teapot.h new file mode 100755 index 0000000..c8d5162 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/teapot.h @@ -0,0 +1,35 @@ +/* +Copyright (c) 2012-2013, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef TEAPOT_H +#define TEAPOT_H + +#include "RaspiTex.h" + +int teapot_open(RASPITEX_STATE *raspitex_state); + +#endif /* TEAPOT_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c new file mode 100755 index 0000000..f47b83e --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c @@ -0,0 +1,332 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2016, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Make the render output CPU accessible by defining a framebuffer texture + * stored in a VCSM (VideoCore shared memory) EGL image. + * + * This example just demonstrates how to use use the APIs by using the CPU. + * to blit an animated rectangle into frame-buffer texture in shared memory. + * + * A more realistic example would be to do a blur, edge-detect in GLSL then pass + * the buffer to OpenCV. There may be some benefit in using multiple GL contexts + * to reduce the impact of serializing operations with a glFlush. + * + * N.B VCSM textures are raster scan order textures. This makes it very + * convenient to read and modify VCSM frame-buffer textures from the CPU. + * However, if the output of the CPU stage is drawn again as a texture that + * is rotated or scaled then it can sometimes be better to use glTexImage2D + * to allow the driver to convert this back into the native texture format. + * + * Example usage + * raspistill -p 0,0,1024,1024 -gw 0,0,1024,1024 -t 10000 --gl -gs vcsm_square + */ +/* Uncomment the next line to compare with the glReadPixels implementation. VCSM + * should run at about 40fps with a 1024x1024 texture compared to about 20fps + * using glReadPixels. + */ +//#define USE_READPIXELS + +#include "vcsm_square.h" +#include +#include +#include +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include "user-vcsm.h" + +/* Draw a scaled quad showing the entire texture with the + * origin defined as an attribute */ +static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_oes_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex"}, +}; +static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "uniform sampler2D tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex"}, +}; + +static GLfloat quad_varray[] = { + -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, +}; + +static GLuint quad_vbo; + +#ifdef USE_READPIXELS +unsigned char *pixel_buffer; +#else +static struct egl_image_brcm_vcsm_info vcsm_info; +static EGLImageKHR eglFbImage; +#endif +static GLuint fb_tex_name; +static GLuint fb_name; + + +// VCSM buffer dimensions must be a power of two. Use glViewPort to draw NPOT +// rectangles within the VCSM buffer. +static int fb_width = 1024; +static int fb_height = 1024; + +static const EGLint vcsm_square_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +static int vcsm_square_init(RASPITEX_STATE *raspitex_state) +{ + int rc = vcsm_init(); + vcos_log_trace("%s: vcsm_init %d", VCOS_FUNCTION, rc); + + raspitex_state->egl_config_attribs = vcsm_square_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(raspitex_state); + + if (rc != 0) + goto end; + + // Shader for drawing the YUV OES texture + rc = raspitexutil_build_shader_program(&vcsm_square_oes_shader); + GLCHK(glUseProgram(vcsm_square_oes_shader.program)); + GLCHK(glUniform1i(vcsm_square_oes_shader.uniform_locations[0], 0)); // tex unit + + // Shader for drawing VCSM sampler2D texture + rc = raspitexutil_build_shader_program(&vcsm_square_shader); + GLCHK(glUseProgram(vcsm_square_shader.program)); + GLCHK(glUniform1i(vcsm_square_shader.uniform_locations[0], 0)); // tex unit + + GLCHK(glGenFramebuffers(1, &fb_name)); + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); + + GLCHK(glGenTextures(1, &fb_tex_name)); + GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + +#ifdef USE_READPIXELS + printf("Using glReadPixels\n"); + GLCHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb_width, fb_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + pixel_buffer = malloc(fb_width * fb_height * 4); + if (! pixel_buffer) { + rc = -1; + goto end; + } +#else /* USE_READPIXELS */ + printf("Using VCSM\n"); + vcsm_info.width = fb_width; + vcsm_info.height = fb_height; + eglFbImage = eglCreateImageKHR(raspitex_state->display, EGL_NO_CONTEXT, + EGL_IMAGE_BRCM_VCSM, &vcsm_info, NULL); + if (eglFbImage == EGL_NO_IMAGE_KHR || vcsm_info.vcsm_handle == 0) { + vcos_log_error("%s: Failed to create EGL VCSM image\n", VCOS_FUNCTION); + rc = -1; + goto end; + } + + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglFbImage)); +#endif /* USE_READPIXELS */ + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex_name, 0)); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + vcos_log_error("GL_FRAMEBUFFER is not complete\n"); + rc = -1; + goto end; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + GLCHK(glGenBuffers(1, &quad_vbo)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW)); + + GLCHK(glClearColor(0, 0, 0, 0)); +end: + return rc; +} + +// Write the shared memory texture writing something to each line. This is +// just to show that the buffer really is CPU modifiable. +static void vcsm_square_draw_pattern(unsigned char *buffer) +{ + static unsigned x_offset; + + unsigned char *line_start = (unsigned char *) buffer; + unsigned width = fb_width > 32 ? 32 : fb_width; + int i = 0; + size_t stride = fb_width << 2; + + x_offset = (x_offset + 1) % (fb_width - width); + for (i = 0; i < fb_height; i++) { + memset(line_start + (x_offset << 2), ~0, width << 2); + line_start += stride; + } +} + +#ifdef USE_READPIXELS +static int vcsm_square_redraw_readpixels(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); + GLCHK(glViewport(0,0,fb_width,fb_height)); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Fill the viewport with the camFill the viewport with the camera image + GLCHK(glUseProgram(vcsm_square_oes_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glReadPixels(0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer)); + + vcsm_square_draw_pattern(pixel_buffer); + + // Enable default window surface + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + // Draw the modified texture buffer to the screen + GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height)); + GLCHK(glUseProgram(vcsm_square_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); + GLCHK(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer)); + GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + + return 0; +} +#else /* USE_READPIXELS */ +static int vcsm_square_redraw(RASPITEX_STATE *raspitex_state) +{ + unsigned char *vcsm_buffer = NULL; + VCSM_CACHE_TYPE_T cache_type; + + vcos_log_trace("%s", VCOS_FUNCTION); + + glClearColor(255, 0, 0, 255); + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); + GLCHK(glViewport(0, 0, fb_width, fb_height)); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Fill the viewport with the camFill the viewport with the camera image + GLCHK(glUseProgram(vcsm_square_oes_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glFinish()); + + // Make the buffer CPU addressable with host cache enabled + vcsm_buffer = (unsigned char *) vcsm_lock_cache(vcsm_info.vcsm_handle, VCSM_CACHE_TYPE_HOST, &cache_type); + if (! vcsm_buffer) { + vcos_log_error("Failed to lock VCSM buffer for handle %d\n", vcsm_info.vcsm_handle); + return -1; + } + vcos_log_trace("Locked vcsm handle %d at %p\n", vcsm_info.vcsm_handle, vcsm_buffer); + + vcsm_square_draw_pattern(vcsm_buffer); + + // Release the locked texture memory to flush the CPU cache and allow GPU + // to read it + vcsm_unlock_ptr(vcsm_buffer); + + // Enable default window surface + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + // Draw the modified texture buffer to the screen + GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height)); + GLCHK(glUseProgram(vcsm_square_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); + GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + + return 0; +} +#endif /* USE_READPIXELS */ + +int vcsm_square_open(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + raspitex_state->ops.gl_init = vcsm_square_init; +#ifdef USE_READPIXELS + raspitex_state->ops.redraw = vcsm_square_redraw_readpixels; +#else + raspitex_state->ops.redraw = vcsm_square_redraw; +#endif /* USE_READPIXELS */ + raspitex_state->ops.update_y_texture = raspitexutil_update_y_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h new file mode 100755 index 0000000..632d640 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2016, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCSM_SQUARE_H +#define VCSM_SQUARE_H + +#include "RaspiTex.h" + +int vcsm_square_open(RASPITEX_STATE *state); + +#endif /* VCSM_SQUARE_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/yuv.c b/host_applications/linux/apps/raspicam/gl_scenes/yuv.c new file mode 100755 index 0000000..89fd6ab --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/yuv.c @@ -0,0 +1,145 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "yuv.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/* Draw a scaled quad showing the the entire texture with the + * origin defined as an attribute */ +static RASPITEXUTIL_SHADER_PROGRAM_T yuv_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "attribute vec2 top_left;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = vertex + vec2(0.0, 1.0);\n" + " gl_Position = vec4(top_left + vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex", "top_left"}, +}; + +static GLfloat varray[] = +{ + 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, +}; + +static const EGLint yuv_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int yuv_init(RASPITEX_STATE *state) +{ + int rc; + state->egl_config_attribs = yuv_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&yuv_shader); + GLCHK(glUseProgram(yuv_shader.program)); + GLCHK(glUniform1i(yuv_shader.uniform_locations[0], 0)); // tex unit +end: + return rc; +} + +/** + * Draws a 2x2 grid with each shell showing the entire MMAL buffer from a + * different EGL image target. + */ +static int yuv_redraw(RASPITEX_STATE *raspitex_state) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + GLCHK(glUseProgram(yuv_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glEnableVertexAttribArray(yuv_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(yuv_shader.attribute_locations[0], + 2, GL_FLOAT, GL_FALSE, 0, varray)); + + // Y plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], -1.0f, 1.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // U plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->u_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], 0.0f, 1.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // V plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->v_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], 0.0f, 0.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // RGB plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], -1.0f, 0.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(yuv_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + return 0; +} + +int yuv_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = yuv_init; + state->ops.redraw = yuv_redraw; + state->ops.update_texture = raspitexutil_update_texture; + state->ops.update_y_texture = raspitexutil_update_y_texture; + state->ops.update_u_texture = raspitexutil_update_u_texture; + state->ops.update_v_texture = raspitexutil_update_v_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/yuv.h b/host_applications/linux/apps/raspicam/gl_scenes/yuv.h new file mode 100755 index 0000000..b0e3a91 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/yuv.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef YUV_H +#define YUV_H + +#include "RaspiTex.h" + +int yuv_open(RASPITEX_STATE *state); + +#endif /* YUV_H */ diff --git a/host_applications/linux/apps/raspicam/imv_examples/README.md b/host_applications/linux/apps/raspicam/imv_examples/README.md new file mode 100755 index 0000000..d85f343 --- /dev/null +++ b/host_applications/linux/apps/raspicam/imv_examples/README.md @@ -0,0 +1,39 @@ +Post-processing example programs to play with the imv buffer: +============================================================= + +Compile: +-------- +gcc imv2pgm.c -o imv2pgm -lm + +gcc imv2txt.c -o imv2txt + +Record and split buffer: +------------------------ +raspivid -x test.imv -o test.h264 + +We need to split the buffer first using split: + +split -a 4 -d -b $(((120+1)\*68\*4)) test.imv frame- + +Play: +----- +Now we can transform the velocity magnitues to a pgm image + +./imv2pgm frame-0001 120 68 frame-0001.pgm + +Or loop over all frames and create a movie + +for i in frame-????; do ./imv2pgm $i 120 68 $i.pgm;convert $i.pgm $i.png; rm $i.pgm; done + +avconv -i frame-%04d.png motion.avi + +And we can create a text file for easy plotting + +./imv2txt frame-0001 120 68 frame-0001.dat + +This textfile has in each line the center position x y of the macro block and +the velocities u and v and the sum of differences sad. + +These can be plot with xmgrace + +xmgrace -autoscale none -settype xyvmap frame-0001.dat -param plot.par diff --git a/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c b/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c new file mode 100755 index 0000000..fd32227 --- /dev/null +++ b/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c @@ -0,0 +1,88 @@ +/* +Copyright (c) 2014, Christian Kroener +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +typedef struct +{ + signed char x_vector; + signed char y_vector; + short sad; +} INLINE_MOTION_VECTOR; + +int main(int argc, const char **argv) +{ + if(argc!=5) + { + printf("usage: %s data.imv mbx mby out.pgm\n",argv[0]); + return 0; + } + int mbx=atoi(argv[2]); + int mby=atoi(argv[3]); + + /////////////////////////////// + // Read raw file to buffer // + /////////////////////////////// + FILE *f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + char *buffer = malloc(fsize + 1); + fread(buffer, fsize, 1, f); + fclose(f); + buffer[fsize] = 0; + + /////////////////// + // Fill struct // + /////////////////// + if(fsize<(mbx+1)*mby*4) + { + printf("File to short!\n"); + return 0; + } + INLINE_MOTION_VECTOR *imv; + imv = malloc((mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR)); + memcpy ( &imv[0], &buffer[0], (mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR) ); + + ///////////////////// + // Export to PGM // + ///////////////////// + FILE *out = fopen(argv[4], "w"); + fprintf(out,"P5\n%d %d\n255\n",mbx,mby); + int i,j; + for(j=0;j +#include +#include + +typedef struct +{ + signed char x_vector; + signed char y_vector; + short sad; +} INLINE_MOTION_VECTOR; + +int main(int argc, const char **argv) +{ + if(argc!=5) + { + printf("usage: %s data.imv mbx mby out.dat\n",argv[0]); + return 0; + } + int mbx=atoi(argv[2]); + int mby=atoi(argv[3]); + + /////////////////////////////// + // Read raw file to buffer // + /////////////////////////////// + FILE *f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + char *buffer = malloc(fsize + 1); + fread(buffer, fsize, 1, f); + fclose(f); + buffer[fsize] = 0; + + /////////////////// + // Fill struct // + /////////////////// + if(fsize<(mbx+1)*mby*4) + { + printf("File to short!\n"); + return 0; + } + INLINE_MOTION_VECTOR *imv; + imv = malloc((mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR)); + memcpy ( &imv[0], &buffer[0], (mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR) ); + + ////////////////////////// + // Export to txt data // + ////////////////////////// + FILE *out = fopen(argv[4], "w"); + fprintf(out,"#x y u v sad\n"); + int i,j; + for(j=0;j

    + *
  • The port shall be populated with a pool of buffers, allocated as required, according + * to the buffer_num and buffer_size values. + *
  • The input port to which it is connected shall be set to the same buffer + * configuration and then be enabled. Should that fail, the original port shall be + * disabled. + *
+ * + * @param port port to enable + * @param cb callback use by the port to send a \ref MMAL_BUFFER_HEADER_T back + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb); + +/** Disable processing on a port + * + * Disabling a port will stop all processing on this port and return all (non-processed) + * buffer headers to the client. + * + * If this is a connected output port, the input port to which it is connected shall + * also be disabled. Any buffer pool shall be released. + * + * @param port port to disable + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_disable(MMAL_PORT_T *port); + +/** Ask a port to release all the buffer headers it currently has. + * + * Flushing a port will ask the port to send all the buffer headers it currently has + * to the client. Flushing is an asynchronous request and the flush call will + * return before all the buffer headers are returned to the client. + * It is up to the client to keep a count on the buffer headers to know when the + * flush operation has completed. + * It is also important to note that flushing will also reset the state of the port + * and any processing which was buffered by the port will be lost. + * + * \attention Flushing a connected port behaviour TBD. + * + * @param port The port to flush. + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_flush(MMAL_PORT_T *port); + +/** Set a parameter on a port. + * + * @param port The port to which the request is sent. + * @param param The pointer to the header of the parameter to set. + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_parameter_set(MMAL_PORT_T *port, + const MMAL_PARAMETER_HEADER_T *param); + +/** Get a parameter from a port. + * The size field must be set on input to the maximum size of the parameter + * (including the header) and will be set on output to the actual size of the + * parameter retrieved. + * + * \note If MMAL_ENOSPC is returned, the parameter is larger than the size + * given. The given parameter will have been filled up to its size and then + * the size field set to the full parameter's size. This can be used to + * resize the parameter buffer so that a second call should succeed. + * + * @param port The port to which the request is sent. + * @param param The pointer to the header of the parameter to get. + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_parameter_get(MMAL_PORT_T *port, + MMAL_PARAMETER_HEADER_T *param); + +/** Send a buffer header to a port. + * + * @param port The port to which the buffer header is to be sent. + * @param buffer The buffer header to send. + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_send_buffer(MMAL_PORT_T *port, + MMAL_BUFFER_HEADER_T *buffer); + +/** Connect an output port to an input port. + * + * When connected and enabled, buffers will automatically progress from the + * output port to the input port when they become available, and released back + * to the output port when no longer required by the input port. + * + * Ports can be given either way around, but one must be an output port and + * the other must be an input port. Neither can be connected or enabled + * already. The format of the output port will be applied to the input port + * on connection. + * + * @param port One of the ports to connect. + * @param other_port The other port to connect. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_port_connect(MMAL_PORT_T *port, MMAL_PORT_T *other_port); + +/** Disconnect a connected port. + * + * If the port is not connected, an error will be returned. Otherwise, if the + * ports are enabled, they will be disabled and any buffer pool created will be + * freed. + * + * @param port The ports to disconnect. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_port_disconnect(MMAL_PORT_T *port); + +/** Allocate a payload buffer. + * This allows a client to allocate memory for a payload buffer based on the preferences + * of a port. This for instance will allow the port to allocate memory which can be shared + * between the host processor and videocore. + * + * See \ref mmal_pool_create_with_allocator(). + * + * @param port Port responsible for allocating the memory. + * @param payload_size Size of the payload buffer which will be allocated. + * + * @return Pointer to the allocated memory. + */ +uint8_t *mmal_port_payload_alloc(MMAL_PORT_T *port, uint32_t payload_size); + +/** Free a payload buffer. + * This allows a client to free memory allocated by a previous call to \ref mmal_port_payload_alloc. + * + * See \ref mmal_pool_create_with_allocator(). + * + * @param port Port responsible for allocating the memory. + * @param payload Pointer to the memory to free. + */ +void mmal_port_payload_free(MMAL_PORT_T *port, uint8_t *payload); + +/** Get an empty event buffer header from a port + * + * @param port The port from which to get the event buffer header. + * @param buffer The address of a buffer header pointer, which will be set on return. + * @param event The specific event FourCC required. See the \ref MmalEvents "pre-defined events". + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_port_event_get(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, uint32_t event); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MMAL_PORT_H */ diff --git a/interface/mmal/mmal_queue.h b/interface/mmal/mmal_queue.h new file mode 100755 index 0000000..ce53a26 --- /dev/null +++ b/interface/mmal/mmal_queue.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_QUEUE_H +#define MMAL_QUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup MmalQueue Queues of buffer headers + * This provides a thread-safe implementation of a queue of buffer headers + * (\ref MMAL_BUFFER_HEADER_T). The queue works in a first-in, first-out basis + * so the buffer headers will be dequeued in the order they have been queued. */ +/* @{ */ + +#include "mmal_buffer.h" + +typedef struct MMAL_QUEUE_T MMAL_QUEUE_T; + +/** Create a queue of MMAL_BUFFER_HEADER_T + * + * @return Pointer to the newly created queue or NULL on failure. + */ +MMAL_QUEUE_T *mmal_queue_create(void); + +/** Put a MMAL_BUFFER_HEADER_T into a queue + * + * @param queue Pointer to a queue + * @param buffer Pointer to the MMAL_BUFFER_HEADER_T to add to the queue + */ +void mmal_queue_put(MMAL_QUEUE_T *queue, MMAL_BUFFER_HEADER_T *buffer); + +/** Put a MMAL_BUFFER_HEADER_T back at the start of a queue. + * This is used when a buffer header was removed from the queue but not + * fully processed and needs to be put back where it was originally taken. + * + * @param queue Pointer to a queue + * @param buffer Pointer to the MMAL_BUFFER_HEADER_T to add to the queue + */ +void mmal_queue_put_back(MMAL_QUEUE_T *queue, MMAL_BUFFER_HEADER_T *buffer); + +/** Get a MMAL_BUFFER_HEADER_T from a queue + * + * @param queue Pointer to a queue + * + * @return pointer to the next MMAL_BUFFER_HEADER_T or NULL if the queue is empty. + */ +MMAL_BUFFER_HEADER_T *mmal_queue_get(MMAL_QUEUE_T *queue); + +/** Wait for a MMAL_BUFFER_HEADER_T from a queue. + * This is the same as a get except that this will block until a buffer header is + * available. + * + * @param queue Pointer to a queue + * + * @return pointer to the next MMAL_BUFFER_HEADER_T. + */ +MMAL_BUFFER_HEADER_T *mmal_queue_wait(MMAL_QUEUE_T *queue); + +/** Wait for a MMAL_BUFFER_HEADER_T from a queue, up to a given timeout. + * This is the same as a wait, except that it will abort in case of timeout. + * + * @param queue Pointer to a queue + * @param timeout Number of milliseconds to wait before + * returning if the semaphore can't be acquired. + * + * @return pointer to the next MMAL_BUFFER_HEADER_T. + */ +MMAL_BUFFER_HEADER_T *mmal_queue_timedwait(MMAL_QUEUE_T *queue, VCOS_UNSIGNED timeout); + +/** Get the number of MMAL_BUFFER_HEADER_T currently in a queue. + * + * @param queue Pointer to a queue + * + * @return length (in elements) of the queue. + */ +unsigned int mmal_queue_length(MMAL_QUEUE_T *queue); + +/** Destroy a queue of MMAL_BUFFER_HEADER_T. + * + * @param queue Pointer to a queue + */ +void mmal_queue_destroy(MMAL_QUEUE_T *queue); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MMAL_QUEUE_H */ diff --git a/interface/mmal/mmal_types.h b/interface/mmal/mmal_types.h new file mode 100755 index 0000000..741f742 --- /dev/null +++ b/interface/mmal/mmal_types.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_TYPES_H +#define MMAL_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup MmalTypes Common types + * Definition for common types */ +/* @{ */ + +#include "mmal_common.h" + +/** Status return codes from the API. + * + * \internal Please try to keep this similar to the standard POSIX codes + * rather than making up new ones! + */ +typedef enum +{ + MMAL_SUCCESS = 0, /**< Success */ + MMAL_ENOMEM, /**< Out of memory */ + MMAL_ENOSPC, /**< Out of resources (other than memory) */ + MMAL_EINVAL, /**< Argument is invalid */ + MMAL_ENOSYS, /**< Function not implemented */ + MMAL_ENOENT, /**< No such file or directory */ + MMAL_ENXIO, /**< No such device or address */ + MMAL_EIO, /**< I/O error */ + MMAL_ESPIPE, /**< Illegal seek */ + MMAL_ECORRUPT, /**< Data is corrupt \attention FIXME: not POSIX */ + MMAL_ENOTREADY, /**< Component is not ready \attention FIXME: not POSIX */ + MMAL_ECONFIG, /**< Component is not configured \attention FIXME: not POSIX */ + MMAL_EISCONN, /**< Port is already connected */ + MMAL_ENOTCONN, /**< Port is disconnected */ + MMAL_EAGAIN, /**< Resource temporarily unavailable. Try again later*/ + MMAL_EFAULT, /**< Bad address */ + /* Do not add new codes here unless they match something from POSIX */ + MMAL_STATUS_MAX = 0x7FFFFFFF /**< Force to 32 bit */ +} MMAL_STATUS_T; + +/** Describes a rectangle */ +typedef struct +{ + int32_t x; /**< x coordinate (from left) */ + int32_t y; /**< y coordinate (from top) */ + int32_t width; /**< width */ + int32_t height; /**< height */ +} MMAL_RECT_T; + +/** Describes a rational number */ +typedef struct +{ + int32_t num; /**< Numerator */ + int32_t den; /**< Denominator */ +} MMAL_RATIONAL_T; + +/** \name Special Unknown Time Value + * Timestamps in MMAL are defined as signed 64 bits integer values representing microseconds. + * However a pre-defined special value is used to signal that a timestamp is not known. */ +/* @{ */ +#define MMAL_TIME_UNKNOWN (INT64_C(1)<<63) /**< Special value signalling that time is not known */ +/* @} */ + +/** Four Character Code type */ +typedef uint32_t MMAL_FOURCC_T; + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MMAL_TYPES_H */ diff --git a/interface/mmal/openmaxil/CMakeLists.txt b/interface/mmal/openmaxil/CMakeLists.txt new file mode 100755 index 0000000..68d9832 --- /dev/null +++ b/interface/mmal/openmaxil/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library (mmal_omx ${LIBRARY_TYPE} + mmalomx_core.c + mmalomx_logging.c + mmalomx_commands.c + mmalomx_buffer.c + mmalomx_marks.c + mmalomx_roles.c + mmalomx_parameters.c + mmalomx_registry.c +) + +add_library (mmal_omxutil ${LIBRARY_TYPE} + mmalomx_util_params.c + mmalomx_util_params_audio.c + mmalomx_util_params_video.c + mmalomx_util_params_camera.c + mmalomx_util_params_misc.c +) + +target_link_libraries (mmal_omx mmal_omxutil mmal_core mmal_util vcos) diff --git a/interface/mmal/openmaxil/mmalomx.h b/interface/mmal/openmaxil/mmalomx.h new file mode 100755 index 0000000..d58971a --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx.h @@ -0,0 +1,130 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL + */ + +#include "interface/vmcs_host/khronos/IL/OMX_Core.h" +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/khronos/IL/OMX_Video.h" +#include "interface/vmcs_host/khronos/IL/OMX_Audio.h" +#include +#include + +/* Define this to 1 if you want to log all buffer transfers */ +#define ENABLE_MMAL_EXTRA_LOGGING 0 + +#ifndef MMALOMX_EXPORT +# define MMALOMX_EXPORT(a) a +#endif +#ifndef MMALOMX_IMPORT +# define MMALOMX_IMPORT(a) a +#endif + +#define MAX_MARKS_NUM 2 +#define MAX_ENCODINGS_NUM 20 + +/** Per-port context data */ +typedef struct MMALOMX_PORT_T +{ + struct MMALOMX_COMPONENT_T *component; + MMAL_PORT_T *mmal; + OMX_DIRTYPE direction; + unsigned int index; + unsigned int buffers; + unsigned int buffers_in_transit; + + MMAL_BOOL_T buffers_allocated:1; + MMAL_BOOL_T enabled:1; + MMAL_BOOL_T populated:1; + MMAL_BOOL_T zero_copy:1; + MMAL_BOOL_T no_cropping:1; + MMAL_BOOL_T format_changed:1; + MMAL_POOL_T *pool; + + uint32_t actions; + + OMX_MARKTYPE marks[MAX_MARKS_NUM]; + unsigned int marks_first:8; + unsigned int marks_num:8; + + OMX_FORMAT_PARAM_TYPE format_param; + + MMAL_PARAMETER_HEADER_T encodings_header; + MMAL_FOURCC_T encodings[MAX_ENCODINGS_NUM]; + unsigned int encodings_num; + +} MMALOMX_PORT_T; + +/** Component context data */ +typedef struct MMALOMX_COMPONENT_T { + OMX_COMPONENTTYPE omx; /**< OMX component type structure */ + + unsigned int registry_id; + const char *name; + uint32_t role; + OMX_CALLBACKTYPE callbacks; + OMX_PTR callbacks_data; + + struct MMAL_COMPONENT_T *mmal; + OMX_STATETYPE state; + unsigned int state_transition; + + MMALOMX_PORT_T *ports; + unsigned int ports_num; + unsigned int ports_domain_num[4]; + + MMAL_BOOL_T actions_running; + + OMX_U32 group_id; + OMX_U32 group_priority; + + /* Support for command queues */ + MMAL_POOL_T *cmd_pool; + MMAL_QUEUE_T *cmd_queue; + VCOS_THREAD_T cmd_thread; + MMAL_BOOL_T cmd_thread_used; + VCOS_SEMAPHORE_T cmd_sema; + + VCOS_MUTEX_T lock; /**< Used to protect component state */ + VCOS_MUTEX_T lock_port; /**< Used to protect port state */ + +} MMALOMX_COMPONENT_T; + +OMX_ERRORTYPE mmalomx_callback_event_handler( + MMALOMX_COMPONENT_T *component, + OMX_EVENTTYPE eEvent, + OMX_U32 nData1, + OMX_U32 nData2, + OMX_PTR pEventData); + +#define MMALOMX_LOCK(a) vcos_mutex_lock(&a->lock) +#define MMALOMX_UNLOCK(a) vcos_mutex_unlock(&a->lock) +#define MMALOMX_LOCK_PORT(a,b) vcos_mutex_lock(&a->lock_port) +#define MMALOMX_UNLOCK_PORT(a,b) vcos_mutex_unlock(&a->lock_port) + diff --git a/interface/mmal/openmaxil/mmalomx_buffer.c b/interface/mmal/openmaxil/mmalomx_buffer.c new file mode 100755 index 0000000..ef78baa --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_buffer.c @@ -0,0 +1,235 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_buffer.h" +#include "mmalomx_commands.h" +#include "mmalomx_marks.h" +#include "mmalomx_logging.h" + +#include + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_buffer_send( + MMALOMX_COMPONENT_T *component, + OMX_BUFFERHEADERTYPE *omx_buffer, + OMX_DIRTYPE direction) +{ + OMX_ERRORTYPE status = OMX_ErrorNone; + MMAL_BUFFER_HEADER_T *mmal_buffer; + MMAL_STATUS_T mmal_status; + MMALOMX_PORT_T *port; + unsigned int index; + + /* Sanity checks */ + if (!component) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + if (!omx_buffer || omx_buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE) || + omx_buffer->nOffset + omx_buffer->nFilledLen > omx_buffer->nAllocLen) + return OMX_ErrorBadParameter; + + index = direction == OMX_DirInput ? omx_buffer->nInputPortIndex : omx_buffer->nOutputPortIndex; + if (index >= component->ports_num) + return OMX_ErrorBadPortIndex; + + port = &component->ports[index]; + if (port->direction != direction) + return OMX_ErrorBadPortIndex; + + MMALOMX_LOCK_PORT(component, port); + + if (component->state != OMX_StatePause && component->state != OMX_StateExecuting) + status = OMX_ErrorIncorrectStateOperation; + if (!port->enabled /* FIXME: || flushing || pending idle */) + status = OMX_ErrorIncorrectStateOperation; + if (status != OMX_ErrorNone) + goto error; + + mmal_buffer = mmal_queue_get( port->pool->queue ); + if (!vcos_verify(mmal_buffer)) /* Should never happen */ + { + status = OMX_ErrorUndefined; + goto error; + } + + mmalomx_mark_process_incoming(component, port, omx_buffer); + + mmal_buffer->user_data = (void *)omx_buffer; + mmalil_buffer_header_to_mmal(mmal_buffer, omx_buffer); + + mmal_status = mmal_port_send_buffer(port->mmal, mmal_buffer); + if (!vcos_verify(mmal_status == MMAL_SUCCESS)) + { + LOG_ERROR("failed to send buffer on %s", port->mmal->name); + mmal_queue_put_back( port->pool->queue, mmal_buffer ); + status = mmalil_error_to_omx(mmal_status); + } + else + { + port->buffers_in_transit++; + } + +error: + MMALOMX_UNLOCK_PORT(component, port); + return status; +} + +/*****************************************************************************/ +static void mmalomx_buffer_event( + MMALOMX_PORT_T *port, + MMAL_BUFFER_HEADER_T *mmal_buffer) +{ + MMALOMX_COMPONENT_T *component = port->component; + MMAL_EVENT_FORMAT_CHANGED_T *event; + + LOG_TRACE("hComponent %p, port %i, event %4.4s", component, port->index, + (char *)&mmal_buffer->cmd); + + if (mmal_buffer->cmd == MMAL_EVENT_ERROR ) + { + mmalomx_callback_event_handler(component, OMX_EventError, + mmalil_error_to_omx(*(MMAL_STATUS_T *)mmal_buffer->data), 0, NULL); + return; + } + + event = mmal_event_format_changed_get(mmal_buffer); + if (event && port->mmal->type == MMAL_PORT_TYPE_OUTPUT && + port->mmal->format->type == MMAL_ES_TYPE_VIDEO) + { + uint32_t diff = mmal_format_compare(event->format, port->mmal->format); + MMAL_ES_FORMAT_T *format = port->mmal->format; + MMAL_VIDEO_FORMAT_T video = format->es->video; + + /* Update the port settings with the new values */ + mmal_format_copy(format, event->format); + port->mmal->buffer_num_min = event->buffer_num_min; + port->mmal->buffer_size_min = event->buffer_size_min; + port->format_changed = MMAL_TRUE; + + if ((diff & MMAL_ES_FORMAT_COMPARE_FLAG_VIDEO_ASPECT_RATIO) && + /* Do not report a change if going from unspecified to 1:1 */ + !(format->es->video.par.num == format->es->video.par.den && !video.par.num)) + { + LOG_DEBUG("aspect ratio change %ix%i->%ix%i", (int)video.par.num, (int)video.par.den, + (int)format->es->video.par.num, (int)format->es->video.par.den); + mmalomx_callback_event_handler(component, OMX_EventPortSettingsChanged, + port->index, OMX_IndexParamBrcmPixelAspectRatio, NULL); + } + + if (diff & (MMAL_ES_FORMAT_COMPARE_FLAG_ENCODING| + MMAL_ES_FORMAT_COMPARE_FLAG_VIDEO_RESOLUTION| + MMAL_ES_FORMAT_COMPARE_FLAG_VIDEO_CROPPING)) + { + LOG_DEBUG("format change %ix%i(%ix%i) -> %ix%i(%ix%i)", + (int)video.width, (int)video.height, + (int)video.crop.width, (int)video.crop.height, + (int)format->es->video.width, (int)format->es->video.height, + (int)format->es->video.crop.width, (int)format->es->video.crop.height); + mmalomx_callback_event_handler(component, OMX_EventPortSettingsChanged, + port->index, 0, NULL); + } + } + else if (event && port->mmal->type == MMAL_PORT_TYPE_OUTPUT && + port->mmal->format->type == MMAL_ES_TYPE_AUDIO) + { + uint32_t diff = mmal_format_compare(event->format, port->mmal->format); + MMAL_ES_FORMAT_T *format = port->mmal->format; + MMAL_AUDIO_FORMAT_T audio = format->es->audio; + + /* Update the port settings with the new values */ + mmal_format_copy(format, event->format); + port->mmal->buffer_num_min = event->buffer_num_min; + port->mmal->buffer_size_min = event->buffer_size_min; + port->format_changed = MMAL_TRUE; + + if (diff) + { + LOG_DEBUG("format change %ich, %iHz, %ibps -> %ich, %iHz, %ibps", + (int)audio.channels, (int)audio.sample_rate, + (int)audio.bits_per_sample, + (int)format->es->audio.channels, + (int)format->es->audio.sample_rate, + (int)format->es->audio.bits_per_sample); + mmalomx_callback_event_handler(component, OMX_EventPortSettingsChanged, + port->index, 0, NULL); + } + } +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_buffer_return( + MMALOMX_PORT_T *port, + MMAL_BUFFER_HEADER_T *mmal_buffer) +{ + MMALOMX_COMPONENT_T *component = port->component; + OMX_BUFFERHEADERTYPE *omx_buffer = (OMX_BUFFERHEADERTYPE *)mmal_buffer->user_data; + MMAL_BOOL_T signal; + + if (mmal_buffer->cmd) + { + mmalomx_buffer_event(port, mmal_buffer); + mmal_buffer_header_release(mmal_buffer); + return OMX_ErrorNone; + } + + if (ENABLE_MMAL_EXTRA_LOGGING) + LOG_TRACE("hComponent %p, port %i, pBuffer %p", component, + port->index, omx_buffer); + + vcos_assert(omx_buffer->pBuffer == mmal_buffer->data); + mmalil_buffer_header_to_omx(omx_buffer, mmal_buffer); + mmal_buffer_header_release(mmal_buffer); + + if ((omx_buffer->nFlags & OMX_BUFFERFLAG_EOS) && port->direction == OMX_DirOutput) + { + mmalomx_callback_event_handler(component, OMX_EventBufferFlag, + port->index, omx_buffer->nFlags, NULL); + } + + mmalomx_mark_process_outgoing(component, port, omx_buffer); + + if (port->direction == OMX_DirInput) + component->callbacks.EmptyBufferDone((OMX_HANDLETYPE)&component->omx, + component->callbacks_data, omx_buffer ); + else + component->callbacks.FillBufferDone((OMX_HANDLETYPE)&component->omx, + component->callbacks_data, omx_buffer ); + + MMALOMX_LOCK_PORT(component, port); + signal = port->actions & MMALOMX_ACTION_CHECK_FLUSHED; + port->buffers_in_transit--; + MMALOMX_UNLOCK_PORT(component, port); + + if (signal) + mmalomx_commands_actions_signal(component); + + return OMX_ErrorNone; +} + diff --git a/interface/mmal/openmaxil/mmalomx_buffer.h b/interface/mmal/openmaxil/mmalomx_buffer.h new file mode 100755 index 0000000..425f7ef --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_buffer.h @@ -0,0 +1,39 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Buffer related functions + */ + +OMX_ERRORTYPE mmalomx_buffer_send( + MMALOMX_COMPONENT_T *component, + OMX_BUFFERHEADERTYPE *omx_buffer, + OMX_DIRTYPE direction); + +OMX_ERRORTYPE mmalomx_buffer_return( + MMALOMX_PORT_T *port, + MMAL_BUFFER_HEADER_T *mmal_buffer); diff --git a/interface/mmal/openmaxil/mmalomx_commands.c b/interface/mmal/openmaxil/mmalomx_commands.c new file mode 100755 index 0000000..dd395d4 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_commands.c @@ -0,0 +1,462 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_commands.h" +#include "mmalomx_buffer.h" +#include "mmalomx_logging.h" + +typedef struct { + OMX_STATETYPE state; + OMX_STATETYPE request; + uint32_t actions; +} MMALOMX_STATE_TRANSITION_T; + +MMALOMX_STATE_TRANSITION_T state_transition_table[] = +{ + {OMX_StateInvalid, OMX_StateInvalid, 0}, + {OMX_StateLoaded, OMX_StateIdle, MMALOMX_ACTION_CHECK_ALLOCATED|MMALOMX_ACTION_ENABLE}, + {OMX_StateLoaded, OMX_StateWaitForResources, 0}, + {OMX_StateWaitForResources, OMX_StateLoaded, 0}, + {OMX_StateWaitForResources, OMX_StateIdle, MMALOMX_ACTION_CHECK_ALLOCATED|MMALOMX_ACTION_ENABLE}, + {OMX_StateIdle, OMX_StateLoaded, MMALOMX_ACTION_CHECK_DEALLOCATED|MMALOMX_ACTION_DISABLE}, + {OMX_StateIdle, OMX_StateExecuting, 0}, + {OMX_StateIdle, OMX_StatePause, 0}, + {OMX_StateExecuting, OMX_StateIdle, MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED}, + {OMX_StateExecuting, OMX_StatePause, 0}, + {OMX_StatePause, OMX_StateIdle, 0}, + {OMX_StatePause, OMX_StateExecuting, 0}, + {OMX_StateMax, OMX_StateMax, 0} +}; + +/*****************************************************************************/ +static unsigned int mmalomx_state_transition_get(OMX_STATETYPE state, OMX_STATETYPE request) +{ + unsigned int i; + + for (i = 0; state_transition_table[i].state != OMX_StateMax; i++) + if (state_transition_table[i].state == state && + state_transition_table[i].request == request) + break; + + return state_transition_table[i].state != OMX_StateMax ? i : 0; +} + +/*****************************************************************************/ +static void mmalomx_buffer_cb_io(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + mmalomx_buffer_return((MMALOMX_PORT_T *)port->userdata, buffer); +} + +/*****************************************************************************/ +static void mmalomx_commands_check_port_actions(MMALOMX_COMPONENT_T *component, + MMALOMX_PORT_T *port) +{ + uint32_t exec_actions = 0; + MMAL_STATUS_T status; + + MMALOMX_LOCK_PORT(component, port); + if (!port->actions) + { + MMALOMX_UNLOCK_PORT(component, port); + return; + } + + if (port->actions & MMALOMX_ACTION_FLUSH) + { + port->actions &= ~MMALOMX_ACTION_FLUSH; + port->actions |= MMALOMX_ACTION_PENDING_FLUSH; + exec_actions |= MMALOMX_ACTION_PENDING_FLUSH; + } + if ((port->actions & MMALOMX_ACTION_DISABLE) && + (!port->buffers_in_transit || + !(port->actions & MMALOMX_ACTION_CHECK_FLUSHED))) + { + port->actions &= ~MMALOMX_ACTION_DISABLE; + port->actions |= MMALOMX_ACTION_PENDING_DISABLE; + exec_actions |= MMALOMX_ACTION_PENDING_DISABLE; + } + if ((port->actions & MMALOMX_ACTION_ENABLE) && + port->buffers) + { + /* We defer enabling the mmal port until the first buffer allocation + * has been done. Only at that point do we know for sure whether we + * are going to use shared memory or not. + * We might want to delay it to just before sending the event to the client ??? + */ + port->actions &= ~MMALOMX_ACTION_ENABLE; + port->actions |= MMALOMX_ACTION_PENDING_ENABLE; + exec_actions |= MMALOMX_ACTION_PENDING_ENABLE; + } + MMALOMX_UNLOCK_PORT(component, port); + + if (exec_actions & MMALOMX_ACTION_PENDING_FLUSH) + mmal_port_flush(port->mmal); + + if (exec_actions & MMALOMX_ACTION_PENDING_DISABLE) + { + mmal_port_disable(port->mmal); + + /* If there was a port format changed event, we need to make sure + * the new format has been committed */ + if (port->format_changed) + { + status = mmal_port_format_commit(port->mmal); + if (status != MMAL_SUCCESS) + LOG_WARN("could not commit new format (%i)", status); + port->format_changed = MMAL_FALSE; + } + } + + if (exec_actions & MMALOMX_ACTION_PENDING_ENABLE) + { + status = mmal_port_enable(port->mmal, mmalomx_buffer_cb_io); + if (status == MMAL_SUCCESS) + status = mmal_pool_resize(port->pool, port->mmal->buffer_num, 0); + if (status != MMAL_SUCCESS) + mmalomx_callback_event_handler(component, OMX_EventError, mmalil_error_to_omx(status), 0, NULL); + /* FIXME: we're still going to generate a cmd complete. Not sure if that's an issue. */ + } + + MMALOMX_LOCK_PORT(component, port); + + port->actions &= ~exec_actions; + if ((port->actions & MMALOMX_ACTION_CHECK_ALLOCATED) && port->populated) + port->actions &= ~MMALOMX_ACTION_CHECK_ALLOCATED; + if ((port->actions & MMALOMX_ACTION_CHECK_DEALLOCATED) && !port->buffers) + port->actions &= ~MMALOMX_ACTION_CHECK_DEALLOCATED; + if ((port->actions & MMALOMX_ACTION_CHECK_FLUSHED) && !port->buffers_in_transit) + port->actions &= ~MMALOMX_ACTION_CHECK_FLUSHED; + exec_actions = port->actions; + + if (port->actions == MMALOMX_ACTION_NOTIFY_FLUSH || + port->actions == MMALOMX_ACTION_NOTIFY_ENABLE || + port->actions == MMALOMX_ACTION_NOTIFY_DISABLE) + port->actions = 0; /* We're done */ + + MMALOMX_UNLOCK_PORT(component, port); + + if (exec_actions == MMALOMX_ACTION_NOTIFY_FLUSH) + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, + OMX_CommandFlush, port->index, NULL); + else if (exec_actions == MMALOMX_ACTION_NOTIFY_ENABLE) + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, + OMX_CommandPortEnable, port->index, NULL); + else if (exec_actions == MMALOMX_ACTION_NOTIFY_DISABLE) + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, + OMX_CommandPortDisable, port->index, NULL); +} + +/*****************************************************************************/ +void mmalomx_commands_actions_check(MMALOMX_COMPONENT_T *component) +{ + uint32_t actions_left = 0; + unsigned int i; + + for (i = 0; i < component->ports_num; i++) + mmalomx_commands_check_port_actions(component, &component->ports[i]); + + MMALOMX_LOCK(component); + for (i = 0; i < component->ports_num; i++) + actions_left |= component->ports[i].actions; + + if (!actions_left && component->state_transition) + { + component->state = state_transition_table[component->state_transition].request; + component->state_transition = 0; + actions_left = MMALOMX_ACTION_NOTIFY_STATE; + } + MMALOMX_UNLOCK(component); + + if (actions_left == MMALOMX_ACTION_NOTIFY_STATE) + { + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, + OMX_CommandStateSet, component->state, NULL); + actions_left = 0; + } + + /* If we're not currently processing a command, we can start processing + * the next one. */ + if (!actions_left) + mmalomx_commands_actions_next(component); +} + +/*****************************************************************************/ +void mmalomx_commands_actions_signal(MMALOMX_COMPONENT_T *component) +{ + if (component->cmd_thread_used) + vcos_semaphore_post(&component->cmd_sema); + else + mmalomx_commands_actions_check(component); +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_state_set( + OMX_HANDLETYPE hComponent, + OMX_STATETYPE state) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + unsigned int i, transition; + + if (component->state == state) + { + mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorSameState, 0, NULL); + return OMX_ErrorNone; + } + + /* We're asked to transition to StateInvalid */ + if (state == OMX_StateInvalid) + { + component->state = state; + mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorInvalidState, 0, NULL); + return OMX_ErrorNone; + } + + /* Commands are being queued so we should never get into that state */ + vcos_assert(!component->state_transition); + + /* Check the transition is valid */ + transition = mmalomx_state_transition_get(component->state, state); + if (!transition) + { + mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorIncorrectStateTransition, 0, NULL); + return OMX_ErrorNone; + } + + /* Special case for transition in and out of Executing */ + if (state == OMX_StateExecuting || component->state == OMX_StateExecuting) + { + MMAL_STATUS_T status; + + if (state == OMX_StateExecuting) + status = mmal_component_enable(component->mmal); + else + status = mmal_component_disable(component->mmal); + + if (status != MMAL_SUCCESS) + { + LOG_ERROR("could not %s %s", state == OMX_StateExecuting ? "enable" : "disable", component->name); + mmalomx_callback_event_handler(component, OMX_EventError, mmalil_error_to_omx(status), 0, NULL); + return OMX_ErrorNone; + } + } + + MMALOMX_LOCK(component); + component->state_transition = transition; + + for (i = 0; i < component->ports_num; i++) + { + if (!component->ports[i].enabled) + continue; + + MMALOMX_LOCK_PORT(component, component->ports + i); + component->ports[i].actions = state_transition_table[transition].actions; + + /* If we're transitionning from Idle to Loaded we'd rather do a flush first + * to avoid the cmd thread to block for too long (mmal_disable is a + * blocking call). */ + if (state_transition_table[transition].state == OMX_StateIdle && + state_transition_table[transition].request == OMX_StateLoaded && + component->cmd_thread_used) + component->ports[i].actions |= MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED; + MMALOMX_UNLOCK_PORT(component, component->ports + i); + } + MMALOMX_UNLOCK(component); + + mmalomx_commands_actions_check(component); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_port_mark( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex, + OMX_PTR *pCmdData) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + OMX_MARKTYPE *mark = (OMX_MARKTYPE *)pCmdData; + MMALOMX_PORT_T *port; + + if (nPortIndex >= component->ports_num) + return OMX_ErrorBadPortIndex; + port = &component->ports[nPortIndex]; + + if (port->marks_num == MAX_MARKS_NUM) + return OMX_ErrorInsufficientResources; + + port->marks[(port->marks_first + port->marks_num) % MAX_MARKS_NUM] = *mark; + port->marks_num++; + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_port_flush( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + + MMALOMX_LOCK_PORT(component, &component->ports[nPortIndex]); + component->ports[nPortIndex].actions = + MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED|MMALOMX_ACTION_NOTIFY_FLUSH; + MMALOMX_UNLOCK_PORT(component, &component->ports[nPortIndex]); + + mmalomx_commands_actions_check(component); + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_port_enable( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + component->ports[nPortIndex].enabled = MMAL_TRUE; + + if (component->state == OMX_StateLoaded || + component->state == OMX_StateWaitForResources) + { + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, OMX_CommandPortEnable, nPortIndex, NULL); + return OMX_ErrorNone; + } + + MMALOMX_LOCK_PORT(component, &component->ports[nPortIndex]); + component->ports[nPortIndex].actions = + MMALOMX_ACTION_CHECK_ALLOCATED|MMALOMX_ACTION_ENABLE|MMALOMX_ACTION_NOTIFY_ENABLE; + MMALOMX_UNLOCK_PORT(component, &component->ports[nPortIndex]); + + mmalomx_commands_actions_check(component); + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_port_disable( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + component->ports[nPortIndex].enabled = MMAL_FALSE; + + if (component->state == OMX_StateLoaded || + component->state == OMX_StateWaitForResources) + { + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, OMX_CommandPortDisable, nPortIndex, NULL); + return OMX_ErrorNone; + } + + MMALOMX_LOCK_PORT(component, &component->ports[nPortIndex]); + component->ports[nPortIndex].actions = + MMALOMX_ACTION_DISABLE|MMALOMX_ACTION_CHECK_DEALLOCATED|MMALOMX_ACTION_NOTIFY_DISABLE; + if (component->cmd_thread_used) + component->ports[nPortIndex].actions |= + MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED; + MMALOMX_UNLOCK_PORT(component, &component->ports[nPortIndex]); + + mmalomx_commands_actions_check(component); + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_queue( + MMALOMX_COMPONENT_T *component, + OMX_U32 arg1, OMX_U32 arg2) +{ + MMAL_BUFFER_HEADER_T *cmd = mmal_queue_get(component->cmd_pool->queue); + + if (!vcos_verify(cmd)) + { + LOG_ERROR("command queue too small"); + return OMX_ErrorInsufficientResources; + } + + cmd->cmd = arg1; + cmd->offset = arg2; + mmal_queue_put(component->cmd_queue, cmd); + + mmalomx_commands_actions_signal(component); + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_command_dequeue( + MMALOMX_COMPONENT_T *component, + OMX_U32 *arg1, OMX_U32 *arg2) +{ + MMAL_BUFFER_HEADER_T *cmd = mmal_queue_get(component->cmd_queue); + if (!cmd) + return OMX_ErrorNoMore; + + *arg1 = cmd->cmd; + *arg2 = cmd->offset; + mmal_buffer_header_release(cmd); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +void mmalomx_commands_actions_next(MMALOMX_COMPONENT_T *component) +{ + OMX_ERRORTYPE status = OMX_ErrorNone; + OMX_COMMANDTYPE cmd; + OMX_U32 arg1, arg2, nParam1; + unsigned int i; + + status = mmalomx_command_dequeue(component, &arg1, &arg2); + if (status != OMX_ErrorNone) + return; + + cmd = (OMX_COMMANDTYPE)arg1; + nParam1 = arg2; + + if (cmd == OMX_CommandStateSet) + { + mmalomx_command_state_set((OMX_HANDLETYPE)&component->omx, nParam1); + } + else if (cmd == OMX_CommandFlush) + { + for (i = 0; i < component->ports_num; i++) + if (i == nParam1 || nParam1 == OMX_ALL) + mmalomx_command_port_flush((OMX_HANDLETYPE)&component->omx, i); + } + else if (cmd == OMX_CommandPortEnable) + { + for (i = 0; i < component->ports_num; i++) + if (i == nParam1 || nParam1 == OMX_ALL) + mmalomx_command_port_enable((OMX_HANDLETYPE)&component->omx, i); + } + else if (cmd == OMX_CommandPortDisable) + { + for (i = 0; i < component->ports_num; i++) + if (i == nParam1 || nParam1 == OMX_ALL) + mmalomx_command_port_disable((OMX_HANDLETYPE)&component->omx, i); + } +} + diff --git a/interface/mmal/openmaxil/mmalomx_commands.h b/interface/mmal/openmaxil/mmalomx_commands.h new file mode 100755 index 0000000..976b598 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_commands.h @@ -0,0 +1,85 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Commands related functions + */ + +OMX_ERRORTYPE mmalomx_command_state_set( + OMX_HANDLETYPE hComponent, + OMX_STATETYPE state); + +OMX_ERRORTYPE mmalomx_command_port_mark( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex, + OMX_PTR *pCmdData); + +OMX_ERRORTYPE mmalomx_command_port_flush( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex); + +OMX_ERRORTYPE mmalomx_command_port_enable( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex); + +OMX_ERRORTYPE mmalomx_command_port_disable( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex); + +#define MMALOMX_ACTION_ENABLE 0x01 +#define MMALOMX_ACTION_DISABLE 0x02 +#define MMALOMX_ACTION_FLUSH 0x04 + +#define MMALOMX_ACTION_PENDING_ENABLE 0x010 +#define MMALOMX_ACTION_PENDING_DISABLE 0x020 +#define MMALOMX_ACTION_PENDING_FLUSH 0x040 + +#define MMALOMX_ACTION_CHECK_ALLOCATED 0x0100 +#define MMALOMX_ACTION_CHECK_DEALLOCATED 0x0200 +#define MMALOMX_ACTION_CHECK_FLUSHED 0x0400 + +#define MMALOMX_ACTION_NOTIFY_DISABLE 0x1000 +#define MMALOMX_ACTION_NOTIFY_ENABLE 0x2000 +#define MMALOMX_ACTION_NOTIFY_FLUSH 0x4000 +#define MMALOMX_ACTION_NOTIFY_STATE 0x8000 + +#define MMALOMX_COMMAND_EXIT 0 +#define MMALOMX_COMMAND_STATE_SET 1 +#define MMALOMX_COMMAND_PORT_MARK 2 +#define MMALOMX_COMMAND_PORT_FLUSH 3 +#define MMALOMX_COMMAND_PORT_ENABLE 4 +#define MMALOMX_COMMAND_PORT_DISABLE 5 + +OMX_ERRORTYPE mmalomx_command_queue( + MMALOMX_COMPONENT_T *component, OMX_U32 arg1, OMX_U32 arg2); +OMX_ERRORTYPE mmalomx_command_dequeue( + MMALOMX_COMPONENT_T *component, OMX_U32 *arg1, OMX_U32 *arg2); + +void mmalomx_commands_actions_check(MMALOMX_COMPONENT_T *component); +void mmalomx_commands_actions_signal(MMALOMX_COMPONENT_T *component); +void mmalomx_commands_actions_next(MMALOMX_COMPONENT_T *component); + diff --git a/interface/mmal/openmaxil/mmalomx_core.c b/interface/mmal/openmaxil/mmalomx_core.c new file mode 100755 index 0000000..20d4fc7 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_core.c @@ -0,0 +1,1577 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" +#include "mmalomx.h" +#include "mmalomx_commands.h" +#include "mmalomx_roles.h" +#include "mmalomx_registry.h" +#include "mmalomx_buffer.h" +#include "mmalomx_parameters.h" +#include "mmalomx_logging.h" + +#include +#include +#include +#include + +#define MAX_CMD_BUFFERS 5 + +#define PARAM_GET_PORT(port, component, index) \ + if (index >= component->ports_num) return OMX_ErrorBadPortIndex; \ + port = &component->ports[index] + +static void *mmalomx_cmd_thread_func(void *arg); +#define MMALOMX_ZERO_COPY_THRESHOLD 256 + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_callback_event_handler( + MMALOMX_COMPONENT_T *component, + OMX_EVENTTYPE eEvent, + OMX_U32 nData1, + OMX_U32 nData2, + OMX_PTR pEventData) +{ + LOG_DEBUG("component %p, eEvent %i, nData1 %u, nData2 %u, pEventData %p", + component, (int)eEvent, (unsigned int)nData1, (unsigned int)nData2, pEventData); + return component->callbacks.EventHandler((OMX_HANDLETYPE)&component->omx, + component->callbacks_data, eEvent, nData1, nData2, pEventData); +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentGetComponentVersion( + OMX_HANDLETYPE hComponent, + OMX_STRING pComponentName, + OMX_VERSIONTYPE* pComponentVersion, + OMX_VERSIONTYPE* pSpecVersion, + OMX_UUIDTYPE* pComponentUUID) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + const char *short_name, *prefix; + + LOG_TRACE("hComponent %p, componentName %p, componentVersion %p, " + "pSpecVersion %p, componentUUID %p", + hComponent, pComponentName, pComponentVersion, pSpecVersion, + pComponentUUID); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + if (!pComponentName || !pComponentVersion || !pSpecVersion || !pComponentUUID ) + return OMX_ErrorBadParameter; + + short_name = mmalomx_registry_component_name(component->registry_id, &prefix); + + snprintf(pComponentName, OMX_MAX_STRINGNAME_SIZE, "%s%s", short_name, prefix); + pComponentVersion->nVersion = 0; + pSpecVersion->nVersion = OMX_VERSION; + snprintf((char *)(*pComponentUUID), sizeof(OMX_UUIDTYPE), "%s", pComponentName); + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentSendCommand( + OMX_HANDLETYPE hComponent, + OMX_COMMANDTYPE Cmd, + OMX_U32 nParam1, + OMX_PTR pCmdData) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + OMX_ERRORTYPE status = OMX_ErrorNone; + + LOG_TRACE("hComponent %p, Cmd %i (%s), nParam1 %i (%s), pCmdData %p", + hComponent, Cmd, mmalomx_cmd_to_string(Cmd), (int)nParam1, + Cmd == OMX_CommandStateSet ? mmalomx_state_to_string((OMX_STATETYPE)nParam1) : "", + pCmdData); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + /* Sanity check port index */ + if (Cmd == OMX_CommandFlush || Cmd == OMX_CommandMarkBuffer || + Cmd == OMX_CommandPortEnable || Cmd == OMX_CommandPortDisable) + { + if (nParam1 != OMX_ALL && nParam1 >= component->ports_num) + return OMX_ErrorBadPortIndex; + } + + if (Cmd == OMX_CommandStateSet || + Cmd == OMX_CommandFlush || + Cmd == OMX_CommandPortEnable || + Cmd == OMX_CommandPortDisable) + { + status = mmalomx_command_queue(component, Cmd, nParam1); + } + else if (Cmd == OMX_CommandMarkBuffer) + { + status = mmalomx_command_port_mark(hComponent, nParam1, pCmdData); + } + else + { + status = OMX_ErrorNotImplemented; + } + + return status; +} + +/*****************************************************************************/ +static MMAL_STATUS_T mmalomx_get_port_settings(MMALOMX_PORT_T *port, OMX_PARAM_PORTDEFINITIONTYPE *def) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *mmal = port->mmal; + + def->eDomain = mmalil_es_type_to_omx_domain(mmal->format->type); + def->eDir = OMX_DirInput; + if (mmal->type == MMAL_PORT_TYPE_OUTPUT) + def->eDir = OMX_DirOutput; + + if (def->eDomain == OMX_PortDomainVideo) + { + def->format.video.eColorFormat = OMX_COLOR_FormatUnused; + def->format.video.eCompressionFormat = mmalil_encoding_to_omx_video_coding(mmal->format->encoding); + if (def->format.video.eCompressionFormat == OMX_VIDEO_CodingUnused) + def->format.video.eColorFormat = mmalil_encoding_to_omx_color_format(mmal->format->encoding); + + def->format.video.nBitrate = mmal->format->bitrate; + def->format.video.nFrameWidth = mmal->format->es->video.width; + if (mmal->format->es->video.crop.width) + def->format.video.nFrameWidth = mmal->format->es->video.crop.width; + def->format.video.nStride = mmal->format->es->video.width; + if (port->no_cropping) + def->format.video.nFrameWidth = def->format.video.nStride; + def->format.video.nStride = + mmal_encoding_width_to_stride(mmal->format->encoding, def->format.video.nStride); + def->format.video.nFrameHeight = mmal->format->es->video.height; + if (mmal->format->es->video.crop.height) + def->format.video.nFrameHeight = mmal->format->es->video.crop.height; + def->format.video.nSliceHeight = mmal->format->es->video.height; + if (port->no_cropping) + def->format.video.nFrameHeight = def->format.video.nSliceHeight; + if (mmal->format->es->video.frame_rate.den) + def->format.video.xFramerate = (((int64_t)mmal->format->es->video.frame_rate.num) << 16) / + mmal->format->es->video.frame_rate.den; + else + def->format.video.xFramerate = 0; + } + else if (def->eDomain == OMX_PortDomainImage) + { + def->format.image.eColorFormat = OMX_COLOR_FormatUnused; + def->format.image.eCompressionFormat = mmalil_encoding_to_omx_image_coding(mmal->format->encoding); + if (def->format.image.eCompressionFormat == OMX_IMAGE_CodingUnused) + def->format.image.eColorFormat = mmalil_encoding_to_omx_color_format(mmal->format->encoding); + if (mmal->format->encoding == MMAL_ENCODING_UNKNOWN) + def->format.image.eCompressionFormat = OMX_IMAGE_CodingAutoDetect; + def->format.image.nFrameWidth = mmal->format->es->video.width; + if (mmal->format->es->video.crop.width) + def->format.image.nFrameWidth = mmal->format->es->video.crop.width; + def->format.image.nStride = mmal->format->es->video.width; + if (port->no_cropping) + def->format.image.nFrameWidth = def->format.image.nStride; + def->format.image.nStride = + mmal_encoding_width_to_stride(mmal->format->encoding, def->format.image.nStride); + def->format.image.nFrameHeight = mmal->format->es->video.height; + if (mmal->format->es->video.crop.height) + def->format.image.nFrameHeight = mmal->format->es->video.crop.height; + def->format.image.nSliceHeight = mmal->format->es->video.height; + if (port->no_cropping) + def->format.image.nFrameHeight = def->format.image.nSliceHeight; + } + else if(def->eDomain == OMX_PortDomainAudio) + { + def->format.audio.eEncoding = mmalil_encoding_to_omx_audio_coding(mmal->format->encoding); + } + else + { + LOG_ERROR("%s: unsupported domain (%u)", mmal->name, def->eDomain); + status = MMAL_EINVAL; + goto finish; + } + + def->nBufferAlignment = mmal->buffer_alignment_min; + def->nBufferCountActual = mmal->buffer_num; + def->nBufferCountMin = mmal->buffer_num_min; + def->nBufferSize = mmal->buffer_size; + if (def->nBufferSize < mmal->buffer_size_min) + def->nBufferSize = mmal->buffer_size_min; + def->bEnabled = port->enabled; + def->bPopulated = port->populated; + + finish: + return status; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentGetParameter( + OMX_HANDLETYPE hComponent, + OMX_INDEXTYPE nParamIndex, + OMX_PTR pParam) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMALOMX_PORT_T *port = NULL; + + LOG_TRACE("hComponent %p, nParamIndex 0x%x (%s), pParam %p", + hComponent, nParamIndex, mmalomx_param_to_string(nParamIndex), pParam); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pParam) + return OMX_ErrorBadParameter; + if (*(OMX_U32 *)pParam < sizeof(OMX_U32) + sizeof(OMX_VERSIONTYPE)) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + switch(nParamIndex) + { + case OMX_IndexParamAudioInit: + case OMX_IndexParamVideoInit: + case OMX_IndexParamImageInit: + case OMX_IndexParamOtherInit: + { + OMX_PORT_PARAM_TYPE *param = (OMX_PORT_PARAM_TYPE *)pParam; + param->nStartPortNumber = 0; + param->nPorts = component->ports_domain_num[OMX_PortDomainAudio]; + if (nParamIndex == OMX_IndexParamAudioInit) + return OMX_ErrorNone; + param->nStartPortNumber += param->nPorts; + param->nPorts = component->ports_domain_num[OMX_PortDomainVideo]; + if (nParamIndex == OMX_IndexParamVideoInit) + return OMX_ErrorNone; + param->nStartPortNumber += param->nPorts; + param->nPorts = component->ports_domain_num[OMX_PortDomainImage]; + if (nParamIndex == OMX_IndexParamImageInit) + return OMX_ErrorNone; + param->nStartPortNumber += param->nPorts; + param->nPorts = component->ports_domain_num[OMX_PortDomainOther]; + } + return OMX_ErrorNone; + break; + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *param = (OMX_PARAM_PORTDEFINITIONTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + return mmalil_error_to_omx(mmalomx_get_port_settings(port, param)); + } + return OMX_ErrorNone; + break; + case OMX_IndexParamCompBufferSupplier: + { + OMX_PARAM_BUFFERSUPPLIERTYPE *param = (OMX_PARAM_BUFFERSUPPLIERTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + param->eBufferSupplier = OMX_BufferSupplyUnspecified; + } + return OMX_ErrorNone; + break; + case OMX_IndexParamPriorityMgmt: + { + OMX_PRIORITYMGMTTYPE *param = (OMX_PRIORITYMGMTTYPE *)pParam; + param->nGroupPriority = component->group_priority; + param->nGroupID = component->group_id; + } + return OMX_ErrorNone; + break; + case OMX_IndexParamVideoPortFormat: + case OMX_IndexParamAudioPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *param = (OMX_VIDEO_PARAM_PORTFORMATTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + + /* Populate our internal list of encodings the first time around */ + if (!port->encodings_num) + { + port->encodings_header.id = MMAL_PARAMETER_SUPPORTED_ENCODINGS; + port->encodings_header.size = sizeof(port->encodings_header) + sizeof(port->encodings); + if (mmal_port_parameter_get(port->mmal, &port->encodings_header) == MMAL_SUCCESS) + { + port->encodings_num = (port->encodings_header.size - sizeof(port->encodings_header)) / + sizeof(port->encodings[0]); + } + if (!port->encodings_num) + { + port->encodings_num = 1; + port->encodings[0] = port->mmal->format->encoding; + } + } + + if (param->nIndex >= port->encodings_num) + return OMX_ErrorNoMore; + + if (nParamIndex == OMX_IndexParamVideoPortFormat) + { + param->eColorFormat = OMX_COLOR_FormatUnused; + param->eCompressionFormat = + mmalil_encoding_to_omx_video_coding(port->encodings[param->nIndex]); + if (param->eCompressionFormat == OMX_VIDEO_CodingUnused) + param->eColorFormat = + mmalil_encoding_to_omx_color_format(port->encodings[param->nIndex]); + param->xFramerate = 0; + } + else + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *aparam = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)pParam; + aparam->eEncoding = + mmalil_encoding_to_omx_audio_coding(port->encodings[param->nIndex]); + } + return OMX_ErrorNone; + } + break; + case OMX_IndexParamImagePortFormat: + case OMX_IndexParamOtherPortFormat: + break; + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *param = (OMX_PARAM_COMPONENTROLETYPE *)pParam; + const char *role = mmalomx_role_to_name(component->role); + if (!role) + role = component->name; + snprintf((char *)param->cRole, sizeof(param->cRole), "%s", role); + } + return OMX_ErrorNone; + default: + return mmalomx_parameter_get(component, nParamIndex, pParam); + } + + return OMX_ErrorNotImplemented; +} + +/*****************************************************************************/ +static MMAL_STATUS_T mmalomx_set_port_settings(MMALOMX_PORT_T *mmalomx_port, + OMX_PARAM_PORTDEFINITIONTYPE *def) +{ + MMAL_PORT_T *port = mmalomx_port->mmal; + uint32_t buffer_size_min = port->buffer_size_min; + MMAL_STATUS_T status; + + port->format->type = mmalil_omx_domain_to_es_type(def->eDomain); + port->format->encoding_variant = 0; + + if(def->eDomain == OMX_PortDomainVideo) + { + if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused) + port->format->encoding = mmalil_omx_video_coding_to_encoding(def->format.video.eCompressionFormat); + else + port->format->encoding = mmalil_omx_color_format_to_encoding(def->format.video.eColorFormat); + + port->format->bitrate = def->format.video.nBitrate; + port->format->es->video.width = def->format.video.nFrameWidth; + if (!mmalomx_port->no_cropping) + port->format->es->video.crop.width = port->format->es->video.width; + if (mmal_encoding_stride_to_width(port->format->encoding, def->format.video.nStride)) + port->format->es->video.width = + mmal_encoding_stride_to_width(port->format->encoding, def->format.video.nStride); + port->format->es->video.height = def->format.video.nFrameHeight; + if (!mmalomx_port->no_cropping) + port->format->es->video.crop.height = port->format->es->video.height; + if (def->format.video.nSliceHeight > def->format.video.nFrameHeight) + port->format->es->video.height = def->format.video.nSliceHeight; + port->format->es->video.frame_rate.num = def->format.video.xFramerate; + port->format->es->video.frame_rate.den = (1<<16); + } + else if(def->eDomain == OMX_PortDomainImage) + { + if (def->format.image.eCompressionFormat != OMX_IMAGE_CodingUnused) + port->format->encoding = mmalil_omx_image_coding_to_encoding(def->format.image.eCompressionFormat); + else + port->format->encoding = mmalil_omx_color_format_to_encoding(def->format.image.eColorFormat); + + port->format->es->video.width = def->format.image.nFrameWidth; + if (!mmalomx_port->no_cropping) + port->format->es->video.crop.width = port->format->es->video.width; + if (mmal_encoding_stride_to_width(port->format->encoding, def->format.image.nStride)) + port->format->es->video.width = + mmal_encoding_stride_to_width(port->format->encoding, def->format.image.nStride); + port->format->es->video.height = def->format.image.nFrameHeight; + if (!mmalomx_port->no_cropping) + port->format->es->video.crop.height = port->format->es->video.height; + if (def->format.image.nSliceHeight > def->format.image.nFrameHeight) + port->format->es->video.height = def->format.image.nSliceHeight; + } + else if(def->eDomain == OMX_PortDomainAudio) + { + port->format->encoding = mmalil_omx_audio_coding_to_encoding(def->format.audio.eEncoding); + } + else + { + port->format->encoding = MMAL_ENCODING_UNKNOWN; + } + + port->buffer_num = def->nBufferCountActual; + port->buffer_size = def->nBufferSize; + if (port->buffer_size < port->buffer_size_min) + port->buffer_size = port->buffer_size_min; + + status = mmal_port_format_commit(port); + if (status != MMAL_SUCCESS) + return status; + + /* Acknowledge any ongoing port format changed event */ + mmalomx_port->format_changed = MMAL_FALSE; + + /* The minimum buffer size only changes when the format significantly changes + * and in that case we want to advertise the new requirement to the client. */ + if (port->buffer_size_min != buffer_size_min) + port->buffer_size = port->buffer_size_min; + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentSetParameter( + OMX_HANDLETYPE hComponent, + OMX_INDEXTYPE nParamIndex, + OMX_PTR pParam) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMALOMX_PORT_T *port = NULL; + + LOG_TRACE("hComponent %p, nParamIndex 0x%x (%s), pParam %p", + hComponent, nParamIndex, mmalomx_param_to_string(nParamIndex), pParam); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pParam) + return OMX_ErrorBadParameter; + if (*(OMX_U32 *)pParam < sizeof(OMX_U32) + sizeof(OMX_VERSIONTYPE)) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + switch(nParamIndex) + { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *param = (OMX_PARAM_PORTDEFINITIONTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + return mmalil_error_to_omx(mmalomx_set_port_settings(port, param)); + } + return OMX_ErrorNone; + break; + case OMX_IndexParamCompBufferSupplier: + { + OMX_PARAM_BUFFERSUPPLIERTYPE *param = (OMX_PARAM_BUFFERSUPPLIERTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + //param->eBufferSupplier = OMX_BufferSupplyUnspecified; + } + return OMX_ErrorNone; + break; + case OMX_IndexParamPriorityMgmt: + { + OMX_PRIORITYMGMTTYPE *param = (OMX_PRIORITYMGMTTYPE *)pParam; + + if (component->state != OMX_StateLoaded) + return OMX_ErrorIncorrectStateOperation; + + component->group_priority = param->nGroupPriority; + component->group_id = param->nGroupID; + } + return OMX_ErrorNone; + break; + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *param = (OMX_AUDIO_PARAM_PORTFORMATTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + port->mmal->format->encoding = mmalil_omx_audio_coding_to_encoding(param->eEncoding); + port->mmal->format->encoding_variant = 0; + if (mmal_port_format_commit(port->mmal) != MMAL_SUCCESS) + LOG_ERROR("OMX_IndexParamAudioPortFormat commit failed"); + return OMX_ErrorNone; + } + break; + case OMX_IndexParamVideoPortFormat: + { + OMX_VIDEO_PARAM_PORTFORMATTYPE *param = (OMX_VIDEO_PARAM_PORTFORMATTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->eCompressionFormat != OMX_VIDEO_CodingUnused) + port->mmal->format->encoding = mmalil_omx_video_coding_to_encoding(param->eCompressionFormat); + else + port->mmal->format->encoding = mmalil_omx_color_format_to_encoding(param->eColorFormat); + port->mmal->format->encoding_variant = 0; + + if (mmal_port_format_commit(port->mmal) != MMAL_SUCCESS) + LOG_ERROR("OMX_IndexParamAudioPortFormat commit failed"); + return OMX_ErrorNone; + } + break; + case OMX_IndexParamImagePortFormat: + case OMX_IndexParamOtherPortFormat: + break; + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *param = (OMX_PARAM_COMPONENTROLETYPE *)pParam; + return mmalomx_role_set(component, (const char *)param->cRole); + } + break; + default: + { + OMX_ERRORTYPE status = mmalomx_parameter_set(component, nParamIndex, pParam); + + /* Keep track of the zero-copy state */ + if (status == OMX_ErrorNone && nParamIndex == OMX_IndexParamBrcmZeroCopy) + { + PARAM_GET_PORT(port, component, ((OMX_CONFIG_PORTBOOLEANTYPE *)pParam)->nPortIndex); + port->zero_copy = ((OMX_CONFIG_PORTBOOLEANTYPE *)pParam)->bEnabled; + } + + return status; + } + } + + return OMX_ErrorNotImplemented; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentGetConfig( + OMX_HANDLETYPE hComponent, + OMX_INDEXTYPE nParamIndex, + OMX_PTR pParam) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + + LOG_TRACE("hComponent %p, nParamIndex 0x%x (%s), pParam %p", + hComponent, nParamIndex, mmalomx_param_to_string(nParamIndex), pParam); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pParam) + return OMX_ErrorBadParameter; + if (*(OMX_U32 *)pParam < sizeof(OMX_U32) + sizeof(OMX_VERSIONTYPE)) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + return mmalomx_parameter_get(component, nParamIndex, pParam); +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentSetConfig( + OMX_HANDLETYPE hComponent, + OMX_INDEXTYPE nParamIndex, + OMX_PTR pParam) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + + LOG_TRACE("hComponent %p, nParamIndex 0x%x (%s), pParam %p", + hComponent, nParamIndex, mmalomx_param_to_string(nParamIndex), pParam); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pParam) + return OMX_ErrorBadParameter; + if (*(OMX_U32 *)pParam < sizeof(OMX_U32) + sizeof(OMX_VERSIONTYPE)) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + return mmalomx_parameter_set(component, nParamIndex, pParam); +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentGetExtensionIndex( + OMX_HANDLETYPE hComponent, + OMX_STRING cParameterName, + OMX_INDEXTYPE* pIndexType) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + + LOG_TRACE("hComponent %p, cParameterName %s, pIndexType %p", + hComponent, cParameterName, pIndexType); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + return mmalomx_parameter_extension_index_get(cParameterName, pIndexType); +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentGetState( + OMX_HANDLETYPE hComponent, + OMX_STATETYPE* pState) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMAL_PARAM_UNUSED(component); + + LOG_TRACE("hComponent %p, pState, %p", hComponent, pState); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pState) + return OMX_ErrorBadParameter; + + *pState = component->state; + return OMX_ErrorNone; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentTunnelRequest( + OMX_HANDLETYPE hComponent, + OMX_U32 nPort, + OMX_HANDLETYPE hTunneledComp, + OMX_U32 nTunneledPort, + OMX_TUNNELSETUPTYPE* pTunnelSetup) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMAL_PARAM_UNUSED(component); + + LOG_TRACE("hComponent %p, nPort %i, hTunneledComp %p, nTunneledPort %i, " + "pTunnelSetup %p", hComponent, (int)nPort, hTunneledComp, + (int)nTunneledPort, pTunnelSetup); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + if (nPort >= component->ports_num) + return OMX_ErrorBadPortIndex; + if (component->state != OMX_StateLoaded && component->ports[nPort].enabled) + return OMX_ErrorIncorrectStateOperation; + if (hTunneledComp && !pTunnelSetup) + return OMX_ErrorBadParameter; + + if (!hTunneledComp) + return OMX_ErrorNone; + return OMX_ErrorNotImplemented; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentUseBuffer( + OMX_HANDLETYPE hComponent, + OMX_BUFFERHEADERTYPE** ppBuffer, + OMX_U32 nPortIndex, + OMX_PTR pAppPrivate, + OMX_U32 nSizeBytes, + OMX_U8* pBuffer) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + OMX_ERRORTYPE status = OMX_ErrorNone; + MMAL_BOOL_T populated = MMAL_FALSE; + OMX_BUFFERHEADERTYPE *buffer; + MMALOMX_PORT_T *port; + + LOG_TRACE("hComponent %p, ppBufferHdr %p, nPortIndex %i, pAppPrivate %p," + " nSizeBytes %i, pBuffer %p", hComponent, ppBuffer, + (int)nPortIndex, pAppPrivate, (int)nSizeBytes, pBuffer); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!ppBuffer) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + if (nPortIndex >= component->ports_num) + return OMX_ErrorBadPortIndex; + + /* Make sure any previous command has been processed. + * This is not ideal since done inline but in practice the actual + * notification to the client will not be done as part of this call. */ + mmalomx_commands_actions_check(component); + + port = &component->ports[nPortIndex]; + MMALOMX_LOCK_PORT(component, port); + + if (!(port->actions & MMALOMX_ACTION_CHECK_ALLOCATED)) + status = OMX_ErrorIncorrectStateOperation; + if (port->populated) + status = OMX_ErrorIncorrectStateOperation; + if (status != OMX_ErrorNone) + goto error; + + /* Check for mismatched calls to UseBuffer/AllocateBuffer */ + if (port->buffers && port->buffers_allocated) + { + status = OMX_ErrorBadParameter; + goto error; + } + + /* Sanity check buffer size */ + if (nSizeBytes < port->mmal->buffer_size_min) + { + LOG_ERROR("buffer size too small (%i/%i)", (int)nSizeBytes, + (int)port->mmal->buffer_size_min); + status = OMX_ErrorBadParameter; + goto error; + } + if (!port->buffers) + port->mmal->buffer_size = nSizeBytes; + if (nSizeBytes > port->mmal->buffer_size) + { + LOG_ERROR("buffer size too big (%i/%i)", (int)nSizeBytes, + (int)port->mmal->buffer_size); + status = OMX_ErrorBadParameter; + goto error; + } + + buffer = calloc( 1, sizeof(*buffer) ); + if (!buffer) + { + status = OMX_ErrorInsufficientResources; + goto error; + } + + buffer->nSize = sizeof(*buffer); + buffer->nVersion.nVersion = OMX_VERSION; + buffer->nAllocLen = nSizeBytes; + buffer->pBuffer = pBuffer; + buffer->pAppPrivate = pAppPrivate; + if (port->direction == OMX_DirInput) + { + buffer->nInputPortIndex = nPortIndex; + buffer->pOutputPortPrivate = pAppPrivate; + } + else + { + buffer->nOutputPortIndex = nPortIndex; + buffer->pInputPortPrivate = pAppPrivate; + } + + *ppBuffer = buffer; + port->buffers++; + port->buffers_allocated = MMAL_FALSE; + port->populated = populated = port->buffers == port->mmal->buffer_num; + + MMALOMX_UNLOCK_PORT(component, port); + + LOG_DEBUG("allocated %i/%i buffers", port->buffers, port->mmal->buffer_num); + + if (populated) + mmalomx_commands_actions_signal(component); + + return OMX_ErrorNone; + +error: + MMALOMX_UNLOCK_PORT(component, port); + return status; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentAllocateBuffer( + OMX_HANDLETYPE hComponent, + OMX_BUFFERHEADERTYPE** ppBuffer, + OMX_U32 nPortIndex, + OMX_PTR pAppPrivate, + OMX_U32 nSizeBytes) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + OMX_ERRORTYPE status = OMX_ErrorNone; + MMAL_BOOL_T populated = MMAL_FALSE; + OMX_BUFFERHEADERTYPE *buffer = 0; + MMALOMX_PORT_T *port; + + LOG_TRACE("hComponent %p, ppBuffer %p, nPortIndex %i, pAppPrivate %p, " + "nSizeBytes %i", hComponent, ppBuffer, (int)nPortIndex, + pAppPrivate, (int)nSizeBytes); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!ppBuffer) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + if (nPortIndex >= component->ports_num) + return OMX_ErrorBadPortIndex; + + /* Make sure any previous command has been processed. + * This is not ideal since done inline but in practice the actual + * notification to the client will not be done as part of this call. */ + mmalomx_commands_actions_check(component); + + port = &component->ports[nPortIndex]; + MMALOMX_LOCK_PORT(component, port); + + if (!(port->actions & MMALOMX_ACTION_CHECK_ALLOCATED)) + status = OMX_ErrorIncorrectStateOperation; + if (port->populated) + status = OMX_ErrorIncorrectStateOperation; + if (status != OMX_ErrorNone) + goto error; + + /* Check for mismatched calls to UseBuffer/AllocateBuffer */ + if (!status && port->buffers && !port->buffers_allocated) + { + status = OMX_ErrorBadParameter; + goto error; + } + + /* Sanity check buffer size */ + if (nSizeBytes < port->mmal->buffer_size_min) + { + LOG_ERROR("buffer size too small (%i/%i)", (int)nSizeBytes, + (int)port->mmal->buffer_size_min); + status = OMX_ErrorBadParameter; + goto error; + } + if (!port->buffers) + port->mmal->buffer_size = nSizeBytes; + if (nSizeBytes > port->mmal->buffer_size) + { + LOG_ERROR("buffer size too big (%i/%i)", (int)nSizeBytes, + (int)port->mmal->buffer_size); + status = OMX_ErrorBadParameter; + goto error; + } + + /* Set the zero-copy mode */ + if (!port->buffers_allocated && nSizeBytes > MMALOMX_ZERO_COPY_THRESHOLD && + !port->zero_copy) + { + MMAL_STATUS_T status = mmal_port_parameter_set_boolean(port->mmal, + MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + LOG_ERROR("failed to enable zero copy on %s", port->mmal->name); + } + + buffer = calloc( 1, sizeof(*buffer) ); + if (!buffer) + { + status = OMX_ErrorInsufficientResources; + goto error; + } + + buffer->pBuffer = mmal_port_payload_alloc(port->mmal, nSizeBytes); + if (!buffer->pBuffer) + { + status = OMX_ErrorInsufficientResources; + goto error; + } + + buffer->nSize = sizeof(*buffer); + buffer->nVersion.nVersion = OMX_VERSION; + buffer->nAllocLen = nSizeBytes; + buffer->pAppPrivate = pAppPrivate; + if (port->direction == OMX_DirInput) + { + buffer->nInputPortIndex = nPortIndex; + buffer->pOutputPortPrivate = pAppPrivate; + } + else + { + buffer->nOutputPortIndex = nPortIndex; + buffer->pInputPortPrivate = pAppPrivate; + } + /* Keep an unmodified copy of the pointer for when we come to free it */ + buffer->pPlatformPrivate = (OMX_PTR)buffer->pBuffer; + + *ppBuffer = buffer; + port->buffers++; + port->buffers_allocated = MMAL_TRUE; + port->populated = populated = port->buffers == port->mmal->buffer_num; + + MMALOMX_UNLOCK_PORT(component, port); + + LOG_DEBUG("allocated %i/%i buffers", port->buffers, port->mmal->buffer_num); + + if (populated) + mmalomx_commands_actions_signal(component); + + return OMX_ErrorNone; + +error: + if (!port->buffers_allocated && !port->zero_copy) + mmal_port_parameter_set_boolean(port->mmal, MMAL_PARAMETER_ZERO_COPY, MMAL_FALSE); + + MMALOMX_UNLOCK_PORT(component, port); + LOG_ERROR("failed to allocate %i/%i buffers", port->buffers, port->mmal->buffer_num); + if (buffer) + free(buffer); + return status; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentFreeBuffer( + OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + OMX_ERRORTYPE status = OMX_ErrorNone; + MMAL_BOOL_T unpopulated, allocated; + MMALOMX_PORT_T *port; + unsigned int buffers; + + LOG_TRACE("hComponent %p, nPortIndex %i, pBuffer %p", + hComponent, (int)nPortIndex, pBuffer); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pBuffer) + return OMX_ErrorBadParameter; + if (nPortIndex >= component->ports_num) + return OMX_ErrorBadPortIndex; + + /* Make sure any previous command has been processed. + * This is not ideal since done inline but in practice the actual + * notification to the client will not be done as part of this call. */ + mmalomx_commands_actions_check(component); + + port = &component->ports[nPortIndex]; + MMALOMX_LOCK_PORT(component, port); + + if (!port->buffers) + { + status = OMX_ErrorBadParameter; + goto error; + } + + buffers = --port->buffers; + port->populated = MMAL_FALSE; + unpopulated = !(port->actions & MMALOMX_ACTION_CHECK_DEALLOCATED); + allocated = port->buffers_allocated; + + MMALOMX_UNLOCK_PORT(component, port); + + if (allocated) /* Free the unmodified pointer */ + mmal_port_payload_free(port->mmal, pBuffer->pPlatformPrivate); + free(pBuffer); + + if (allocated && !port->zero_copy) /* Reset the zero-copy status */ + mmal_port_parameter_set_boolean(port->mmal, MMAL_PARAMETER_ZERO_COPY, MMAL_FALSE); + + LOG_DEBUG("freed %i/%i buffers", port->mmal->buffer_num - port->buffers, port->mmal->buffer_num); + + if (unpopulated) + mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorPortUnpopulated, 0, NULL); + + if (!buffers) + mmalomx_commands_actions_signal(component); + + return OMX_ErrorNone; + +error: + MMALOMX_UNLOCK_PORT(component, port); + return status; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentEmptyThisBuffer( + OMX_HANDLETYPE hComponent, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + + if (ENABLE_MMAL_EXTRA_LOGGING) + LOG_TRACE("hComponent %p, port %i, pBuffer %p", hComponent, + pBuffer ? (int)pBuffer->nInputPortIndex : -1, pBuffer); + + return mmalomx_buffer_send(component, pBuffer, OMX_DirInput); +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentFillThisBuffer( + OMX_HANDLETYPE hComponent, + OMX_BUFFERHEADERTYPE* pBuffer) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + + if (ENABLE_MMAL_EXTRA_LOGGING) + LOG_TRACE("hComponent %p, port %i, pBuffer %p", hComponent, + pBuffer ? (int)pBuffer->nOutputPortIndex : -1, pBuffer); + + return mmalomx_buffer_send(component, pBuffer, OMX_DirOutput); +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentSetCallbacks( + OMX_HANDLETYPE hComponent, + OMX_CALLBACKTYPE* pCallbacks, + OMX_PTR pAppData) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMAL_PARAM_UNUSED(component); + + LOG_TRACE("hComponent %p, pCallbacks %p, pAppData %p", + hComponent, pCallbacks, pAppData); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (!pCallbacks) + return OMX_ErrorBadParameter; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + if (component->state != OMX_StateLoaded) + return OMX_ErrorInvalidState; + + component->callbacks = *pCallbacks; + component->callbacks_data = pAppData; + return OMX_ErrorNone; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentDeInit( + OMX_HANDLETYPE hComponent) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMAL_PARAM_UNUSED(component); + + LOG_TRACE("hComponent %p", hComponent); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentUseEGLImage( + OMX_HANDLETYPE hComponent, + OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_U32 nPortIndex, + OMX_PTR pAppPrivate, + void* eglImage) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMAL_PARAM_UNUSED(component); + + LOG_TRACE("hComponent %p, ppBufferHdr %p, nPortIndex %i, pAppPrivate %p," + " eglImage %p", hComponent, ppBufferHdr, (int)nPortIndex, + pAppPrivate, eglImage); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + return OMX_ErrorNotImplemented; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_ComponentRoleEnum( + OMX_HANDLETYPE hComponent, + OMX_U8 *cRole, + OMX_U32 nIndex) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + MMALOMX_ROLE_T role; + + LOG_TRACE("hComponent %p, cRole %p, nIndex %i", + hComponent, cRole, (int)nIndex); + + /* Sanity checks */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + if (component->state == OMX_StateInvalid) + return OMX_ErrorInvalidState; + + role = mmalomx_registry_component_roles(component->registry_id, nIndex); + if (!role) + return OMX_ErrorNoMore; + if (!mmalomx_role_to_name(role)) + return OMX_ErrorNoMore; + + strcpy((char *)cRole, mmalomx_role_to_name(role)); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE OMX_APIENTRY MMALOMX_EXPORT(OMX_Init)(void) +{ + mmalomx_logging_init(); + LOG_TRACE("Init"); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE OMX_APIENTRY MMALOMX_EXPORT(OMX_Deinit)(void) +{ + LOG_TRACE("Deinit"); + mmalomx_logging_deinit(); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE OMX_APIENTRY MMALOMX_EXPORT(OMX_ComponentNameEnum)( + OMX_STRING cComponentName, + OMX_U32 nNameLength, + OMX_U32 nIndex) +{ + const char *prefix, *name; + name = mmalomx_registry_component_name(nIndex, &prefix); + + LOG_TRACE("cComponentName %p, nNameLength %i, nIndex %i", + cComponentName, (int)nNameLength, (int)nIndex); + + /* Sanity checking */ + if (!cComponentName) + return OMX_ErrorBadParameter; + if (!name) + return OMX_ErrorNoMore; + if (nNameLength <= strlen(name) + strlen(prefix)) + return OMX_ErrorBadParameter; + + sprintf(cComponentName, "%s%s", prefix, name); + LOG_TRACE("cComponentName: %s", cComponentName); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +static void mmalomx_buffer_cb_control( + MMAL_PORT_T *port, + MMAL_BUFFER_HEADER_T *buffer) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)port->userdata; + + LOG_DEBUG("received event %4.4s on port %s", (char *)&buffer->cmd, port->name); + + if (buffer->cmd == MMAL_EVENT_ERROR) + { + mmalomx_callback_event_handler(component, OMX_EventError, + mmalil_error_to_omx(*(MMAL_STATUS_T *)buffer->data), 0, NULL); + } + else if (buffer->cmd == MMAL_EVENT_EOS && + buffer->length == sizeof(MMAL_EVENT_END_OF_STREAM_T)) + { + MMAL_EVENT_END_OF_STREAM_T *eos = (MMAL_EVENT_END_OF_STREAM_T *)buffer->data; + if (eos->port_index < port->component->input_num) + { + MMALOMX_PORT_T *omx_port = (MMALOMX_PORT_T *) + port->component->input[eos->port_index]->userdata; + LOG_DEBUG("send EOS on %i", omx_port->index); + mmalomx_callback_event_handler(component, OMX_EventBufferFlag, + omx_port->index, OMX_BUFFERFLAG_EOS, NULL); + } + } + + mmal_buffer_header_release(buffer); +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE OMX_APIENTRY MMALOMX_EXPORT(OMX_GetHandle)( + OMX_HANDLETYPE* pHandle, + OMX_STRING cComponentName, + OMX_PTR pAppData, + OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE status = OMX_ErrorInsufficientResources; + MMALOMX_COMPONENT_T *component = 0; + MMAL_COMPONENT_T *mmal_component = 0; + MMAL_STATUS_T mmal_status; + unsigned int i, ports_num; + OMX_PORTDOMAINTYPE domain; + const char *mmal_name; + int registry_id; + + LOG_TRACE("pHandle %p, cComponentName %s, pAppData %p, pCallBacks %p", + pHandle, cComponentName, pAppData, pCallBacks); + + /* Sanity check params */ + if (!pHandle || !cComponentName || !pCallBacks) + return OMX_ErrorBadParameter; + + /* Find component */ + registry_id = mmalomx_registry_find_component(cComponentName); + if (registry_id < 0) + return OMX_ErrorComponentNotFound; + + /* create and setup component */ + mmal_name = mmalomx_registry_component_mmal(registry_id); + mmal_status = mmal_component_create(mmal_name, &mmal_component); + if (mmal_status != MMAL_SUCCESS) + { + LOG_ERROR("could not create mmal component %s", mmal_name); + return mmalil_error_to_omx(mmal_status); + } + mmal_status = mmal_port_enable(mmal_component->control, mmalomx_buffer_cb_control); + if (mmal_status != MMAL_SUCCESS) + { + LOG_ERROR("could not enable %s", mmal_component->control->name); + mmal_component_destroy(mmal_component); + return mmalil_error_to_omx(mmal_status); + } + + ports_num = mmal_component->port_num - 1; + + component = calloc(1, sizeof(*component) + ports_num * sizeof(*component->ports)); + if (!component) + { + mmal_component_destroy(mmal_component); + return OMX_ErrorInsufficientResources; + } + + if (vcos_mutex_create(&component->lock, "mmalomx lock") != VCOS_SUCCESS) + { + mmal_component_destroy(mmal_component); + free(component); + return OMX_ErrorInsufficientResources; + } + if (vcos_mutex_create(&component->lock_port, "mmalomx port lock") != VCOS_SUCCESS) + { + vcos_mutex_delete(&component->lock); + mmal_component_destroy(mmal_component); + free(component); + return OMX_ErrorInsufficientResources; + } + + component->omx.nSize = sizeof(component->omx); + component->omx.nVersion.nVersion = OMX_VERSION; + component->mmal = mmal_component; + component->state = OMX_StateLoaded; + component->callbacks = *pCallBacks; + component->callbacks_data = pAppData; + component->ports = (MMALOMX_PORT_T *)&component[1]; + component->registry_id = registry_id; + component->name = mmalomx_registry_component_name(registry_id, 0); + component->role = mmalomx_registry_component_roles(registry_id, 0); + + // FIXME: make this configurable + component->cmd_thread_used = MMAL_TRUE; + + /* Sort the ports into separate OMX domains */ + for (domain = OMX_PortDomainAudio; domain < OMX_PortDomainOther; domain++) + { + for (i = 1; i < mmal_component->port_num; i++) + { + if (domain == mmalil_es_type_to_omx_domain(mmal_component->port[i]->format->type)) + { + component->ports[component->ports_num].mmal = mmal_component->port[i]; + component->ports_domain_num[domain]++; + component->ports_num++; + } + } + } + LOG_DEBUG("ports: %i audio, %i video", + component->ports_domain_num[OMX_PortDomainAudio], + component->ports_domain_num[OMX_PortDomainVideo]); + + /* Setup our ports */ + for (i = 0; i < component->ports_num; i++) + { + component->ports[i].component = component; + if (component->ports[i].mmal->type == MMAL_PORT_TYPE_OUTPUT) + component->ports[i].direction = OMX_DirOutput; + component->ports[i].index = i; + component->ports[i].enabled = MMAL_TRUE; + component->ports[i].pool = + mmal_port_pool_create(component->ports[i].mmal, 0, 0); + if (!component->ports[i].pool) + goto error; + component->ports[i].mmal->userdata = (struct MMAL_PORT_USERDATA_T *)&component->ports[i]; + } + mmal_component->control->userdata = (struct MMAL_PORT_USERDATA_T *)component; + + /* Create our OMX commands queue */ + component->cmd_queue = mmal_queue_create(); + if (!component->cmd_queue) + goto error; + component->cmd_pool = mmal_pool_create(MAX_CMD_BUFFERS, 0); + if (!component->cmd_pool) + goto error; + + if (component->cmd_thread_used && + vcos_semaphore_create(&component->cmd_sema, + "mmalomx sema", 0) != VCOS_SUCCESS) + { + component->cmd_thread_used = MMAL_FALSE; + goto error; + } + + if (component->cmd_thread_used && + vcos_thread_create(&component->cmd_thread, component->name, NULL, + mmalomx_cmd_thread_func, component) != VCOS_SUCCESS) + { + vcos_semaphore_delete(&component->cmd_sema); + component->cmd_thread_used = MMAL_FALSE; + goto error; + } + + /* Set the function pointer for the component's interface */ + component->omx.GetComponentVersion = mmalomx_ComponentGetComponentVersion; + component->omx.SendCommand = mmalomx_ComponentSendCommand; + component->omx.GetParameter = mmalomx_ComponentGetParameter; + component->omx.SetParameter = mmalomx_ComponentSetParameter; + component->omx.GetConfig = mmalomx_ComponentGetConfig; + component->omx.SetConfig = mmalomx_ComponentSetConfig; + component->omx.GetExtensionIndex = mmalomx_ComponentGetExtensionIndex; + component->omx.GetState = mmalomx_ComponentGetState; + component->omx.ComponentTunnelRequest = mmalomx_ComponentTunnelRequest; + component->omx.UseBuffer = mmalomx_ComponentUseBuffer; + component->omx.AllocateBuffer = mmalomx_ComponentAllocateBuffer; + component->omx.FreeBuffer = mmalomx_ComponentFreeBuffer; + component->omx.EmptyThisBuffer = mmalomx_ComponentEmptyThisBuffer; + component->omx.FillThisBuffer = mmalomx_ComponentFillThisBuffer; + component->omx.SetCallbacks = mmalomx_ComponentSetCallbacks; + component->omx.ComponentDeInit = mmalomx_ComponentDeInit; + component->omx.UseEGLImage = mmalomx_ComponentUseEGLImage; + component->omx.ComponentRoleEnum = mmalomx_ComponentRoleEnum; + *pHandle = (OMX_HANDLETYPE)&component->omx; + + return OMX_ErrorNone; + + error: + MMALOMX_IMPORT(OMX_FreeHandle)((OMX_HANDLETYPE)&component->omx); + return status; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE OMX_APIENTRY MMALOMX_EXPORT(OMX_FreeHandle)( + OMX_HANDLETYPE hComponent) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent; + OMX_ERRORTYPE status; + unsigned int i; + + LOG_TRACE("hComponent %p", hComponent); + + /* Sanity check */ + if (!hComponent) + return OMX_ErrorInvalidComponent; + + if (component->omx.ComponentDeInit) + { + status = component->omx.ComponentDeInit(hComponent); + if (status != OMX_ErrorNone) + { + LOG_ERROR("ComponentDeInit failed"); + return status; + } + } + + if (component->cmd_thread_used) + { + component->cmd_thread_used = MMAL_FALSE; + vcos_semaphore_post(&component->cmd_sema); + vcos_thread_join(&component->cmd_thread, NULL); + } + + mmal_component_destroy(component->mmal); + for (i = 0; i < component->ports_num; i++) + if (component->ports[i].pool) + mmal_pool_destroy(component->ports[i].pool); + + if (component->cmd_pool) + mmal_pool_destroy(component->cmd_pool); + if (component->cmd_queue) + mmal_queue_destroy(component->cmd_queue); + if (component->cmd_thread_used) + vcos_semaphore_delete(&component->cmd_sema); + vcos_mutex_delete(&component->lock_port); + vcos_mutex_delete(&component->lock); + free(component); + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE MMALOMX_EXPORT(OMX_GetRolesOfComponent)( + OMX_STRING compName, + OMX_U32 *pNumRoles, + OMX_U8 **roles) +{ + OMX_U32 i, num_roles; + MMALOMX_ROLE_T role; + int registry_id; + + LOG_TRACE("compName %s, pNumRoles %p, roles %p", compName, pNumRoles, roles); + + /* Sanity checks */ + if (!compName || !pNumRoles) + return OMX_ErrorBadParameter; + + if (!roles || *pNumRoles > MMALOMX_MAX_ROLES) + num_roles = MMALOMX_MAX_ROLES; + else + num_roles = *pNumRoles; + *pNumRoles = 0; + + /* Find component */ + registry_id = mmalomx_registry_find_component(compName); + if (registry_id < 0) + return OMX_ErrorComponentNotFound; + + /* Enumerate Roles */ + for (i = 0; i < num_roles; i++) + { + role = mmalomx_registry_component_roles(registry_id, i); + if (!role || !mmalomx_role_to_name(role)) + break; + + if(roles) + { + strncpy((char *)roles[i], mmalomx_role_to_name(role), OMX_MAX_STRINGNAME_SIZE); + LOG_DEBUG("found role: %s", roles[i]); + } + } + LOG_DEBUG("found %i roles", (int)i); + *pNumRoles = i; + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE MMALOMX_EXPORT(OMX_GetComponentsOfRole)( + OMX_STRING role, + OMX_U32 *pNumComps, + OMX_U8 **compNames) +{ + OMX_ERRORTYPE status; + OMX_HANDLETYPE handle; + OMX_COMPONENTTYPE *comp; + OMX_U8 name[OMX_MAX_STRINGNAME_SIZE], compRole[OMX_MAX_STRINGNAME_SIZE]; + OMX_U32 nNameLength = OMX_MAX_STRINGNAME_SIZE, nIndex = 0; + OMX_U32 nRoles, nIndexRoles, nComps = 0; + OMX_CALLBACKTYPE callbacks = {0,0,0}; + + LOG_TRACE("role %s, pNumComps %p, compNames %p", role, pNumComps, compNames); + + /* Sanity checks */ + if (!role || !pNumComps) + return OMX_ErrorBadParameter; + + /* Enumerates components */ + while ((status = OMX_ComponentNameEnum((OMX_STRING)name, nNameLength, + nIndex++)) == OMX_ErrorNone) + { + /* Find component */ + status = MMALOMX_IMPORT(OMX_GetHandle)(&handle, (OMX_STRING)name, 0, &callbacks); + if(status != OMX_ErrorNone) continue; + comp = (OMX_COMPONENTTYPE *)handle; + + /* Enumerate Roles */ + status = MMALOMX_IMPORT(OMX_GetRolesOfComponent)((OMX_STRING)name, &nRoles, 0); + if(status != OMX_ErrorNone) continue; + + for (nIndexRoles = 0; nIndexRoles < nRoles; nIndexRoles++) + { + status = comp->ComponentRoleEnum(handle, compRole, nIndexRoles); + if(status != OMX_ErrorNone) break; + + if(!strncmp((char *)role, (char *)compRole, OMX_MAX_STRINGNAME_SIZE)) + { + /* Found one */ + nComps++; + + if(!compNames) break; + + /* Check if enough space was provided for all the component names */ + if(nComps > *pNumComps) return OMX_ErrorBadParameter; + + strncpy((char *)compNames[nComps-1], (char *)name, OMX_MAX_STRINGNAME_SIZE); + + LOG_DEBUG("found component: %s", name); + } + } + + MMALOMX_IMPORT(OMX_FreeHandle)(handle); + } + LOG_DEBUG("found %i components", (int)nComps); + *pNumComps = nComps; + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_API OMX_ERRORTYPE OMX_APIENTRY MMALOMX_EXPORT(OMX_SetupTunnel)( + OMX_HANDLETYPE hOutput, + OMX_U32 nPortOutput, + OMX_HANDLETYPE hInput, + OMX_U32 nPortInput) +{ + OMX_TUNNELSETUPTYPE tunnel_setup = {0, OMX_BufferSupplyUnspecified}; + OMX_ERRORTYPE status = OMX_ErrorNone; + + LOG_TRACE("hOutput %p, nPortOutput %d, hInput %p, nPortInput %d", + hOutput, (int)nPortOutput, hInput, (int)nPortInput); + + /* Sanity checks */ + if (!hOutput && !hInput) + return OMX_ErrorBadParameter; + + if (hOutput) + { + status = ((OMX_COMPONENTTYPE *)hOutput)->ComponentTunnelRequest( + hOutput, nPortOutput, hInput, nPortInput, &tunnel_setup); + if (status != OMX_ErrorNone) + LOG_DEBUG("OMX_SetupTunnel failed on output port (%i)", status); + } + + if (status == OMX_ErrorNone && hInput) + { + status = ((OMX_COMPONENTTYPE *)hInput)->ComponentTunnelRequest( + hInput, nPortInput, hOutput, nPortOutput, &tunnel_setup); + if (status != OMX_ErrorNone) + { + LOG_DEBUG("OMX_SetupTunnel failed on input port (%i)", status); + /* Cancel request on output port */ + if (hOutput) + ((OMX_COMPONENTTYPE *)hOutput)->ComponentTunnelRequest( + hOutput, nPortOutput, NULL, 0, NULL); + } + } + + return status; +} + +OMX_API OMX_ERRORTYPE MMALOMX_EXPORT(OMX_GetContentPipe)( + OMX_HANDLETYPE *hPipe, + OMX_STRING szURI) +{ + MMAL_PARAM_UNUSED(hPipe); + MMAL_PARAM_UNUSED(szURI); + + LOG_TRACE("hPipe %p, szURI %s", hPipe, szURI); + + return OMX_ErrorNotImplemented; +} + +/***************************************************************************** + * Processing thread + *****************************************************************************/ +static void *mmalomx_cmd_thread_func(void *arg) +{ + MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)arg; + VCOS_STATUS_T status; + + while (component->cmd_thread_used) + { + status = vcos_semaphore_wait(&component->cmd_sema); + if (status == VCOS_EAGAIN) + continue; + mmalomx_commands_actions_check(component); + } + + return 0; +} + diff --git a/interface/mmal/openmaxil/mmalomx_logging.c b/interface/mmal/openmaxil/mmalomx_logging.c new file mode 100755 index 0000000..511e9c3 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_logging.c @@ -0,0 +1,176 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vmcs_host/khronos/IL/OMX_Core.h" +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/khronos/IL/OMX_Video.h" +#include "interface/vmcs_host/khronos/IL/OMX_Audio.h" +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" +#include "mmalomx_logging.h" +#include "mmalomx.h" +#include "mmalomx_util_params.h" +#include "interface/vcos/vcos_types.h" + +VCOS_LOG_CAT_T mmalomx_log_category; +static VCOS_LOG_LEVEL_T mmalomx_log_level = VCOS_LOG_ERROR; + +#define MMALOMX_SAT(a,b,c) ((a)<(b)?(a):(a)>(c)?(c):(a)) + +void mmalomx_logging_init(void) +{ + vcos_log_set_level(VCOS_LOG_CATEGORY, mmalomx_log_level); + vcos_log_register("mmalomx", VCOS_LOG_CATEGORY); +} + +void mmalomx_logging_deinit(void) +{ + mmalomx_log_level = mmalomx_log_category.level; + vcos_log_unregister(VCOS_LOG_CATEGORY); +} + +const char *mmalomx_param_to_string(OMX_INDEXTYPE param) +{ + static const struct { + const char *string; + const OMX_INDEXTYPE param; + } param_to_names[] = + { + {"OMX_IndexParamPriorityMgmt", OMX_IndexParamPriorityMgmt}, + {"OMX_IndexParamAudioInit", OMX_IndexParamAudioInit}, + {"OMX_IndexParamImageInit", OMX_IndexParamImageInit}, + {"OMX_IndexParamVideoInit", OMX_IndexParamVideoInit}, + {"OMX_IndexParamOtherInit", OMX_IndexParamOtherInit}, + {"OMX_IndexParamPortDefinition", OMX_IndexParamPortDefinition}, + {"OMX_IndexParamCompBufferSupplier", OMX_IndexParamCompBufferSupplier}, + {"OMX_IndexParamAudioPortFormat", OMX_IndexParamAudioPortFormat}, + {"OMX_IndexParamVideoPortFormat", OMX_IndexParamVideoPortFormat}, + {"OMX_IndexParamImagePortFormat", OMX_IndexParamImagePortFormat}, + {"OMX_IndexParamOtherPortFormat", OMX_IndexParamOtherPortFormat}, + {"OMX_IndexParamAudioPcm", OMX_IndexParamAudioPcm}, + {"OMX_IndexParamAudioAac", OMX_IndexParamAudioAac}, + {"OMX_IndexParamAudioMp3", OMX_IndexParamAudioMp3}, + {"OMX_IndexParamVideoMpeg2", OMX_IndexParamVideoMpeg2}, + {"OMX_IndexParamVideoMpeg4", OMX_IndexParamVideoMpeg4}, + {"OMX_IndexParamVideoWmv", OMX_IndexParamVideoWmv}, + {"OMX_IndexParamVideoRv", OMX_IndexParamVideoRv}, + {"OMX_IndexParamVideoAvc", OMX_IndexParamVideoAvc}, + {"OMX_IndexParamVideoH263", OMX_IndexParamVideoH263}, + {"OMX_IndexParamStandardComponentRole", OMX_IndexParamStandardComponentRole}, + {"OMX_IndexParamContentURI", OMX_IndexParamContentURI}, + {"OMX_IndexParamCommonSensorMode", OMX_IndexParamCommonSensorMode}, + {"OMX_IndexConfigCommonWhiteBalance", OMX_IndexConfigCommonWhiteBalance}, + {"OMX_IndexConfigCommonDigitalZoom", OMX_IndexConfigCommonDigitalZoom}, + {"OMX_IndexConfigCommonExposureValue", OMX_IndexConfigCommonExposureValue}, + {"OMX_IndexConfigCapturing", OMX_IndexConfigCapturing}, + {"OMX_IndexAutoPauseAfterCapture", OMX_IndexAutoPauseAfterCapture}, + {"OMX_IndexConfigCommonRotate", OMX_IndexConfigCommonRotate}, + {"OMX_IndexConfigCommonMirror", OMX_IndexConfigCommonMirror}, + {"OMX_IndexConfigCommonScale", OMX_IndexConfigCommonScale}, + {"OMX_IndexConfigCommonInputCrop", OMX_IndexConfigCommonInputCrop}, + {"OMX_IndexConfigCommonOutputCrop", OMX_IndexConfigCommonOutputCrop}, + {"OMX_IndexParamNumAvailableStreams", OMX_IndexParamNumAvailableStreams}, + {"OMX_IndexParamActiveStream", OMX_IndexParamActiveStream}, + {"OMX_IndexParamVideoBitrate", OMX_IndexParamVideoBitrate}, + {"OMX_IndexParamVideoProfileLevelQuerySupported", OMX_IndexParamVideoProfileLevelQuerySupported}, + + {"OMX_IndexParam unknown", (OMX_INDEXTYPE)0} + }; + const char *name = mmalomx_parameter_name_omx((uint32_t)param); + int i; + + if (name) + return name; + + for(i = 0; param_to_names[i].param && + param_to_names[i].param != param; i++); + + return param_to_names[i].string; +} + +const char *mmalomx_cmd_to_string(OMX_COMMANDTYPE cmd) +{ + static const char *names[] = { + "OMX_CommandStateSet", "OMX_CommandFlush", "OMX_CommandPortDisable", + "OMX_CommandPortEnable", "OMX_CommandMarkBuffer", "OMX_Command unknown" + }; + + return names[MMALOMX_SAT((int)cmd, 0, (int)vcos_countof(names)-1)]; +} + +const char *mmalomx_state_to_string(OMX_STATETYPE state) +{ + static const char *names[] = { + "OMX_StateInvalid", "OMX_StateLoaded", "OMX_StateIdle", + "OMX_StateExecuting", "OMX_StatePause", "OMX_StateWaitForResources", + "OMX_State unknown" + }; + + return names[MMALOMX_SAT((int)state, 0, (int)vcos_countof(names)-1)]; +} + +const char *mmalomx_event_to_string(OMX_EVENTTYPE event) +{ + static const char *names[] = { + "OMX_EventCmdComplete", "OMX_EventError", "OMX_EventMark", + "OMX_EventPortSettingsChanged", "OMX_EventBufferFlag", + "OMX_EventResourcesAcquired", "OMX_EventComponentResumed", + "OMX_EventDynamicResourcesAvailable", "OMX_EventPortFormatDetected", + "OMX_Event unknown" + }; + + return names[MMALOMX_SAT((int)event, 0, (int)vcos_countof(names)-1)]; +} + +const char *mmalomx_error_to_string(OMX_ERRORTYPE error) +{ + static const char *names[] = { + "OMX_ErrorInsufficientResources", "OMX_ErrorUndefined", + "OMX_ErrorInvalidComponentName", "OMX_ErrorComponentNotFound", + "OMX_ErrorInvalidComponent", "OMX_ErrorBadParameter", + "OMX_ErrorNotImplemented", "OMX_ErrorUnderflow", + "OMX_ErrorOverflow", "OMX_ErrorHardware", "OMX_ErrorInvalidState", + "OMX_ErrorStreamCorrupt", "OMX_ErrorPortsNotCompatible", + "OMX_ErrorResourcesLost", "OMX_ErrorNoMore", "OMX_ErrorVersionMismatch", + "OMX_ErrorNotReady", "OMX_ErrorTimeout", "OMX_ErrorSameState", + "OMX_ErrorResourcesPreempted", "OMX_ErrorPortUnresponsiveDuringAllocation", + "OMX_ErrorPortUnresponsiveDuringDeallocation", + "OMX_ErrorPortUnresponsiveDuringStop", "OMX_ErrorIncorrectStateTransition", + "OMX_ErrorIncorrectStateOperation", "OMX_ErrorUnsupportedSetting", + "OMX_ErrorUnsupportedIndex", "OMX_ErrorBadPortIndex", + "OMX_ErrorPortUnpopulated", "OMX_ErrorComponentSuspended", + "OMX_ErrorDynamicResourcesUnavailable", "OMX_ErrorMbErrorsInFrame", + "OMX_ErrorFormatNotDetected", "OMX_ErrorContentPipeOpenFailed", + "OMX_ErrorContentPipeCreationFailed", "OMX_ErrorSeperateTablesUsed", + "OMX_ErrorTunnelingUnsupported", + "OMX_Error unknown" + }; + + if(error == OMX_ErrorNone) return "OMX_ErrorNone"; + + error -= OMX_ErrorInsufficientResources; + return names[MMALOMX_SAT((int)error, 0, (int)vcos_countof(names)-1)]; +} diff --git a/interface/mmal/openmaxil/mmalomx_logging.h b/interface/mmal/openmaxil/mmalomx_logging.h new file mode 100755 index 0000000..eb583ab --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_logging.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Logging functions + */ + +#include "mmal_common.h" +#include "interface/vcos/vcos_logging.h" + +#define VCOS_LOG_CATEGORY (&mmalomx_log_category) +extern VCOS_LOG_CAT_T mmalomx_log_category; +#include + +void mmalomx_logging_init(void); +void mmalomx_logging_deinit(void); + +const char *mmalomx_param_to_string(OMX_INDEXTYPE param); +const char *mmalomx_cmd_to_string(OMX_COMMANDTYPE cmd); +const char *mmalomx_state_to_string(OMX_STATETYPE state); +const char *mmalomx_event_to_string(OMX_EVENTTYPE event); +const char *mmalomx_error_to_string(OMX_ERRORTYPE error); diff --git a/interface/mmal/openmaxil/mmalomx_marks.c b/interface/mmal/openmaxil/mmalomx_marks.c new file mode 100755 index 0000000..a645af6 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_marks.c @@ -0,0 +1,101 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Marking related functions + * + * Note that we do not support buffer marks properly other than for conformance + * testing. For input ports, we just move the mark over to the output port. + */ + +#include "mmalomx.h" +#include "mmalomx_buffer.h" +#include "mmalomx_marks.h" +#include "mmalomx_commands.h" +#include "mmalomx_logging.h" + +#define MMALOMX_GET_MARK(port, mark) \ + mark = &port->marks[port->marks_first]; \ + port->marks_num--; \ + port->marks_first = ++port->marks_first == MAX_MARKS_NUM ? 0 : port->marks_first +#define MMALOMX_PUT_MARK(port, mark) \ + port->marks[(port->marks_first + port->marks_num) % MAX_MARKS_NUM] = *mark; \ + port->marks_num++; + +void mmalomx_mark_process_incoming(MMALOMX_COMPONENT_T *component, + MMALOMX_PORT_T *port, OMX_BUFFERHEADERTYPE *omx_buffer) +{ + /* Tag buffers with OMX marks */ + if (!omx_buffer->hMarkTargetComponent && port->marks_num > 0 && + port->direction == OMX_DirInput) + { + OMX_MARKTYPE *mark; + MMALOMX_GET_MARK(port, mark); + omx_buffer->hMarkTargetComponent = mark->hMarkTargetComponent; + omx_buffer->pMarkData = mark->pMarkData; + + mmalomx_callback_event_handler(component, OMX_EventCmdComplete, + OMX_CommandMarkBuffer, port->index, NULL); + } + /* We do not support buffer marks properly other than for conformance testing. + * For input ports, we just move the mark over to the output port. */ + if (port->direction == OMX_DirInput && omx_buffer->hMarkTargetComponent) + { + OMX_MARKTYPE mark = {omx_buffer->hMarkTargetComponent, omx_buffer->pMarkData}; + unsigned int i; + for (i = 0; i < component->ports_num; i++) + { + if (component->ports[i].direction != OMX_DirOutput || + component->ports[i].marks_num >= MAX_MARKS_NUM) + continue; + + MMALOMX_PUT_MARK((&component->ports[i]), (&mark)); + } + } +} + +void mmalomx_mark_process_outgoing(MMALOMX_COMPONENT_T *component, + MMALOMX_PORT_T *port, OMX_BUFFERHEADERTYPE *omx_buffer) +{ + /* Tag buffers with OMX marks */ + if (port->direction == OMX_DirOutput && + !omx_buffer->hMarkTargetComponent && port->marks_num) + { + OMX_MARKTYPE *mark; + MMALOMX_GET_MARK(port, mark); + omx_buffer->hMarkTargetComponent = mark->hMarkTargetComponent; + omx_buffer->pMarkData = mark->pMarkData; + } + /* Check if we need to trigger a Mark event */ + if (omx_buffer->hMarkTargetComponent && + omx_buffer->hMarkTargetComponent == (OMX_HANDLETYPE)&component->omx) + { + mmalomx_callback_event_handler(component, OMX_EventMark, 0, 0, omx_buffer->pMarkData); + omx_buffer->hMarkTargetComponent = NULL; + omx_buffer->pMarkData = NULL; + } +} diff --git a/interface/mmal/openmaxil/mmalomx_marks.h b/interface/mmal/openmaxil/mmalomx_marks.h new file mode 100755 index 0000000..f543396 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_marks.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Marking related functions + */ + +void mmalomx_mark_process_incoming(MMALOMX_COMPONENT_T *component, + MMALOMX_PORT_T *port, OMX_BUFFERHEADERTYPE *omx_buffer); +void mmalomx_mark_process_outgoing(MMALOMX_COMPONENT_T *component, + MMALOMX_PORT_T *port, OMX_BUFFERHEADERTYPE *omx_buffer); + diff --git a/interface/mmal/openmaxil/mmalomx_parameters.c b/interface/mmal/openmaxil/mmalomx_parameters.c new file mode 100755 index 0000000..c4f97d4 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_parameters.c @@ -0,0 +1,578 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" +#include "mmalomx.h" +#include "mmalomx_parameters.h" +#include "mmalomx_util_params.h" +#include "mmalomx_roles.h" +#include "mmalomx_registry.h" +#include "mmalomx_logging.h" +#include +#include +#include + +#define PARAM_GET_PORT(port, component, index) \ + if (index >= component->ports_num) return OMX_ErrorBadPortIndex; \ + port = &component->ports[index] + +#define MMALOMX_PARAM_GENERIC_MAX 256 + +/** A structure capable of holding any OMX parameter that contains a port */ +typedef struct MMALOMX_PARAM_OMX_GENERIC_T +{ + MMALOMX_PARAM_OMX_HEADER_T header; + uint8_t data[MMALOMX_PARAM_GENERIC_MAX]; +} MMALOMX_PARAM_OMX_GENERIC_T; + +/** A structure capable of holding any OMX parameter that doesn't contain a port */ +typedef struct MMALOMX_PARAM_OMX_GENERIC_PORTLESS_T +{ + MMALOMX_PARAM_OMX_HEADER_PORTLESS_T hdr; + uint8_t data[MMALOMX_PARAM_GENERIC_MAX]; +} MMALOMX_PARAM_OMX_GENERIC_PORTLESS_T; + +/** A structure capable of holding any MMAL parameter */ +typedef struct MMALOMX_PARAM_MMAL_GENERIC_T +{ + MMAL_PARAMETER_HEADER_T header; + uint8_t data[MMALOMX_PARAM_GENERIC_MAX]; +} MMALOMX_PARAM_MMAL_GENERIC_T; + +static OMX_ERRORTYPE mmalomx_parameter_set_xlat(MMALOMX_COMPONENT_T *component, + OMX_INDEXTYPE nParamIndex, OMX_PTR pParam) +{ + const MMALOMX_PARAM_TRANSLATION_T *xlat = mmalomx_find_parameter_from_omx_id(nParamIndex); + MMALOMX_PARAM_OMX_HEADER_T *omx_header = (MMALOMX_PARAM_OMX_HEADER_T *)pParam; + MMALOMX_PARAM_MMAL_GENERIC_T mmal_generic; + MMAL_PARAMETER_HEADER_T *mmal_header = &mmal_generic.header; + MMAL_PORT_T *mmal_port = component->mmal->control; + MMAL_STATUS_T status; + + if (!xlat) + { + LOG_DEBUG("no translation for omx id 0x%08x", nParamIndex); + return OMX_ErrorNotImplemented; + } + + if (!xlat->portless) + { + if (omx_header->nSize < sizeof(*omx_header)) + return OMX_ErrorBadParameter; + if (omx_header->nPortIndex >= component->ports_num) + return OMX_ErrorBadPortIndex; + mmal_port = component->ports[omx_header->nPortIndex].mmal; + } + + if (omx_header->nSize < xlat->omx_size) + return OMX_ErrorBadParameter; + + /* Handle the direct case first */ + if (xlat->type == MMALOMX_PARAM_TRANSLATION_TYPE_DIRECT) + { + mmal_header = (MMAL_PARAMETER_HEADER_T *)(((uint8_t *)pParam) + (xlat->portless ? 0 : 4)); + mmal_generic.header = *mmal_header; + mmal_header->size = omx_header->nSize - (xlat->portless ? 0 : 4); + mmal_header->id = xlat->mmal_id; + status = mmal_port_parameter_set(mmal_port, mmal_header); + *mmal_header = mmal_generic.header; + return mmalil_error_to_omx(status); + } + + if (!xlat->fn.generic && !xlat->fn.simple) + { + // FIXME + return OMX_ErrorNotImplemented; + } + + // FIXME: check size of mmal_generic is sufficient + if (sizeof(mmal_generic) < xlat->mmal_size) + return OMX_ErrorBadParameter; + + mmal_header->size = xlat->mmal_size; + mmal_header->id = xlat->mmal_id; + if (xlat->fn.generic) + status = xlat->fn.generic(MMALOMX_PARAM_MAPPING_TO_MMAL, xlat, mmal_header, pParam, mmal_port); + else + status = xlat->fn.simple(MMALOMX_PARAM_MAPPING_TO_MMAL, mmal_header, pParam); + if (status != MMAL_SUCCESS) + goto error; + + status = mmal_port_parameter_set(mmal_port, mmal_header); + + error: + return mmalil_error_to_omx(status); +} + +static OMX_ERRORTYPE mmalomx_parameter_get_xlat(MMALOMX_COMPONENT_T *component, + OMX_INDEXTYPE nParamIndex, OMX_PTR pParam) +{ + const MMALOMX_PARAM_TRANSLATION_T *xlat = mmalomx_find_parameter_from_omx_id(nParamIndex); + MMALOMX_PARAM_OMX_HEADER_T *omx_header = (MMALOMX_PARAM_OMX_HEADER_T *)pParam; + MMALOMX_PARAM_MMAL_GENERIC_T mmal_generic; + MMAL_PARAMETER_HEADER_T *mmal_header = &mmal_generic.header; + MMAL_PORT_T *mmal_port = component->mmal->control; + MMAL_STATUS_T status = MMAL_SUCCESS; + + if (!xlat) + return OMX_ErrorNotImplemented; + + if (!xlat->portless) + { + if (omx_header->nSize < sizeof(*omx_header)) + return OMX_ErrorBadParameter; + if (omx_header->nPortIndex >= component->ports_num) + return OMX_ErrorBadPortIndex; + mmal_port = component->ports[omx_header->nPortIndex].mmal; + } + + if (omx_header->nSize < xlat->omx_size) + return OMX_ErrorBadParameter; + + /* Handle the direct case first */ + if (xlat->type == MMALOMX_PARAM_TRANSLATION_TYPE_DIRECT) + { + OMX_U32 size; + mmal_header = (MMAL_PARAMETER_HEADER_T *)(((uint8_t *)pParam) + (xlat->portless ? 0 : 4)); + mmal_generic.header = *mmal_header; + mmal_header->size = omx_header->nSize - (xlat->portless ? 0 : 4); + mmal_header->id = xlat->mmal_id; + status = mmal_port_parameter_get(mmal_port, mmal_header); + *mmal_header = mmal_generic.header; + size = mmal_header->size + (xlat->portless ? 0 : 4); + omx_header->nSize = size; + return mmalil_error_to_omx(status); + } + + if (xlat->fn.custom) + { + return mmalil_error_to_omx(xlat->fn.custom(MMALOMX_PARAM_MAPPING_TO_OMX, xlat, mmal_header, + pParam, mmal_port)); + } + + if (xlat->fn.list) + { + OMX_U32 index, elements; + mmal_header = mmal_port_parameter_alloc_get(mmal_port, xlat->mmal_id, + 10*xlat->mmal_size, &status); + if (!mmal_header) + return OMX_ErrorInsufficientResources; + + /* Check we're not requesting too much */ + index = *(OMX_U32 *)(((uint8_t *)pParam) + xlat->xlat_enum_num); + elements = (mmal_header->size - sizeof(MMAL_PARAMETER_HEADER_T)) / + (xlat->mmal_size - sizeof(MMAL_PARAMETER_HEADER_T)); + if (index >= elements) + { + vcos_free(mmal_header); + return OMX_ErrorNoMore; + } + status = xlat->fn.list(MMALOMX_PARAM_MAPPING_TO_OMX, xlat, index, mmal_header, pParam, mmal_port); + vcos_free(mmal_header); + return mmalil_error_to_omx(status); + } + + if (!xlat->fn.generic && !xlat->fn.simple) + { + // FIXME + return OMX_ErrorNotImplemented; + } + + // FIXME: check size of mmal_generic is sufficient + if (sizeof(mmal_generic) < xlat->mmal_size) + return OMX_ErrorBadParameter; + + mmal_header->size = xlat->mmal_size; + mmal_header->id = xlat->mmal_id; + + if (xlat->double_translation) + { + if (xlat->fn.generic) + status = xlat->fn.generic(MMALOMX_PARAM_MAPPING_TO_MMAL, xlat, mmal_header, pParam, mmal_port); + else + status = xlat->fn.simple(MMALOMX_PARAM_MAPPING_TO_MMAL, mmal_header, pParam); + } + if (status != MMAL_SUCCESS) + goto error; + + status = mmal_port_parameter_get(mmal_port, mmal_header); + if (status != MMAL_SUCCESS) + goto error; + + if (xlat->fn.generic) + status = xlat->fn.generic(MMALOMX_PARAM_MAPPING_TO_OMX, xlat, mmal_header, pParam, mmal_port); + else + status = xlat->fn.simple(MMALOMX_PARAM_MAPPING_TO_OMX, mmal_header, pParam); + + error: + return mmalil_error_to_omx(status); +} + +OMX_ERRORTYPE mmalomx_parameter_extension_index_get(OMX_STRING cParameterName, + OMX_INDEXTYPE *pIndex) +{ + const MMALOMX_PARAM_TRANSLATION_T *xlat; + MMAL_BOOL_T config = MMAL_FALSE; + unsigned int i = 0; + + /* Check we're dealing with our extensions */ + if (!vcos_strncasecmp(cParameterName, MMALOMX_COMPONENT_PREFIX, sizeof(MMALOMX_COMPONENT_PREFIX)-1)) + return OMX_ErrorNotImplemented; + cParameterName += sizeof(MMALOMX_COMPONENT_PREFIX)-1; + + /* Check if we're dealing with a config or param */ + if (!vcos_strncasecmp(cParameterName, "index.config.", sizeof("index.config.")-1)) + config = MMAL_TRUE; + if (!config && vcos_strncasecmp(cParameterName, "index.param.", sizeof("index.param.")-1)) + return OMX_ErrorNotImplemented; + if (config) + cParameterName += sizeof("index.config.")-1; + else + cParameterName += sizeof("index.param.")-1; + + /* Loop through all the */ + while ((xlat = mmalomx_find_parameter_enum(i++)) != NULL) + { + const char *name = xlat->omx_name; + + /* We only report vendor extensions */ + if (xlat->omx_id < OMX_IndexVendorStartUnused) + continue; + + /* Strip out the standard prefix */ + if (config) + { + if (!strncmp(name, "OMX_IndexConfigBrcm", sizeof("OMX_IndexConfigBrcm")-1)) + name += sizeof("OMX_IndexConfigBrcm")-1; + else if (!strncmp(name, "OMX_IndexConfig", sizeof("OMX_IndexConfig")-1)) + name += sizeof("OMX_IndexConfig")-1; + else continue; + } + else + { + if (!strncmp(name, "OMX_IndexParamBrcm", sizeof("OMX_IndexParamBrcm")-1)) + name += sizeof("OMX_IndexParamBrcm")-1; + else if (!strncmp(name, "OMX_IndexParam", sizeof("OMX_IndexParam")-1)) + name += sizeof("OMX_IndexParam")-1; + else continue; + } + + /* Compare the last part of the name */ + if (!vcos_strcasecmp(name, cParameterName)) + { + *pIndex = xlat->omx_id; + return OMX_ErrorNone; + } + } + + return OMX_ErrorNotImplemented; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_get_video_param(MMALOMX_PORT_T *port, + uint32_t *profile, uint32_t *level, uint32_t *intraperiod) +{ + MMAL_PARAMETER_VIDEO_PROFILE_T mmal_param = {{MMAL_PARAMETER_PROFILE, sizeof(mmal_param)}, + {{(MMAL_VIDEO_PROFILE_T)0, (MMAL_VIDEO_LEVEL_T)0}}}; + + *profile = *level = *intraperiod = 0; + + mmal_port_parameter_get_uint32(port->mmal, MMAL_PARAMETER_INTRAPERIOD, intraperiod); + + if (mmal_port_parameter_get(port->mmal, &mmal_param.hdr) == MMAL_SUCCESS) + { + *profile = mmalil_video_profile_to_omx(mmal_param.profile[0].profile); + *level = mmalil_video_level_to_omx(mmal_param.profile[0].level); + } + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_parameter_get(MMALOMX_COMPONENT_T *component, + OMX_INDEXTYPE nParamIndex, OMX_PTR pParam) +{ + MMALOMX_PORT_T *port = NULL; + + switch(nParamIndex) + { + /* All OMX_IndexParamVideo parameters are only partially implemented + * and we try and use sensible hard-coded values for the rest. */ + case OMX_IndexParamVideoAvc: + { + OMX_VIDEO_PARAM_AVCTYPE *param = (OMX_VIDEO_PARAM_AVCTYPE *)pParam; + uint32_t profile, level, intraperiod; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->nSize < sizeof(*param)) + return OMX_ErrorBadParameter; + memset(¶m->nSliceHeaderSpacing, 0, + param->nSize - offsetof(OMX_VIDEO_PARAM_AVCTYPE, nSliceHeaderSpacing)); + + mmalomx_get_video_param(port, &profile, &level, &intraperiod); + param->eProfile = (OMX_VIDEO_AVCPROFILETYPE)profile; + param->eLevel = (OMX_VIDEO_AVCLEVELTYPE)level; + param->nPFrames = intraperiod - 1; + param->bUseHadamard = OMX_TRUE; + param->nRefFrames = 1; + param->nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + param->bFrameMBsOnly = OMX_TRUE; + if (param->eProfile != OMX_VIDEO_AVCProfileBaseline) + param->bEntropyCodingCABAC = OMX_TRUE; + param->eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable; + } + return OMX_ErrorNone; + case OMX_IndexParamVideoMpeg4: + { + OMX_VIDEO_PARAM_MPEG4TYPE *param = (OMX_VIDEO_PARAM_MPEG4TYPE *)pParam; + uint32_t profile, level, intraperiod; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->nSize < sizeof(*param)) + return OMX_ErrorBadParameter; + memset(¶m->nSliceHeaderSpacing, 0, + param->nSize - offsetof(OMX_VIDEO_PARAM_MPEG4TYPE, nSliceHeaderSpacing)); + + mmalomx_get_video_param(port, &profile, &level, &intraperiod); + param->eProfile = (OMX_VIDEO_MPEG4PROFILETYPE)profile; + param->eLevel = (OMX_VIDEO_MPEG4LEVELTYPE)level; + param->nPFrames = intraperiod - 1; + param->bACPred = OMX_TRUE; + param->nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + } + return OMX_ErrorNone; + case OMX_IndexParamVideoH263: + { + OMX_VIDEO_PARAM_H263TYPE *param = (OMX_VIDEO_PARAM_H263TYPE *)pParam; + uint32_t profile, level, intraperiod; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->nSize < sizeof(*param)) + return OMX_ErrorBadParameter; + memset(¶m->nPFrames, 0, + param->nSize - offsetof(OMX_VIDEO_PARAM_H263TYPE, nPFrames)); + + mmalomx_get_video_param(port, &profile, &level, &intraperiod); + param->eProfile = (OMX_VIDEO_H263PROFILETYPE)profile; + param->eLevel = (OMX_VIDEO_H263LEVELTYPE)level; + param->nPFrames = intraperiod - 1; + param->nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + } + return OMX_ErrorNone; + case OMX_IndexParamVideoMpeg2: + case OMX_IndexParamVideoWmv: + case OMX_IndexParamVideoRv: + { + OMX_FORMAT_PARAM_TYPE *param = (OMX_FORMAT_PARAM_TYPE *)pParam; + PARAM_GET_PORT(port, component, param->common.nPortIndex); + OMX_U32 offset = offsetof(OMX_PARAM_U32TYPE, nU32); + if (param->common.nSize > sizeof(port->format_param) || + param->common.nSize < offset) + return OMX_ErrorBadParameter; + memcpy(¶m->common.nU32, &port->format_param.common.nU32, + param->common.nSize - offset); + return OMX_ErrorNone; + } + case OMX_IndexParamAudioPcm: + case OMX_IndexParamAudioAac: + case OMX_IndexParamAudioMp3: + case OMX_IndexParamAudioDdp: + { + OMX_FORMAT_PARAM_TYPE *param = (OMX_FORMAT_PARAM_TYPE *)pParam; + PARAM_GET_PORT(port, component, param->common.nPortIndex); + OMX_U32 offset = offsetof(OMX_PARAM_U32TYPE, nU32); + if (param->common.nSize > sizeof(port->format_param) || + param->common.nSize < offset) + return OMX_ErrorBadParameter; + memcpy(¶m->common.nU32, &port->format_param.common.nU32, + param->common.nSize - offset); + mmalil_format_to_omx_audio_param(param, NULL, port->mmal->format); + return OMX_ErrorNone; + } + case OMX_IndexParamBrcmPixelAspectRatio: + { + OMX_CONFIG_POINTTYPE *param = (OMX_CONFIG_POINTTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + param->nX = port->mmal->format->es->video.par.num; + param->nY = port->mmal->format->es->video.par.den; + return OMX_ErrorNone; + } + case OMX_IndexParamColorSpace: + { + OMX_PARAM_COLORSPACETYPE *param = (OMX_PARAM_COLORSPACETYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + param->eColorSpace = mmalil_color_space_to_omx(port->mmal->format->es->video.color_space); + return OMX_ErrorNone; + } + case OMX_IndexConfigCommonOutputCrop: + { + OMX_CONFIG_RECTTYPE *param = (OMX_CONFIG_RECTTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + param->nLeft = port->mmal->format->es->video.crop.x; + param->nTop = port->mmal->format->es->video.crop.y; + param->nWidth = port->mmal->format->es->video.width; + if (port->mmal->format->es->video.crop.width) + param->nWidth = port->mmal->format->es->video.crop.width; + param->nHeight = port->mmal->format->es->video.height; + if (port->mmal->format->es->video.crop.height) + param->nHeight = port->mmal->format->es->video.crop.height; + return OMX_ErrorNone; + } + case OMX_IndexConfigCommonScale: + { + OMX_CONFIG_SCALEFACTORTYPE *param = (OMX_CONFIG_SCALEFACTORTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + param->xWidth = param->xHeight = 1<<16; + if (port->mmal->format->es->video.par.num && + port->mmal->format->es->video.par.den) + param->xWidth = mmal_rational_to_fixed_16_16(port->mmal->format->es->video.par); + return OMX_ErrorNone; + } + default: + return mmalomx_parameter_get_xlat(component, nParamIndex, pParam); + } + + return OMX_ErrorNotImplemented; +} + +/*****************************************************************************/ +static OMX_ERRORTYPE mmalomx_set_video_param(MMALOMX_PORT_T *port, + uint32_t profile, uint32_t level, uint32_t intraperiod) +{ + MMAL_PARAMETER_VIDEO_PROFILE_T mmal_param = {{MMAL_PARAMETER_PROFILE, sizeof(mmal_param)}, + {{(MMAL_VIDEO_PROFILE_T)0, (MMAL_VIDEO_LEVEL_T)0}}}; + OMX_VIDEO_CODINGTYPE coding = + mmalil_encoding_to_omx_video_coding(port->mmal->format->encoding); + + if (mmal_port_parameter_set_uint32(port->mmal, MMAL_PARAMETER_INTRAPERIOD, + intraperiod) != MMAL_SUCCESS) + return OMX_ErrorBadParameter; + + mmal_param.profile[0].profile = (MMAL_VIDEO_PROFILE_T) + mmalil_omx_video_profile_to_mmal(profile, coding); + mmal_param.profile[0].level = (MMAL_VIDEO_LEVEL_T) + mmalil_omx_video_level_to_mmal(level, coding); + if (mmal_port_parameter_set(port->mmal, &mmal_param.hdr) != MMAL_SUCCESS) + return OMX_ErrorBadParameter; + + return OMX_ErrorNone; +} + +/*****************************************************************************/ +OMX_ERRORTYPE mmalomx_parameter_set(MMALOMX_COMPONENT_T *component, + OMX_INDEXTYPE nParamIndex, OMX_PTR pParam) +{ + MMALOMX_PORT_T *port = NULL; + + switch(nParamIndex) + { + /* All OMX_IndexParamVideo parameters are only partially implemented */ + case OMX_IndexParamVideoAvc: + { + OMX_VIDEO_PARAM_AVCTYPE *param = (OMX_VIDEO_PARAM_AVCTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->nSize < sizeof(*param)) + return OMX_ErrorBadParameter; + return mmalomx_set_video_param(port, param->eProfile, param->eLevel, + param->nPFrames + 1); + } + case OMX_IndexParamVideoMpeg4: + { + OMX_VIDEO_PARAM_MPEG4TYPE *param = (OMX_VIDEO_PARAM_MPEG4TYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->nSize < sizeof(*param)) + return OMX_ErrorBadParameter; + return mmalomx_set_video_param(port, param->eProfile, param->eLevel, + param->nPFrames + 1); + } + case OMX_IndexParamVideoH263: + { + OMX_VIDEO_PARAM_H263TYPE *param = (OMX_VIDEO_PARAM_H263TYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + if (param->nSize < sizeof(*param)) + return OMX_ErrorBadParameter; + return mmalomx_set_video_param(port, param->eProfile, param->eLevel, + param->nPFrames + 1); + } + case OMX_IndexParamVideoMpeg2: + case OMX_IndexParamVideoWmv: + case OMX_IndexParamVideoRv: + { + OMX_FORMAT_PARAM_TYPE *param = (OMX_FORMAT_PARAM_TYPE *)pParam; + OMX_U32 offset = offsetof(OMX_PARAM_U32TYPE, nU32); + PARAM_GET_PORT(port, component, param->common.nPortIndex); + if (param->common.nSize > sizeof(port->format_param) || + param->common.nSize < offset) + return OMX_ErrorBadParameter; + memcpy(&port->format_param.common.nU32, ¶m->common.nU32, + param->common.nSize - offset); + return OMX_ErrorNone; + } + case OMX_IndexParamAudioPcm: + case OMX_IndexParamAudioAac: + case OMX_IndexParamAudioMp3: + case OMX_IndexParamAudioDdp: + { + OMX_FORMAT_PARAM_TYPE *param = (OMX_FORMAT_PARAM_TYPE *)pParam; + OMX_U32 offset = offsetof(OMX_PARAM_U32TYPE, nU32); + PARAM_GET_PORT(port, component, param->common.nPortIndex); + if (param->common.nSize > sizeof(port->format_param) || + param->common.nSize < offset) + return OMX_ErrorBadParameter; + memcpy(&port->format_param.common.nU32, ¶m->common.nU32, + param->common.nSize - offset); + mmalil_omx_audio_param_to_format(port->mmal->format, + mmalil_omx_audio_param_index_to_coding(nParamIndex), param); + mmal_port_format_commit(port->mmal); + return OMX_ErrorNone; + } + case OMX_IndexParamBrcmPixelAspectRatio: + { + OMX_CONFIG_POINTTYPE *param = (OMX_CONFIG_POINTTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + port->mmal->format->es->video.par.num = param->nX; + port->mmal->format->es->video.par.den = param->nY; + mmal_rational_simplify(&port->mmal->format->es->video.par); + return mmalil_error_to_omx(mmal_port_format_commit(port->mmal)); + } + case OMX_IndexParamColorSpace: + { + OMX_PARAM_COLORSPACETYPE *param = (OMX_PARAM_COLORSPACETYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + port->mmal->format->es->video.color_space = mmalil_omx_color_space_to_mmal(param->eColorSpace); + return mmalil_error_to_omx(mmal_port_format_commit(port->mmal)); + } + case OMX_IndexParamBrcmVideoCroppingDisable: + { + OMX_CONFIG_PORTBOOLEANTYPE *param = (OMX_CONFIG_PORTBOOLEANTYPE *)pParam; + PARAM_GET_PORT(port, component, param->nPortIndex); + port->no_cropping = param->bEnabled; + return OMX_ErrorNone; + } + default: + return mmalomx_parameter_set_xlat(component, nParamIndex, pParam); + } + + return OMX_ErrorNotImplemented; +} diff --git a/interface/mmal/openmaxil/mmalomx_parameters.h b/interface/mmal/openmaxil/mmalomx_parameters.h new file mode 100755 index 0000000..5c58c86 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_parameters.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Parameters related functions + */ + +OMX_ERRORTYPE mmalomx_parameter_get(MMALOMX_COMPONENT_T *component, + OMX_INDEXTYPE nParamIndex, OMX_PTR pParam); +OMX_ERRORTYPE mmalomx_parameter_set(MMALOMX_COMPONENT_T *component, + OMX_INDEXTYPE nParamIndex, OMX_PTR pParam); +OMX_ERRORTYPE mmalomx_parameter_extension_index_get(OMX_STRING cParameterName, + OMX_INDEXTYPE *pIndex); diff --git a/interface/mmal/openmaxil/mmalomx_registry.c b/interface/mmal/openmaxil/mmalomx_registry.c new file mode 100755 index 0000000..059ed6b --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_registry.c @@ -0,0 +1,159 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_roles.h" +#include "mmalomx_registry.h" +#include "mmalomx_logging.h" +#include + +#ifndef ENABLE_MMALOMX_AUDIO_HW_DECODER +# define ENABLE_MMALOMX_AUDIO_HW_DECODER 0 +#endif +#ifndef ENABLE_MMALOMX_AUDIO_SPDIF +# define ENABLE_MMALOMX_AUDIO_SPDIF 1 +#endif + +static const struct { + const char *omx; + const char *omx_prefix; + const char *mmal; + MMALOMX_ROLE_T roles[MMALOMX_ROLE_MAX]; +} mmalomx_components[] = +{ + {"video.hw.decoder", 0, MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, + {MMALOMX_ROLE_VIDEO_DECODER_H263, MMALOMX_ROLE_VIDEO_DECODER_MPEG2, + MMALOMX_ROLE_VIDEO_DECODER_MPEG4, MMALOMX_ROLE_VIDEO_DECODER_AVC, + MMALOMX_ROLE_VIDEO_DECODER_WMV, MMALOMX_ROLE_VIDEO_DECODER_VPX, + MMALOMX_ROLE_UNDEFINED}}, + {"video.hw.decoder.secure", 0, "drm_alloc.video_decode", + {MMALOMX_ROLE_VIDEO_DECODER_H263, MMALOMX_ROLE_VIDEO_DECODER_MPEG2, + MMALOMX_ROLE_VIDEO_DECODER_MPEG4, MMALOMX_ROLE_VIDEO_DECODER_AVC, + MMALOMX_ROLE_VIDEO_DECODER_WMV, MMALOMX_ROLE_VIDEO_DECODER_VPX, + MMALOMX_ROLE_UNDEFINED}}, + {"video.hw.decoder.divx_drm", 0, "aggregator.pipeline:divx_drm:vc.video_decode", + {MMALOMX_ROLE_VIDEO_DECODER_MPEG4, MMALOMX_ROLE_UNDEFINED}}, + {"video.vpx.decoder", 0, "libvpx", + {MMALOMX_ROLE_VIDEO_DECODER_VPX, MMALOMX_ROLE_UNDEFINED}}, + + {"video.hw.encoder", 0, MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, + {MMALOMX_ROLE_VIDEO_ENCODER_H263, MMALOMX_ROLE_VIDEO_ENCODER_MPEG4, + MMALOMX_ROLE_VIDEO_ENCODER_AVC, MMALOMX_ROLE_UNDEFINED}}, + + {"AIV.play", "", "aivplay", + {MMALOMX_ROLE_AIV_PLAY_101, MMALOMX_ROLE_AIV_PLAY_AVCDDP, MMALOMX_ROLE_UNDEFINED}}, + {"AIV.play.avcddp", "", "aivplay.ddp", + {MMALOMX_ROLE_AIV_PLAY_AVCDDP, MMALOMX_ROLE_AIV_PLAY_101, MMALOMX_ROLE_UNDEFINED}}, + +#if ENABLE_MMALOMX_AUDIO_HW_DECODER + {"audio.hw.decoder", 0, "vc.ril.audio_decode", + {MMALOMX_ROLE_AUDIO_DECODER_AAC, MMALOMX_ROLE_AUDIO_DECODER_MPGA_L1, + MMALOMX_ROLE_AUDIO_DECODER_MPGA_L2, MMALOMX_ROLE_AUDIO_DECODER_MPGA_L3, + MMALOMX_ROLE_AUDIO_DECODER_DDP, MMALOMX_ROLE_UNDEFINED}}, +#endif + +#if ENABLE_MMALOMX_AUDIO_SPDIF + {"audio.spdif", 0, "spdif", + {MMALOMX_ROLE_AUDIO_DECODER_DDP, MMALOMX_ROLE_UNDEFINED}}, +#endif + + {0, 0, 0, {MMALOMX_ROLE_UNDEFINED}} +}; + +int mmalomx_registry_find_component(const char *name) +{ + int i, prefix_size; + const char *prefix; + + for (i = 0; mmalomx_components[i].omx; i++) + { + /* Check the prefix first */ + prefix = mmalomx_components[i].omx_prefix; + if (!prefix) + prefix = MMALOMX_COMPONENT_PREFIX; + prefix_size = strlen(prefix); + if (strncmp(name, prefix, prefix_size)) + continue; + + /* Check the rest of the name */ + if (!strcmp(name + prefix_size, mmalomx_components[i].omx)) + break; + } + + return mmalomx_components[i].mmal ? i : -1; +} + +const char *mmalomx_registry_component_mmal(int id) +{ + if (id >= (int)MMAL_COUNTOF(mmalomx_components) || id < 0) + id = MMAL_COUNTOF(mmalomx_components) - 1; + + return mmalomx_components[id].mmal; +} + +MMALOMX_ROLE_T mmalomx_registry_component_roles(int id, unsigned int index) +{ + unsigned int i; + + if (id >= (int)MMAL_COUNTOF(mmalomx_components) || id < 0) + id = MMAL_COUNTOF(mmalomx_components) - 1; + + for (i = 0; i < index; i++) + if (mmalomx_components[id].roles[i] == MMALOMX_ROLE_UNDEFINED) + break; + + return mmalomx_components[id].roles[i]; +} + +MMAL_BOOL_T mmalomx_registry_component_supports_role(int id, MMALOMX_ROLE_T role) +{ + unsigned int i; + + if (id >= (int)MMAL_COUNTOF(mmalomx_components) || id < 0) + id = MMAL_COUNTOF(mmalomx_components) - 1; + + for (i = 0; mmalomx_components[id].roles[i] != MMALOMX_ROLE_UNDEFINED; i++) + if (mmalomx_components[id].roles[i] == role) + return MMAL_TRUE; + + return MMAL_FALSE; +} + +const char *mmalomx_registry_component_name(int id, const char **prefix) +{ + if (id >= (int)MMAL_COUNTOF(mmalomx_components) || id < 0) + id = MMAL_COUNTOF(mmalomx_components) - 1; + + if (prefix) + { + *prefix = mmalomx_components[id].omx_prefix; + if (!*prefix) + *prefix = MMALOMX_COMPONENT_PREFIX; + } + + return mmalomx_components[id].omx; +} diff --git a/interface/mmal/openmaxil/mmalomx_registry.h b/interface/mmal/openmaxil/mmalomx_registry.h new file mode 100755 index 0000000..3a7c7f5 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_registry.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Registry of components + */ + +#define MMALOMX_COMPONENT_PREFIX "OMX.brcm." + +int mmalomx_registry_find_component(const char *name); + +const char *mmalomx_registry_component_mmal(int id); + +MMALOMX_ROLE_T mmalomx_registry_component_roles(int id, unsigned int index); +MMAL_BOOL_T mmalomx_registry_component_supports_role(int id, MMALOMX_ROLE_T role); + +const char *mmalomx_registry_component_name(int index, const char **prefix); diff --git a/interface/mmal/openmaxil/mmalomx_roles.c b/interface/mmal/openmaxil/mmalomx_roles.c new file mode 100755 index 0000000..17de66f --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_roles.c @@ -0,0 +1,210 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_roles.h" +#include "mmalomx_registry.h" +#include "mmalomx_logging.h" + +static const struct { + const char *name; + MMALOMX_ROLE_T role; +} mmalomx_roles[] = +{ + {"video_decoder.h263", MMALOMX_ROLE_VIDEO_DECODER_H263}, + {"video_decoder.mpeg4", MMALOMX_ROLE_VIDEO_DECODER_MPEG4}, + {"video_decoder.avc", MMALOMX_ROLE_VIDEO_DECODER_AVC}, + {"video_decoder.mpeg2", MMALOMX_ROLE_VIDEO_DECODER_MPEG2}, + {"video_decoder.wmv", MMALOMX_ROLE_VIDEO_DECODER_WMV}, + {"video_decoder.vpx", MMALOMX_ROLE_VIDEO_DECODER_VPX}, + + {"video_encoder.h263", MMALOMX_ROLE_VIDEO_ENCODER_H263}, + {"video_encoder.mpeg4", MMALOMX_ROLE_VIDEO_ENCODER_MPEG4}, + {"video_encoder.avc", MMALOMX_ROLE_VIDEO_ENCODER_AVC}, + + {"audio_decoder.aac", MMALOMX_ROLE_AUDIO_DECODER_AAC}, + {"audio_decoder.mp1", MMALOMX_ROLE_AUDIO_DECODER_MPGA_L1}, + {"audio_decoder.mp2", MMALOMX_ROLE_AUDIO_DECODER_MPGA_L2}, + {"audio_decoder.mp3", MMALOMX_ROLE_AUDIO_DECODER_MPGA_L3}, + {"audio_decoder.ddp", MMALOMX_ROLE_AUDIO_DECODER_DDP}, + + {"AIV.play.101", MMALOMX_ROLE_AIV_PLAY_101}, + {"play.avcddp", MMALOMX_ROLE_AIV_PLAY_AVCDDP}, + + {0, 0} +}; + +const char *mmalomx_role_to_name(MMALOMX_ROLE_T role) +{ + unsigned int i; + for (i = 0; mmalomx_roles[i].name; i++) + if (mmalomx_roles[i].role == role) + break; + return mmalomx_roles[i].name; +} + +MMALOMX_ROLE_T mmalomx_role_from_name(const char *name) +{ + unsigned int i; + for (i = 0; mmalomx_roles[i].name; i++) + if (!strcmp(mmalomx_roles[i].name, name)) + break; + return mmalomx_roles[i].role; +} + +static void mmalomx_format_encoding_from_role(MMALOMX_ROLE_T role, + MMAL_FOURCC_T *encoding, MMAL_ES_TYPE_T *es_type, unsigned int *port) +{ + switch (role) + { + case MMALOMX_ROLE_VIDEO_DECODER_MPEG4: + case MMALOMX_ROLE_VIDEO_ENCODER_MPEG4: + *encoding = MMAL_ENCODING_MP4V; + *es_type = MMAL_ES_TYPE_VIDEO; + break; + case MMALOMX_ROLE_VIDEO_DECODER_AVC: + case MMALOMX_ROLE_VIDEO_ENCODER_AVC: + *encoding = MMAL_ENCODING_H264; + *es_type = MMAL_ES_TYPE_VIDEO; + break; + case MMALOMX_ROLE_VIDEO_DECODER_MPEG2: + *encoding = MMAL_ENCODING_MP2V; + *es_type = MMAL_ES_TYPE_VIDEO; + break; + case MMALOMX_ROLE_VIDEO_DECODER_WMV: + *encoding = MMAL_ENCODING_WMV3; + *es_type = MMAL_ES_TYPE_VIDEO; + break; + case MMALOMX_ROLE_VIDEO_DECODER_VPX: + *encoding = MMAL_ENCODING_VP8; + *es_type = MMAL_ES_TYPE_VIDEO; + break; + case MMALOMX_ROLE_VIDEO_DECODER_H263: + case MMALOMX_ROLE_VIDEO_ENCODER_H263: + *encoding = MMAL_ENCODING_H263; + *es_type = MMAL_ES_TYPE_VIDEO; + break; + case MMALOMX_ROLE_AUDIO_DECODER_AAC: + *encoding = MMAL_ENCODING_MP4A; + *es_type = MMAL_ES_TYPE_AUDIO; + break; + case MMALOMX_ROLE_AUDIO_DECODER_MPGA_L1: + case MMALOMX_ROLE_AUDIO_DECODER_MPGA_L2: + case MMALOMX_ROLE_AUDIO_DECODER_MPGA_L3: + *encoding = MMAL_ENCODING_MPGA; + *es_type = MMAL_ES_TYPE_AUDIO; + break; + + case MMALOMX_ROLE_AUDIO_DECODER_DDP: + *encoding = MMAL_ENCODING_AC3; + *es_type = MMAL_ES_TYPE_AUDIO; + break; + + default: + *encoding = MMAL_ENCODING_UNKNOWN; + *es_type = MMAL_ES_TYPE_UNKNOWN; + break; + } + + switch (role) + { + case MMALOMX_ROLE_VIDEO_ENCODER_H263: + case MMALOMX_ROLE_VIDEO_ENCODER_MPEG4: + case MMALOMX_ROLE_VIDEO_ENCODER_AVC: + *port = 1; + break; + default: + *port = 0; + break; + } +} + +OMX_ERRORTYPE mmalomx_role_set(MMALOMX_COMPONENT_T *component, const char *name) +{ + const MMALOMX_ROLE_T role = mmalomx_role_from_name(name); + MMAL_FOURCC_T encoding = MMAL_ENCODING_UNKNOWN; + MMAL_ES_TYPE_T es_type = MMAL_ES_TYPE_UNKNOWN; + unsigned int port; + MMAL_ES_FORMAT_T *format; + + if (!role || !mmalomx_registry_component_supports_role(component->registry_id, role)) + return OMX_ErrorUnsupportedSetting; + + component->role = role; + + mmalomx_format_encoding_from_role(role, &encoding, &es_type, &port); + if (encoding == MMAL_ENCODING_UNKNOWN) + return OMX_ErrorNone; + + format = component->ports[port].mmal->format; + format->type = es_type; + format->encoding = encoding; + format->bitrate = 64000; + switch (es_type) + { + case MMAL_ES_TYPE_VIDEO: + format->es->video.width = 176; + format->es->video.height = 144; + format->es->video.frame_rate.num = 15; + format->es->video.frame_rate.den = 1; + break; + default: + break; + } + + switch (role) + { + case MMALOMX_ROLE_VIDEO_DECODER_H263: + case MMALOMX_ROLE_VIDEO_ENCODER_H263: + component->ports[port].format_param.h263.eProfile = OMX_VIDEO_H263ProfileBaseline; + component->ports[port].format_param.h263.eLevel = OMX_VIDEO_H263Level10; + component->ports[port].format_param.h263.bPLUSPTYPEAllowed = OMX_FALSE; + component->ports[port].format_param.h263.bForceRoundingTypeToZero = OMX_TRUE; + break; + case MMALOMX_ROLE_VIDEO_DECODER_MPEG4: + case MMALOMX_ROLE_VIDEO_ENCODER_MPEG4: + component->ports[port].format_param.mpeg4.eProfile = OMX_VIDEO_MPEG4ProfileSimple; + component->ports[port].format_param.mpeg4.eLevel = OMX_VIDEO_MPEG4Level1; + break; + case MMALOMX_ROLE_VIDEO_DECODER_AVC: + case MMALOMX_ROLE_VIDEO_ENCODER_AVC: + component->ports[port].format_param.avc.eProfile = OMX_VIDEO_AVCProfileBaseline; + component->ports[port].format_param.avc.eLevel = OMX_VIDEO_AVCLevel1; + break; + case MMALOMX_ROLE_VIDEO_DECODER_WMV: + component->ports[port].format_param.wmv.eFormat = OMX_VIDEO_WMVFormat9; + break; + default: + break; + } + + if (mmal_port_format_commit(component->ports[port].mmal) != MMAL_SUCCESS) + LOG_ERROR("failed to commit format to %s for role %s", + component->ports[port].mmal->name, name); + + return OMX_ErrorNone; +} diff --git a/interface/mmal/openmaxil/mmalomx_roles.h b/interface/mmal/openmaxil/mmalomx_roles.h new file mode 100755 index 0000000..cd4828d --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_roles.h @@ -0,0 +1,61 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Role specific functions + */ + +#define MMALOMX_MAX_ROLES 16 + +typedef enum MMALOMX_ROLE_T { + MMALOMX_ROLE_UNDEFINED = 0, + MMALOMX_ROLE_VIDEO_DECODER_H263, + MMALOMX_ROLE_VIDEO_DECODER_MPEG4, + MMALOMX_ROLE_VIDEO_DECODER_AVC, + MMALOMX_ROLE_VIDEO_DECODER_MPEG2, + MMALOMX_ROLE_VIDEO_DECODER_WMV, + MMALOMX_ROLE_VIDEO_DECODER_VPX, + + MMALOMX_ROLE_VIDEO_ENCODER_H263, + MMALOMX_ROLE_VIDEO_ENCODER_MPEG4, + MMALOMX_ROLE_VIDEO_ENCODER_AVC, + + MMALOMX_ROLE_AUDIO_DECODER_AAC, + MMALOMX_ROLE_AUDIO_DECODER_MPGA_L1, + MMALOMX_ROLE_AUDIO_DECODER_MPGA_L2, + MMALOMX_ROLE_AUDIO_DECODER_MPGA_L3, + MMALOMX_ROLE_AUDIO_DECODER_DDP, + + MMALOMX_ROLE_AIV_PLAY_101, + MMALOMX_ROLE_AIV_PLAY_AVCDDP, + + MMALOMX_ROLE_MAX +} MMALOMX_ROLE_T; + +const char *mmalomx_role_to_name(MMALOMX_ROLE_T role); +MMALOMX_ROLE_T mmalomx_role_from_name(const char *name); +OMX_ERRORTYPE mmalomx_role_set(struct MMALOMX_COMPONENT_T *component, const char *name); diff --git a/interface/mmal/openmaxil/mmalomx_util_params.c b/interface/mmal/openmaxil/mmalomx_util_params.c new file mode 100755 index 0000000..f03b43f --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params.c @@ -0,0 +1,193 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" +#include "mmalomx.h" +#include "mmalomx_util_params.h" +#include "mmalomx_util_params_common.h" + +static const MMALOMX_PARAM_TRANSLATION_T *mmalomx_param_list[] = { + mmalomx_param_xlator_audio, mmalomx_param_xlator_video, + mmalomx_param_xlator_camera, mmalomx_param_xlator_misc}; + +const MMALOMX_PARAM_TRANSLATION_T *mmalomx_find_parameter_enum(unsigned int index) +{ + unsigned int i, j; + + for (i = 0; i < MMAL_COUNTOF(mmalomx_param_list); i++) + { + for (j = 0; mmalomx_param_list[i][j].mmal_id != MMAL_PARAMETER_UNUSED; j++) + { + if (!index--) + break; + } + if (mmalomx_param_list[i][j].mmal_id != MMAL_PARAMETER_UNUSED) + break; + } + + return i < MMAL_COUNTOF(mmalomx_param_list) ? &mmalomx_param_list[i][j] : NULL; +} + +const MMALOMX_PARAM_TRANSLATION_T *mmalomx_find_parameter_from_omx_id(uint32_t id) +{ + unsigned int i, j; + + for (i = 0; i < MMAL_COUNTOF(mmalomx_param_list); i++) + { + for (j = 0; mmalomx_param_list[i][j].mmal_id != MMAL_PARAMETER_UNUSED; j++) + { + if (mmalomx_param_list[i][j].omx_id == id) + break; + } + if (mmalomx_param_list[i][j].mmal_id != MMAL_PARAMETER_UNUSED) + break; + } + + return i < MMAL_COUNTOF(mmalomx_param_list) ? &mmalomx_param_list[i][j] : NULL; +} + +const MMALOMX_PARAM_TRANSLATION_T *mmalomx_find_parameter_from_mmal_id(uint32_t id) +{ + unsigned int i, j; + + for (i = 0; i < MMAL_COUNTOF(mmalomx_param_list); i++) + { + for (j = 0; mmalomx_param_list[i][j].mmal_id != MMAL_PARAMETER_UNUSED; j++) + { + if (mmalomx_param_list[i][j].mmal_id == id) + break; + } + if (mmalomx_param_list[i][j].mmal_id != MMAL_PARAMETER_UNUSED) + break; + } + + return i < MMAL_COUNTOF(mmalomx_param_list) ? &mmalomx_param_list[i][j] : NULL; +} + +const char *mmalomx_parameter_name_omx(uint32_t id) +{ + const MMALOMX_PARAM_TRANSLATION_T *xlat = mmalomx_find_parameter_from_omx_id(id); + return xlat ? xlat->omx_name : 0; +} + +const char *mmalomx_parameter_name_mmal(uint32_t id) +{ + const MMALOMX_PARAM_TRANSLATION_T *xlat = mmalomx_find_parameter_from_mmal_id(id); + return xlat ? xlat->mmal_name : 0; +} + +MMAL_STATUS_T mmalomx_param_mapping_generic(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port) +{ + MMALOMX_PARAM_OMX_HEADER_T *omx_header = (MMALOMX_PARAM_OMX_HEADER_T *)omx_param; + uint8_t *mmal_data = ((uint8_t *)mmal_param) + sizeof(MMAL_PARAMETER_HEADER_T); + uint8_t *omx_data = ((uint8_t *)omx_param) + sizeof(MMALOMX_PARAM_OMX_HEADER_T); + unsigned int size = mmal_param->size - sizeof(MMAL_PARAMETER_HEADER_T); + MMAL_PARAM_UNUSED(mmal_port); + + if (xlat->portless) + omx_data -= sizeof(OMX_U32); + + if (((uint8_t *)omx_param) + omx_header->nSize != + omx_data + size) + { + VCOS_ALERT("mmalomx_param_mapping_generic: mismatch between mmal and omx parameters for (%u)", + (unsigned int)mmal_param->id); + return MMAL_EINVAL; + } + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + memcpy(mmal_data, omx_data, size); + else + memcpy(omx_data, mmal_data, size); + + return MMAL_SUCCESS; +} + +MMAL_STATUS_T mmalomx_param_enum_generic(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port) +{ + uint32_t *mmal = (uint32_t *)(((uint8_t *)mmal_param) + sizeof(MMAL_PARAMETER_HEADER_T)); + uint32_t *omx = (uint32_t *)(((uint8_t *)omx_param) + sizeof(MMALOMX_PARAM_OMX_HEADER_T)); + unsigned int i = 0; + MMAL_PARAM_UNUSED(mmal_port); + + if (xlat->portless) + omx -= 1; + + /* Find translation entry in lookup table */ + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + for (i = 0; i < xlat->xlat_enum_num && xlat->xlat_enum->omx != *omx; i++); + else + for (i = 0; i < xlat->xlat_enum_num && xlat->xlat_enum->mmal != *mmal; i++); + + if (i == xlat->xlat_enum_num) + { + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + VCOS_ALERT("mmalomx_param_enum_generic: omx enum value %u not supported", (unsigned int)*omx); + else + VCOS_ALERT("mmalomx_param_enum_generic: mmal enum value %u not supported", (unsigned int)*mmal); + return MMAL_EINVAL; + } + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + *mmal = xlat->xlat_enum[i].mmal; + else + *omx = xlat->xlat_enum[i].omx; + + return MMAL_SUCCESS; +} + +MMAL_STATUS_T mmalomx_param_rational_generic(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port) +{ + MMAL_RATIONAL_T *mmal = (MMAL_RATIONAL_T *)(((uint8_t *)mmal_param) + sizeof(MMAL_PARAMETER_HEADER_T)); + int32_t *omx = (int32_t *)(((uint8_t *)omx_param) + sizeof(MMALOMX_PARAM_OMX_HEADER_T)); + MMAL_PARAM_UNUSED(mmal_port); + + if (xlat->portless) + omx -= 1; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->num = *omx; + mmal->den = xlat->xlat_enum_num; + mmal_rational_simplify(mmal); + } + else + { + mmal_rational_simplify(mmal); + *omx = 0; + if (mmal->den) + *omx = mmal->num * xlat->xlat_enum_num / mmal->den; + } + + return MMAL_SUCCESS; +} diff --git a/interface/mmal/openmaxil/mmalomx_util_params.h b/interface/mmal/openmaxil/mmalomx_util_params.h new file mode 100755 index 0000000..2520dfb --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params.h @@ -0,0 +1,114 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Parameters related functions + */ + +#ifndef MMALOMX_UTIL_PARAMS_H +#define MMALOMX_UTIL_PARAMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** The structure that all OMX parameters containing a port start with */ +typedef struct MMALOMX_PARAM_OMX_HEADER_T +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; +} MMALOMX_PARAM_OMX_HEADER_T; + +/** The structure that all OMX parameters without a port start with */ +typedef struct MMALOMX_PARAM_OMX_HEADER_PORTLESS_T +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; +} MMALOMX_PARAM_OMX_HEADER_PORTLESS_T; + +typedef enum { + MMALOMX_PARAM_MAPPING_TO_MMAL, + MMALOMX_PARAM_MAPPING_TO_OMX +} MMALOMX_PARAM_MAPPING_DIRECTION; + +typedef struct MMALOMX_PARAM_ENUM_TRANSLATE_T { + uint32_t mmal; + uint32_t omx; +} MMALOMX_PARAM_ENUM_TRANSLATE_T; + +typedef enum MMALOMX_PARAM_TRANSLATION_TYPE_T { + MMALOMX_PARAM_TRANSLATION_TYPE_NONE = 0, + MMALOMX_PARAM_TRANSLATION_TYPE_SIMPLE, + MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, + MMALOMX_PARAM_TRANSLATION_TYPE_DIRECT, +} MMALOMX_PARAM_TRANSLATION_TYPE_T; + +/** MMAL <-> OMX parameter translation information */ +typedef struct MMALOMX_PARAM_TRANSLATION_T +{ + uint32_t mmal_id; /**< MMAL parameter id */ + uint32_t omx_id; /**< OpenMAX IL parameter index */ + unsigned int mmal_size:16; + unsigned int omx_size:16; + unsigned int portless:1; + unsigned int double_translation:1; + MMALOMX_PARAM_TRANSLATION_TYPE_T type:4; + + struct { + MMAL_STATUS_T (*simple)(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param); + MMAL_STATUS_T (*generic)(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const struct MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port); + MMAL_STATUS_T (*list)(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const struct MMALOMX_PARAM_TRANSLATION_T *xlat, unsigned int index, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port); + MMAL_STATUS_T (*custom)(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const struct MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port); + } fn; + + const struct MMALOMX_PARAM_ENUM_TRANSLATE_T *xlat_enum; + unsigned int xlat_enum_num; + + const char *mmal_name; /**< MMAL parameter name */ + const char *omx_name; /**< OMX parameter name */ + +} MMALOMX_PARAM_TRANSLATION_T; + +const char *mmalomx_parameter_name_omx(uint32_t id); +const char *mmalomx_parameter_name_mmal(uint32_t id); +const MMALOMX_PARAM_TRANSLATION_T *mmalomx_find_parameter_from_omx_id(uint32_t id); +const MMALOMX_PARAM_TRANSLATION_T *mmalomx_find_parameter_from_mmal_id(uint32_t id); +const MMALOMX_PARAM_TRANSLATION_T *mmalomx_find_parameter_enum(unsigned int index); + +#ifdef __cplusplus +} +#endif + +#endif /* MMALOMX_UTIL_PARAMS_H */ diff --git a/interface/mmal/openmaxil/mmalomx_util_params_audio.c b/interface/mmal/openmaxil/mmalomx_util_params_audio.c new file mode 100755 index 0000000..2fb81bb --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params_audio.c @@ -0,0 +1,34 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_util_params_common.h" +#include "mmalomx_logging.h" + +const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_audio[] = { + MMALOMX_PARAM_TERMINATE() +}; diff --git a/interface/mmal/openmaxil/mmalomx_util_params_camera.c b/interface/mmal/openmaxil/mmalomx_util_params_camera.c new file mode 100755 index 0000000..936eadc --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params_camera.c @@ -0,0 +1,556 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_util_params_common.h" +#include "mmalomx_logging.h" + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_awb_mode[] = { + {MMAL_PARAM_AWBMODE_OFF, OMX_WhiteBalControlOff}, + {MMAL_PARAM_AWBMODE_AUTO, OMX_WhiteBalControlAuto}, + {MMAL_PARAM_AWBMODE_SUNLIGHT, OMX_WhiteBalControlSunLight}, + {MMAL_PARAM_AWBMODE_CLOUDY, OMX_WhiteBalControlCloudy}, + {MMAL_PARAM_AWBMODE_SHADE, OMX_WhiteBalControlShade}, + {MMAL_PARAM_AWBMODE_TUNGSTEN, OMX_WhiteBalControlTungsten}, + {MMAL_PARAM_AWBMODE_FLUORESCENT, OMX_WhiteBalControlFluorescent}, + {MMAL_PARAM_AWBMODE_INCANDESCENT,OMX_WhiteBalControlIncandescent}, + {MMAL_PARAM_AWBMODE_FLASH, OMX_WhiteBalControlFlash}, + {MMAL_PARAM_AWBMODE_HORIZON, OMX_WhiteBalControlHorizon}, +}; + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_image_effect[] = { + {MMAL_PARAM_IMAGEFX_NONE, OMX_ImageFilterNone}, + {MMAL_PARAM_IMAGEFX_NEGATIVE, OMX_ImageFilterNegative}, + {MMAL_PARAM_IMAGEFX_SOLARIZE, OMX_ImageFilterSolarize}, + {MMAL_PARAM_IMAGEFX_SKETCH, OMX_ImageFilterSketch}, + {MMAL_PARAM_IMAGEFX_DENOISE, OMX_ImageFilterNoise}, + {MMAL_PARAM_IMAGEFX_EMBOSS, OMX_ImageFilterEmboss}, + {MMAL_PARAM_IMAGEFX_OILPAINT, OMX_ImageFilterOilPaint}, + {MMAL_PARAM_IMAGEFX_HATCH, OMX_ImageFilterHatch}, + {MMAL_PARAM_IMAGEFX_GPEN, OMX_ImageFilterGpen}, + {MMAL_PARAM_IMAGEFX_PASTEL, OMX_ImageFilterPastel}, + {MMAL_PARAM_IMAGEFX_WATERCOLOUR, OMX_ImageFilterWatercolor}, + {MMAL_PARAM_IMAGEFX_FILM, OMX_ImageFilterFilm}, + {MMAL_PARAM_IMAGEFX_BLUR, OMX_ImageFilterBlur}, + {MMAL_PARAM_IMAGEFX_SATURATION, OMX_ImageFilterSaturation}, + {MMAL_PARAM_IMAGEFX_COLOURSWAP, OMX_ImageFilterColourSwap}, + {MMAL_PARAM_IMAGEFX_WASHEDOUT, OMX_ImageFilterWashedOut}, + {MMAL_PARAM_IMAGEFX_POSTERISE, OMX_ImageFilterPosterise}, + {MMAL_PARAM_IMAGEFX_COLOURPOINT, OMX_ImageFilterColourPoint}, + {MMAL_PARAM_IMAGEFX_COLOURBALANCE, OMX_ImageFilterColourBalance}, + {MMAL_PARAM_IMAGEFX_CARTOON, OMX_ImageFilterCartoon}, +}; + +static MMAL_STATUS_T mmalomx_param_mapping_colour_effect(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_COLORENHANCEMENTTYPE *omx = (OMX_CONFIG_COLORENHANCEMENTTYPE *)omx_param; + MMAL_PARAMETER_COLOURFX_T *mmal = (MMAL_PARAMETER_COLOURFX_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->enable = omx->bColorEnhancement; + mmal->u = omx->nCustomizedU; + mmal->v = omx->nCustomizedV; + } + else + { + omx->bColorEnhancement = mmal->enable; + omx->nCustomizedU = mmal->u; + omx->nCustomizedV = mmal->v; + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_flicker_avoid[] = { + {MMAL_PARAM_FLICKERAVOID_OFF, OMX_COMMONFLICKERCANCEL_OFF}, + {MMAL_PARAM_FLICKERAVOID_AUTO, OMX_COMMONFLICKERCANCEL_AUTO}, + {MMAL_PARAM_FLICKERAVOID_50HZ, OMX_COMMONFLICKERCANCEL_50}, + {MMAL_PARAM_FLICKERAVOID_60HZ, OMX_COMMONFLICKERCANCEL_60}, +}; + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_flash[] = { + {MMAL_PARAM_FLASH_OFF, OMX_IMAGE_FlashControlOff}, + {MMAL_PARAM_FLASH_AUTO, OMX_IMAGE_FlashControlAuto}, + {MMAL_PARAM_FLASH_ON, OMX_IMAGE_FlashControlOn}, + {MMAL_PARAM_FLASH_REDEYE, OMX_IMAGE_FlashControlRedEyeReduction}, + {MMAL_PARAM_FLASH_FILLIN, OMX_IMAGE_FlashControlFillin}, + {MMAL_PARAM_FLASH_TORCH, OMX_IMAGE_FlashControlTorch}, +}; + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_redeye[] = { + {MMAL_PARAM_REDEYE_OFF, OMX_RedEyeRemovalNone}, + {MMAL_PARAM_REDEYE_ON, OMX_RedEyeRemovalOn}, + {MMAL_PARAM_REDEYE_ON, OMX_RedEyeRemovalAuto}, + {MMAL_PARAM_REDEYE_SIMPLE, OMX_RedEyeRemovalSimple} +}; + +static MMAL_STATUS_T mmalomx_param_mapping_focus(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + static const struct MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_focus[] = { + {MMAL_PARAM_FOCUS_AUTO, OMX_IMAGE_FocusControlAutoLock}, + {MMAL_PARAM_FOCUS_CAF, OMX_IMAGE_FocusControlAuto}, + {MMAL_PARAM_FOCUS_FIXED_INFINITY, OMX_IMAGE_FocusControlInfinityFixed}, + {MMAL_PARAM_FOCUS_FIXED_HYPERFOCAL, OMX_IMAGE_FocusControlHyperfocal}, + {MMAL_PARAM_FOCUS_FIXED_NEAR, OMX_IMAGE_FocusControlNearFixed}, + {MMAL_PARAM_FOCUS_FIXED_MACRO, OMX_IMAGE_FocusControlMacroFixed}, + {MMAL_PARAM_FOCUS_AUTO_MACRO, OMX_IMAGE_FocusControlAutoLockMacro}, + {MMAL_PARAM_FOCUS_AUTO_NEAR, OMX_IMAGE_FocusControlAutoLock}, + {MMAL_PARAM_FOCUS_CAF_NEAR, OMX_IMAGE_FocusControlAutoNear}, + {MMAL_PARAM_FOCUS_CAF_MACRO, OMX_IMAGE_FocusControlAutoMacro}, + {MMAL_PARAM_FOCUS_CAF_FAST, OMX_IMAGE_FocusControlAutoFast}, + {MMAL_PARAM_FOCUS_CAF_MACRO_FAST, OMX_IMAGE_FocusControlAutoMacroFast}, + {MMAL_PARAM_FOCUS_CAF_NEAR_FAST, OMX_IMAGE_FocusControlAutoNearFast}, + /* {MMAL_PARAM_FOCUS_EDOF, ???}, */ + }; + OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE *omx = (OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE *)omx_param; + MMAL_PARAMETER_FOCUS_T *mmal = (MMAL_PARAMETER_FOCUS_T *)mmal_param; + MMALOMX_PARAM_ENUM_FIND(struct MMALOMX_PARAM_ENUM_TRANSLATE_T, xlat_enum, mmalomx_param_enum_focus, + dir, mmal->value, omx->eFocusControl); + + if (!xlat_enum) + return MMAL_EINVAL; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->value = xlat_enum->mmal; + } + else + { + omx->eFocusControl = xlat_enum->omx; + omx->nFocusStepIndex = -1; + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_mirror[] = { + {MMAL_PARAM_MIRROR_NONE, OMX_MirrorNone}, + {MMAL_PARAM_MIRROR_VERTICAL, OMX_MirrorVertical}, + {MMAL_PARAM_MIRROR_HORIZONTAL, OMX_MirrorHorizontal}, + {MMAL_PARAM_MIRROR_BOTH, OMX_MirrorBoth} +}; + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_exposure_mode[] = { + {MMAL_PARAM_EXPOSUREMODE_OFF, OMX_ExposureControlOff}, + {MMAL_PARAM_EXPOSUREMODE_AUTO, OMX_ExposureControlAuto}, + {MMAL_PARAM_EXPOSUREMODE_NIGHT, OMX_ExposureControlNight}, + {MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, OMX_ExposureControlNightWithPreview}, + {MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, OMX_ExposureControlBackLight}, + {MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, OMX_ExposureControlSpotLight}, + {MMAL_PARAM_EXPOSUREMODE_SPORTS, OMX_ExposureControlSports}, + {MMAL_PARAM_EXPOSUREMODE_SNOW, OMX_ExposureControlSnow}, + {MMAL_PARAM_EXPOSUREMODE_BEACH, OMX_ExposureControlBeach}, + {MMAL_PARAM_EXPOSUREMODE_VERYLONG, OMX_ExposureControlVeryLong}, + {MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, OMX_ExposureControlFixedFps}, + {MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, OMX_ExposureControlAntishake}, + {MMAL_PARAM_EXPOSUREMODE_FIREWORKS, OMX_ExposureControlFireworks}, +}; + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_capture_status[] = { + {MMAL_PARAM_CAPTURE_STATUS_NOT_CAPTURING, OMX_NotCapturing}, + {MMAL_PARAM_CAPTURE_STATUS_CAPTURE_STARTED, OMX_CaptureStarted}, + {MMAL_PARAM_CAPTURE_STATUS_CAPTURE_ENDED, OMX_CaptureComplete}, +}; + +static MMAL_STATUS_T mmalomx_param_mapping_face_track(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_face_track[] = { + {MMAL_PARAM_FACE_DETECT_NONE, OMX_FaceDetectionControlNone}, + {MMAL_PARAM_FACE_DETECT_ON, OMX_FaceDetectionControlOn}, + }; + OMX_CONFIG_FACEDETECTIONCONTROLTYPE *omx = (OMX_CONFIG_FACEDETECTIONCONTROLTYPE *)omx_param; + MMAL_PARAMETER_FACE_TRACK_T *mmal = (MMAL_PARAMETER_FACE_TRACK_T *)mmal_param; + MMALOMX_PARAM_ENUM_FIND(MMALOMX_PARAM_ENUM_TRANSLATE_T, xenum, mmalomx_param_enum_face_track, + dir, mmal->mode, omx->eMode); + + if (!xenum) + return MMAL_EINVAL; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->mode = xenum->mmal; + mmal->maxRegions = omx->nMaxRegions; + mmal->frames = omx->nFrames; + mmal->quality = omx->nQuality; + } + else + { + omx->eMode = xenum->omx; + omx->nMaxRegions = mmal->maxRegions; + omx->nFrames = mmal->frames; + omx->nQuality = mmal->quality; + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_mapping_thumb_cfg(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_PARAM_BRCMTHUMBNAILTYPE *omx = (OMX_PARAM_BRCMTHUMBNAILTYPE *)omx_param; + MMAL_PARAMETER_THUMBNAIL_CONFIG_T *mmal = (MMAL_PARAMETER_THUMBNAIL_CONFIG_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->enable = !!omx->bEnable; + mmal->width = omx->nWidth; + mmal->height = omx->nHeight; + mmal->quality = 0; + } + else + { + omx->bEnable = mmal->enable ? OMX_TRUE : OMX_FALSE; + omx->bUsePreview = OMX_FALSE; + omx->nWidth = mmal->width; + omx->nHeight = mmal->height; + /* We don't have an API for setting the thumbnail quality */ + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_stc[] = { + {MMAL_PARAM_STC_MODE_OFF, OMX_TimestampModeZero}, + {MMAL_PARAM_STC_MODE_RAW, OMX_TimestampModeRawStc}, + {MMAL_PARAM_STC_MODE_COOKED, OMX_TimestampModeResetStc}, +}; + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_capture_mode[] = { + {MMAL_PARAM_CAPTUREMODE_WAIT_FOR_END, OMX_CameraCaptureModeWaitForCaptureEnd}, + {MMAL_PARAM_CAPTUREMODE_RESUME_VF_IMMEDIATELY, OMX_CameraCaptureModeResumeViewfinderImmediately}, + /*{MMAL_PARAM_CAPTUREMODE_WAIT_FOR_END_AND_HOLD, OMX_CameraCaptureModeWaitForCaptureEndAndUsePreviousInputImage}, Don't enable for now as not working */ +}; + +static MMAL_STATUS_T mmalomx_param_mapping_sensor_info(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_CAMERAINFOTYPE *omx = (OMX_CONFIG_CAMERAINFOTYPE *)omx_param; + MMAL_PARAMETER_SENSOR_INFORMATION_T *mmal = (MMAL_PARAMETER_SENSOR_INFORMATION_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->f_number = mmal_rational_from_fixed_16_16(omx->xFNumber); + mmal->focal_length = mmal_rational_from_fixed_16_16(omx->xFocalLength); + mmal->model_id = omx->nModelId; + mmal->manufacturer_id = omx->nManufacturerId; + mmal->revision = omx->nRevNum; + } + else + { + omx->xFNumber = mmal_rational_to_fixed_16_16(mmal->f_number); + omx->xFocalLength = mmal_rational_to_fixed_16_16(mmal->focal_length); + omx->nModelId = mmal->model_id; + omx->nManufacturerId = mmal->manufacturer_id; + omx->nRevNum = mmal->revision; + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_flash_select[] = { + {MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_XENON, OMX_CameraFlashXenon}, + {MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_LED, OMX_CameraFlashLED}, + {MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_OTHER, OMX_CameraFlashNone}, +}; + +static MMAL_STATUS_T mmalomx_param_mapping_fov(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_BRCMFOVTYPE *omx = (OMX_CONFIG_BRCMFOVTYPE *)omx_param; + MMAL_PARAMETER_FIELD_OF_VIEW_T *mmal = (MMAL_PARAMETER_FIELD_OF_VIEW_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->fov_h = mmal_rational_from_fixed_16_16(omx->xFieldOfViewHorizontal); + mmal->fov_v = mmal_rational_from_fixed_16_16(omx->xFieldOfViewVertical); + } + else + { + omx->xFieldOfViewHorizontal = mmal_rational_to_fixed_16_16(mmal->fov_h); + omx->xFieldOfViewVertical = mmal_rational_to_fixed_16_16(mmal->fov_v); + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_drc[] = { + {MMAL_PARAMETER_DRC_STRENGTH_OFF, OMX_DynRangeExpOff}, + {MMAL_PARAMETER_DRC_STRENGTH_LOW, OMX_DynRangeExpLow}, + {MMAL_PARAMETER_DRC_STRENGTH_MEDIUM, OMX_DynRangeExpMedium}, + {MMAL_PARAMETER_DRC_STRENGTH_HIGH, OMX_DynRangeExpHigh}, +}; + +static MMAL_STATUS_T mmalomx_param_mapping_algo_ctrl(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_algo_ctrl[] = { + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_FACETRACKING, OMX_CameraDisableAlgorithmFacetracking}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_REDEYE_REDUCTION, OMX_CameraDisableAlgorithmRedEyeReduction}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_VIDEO_STABILISATION, OMX_CameraDisableAlgorithmVideoStabilisation}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_WRITE_RAW, OMX_CameraDisableAlgorithmWriteRaw}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_VIDEO_DENOISE, OMX_CameraDisableAlgorithmVideoDenoise}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_STILLS_DENOISE, OMX_CameraDisableAlgorithmStillsDenoise}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_TEMPORAL_DENOISE, OMX_CameraDisableAlgorithmMax}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_ANTISHAKE, OMX_CameraDisableAlgorithmAntiShake}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_IMAGE_EFFECTS, OMX_CameraDisableAlgorithmImageEffects}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_DYNAMIC_RANGE_COMPRESSION,OMX_CameraDisableAlgorithmDynamicRangeExpansion}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_FACE_RECOGNITION, OMX_CameraDisableAlgorithmFaceRecognition}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_FACE_BEAUTIFICATION, OMX_CameraDisableAlgorithmFaceBeautification}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_SCENE_DETECTION, OMX_CameraDisableAlgorithmSceneDetection}, + { MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_HIGH_DYNAMIC_RANGE, OMX_CameraDisableAlgorithmHighDynamicRange}, + }; + OMX_PARAM_CAMERADISABLEALGORITHMTYPE *omx = (OMX_PARAM_CAMERADISABLEALGORITHMTYPE *)omx_param; + MMAL_PARAMETER_ALGORITHM_CONTROL_T *mmal = (MMAL_PARAMETER_ALGORITHM_CONTROL_T *)mmal_param; + MMALOMX_PARAM_ENUM_FIND(MMALOMX_PARAM_ENUM_TRANSLATE_T, xenum, mmalomx_param_enum_algo_ctrl, + dir, mmal->algorithm, omx->eAlgorithm); + + if (!xenum) + return MMAL_EINVAL; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->algorithm = xenum->mmal; + mmal->enabled = !omx->bDisabled; + } + else + { + omx->eAlgorithm = xenum->omx; + omx->bDisabled = !mmal->enabled; + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_mapping_image_effect_params(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_IMAGEFILTERPARAMSTYPE *omx = (OMX_CONFIG_IMAGEFILTERPARAMSTYPE *)omx_param; + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T *mmal = (MMAL_PARAMETER_IMAGEFX_PARAMETERS_T *)mmal_param; + MMALOMX_PARAM_ENUM_FIND(MMALOMX_PARAM_ENUM_TRANSLATE_T, xenum, mmalomx_param_enum_image_effect, + dir, mmal->effect, omx->eImageFilter); + + if (!xenum) + return MMAL_EINVAL; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + if (omx->nNumParams > MMAL_COUNTOF(mmal->effect_parameter)) + return MMAL_EINVAL; + mmal->effect = xenum->mmal; + mmal->num_effect_params = omx->nNumParams; + memcpy(mmal->effect_parameter, omx->nParams, sizeof(uint32_t) * omx->nNumParams); + } + else + { + if (mmal->num_effect_params > MMAL_COUNTOF(omx->nParams)) + return MMAL_EINVAL; + omx->eImageFilter = xenum->omx; + omx->nNumParams = mmal->num_effect_params; + memcpy(omx->nParams, mmal->effect_parameter, sizeof(uint32_t) * omx->nNumParams); + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_use_case[] = { + {MMAL_PARAM_CAMERA_USE_CASE_UNKNOWN, OMX_CameraUseCaseAuto}, + {MMAL_PARAM_CAMERA_USE_CASE_STILLS_CAPTURE, OMX_CameraUseCaseStills}, + {MMAL_PARAM_CAMERA_USE_CASE_VIDEO_CAPTURE, OMX_CameraUseCaseVideo}, +}; + +static MMAL_STATUS_T mmalomx_param_mapping_fps_range(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_PARAM_BRCMFRAMERATERANGETYPE *omx = (OMX_PARAM_BRCMFRAMERATERANGETYPE *)omx_param; + MMAL_PARAMETER_FPS_RANGE_T *mmal = (MMAL_PARAMETER_FPS_RANGE_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->fps_low = mmal_rational_from_fixed_16_16(omx->xFramerateLow); + mmal->fps_high = mmal_rational_from_fixed_16_16(omx->xFramerateLow); + } + else + { + omx->xFramerateLow = mmal_rational_to_fixed_16_16(mmal->fps_low); + omx->xFramerateLow = mmal_rational_to_fixed_16_16(mmal->fps_high); + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_mapping_ev_comp(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_PARAM_S32TYPE *omx = (OMX_PARAM_S32TYPE *)omx_param; + MMAL_PARAMETER_INT32_T *mmal = (MMAL_PARAMETER_INT32_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + mmal->value = (omx->nS32 * 6) >> 16; + else + omx->nS32 = (mmal->value << 16) / 6; + + return MMAL_SUCCESS; +} + +const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_camera[] = { + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_ROTATION, MMAL_PARAMETER_INT32_T, + OMX_IndexConfigCommonRotate, OMX_CONFIG_ROTATIONTYPE), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_AWB_MODE, MMAL_PARAM_AWBMODE_T, + OMX_IndexConfigCommonWhiteBalance, OMX_CONFIG_WHITEBALCONTROLTYPE, mmalomx_param_enum_awb_mode), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_IMAGE_EFFECT, MMAL_PARAMETER_IMAGEFX_T, + OMX_IndexConfigCommonImageFilter, OMX_CONFIG_IMAGEFILTERTYPE, mmalomx_param_enum_image_effect), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_COLOUR_EFFECT, MMAL_PARAMETER_COLOURFX_T, + OMX_IndexConfigCommonColorEnhancement, OMX_CONFIG_COLORENHANCEMENTTYPE, mmalomx_param_mapping_colour_effect), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_FLICKER_AVOID, MMAL_PARAMETER_FLICKERAVOID_T, + OMX_IndexConfigCommonFlickerCancellation, OMX_CONFIG_FLICKERCANCELTYPE, mmalomx_param_enum_flicker_avoid), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_FLASH, MMAL_PARAMETER_FLASH_T, + OMX_IndexParamFlashControl, OMX_IMAGE_PARAM_FLASHCONTROLTYPE, mmalomx_param_enum_flash), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_REDEYE, MMAL_PARAMETER_REDEYE_T, + OMX_IndexConfigCommonRedEyeRemoval, OMX_CONFIG_REDEYEREMOVALTYPE, mmalomx_param_enum_redeye), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FOCUS, MMAL_PARAMETER_FOCUS_T, + OMX_IndexConfigFocusControl, OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE, mmalomx_param_mapping_focus), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_REDEYE, MMAL_PARAMETER_REDEYE_T, + OMX_IndexConfigCommonRedEyeRemoval, OMX_CONFIG_REDEYEREMOVALTYPE, mmalomx_param_enum_flash), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_ZOOM, MMAL_PARAMETER_SCALEFACTOR_T, + OMX_IndexConfigCommonDigitalZoom, OMX_CONFIG_SCALEFACTORTYPE), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_MIRROR, MMAL_PARAMETER_MIRROR_T, + OMX_IndexConfigCommonMirror, OMX_CONFIG_MIRRORTYPE, mmalomx_param_enum_mirror), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_CAMERA_NUM, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamCameraDeviceNumber, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_BOOLEAN(MMAL_PARAMETER_CAPTURE, + OMX_IndexConfigPortCapturing), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_EXPOSURE_MODE, MMAL_PARAMETER_EXPOSUREMODE_T, + OMX_IndexConfigCommonExposure, OMX_CONFIG_EXPOSURECONTROLTYPE, mmalomx_param_enum_exposure_mode), + MMALOMX_PARAM_ENUM_PORTLESS(MMAL_PARAMETER_CAPTURE_STATUS, MMAL_PARAMETER_CAPTURE_STATUS_T, + OMX_IndexParamCaptureStatus, OMX_PARAM_CAPTURESTATETYPE, mmalomx_param_enum_capture_status), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FACE_TRACK, MMAL_PARAMETER_FACE_TRACK_T, + OMX_IndexConfigCommonFaceDetectionControl, OMX_CONFIG_FACEDETECTIONCONTROLTYPE, mmalomx_param_mapping_face_track), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, + OMX_IndexConfigDrawBoxAroundFaces), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_JPEG_Q_FACTOR, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamQFactor, OMX_IMAGE_PARAM_QFACTORTYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_EXIF_DISABLE, + OMX_IndexParamBrcmDisableEXIF), + MMALOMX_PARAM_STRAIGHT_MAPPING_PORTLESS(MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, MMAL_PARAMETER_THUMBNAIL_CONFIG_T, + OMX_IndexParamBrcmThumbnail, OMX_PARAM_BRCMTHUMBNAILTYPE, mmalomx_param_mapping_thumb_cfg), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_USE_STC, MMAL_PARAMETER_CAMERA_STC_MODE_T, + OMX_IndexParamCommonUseStcTimestamps, OMX_PARAM_TIMESTAMPMODETYPE, mmalomx_param_enum_stc), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_STABILISATION, MMAL_PARAMETER_BOOLEAN_T, + OMX_IndexConfigCommonFrameStabilisation, OMX_CONFIG_FRAMESTABTYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_ENABLE_DPF_FILE, + OMX_IndexParamUseDynamicParameterFile), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_DPF_FAIL_IS_FATAL, + OMX_IndexParamDynamicParameterFileFailFatal), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_CAPTURE_MODE, MMAL_PARAMETER_CAPTUREMODE_T, + OMX_IndexParamCameraCaptureMode, OMX_PARAM_CAMERACAPTUREMODETYPE, mmalomx_param_enum_capture_mode), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_INPUT_CROP, MMAL_PARAMETER_INPUT_CROP_T, + OMX_IndexConfigInputCropPercentages, OMX_CONFIG_INPUTCROPTYPE), + MMALOMX_PARAM_STRAIGHT_MAPPING_PORTLESS(MMAL_PARAMETER_SENSOR_INFORMATION, MMAL_PARAMETER_SENSOR_INFORMATION_T, + OMX_IndexConfigCameraInfo, OMX_CONFIG_CAMERAINFOTYPE, mmalomx_param_mapping_sensor_info), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_FLASH_SELECT, MMAL_PARAMETER_FLASH_SELECT_T, + OMX_IndexParamCameraFlashType, OMX_PARAM_CAMERAFLASHTYPE, mmalomx_param_enum_flash_select), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FIELD_OF_VIEW, MMAL_PARAMETER_FIELD_OF_VIEW_T, + OMX_IndexConfigFieldOfView, OMX_CONFIG_BRCMFOVTYPE, mmalomx_param_mapping_fov), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, + OMX_IndexConfigBrcmHighDynamicRange), + MMALOMX_PARAM_ENUM_PORTLESS(MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, MMAL_PARAMETER_DRC_T, + OMX_IndexConfigDynamicRangeExpansion, OMX_CONFIG_DYNAMICRANGEEXPANSIONTYPE, mmalomx_param_enum_drc), + MMALOMX_PARAM_STRAIGHT_MAPPING_PORTLESS(MMAL_PARAMETER_ALGORITHM_CONTROL, MMAL_PARAMETER_ALGORITHM_CONTROL_T, + OMX_IndexParamCameraDisableAlgorithm, OMX_PARAM_CAMERADISABLEALGORITHMTYPE, mmalomx_param_mapping_algo_ctrl), + MMALOMX_PARAM_RATIONAL(MMAL_PARAMETER_SHARPNESS, MMAL_PARAMETER_RATIONAL_T, + OMX_IndexConfigCommonSharpness, OMX_CONFIG_SHARPNESSTYPE, 100), + MMALOMX_PARAM_RATIONAL(MMAL_PARAMETER_CONTRAST, MMAL_PARAMETER_RATIONAL_T, + OMX_IndexConfigCommonContrast, OMX_CONFIG_CONTRASTTYPE, 100), + MMALOMX_PARAM_RATIONAL(MMAL_PARAMETER_BRIGHTNESS, MMAL_PARAMETER_RATIONAL_T, + OMX_IndexConfigCommonContrast, OMX_CONFIG_CONTRASTTYPE, 100), + MMALOMX_PARAM_RATIONAL(MMAL_PARAMETER_SATURATION, MMAL_PARAMETER_RATIONAL_T, + OMX_IndexConfigCommonSaturation, OMX_CONFIG_SATURATIONTYPE, 100), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_ANTISHAKE, + OMX_IndexConfigStillsAntiShakeEnable), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, MMAL_PARAMETER_IMAGEFX_PARAMETERS_T, + OMX_IndexConfigCommonImageFilterParameters, OMX_CONFIG_IMAGEFILTERPARAMSTYPE, mmalomx_param_mapping_image_effect_params), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_CAMERA_BURST_CAPTURE, + OMX_IndexConfigBurstCapture), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_CAMERA_MIN_ISO, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigCameraIsoReferenceValue, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_ENUM_PORTLESS(MMAL_PARAMETER_CAMERA_USE_CASE, MMAL_PARAMETER_CAMERA_USE_CASE_T, + OMX_IndexConfigCameraUseCase, OMX_CONFIG_CAMERAUSECASETYPE, mmalomx_param_enum_use_case), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_CAPTURE_STATS_PASS, + OMX_IndexConfigCameraEnableStatsPass), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamCameraCustomSensorConfig, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_ENABLE_REGISTER_FILE, + OMX_IndexConfigBrcmUseRegisterFile), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, + OMX_IndexConfigBrcmRegisterFileFailFatal), + MMALOMX_PARAM_PASSTHROUGH_PORTLESS(MMAL_PARAMETER_CONFIGFILE_REGISTERS, MMAL_PARAMETER_CONFIGFILE_T, + OMX_IndexParamBrcmConfigFileRegisters, OMX_PARAM_BRCMCONFIGFILETYPE), + MMALOMX_PARAM_PASSTHROUGH_PORTLESS(MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, MMAL_PARAMETER_CONFIGFILE_CHUNK_T, + OMX_IndexParamBrcmConfigFileChunkRegisters, OMX_PARAM_BRCMCONFIGFILECHUNKTYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_JPEG_ATTACH_LOG, + OMX_IndexParamBrcmAttachLog), + MMALOMX_PARAM_PASSTHROUGH_PORTLESS(MMAL_PARAMETER_ZERO_SHUTTER_LAG, MMAL_PARAMETER_ZEROSHUTTERLAG_T, + OMX_IndexParamCameraZeroShutterLag, OMX_CONFIG_ZEROSHUTTERLAGTYPE), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FPS_RANGE, MMAL_PARAMETER_FPS_RANGE_T, + OMX_IndexParamBrcmFpsRange, OMX_PARAM_BRCMFRAMERATERANGETYPE, mmalomx_param_mapping_fps_range), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, MMAL_PARAMETER_INT32_T, + OMX_IndexParamCaptureExposureCompensation, OMX_PARAM_S32TYPE, mmalomx_param_mapping_ev_comp), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_SW_SHARPEN_DISABLE, + OMX_IndexParamSWSharpenDisable), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_FLASH_REQUIRED, + OMX_IndexConfigBrcmFlashRequired), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_SW_SATURATION_DISABLE, + OMX_IndexParamSWSaturationDisable), + MMALOMX_PARAM_TERMINATE() +}; + +#if 0 /* Conversions which are still left to implement */ +MMALOMX_PARAM_CUSTOM(MMAL_PARAMETER_CAMERA_CONFIG, MMAL_PARAMETER_CAMERA_CONFIG_T, + 0, 0, mmal_ril_param_set_cam_config), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_EXPOSURE_COMP, MMAL_PARAMETER_INT32_T, + OMX_IndexConfigCommonExposureValue, OMX_CONFIG_EXPOSUREVALUETYPE, 0), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_EXP_METERING_MODE, MMAL_PARAMETER_EXPOSUREMETERINGMODE_T, + OMX_IndexConfigCommonExposureValue, OMX_CONFIG_EXPOSUREVALUETYPE, 0), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_ISO, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigCommonExposureValue, OMX_CONFIG_EXPOSUREVALUETYPE, 0), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FOCUS_STATUS, MMAL_PARAMETER_FOCUS_STATUS_T, + OMX_IndexConfigCommonFocusStatus, OMX_PARAM_FOCUSSTATUSTYPE, mmalomx_param_mapping_focus_status), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_EXIF, MMAL_PARAMETER_EXIF_T, + OMX_IndexConfigMetadataItem, OMX_CONFIG_METADATAITEMTYPE, 0), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FACE_TRACK_RESULTS, MMAL_PARAMETER_FACE_TRACK_RESULTS_T, + OMX_IndexConfigCommonFaceDetectionRegion, OMX_CONFIG_FACEDETECTIONREGIONTYPE, 0), +MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_ENABLE_RAW_CAPTURE, MMAL_PARAMETER_BOOLEAN_T, + OMX_IndexConfigCaptureRawImageURI, OMX_PARAM_CONTENTURITYPE, 0), +MMALOMX_PARAM_PASSTHROUGH_PORTLESS(MMAL_PARAMETER_DPF_FILE, MMAL_PARAMETER_URI_T, + OMX_IndexParamDynamicParameterFile, OMX_PARAM_CONTENTURITYPE), +MMALOMX_PARAM_PASSTHROUGH_PORTLESS(MMAL_PARAMETER_FOCUS_REGIONS, , + OMX_IndexConfigCommonFocusRegionXY, ), +#endif diff --git a/interface/mmal/openmaxil/mmalomx_util_params_common.h b/interface/mmal/openmaxil/mmalomx_util_params_common.h new file mode 100755 index 0000000..f0f2f96 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params_common.h @@ -0,0 +1,133 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * OpenMAX IL adaptation layer for MMAL - Parameters related functions + */ + +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" +#include "mmalomx_util_params.h" +#include "util/mmal_util_rational.h" + +/* Sanity check that OMX is defining the right int32 types */ +vcos_static_assert(sizeof(OMX_U32) == 4); + +MMAL_STATUS_T mmalomx_param_enum_generic(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const struct MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal, OMX_PTR omx, MMAL_PORT_T *mmal_port); +MMAL_STATUS_T mmalomx_param_rational_generic(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const struct MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal, OMX_PTR omx, MMAL_PORT_T *mmal_port); +MMAL_STATUS_T mmalomx_param_mapping_generic(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const struct MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal, OMX_PTR omx, MMAL_PORT_T *mmal_port); + +extern const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_audio[]; +extern const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_video[]; +extern const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_camera[]; +extern const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_misc[]; + +#define MMALOMX_PARAM_ENUM_FIND(TYPE, VAR, TABLE, DIR, MMAL, OMX) \ + const TYPE *VAR = TABLE; \ + const TYPE *VAR##_end = VAR + MMAL_COUNTOF(TABLE); \ + if (DIR == MMALOMX_PARAM_MAPPING_TO_MMAL) \ + while (VAR < VAR##_end && VAR->omx != OMX) VAR++; \ + else \ + while (VAR < VAR##_end && VAR->mmal != MMAL) VAR++; \ + do { if (VAR == VAR##_end) { \ + VAR = 0; \ + if (DIR == MMALOMX_PARAM_MAPPING_TO_MMAL) \ + VCOS_ALERT("omx enum value %u not supported", (unsigned int)OMX); \ + else \ + VCOS_ALERT("mmal enum value %u not supported", (unsigned int)MMAL); \ + } } while(0) + +#define mmalomx_ct_assert(e) (sizeof(char[1 - 2*!(e)])) + +/** List of macros used to define parameters mapping */ +#define MMALOMX_PARAM_PASSTHROUGH(a,b,c,d) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), \ + !(offsetof(d, nPortIndex) | mmalomx_ct_assert(sizeof(b)+4==sizeof(d))), \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_mapping_generic, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_BOOLEAN(a, b) \ + MMALOMX_PARAM_PASSTHROUGH(a, MMAL_PARAMETER_BOOLEAN_T, b, OMX_CONFIG_PORTBOOLEANTYPE) +#define MMALOMX_PARAM_PASSTHROUGH_PORTLESS(a,b,c,d) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), \ + !!(mmalomx_ct_assert(sizeof(b)==sizeof(d))), \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_mapping_generic, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_BOOLEAN_PORTLESS(a, b) \ + MMALOMX_PARAM_PASSTHROUGH_PORTLESS(a, MMAL_PARAMETER_BOOLEAN_T, b, OMX_CONFIG_BOOLEANTYPE) +#define MMALOMX_PARAM_PASSTHROUGH_PORTLESS_DOUBLE_TRANSLATION(a,b,c,d) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), \ + !!(mmalomx_ct_assert(sizeof(b)==sizeof(d))), \ + 1, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_mapping_generic, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_DIRECT_PORTLESS(a,b,c,d) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 1, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_DIRECT, {0, 0, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_STRAIGHT_MAPPING(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), \ + !offsetof(d, nPortIndex), \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_SIMPLE, {e, 0, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_STRAIGHT_MAPPING_PORTLESS(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 1, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_SIMPLE, {e, 0, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_STRAIGHT_MAPPING_DOUBLE_TRANSLATION(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 0, \ + 1, MMALOMX_PARAM_TRANSLATION_TYPE_SIMPLE, {e, 0, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_ENUM(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 0, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_enum_generic, 0, 0}, e, MMAL_COUNTOF(e), \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_ENUM_PORTLESS(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 1, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_enum_generic, 0, 0}, e, MMAL_COUNTOF(e), \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_RATIONAL(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 0, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_rational_generic, 0, 0}, 0, e, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_RATIONAL_PORTLESS(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 1, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, mmalomx_param_rational_generic, 0, 0}, 0, e, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_CUSTOM(a,b,c,d,e) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 0, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, e, 0, 0}, 0, 0, \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_LIST(a,b,c,d,e,f) \ + {a, (uint32_t)c, sizeof(b), sizeof(d), 0, \ + 0, MMALOMX_PARAM_TRANSLATION_TYPE_CUSTOM, {0, 0, f, 0}, 0, offsetof(d,e), \ + MMAL_TO_STRING(a), MMAL_TO_STRING(c)} +#define MMALOMX_PARAM_TERMINATE() \ + {MMAL_PARAMETER_UNUSED, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, 0, 0, 0, 0} diff --git a/interface/mmal/openmaxil/mmalomx_util_params_misc.c b/interface/mmal/openmaxil/mmalomx_util_params_misc.c new file mode 100755 index 0000000..e364033 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params_misc.c @@ -0,0 +1,157 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_util_params_common.h" +#include "mmalomx_logging.h" + +static MMAL_STATUS_T mmalomx_param_mapping_event_request(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_REQUESTCALLBACKTYPE *omx = (OMX_CONFIG_REQUESTCALLBACKTYPE *)omx_param; + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T *mmal = (MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T *)mmal_param; + const MMALOMX_PARAM_TRANSLATION_T *change_xlat; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + change_xlat = mmalomx_find_parameter_from_omx_id(omx->nIndex); + if (!change_xlat) + { + VCOS_ALERT("ommalomx_param_mapping_event_request: omx parameter " + "0x%08x not recognised", omx->nIndex); + return MMAL_EINVAL; + } + + mmal->change_id = change_xlat->mmal_id; + mmal->enable = omx->bEnable; + } + else + { + change_xlat = mmalomx_find_parameter_from_mmal_id(mmal->change_id); + if (!change_xlat) + { + VCOS_ALERT("mmalomx_param_mapping_event_request: mmal parameter " + "0x%08x not recognised", mmal->change_id); + return MMAL_EINVAL; + } + + omx->nIndex = change_xlat->omx_id; + omx->bEnable = mmal->enable; + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_mapping_statistics(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_BRCMPORTSTATSTYPE *omx = (OMX_CONFIG_BRCMPORTSTATSTYPE *)omx_param; + MMAL_PARAMETER_STATISTICS_T *mmal = (MMAL_PARAMETER_STATISTICS_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->buffer_count = omx->nBufferCount; + mmal->frame_count = omx->nImageCount + omx->nFrameCount; + mmal->frames_skipped = omx->nFrameSkips; + mmal->frames_discarded = omx->nDiscards; + mmal->eos_seen = omx->nEOS; + mmal->maximum_frame_bytes = omx->nMaxFrameSize; + mmal->total_bytes = omx_ticks_to_s64(omx->nByteCount); + mmal->corrupt_macroblocks = omx->nCorruptMBs; + } + else + { + omx->nBufferCount = mmal->buffer_count; + omx->nFrameCount = mmal->frame_count; + omx->nImageCount = 0; + omx->nFrameSkips = mmal->frames_skipped; + omx->nDiscards = mmal->frames_discarded; + omx->nEOS = mmal->eos_seen; + omx->nMaxFrameSize = mmal->maximum_frame_bytes; + omx->nByteCount = omx_ticks_from_s64(mmal->total_bytes); + omx->nCorruptMBs = mmal->corrupt_macroblocks; + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_mapping_buffer_flags(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_PARAM_U32TYPE *omx = (OMX_PARAM_U32TYPE *)omx_param; + MMAL_PARAMETER_UINT32_T *mmal = (MMAL_PARAMETER_UINT32_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + mmal->value = mmalil_buffer_flags_to_mmal(omx->nU32); + else + omx->nU32 = mmalil_buffer_flags_to_omx(mmal->value); + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_mapping_time(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_TIME_CONFIG_TIMESTAMPTYPE *omx = (OMX_TIME_CONFIG_TIMESTAMPTYPE *)omx_param; + MMAL_PARAMETER_INT64_T *mmal = (MMAL_PARAMETER_INT64_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + mmal->value = omx_ticks_to_s64(omx->nTimestamp); + else + omx->nTimestamp = omx_ticks_from_s64(mmal->value); + + return MMAL_SUCCESS; +} + +const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_misc[] = { + MMALOMX_PARAM_STRAIGHT_MAPPING_DOUBLE_TRANSLATION(MMAL_PARAMETER_CHANGE_EVENT_REQUEST, MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T, + OMX_IndexConfigRequestCallback, OMX_CONFIG_REQUESTCALLBACKTYPE, + mmalomx_param_mapping_event_request), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_STATISTICS, MMAL_PARAMETER_STATISTICS_T, + OMX_IndexConfigBrcmPortStats, OMX_CONFIG_BRCMPORTSTATSTYPE, + mmalomx_param_mapping_statistics), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_MEM_USAGE, MMAL_PARAMETER_MEM_USAGE_T, + OMX_IndexConfigBrcmPoolMemAllocSize, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_BUFFER_FLAG_FILTER, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigBrcmBufferFlagFilter, OMX_PARAM_U32TYPE, + mmalomx_param_mapping_buffer_flags), + MMALOMX_PARAM_BOOLEAN(MMAL_PARAMETER_ZERO_COPY, + OMX_IndexParamBrcmZeroCopy), + MMALOMX_PARAM_BOOLEAN(MMAL_PARAMETER_LOCKSTEP_ENABLE, + OMX_IndexParamBrcmLockStepEnable), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_POWERMON_ENABLE, + OMX_IndexConfigBrcmPowerMonitor), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_CLOCK_TIME, MMAL_PARAMETER_INT64_T, + OMX_IndexConfigTimeCurrentMediaTime, OMX_TIME_CONFIG_TIMESTAMPTYPE, + mmalomx_param_mapping_time), + MMALOMX_PARAM_TERMINATE() +}; + +#if 0 +/* Conversions which are not done here. Should part of the core. */ +MMAL_PARAMETER_SUPPORTED_ENCODINGS +#endif diff --git a/interface/mmal/openmaxil/mmalomx_util_params_video.c b/interface/mmal/openmaxil/mmalomx_util_params_video.c new file mode 100755 index 0000000..83e3724 --- /dev/null +++ b/interface/mmal/openmaxil/mmalomx_util_params_video.c @@ -0,0 +1,277 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmalomx.h" +#include "mmalomx_util_params_common.h" +#include "mmalomx_logging.h" + +static void rect_to_omx(OMX_DISPLAYRECTTYPE *dst, const MMAL_RECT_T *src) +{ + dst->x_offset = src->x; + dst->y_offset = src->y; + dst->width = src->width; + dst->height = src->height; +} + +static void rect_to_mmal(MMAL_RECT_T *dst, const OMX_DISPLAYRECTTYPE *src) +{ + dst->x = src->x_offset; + dst->y = src->y_offset; + dst->width = src->width; + dst->height = src->height; +} + +static MMAL_STATUS_T mmalomx_param_mapping_displayregion(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_DISPLAYREGIONTYPE *omx = (OMX_CONFIG_DISPLAYREGIONTYPE *)omx_param; + MMAL_DISPLAYREGION_T *mmal = (MMAL_DISPLAYREGION_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->set = omx->set; + mmal->display_num = omx->num; + mmal->fullscreen = omx->fullscreen; + mmal->transform = (MMAL_DISPLAYTRANSFORM_T)omx->transform; + rect_to_mmal(&mmal->dest_rect, &omx->dest_rect); + rect_to_mmal(&mmal->src_rect, &omx->src_rect); + mmal->noaspect = omx->noaspect; + mmal->mode = (MMAL_DISPLAYMODE_T)omx->mode; + mmal->pixel_x = omx->pixel_x; + mmal->pixel_y = omx->pixel_y; + mmal->layer = omx->layer; + mmal->copyprotect_required = omx->copyprotect_required; + mmal->alpha = omx->alpha; + } + else + { + omx->set = mmal->set; + omx->num = mmal->display_num; + omx->fullscreen = mmal->fullscreen; + omx->transform = (OMX_DISPLAYTRANSFORMTYPE)mmal->transform; + rect_to_omx(&omx->dest_rect, &mmal->dest_rect); + rect_to_omx(&omx->src_rect, &mmal->src_rect); + omx->noaspect = mmal->noaspect; + omx->mode = (OMX_DISPLAYMODETYPE)mmal->mode; + omx->pixel_x = mmal->pixel_x; + omx->pixel_y = mmal->pixel_y; + omx->layer = mmal->layer; + omx->copyprotect_required = mmal->copyprotect_required; + omx->alpha = mmal->alpha; + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_list_supported_profiles(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const MMALOMX_PARAM_TRANSLATION_T *xlat, unsigned int index, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port) +{ + OMX_VIDEO_PARAM_PROFILELEVELTYPE *omx = (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)omx_param; + MMAL_PARAMETER_VIDEO_PROFILE_T *mmal = (MMAL_PARAMETER_VIDEO_PROFILE_T *)mmal_param; + MMAL_PARAM_UNUSED(xlat); + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + OMX_VIDEO_CODINGTYPE coding = mmalil_encoding_to_omx_video_coding(mmal_port->format->encoding); + mmal->profile[index].profile = mmalil_omx_video_profile_to_mmal(omx->eProfile, coding); + mmal->profile[index].level = mmalil_omx_video_level_to_mmal(omx->eLevel, coding); + } + else + { + omx->eProfile = mmalil_video_profile_to_omx(mmal->profile[index].profile); + omx->eLevel = mmalil_video_level_to_omx(mmal->profile[index].level); + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_custom_profile(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port) +{ + OMX_VIDEO_PARAM_PROFILELEVELTYPE *omx = (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)omx_param; + MMAL_PARAMETER_VIDEO_PROFILE_T *mmal = (MMAL_PARAMETER_VIDEO_PROFILE_T *)mmal_param; + MMAL_PARAM_UNUSED(xlat); + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + OMX_VIDEO_CODINGTYPE coding = mmalil_encoding_to_omx_video_coding(mmal_port->format->encoding); + mmal->profile[0].profile = mmalil_omx_video_profile_to_mmal(omx->eProfile, coding); + mmal->profile[0].level = mmalil_omx_video_level_to_mmal(omx->eLevel, coding); + } + else + { + omx->eProfile = mmalil_video_profile_to_omx(mmal->profile[0].profile); + omx->eLevel = mmalil_video_level_to_omx(mmal->profile[0].level); + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmalomx_param_custom_ratecontrol(MMALOMX_PARAM_MAPPING_DIRECTION dir, + const MMALOMX_PARAM_TRANSLATION_T *xlat, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param, MMAL_PORT_T *mmal_port) +{ + OMX_VIDEO_PARAM_BITRATETYPE *omx = (OMX_VIDEO_PARAM_BITRATETYPE *)omx_param; + MMAL_PARAMETER_VIDEO_RATECONTROL_T *mmal = (MMAL_PARAMETER_VIDEO_RATECONTROL_T *)mmal_param; + MMAL_PARAM_UNUSED(xlat); + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->control = mmalil_omx_video_ratecontrol_to_mmal(omx->eControlRate); + /* This does not apply nTargetBitrate but should not be necessary */ + } + else + { + omx->eControlRate = mmalil_video_ratecontrol_to_omx(mmal->control); + omx->nTargetBitrate = mmal_port->format->bitrate; /* Should not really be necessary */ + } + + return MMAL_SUCCESS; +} + +static const MMALOMX_PARAM_ENUM_TRANSLATE_T mmalomx_param_enum_nalunitformat[] = { + {MMAL_VIDEO_NALUNITFORMAT_STARTCODES, OMX_NaluFormatStartCodes}, + {MMAL_VIDEO_NALUNITFORMAT_NALUNITPERBUFFER, OMX_NaluFormatOneNaluPerBuffer}, + {MMAL_VIDEO_NALUNITFORMAT_ONEBYTEINTERLEAVELENGTH, OMX_NaluFormatOneByteInterleaveLength}, + {MMAL_VIDEO_NALUNITFORMAT_TWOBYTEINTERLEAVELENGTH, OMX_NaluFormatTwoByteInterleaveLength}, + {MMAL_VIDEO_NALUNITFORMAT_FOURBYTEINTERLEAVELENGTH, OMX_NaluFormatFourByteInterleaveLength}, +}; + +static MMAL_STATUS_T mmalomx_param_mapping_frame_rate(MMALOMX_PARAM_MAPPING_DIRECTION dir, + MMAL_PARAMETER_HEADER_T *mmal_param, OMX_PTR omx_param) +{ + OMX_CONFIG_FRAMERATETYPE *omx = (OMX_CONFIG_FRAMERATETYPE *)omx_param; + MMAL_PARAMETER_FRAME_RATE_T *mmal = (MMAL_PARAMETER_FRAME_RATE_T *)mmal_param; + + if (dir == MMALOMX_PARAM_MAPPING_TO_MMAL) + { + mmal->frame_rate.num = omx->xEncodeFramerate; + mmal->frame_rate.den = (1<<16); + } + else + { + omx->xEncodeFramerate = 0; + if (mmal->frame_rate.den) + omx->xEncodeFramerate = (((int64_t)mmal->frame_rate.num)<<16)/mmal->frame_rate.den; + } + + return MMAL_SUCCESS; +} + +const MMALOMX_PARAM_TRANSLATION_T mmalomx_param_xlator_video[] = { + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_DISPLAYREGION, MMAL_DISPLAYREGION_T, + OMX_IndexConfigDisplayRegion, OMX_CONFIG_DISPLAYREGIONTYPE, + mmalomx_param_mapping_displayregion), + MMALOMX_PARAM_LIST(MMAL_PARAMETER_SUPPORTED_PROFILES, MMAL_PARAMETER_VIDEO_PROFILE_T, + OMX_IndexParamVideoProfileLevelQuerySupported, OMX_VIDEO_PARAM_PROFILELEVELTYPE, + nProfileIndex, mmalomx_param_list_supported_profiles), + MMALOMX_PARAM_CUSTOM(MMAL_PARAMETER_PROFILE, MMAL_PARAMETER_VIDEO_PROFILE_T, + OMX_IndexParamVideoProfileLevelCurrent, OMX_VIDEO_PARAM_PROFILELEVELTYPE, + mmalomx_param_custom_profile), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_INTRAPERIOD, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigBrcmVideoIntraPeriod, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_CUSTOM(MMAL_PARAMETER_RATECONTROL, MMAL_PARAMETER_VIDEO_RATECONTROL_T, + OMX_IndexParamVideoBitrate, OMX_VIDEO_PARAM_BITRATETYPE, + mmalomx_param_custom_ratecontrol), + MMALOMX_PARAM_ENUM(MMAL_PARAMETER_NALUNITFORMAT, MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T, + OMX_IndexParamNalStreamFormatSelect, OMX_NALSTREAMFORMATTYPE, mmalomx_param_enum_nalunitformat), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_MINIMISE_FRAGMENTATION, + OMX_IndexConfigMinimiseFragmentation), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_MB_ROWS_PER_SLICE, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigBrcmVideoEncoderMBRowsPerSlice, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T, + OMX_IndexConfigEncLevelExtension, OMX_VIDEO_CONFIG_LEVEL_EXTEND), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_INTRA_REFRESH, MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T, + OMX_IndexConfigBrcmVideoIntraRefresh, OMX_VIDEO_PARAM_INTRAREFRESHTYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_INTRA_REFRESH, MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T, + OMX_IndexParamVideoIntraRefresh, OMX_VIDEO_PARAM_INTRAREFRESHTYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_EEDE_ENABLE, MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T, + OMX_IndexParamBrcmEEDEEnable, OMX_VIDEO_EEDE_ENABLE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T, + OMX_IndexParamBrcmEEDELossRate, OMX_VIDEO_EEDE_LOSSRATE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, + OMX_IndexConfigBrcmVideoRequestIFrame), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, + OMX_IndexParamBrcmImmutableInput), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_BIT_RATE, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigVideoBitrate, OMX_VIDEO_CONFIG_BITRATETYPE), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_VIDEO_FRAME_RATE, MMAL_PARAMETER_FRAME_RATE_T, + OMX_IndexConfigVideoFramerate, OMX_CONFIG_FRAMERATETYPE, mmalomx_param_mapping_frame_rate), + MMALOMX_PARAM_STRAIGHT_MAPPING(MMAL_PARAMETER_FRAME_RATE, MMAL_PARAMETER_FRAME_RATE_T, + OMX_IndexConfigVideoFramerate, OMX_CONFIG_FRAMERATETYPE, mmalomx_param_mapping_frame_rate), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoEncodeMinQuant, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoEncodeMaxQuant, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T, + OMX_IndexParamRateControlModel, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_EXTRA_BUFFERS, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmExtraBuffers, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmAlignHoriz, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ALIGN_VERT, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmAlignVert, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, + OMX_IndexParamBrcmDroppablePFrames), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoInitialQuant, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_QP_P, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoInitialQuant, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoRCSliceDQuant, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoFrameLimitBits, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, MMAL_PARAMETER_UINT32_T, + OMX_IndexParamBrcmVideoPeakRate, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, + OMX_IndexConfigBrcmVideoH264DisableCABAC), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, + OMX_IndexConfigBrcmVideoH264LowLatency), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, + OMX_IndexConfigBrcmVideoH264AUDelimiters), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, MMAL_PARAMETER_UINT32_T, + OMX_IndexConfigBrcmVideoH264DeblockIDC, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_PASSTHROUGH(MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T, + OMX_IndexConfigBrcmVideoH264IntraMBMode, OMX_PARAM_U32TYPE), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, + OMX_IndexParamBrcmHeaderOnOpen), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, + OMX_IndexParamBrcmVideoPrecodeForQP), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, + OMX_IndexParamBrcmVideoTimestampFifo), + MMALOMX_PARAM_BOOLEAN_PORTLESS(MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, + OMX_IndexParamBrcmVideoDecodeErrorConcealment), + MMALOMX_PARAM_PASSTHROUGH_PORTLESS_DOUBLE_TRANSLATION(MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T, + OMX_IndexParamBrcmVideoDrmProtectBuffer, OMX_PARAM_BRCMVIDEODRMPROTECTBUFFERTYPE), + MMALOMX_PARAM_PASSTHROUGH_PORTLESS(MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3, MMAL_PARAMETER_BYTES_T, + OMX_IndexParamBrcmVideoDecodeConfigVD3, OMX_PARAM_BRCMVIDEODECODECONFIGVD3TYPE), + MMALOMX_PARAM_BOOLEAN(MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, + OMX_IndexParamBrcmVideoAVCInlineHeaderEnable), + MMALOMX_PARAM_TERMINATE() +}; diff --git a/interface/mmal/test/CMakeLists.txt b/interface/mmal/test/CMakeLists.txt new file mode 100755 index 0000000..4ee91d9 --- /dev/null +++ b/interface/mmal/test/CMakeLists.txt @@ -0,0 +1,29 @@ +SET( MMAL_TOP ../../.. ) + +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) + +SET( MMALPLAY_TOP ${MMAL_TOP}/host_applications/vmcs/test_apps/mmalplay ) +add_executable(mmalplay ${MMALPLAY_TOP}/playback.c ${MMALPLAY_TOP}/mmalplay.c) +target_link_libraries(mmalplay mmal_core mmal_util bcm_host mmal_vc_client) +target_link_libraries(mmalplay -Wl,--whole-archive mmal_components containers -Wl,--no-whole-archive mmal_core) +target_link_libraries(mmalplay vcos) + +SET( MMALCAM_TOP ${MMAL_TOP}/host_applications/vmcs/test_apps/mmalcam ) +add_executable(mmalcam ${MMALCAM_TOP}/viewfinder.c ${MMALCAM_TOP}/mmalcam.c) +target_link_libraries(mmalcam mmal_core mmal_util bcm_host mmal_vc_client) +target_link_libraries(mmalcam -Wl,--whole-archive mmal_components -Wl,--no-whole-archive mmal_core) +target_link_libraries(mmalcam vcos) + +SET( MMALEXAMPLES_TOP ${MMAL_TOP}/interface/mmal/test/examples ) +add_executable(mmal_example_connections ${MMALEXAMPLES_TOP}/example_connections.c) +target_link_libraries(mmal_example_connections mmal_core mmal_util bcm_host mmal_vc_client) +target_link_libraries(mmal_example_connections -Wl,--whole-archive mmal_components -Wl,--no-whole-archive mmal_core) +add_executable(mmal_example_graph ${MMALEXAMPLES_TOP}/example_graph.c) +target_link_libraries(mmal_example_graph mmal_core mmal_util bcm_host mmal_vc_client) +target_link_libraries(mmal_example_graph -Wl,--whole-archive mmal_components -Wl,--no-whole-archive mmal_core) +add_executable(mmal_example_basic_1 ${MMALEXAMPLES_TOP}/example_basic_1.c) +target_link_libraries(mmal_example_basic_1 mmal_core mmal_util bcm_host mmal_vc_client) +target_link_libraries(mmal_example_basic_1 -Wl,--whole-archive mmal_components -Wl,--no-whole-archive mmal_core) +add_executable(mmal_example_basic_2 ${MMALEXAMPLES_TOP}/example_basic_2.c) +target_link_libraries(mmal_example_basic_2 mmal_core mmal_util bcm_host mmal_vc_client) +target_link_libraries(mmal_example_basic_2 -Wl,--whole-archive mmal_components -Wl,--no-whole-archive mmal_core) diff --git a/interface/mmal/test/examples/example_basic_1.c b/interface/mmal/test/examples/example_basic_1.c new file mode 100755 index 0000000..49b984e --- /dev/null +++ b/interface/mmal/test/examples/example_basic_1.c @@ -0,0 +1,241 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "bcm_host.h" +#include "mmal.h" +#include "util/mmal_default_components.h" +#include "interface/vcos/vcos.h" +#include + +#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); goto error; } + +static uint8_t codec_header_bytes[512]; +static unsigned int codec_header_bytes_size = sizeof(codec_header_bytes); + +static FILE *source_file; + +/* Macros abstracting the I/O, just to make the example code clearer */ +#define SOURCE_OPEN(uri) \ + source_file = fopen(uri, "rb"); if (!source_file) goto error; +#define SOURCE_READ_CODEC_CONFIG_DATA(bytes, size) \ + size = fread(bytes, 1, size, source_file); rewind(source_file) +#define SOURCE_READ_DATA_INTO_BUFFER(a) \ + a->length = fread(a->data, 1, a->alloc_size - 128, source_file); \ + a->offset = 0; a->pts = a->dts = MMAL_TIME_UNKNOWN +#define SOURCE_CLOSE() \ + if (source_file) fclose(source_file) + +/** Context for our application */ +static struct CONTEXT_T { + VCOS_SEMAPHORE_T semaphore; + MMAL_QUEUE_T *queue; +} context; + +/** Callback from the input port. + * Buffer has been consumed and is available to be used again. */ +static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata; + + /* The decoder is done with the data, just recycle the buffer header into its pool */ + mmal_buffer_header_release(buffer); + + /* Kick the processing thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +/** Callback from the output port. + * Buffer has been produced by the port and is available for processing. */ +static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata; + + /* Queue the decoded video frame */ + mmal_queue_put(ctx->queue, buffer); + + /* Kick the processing thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +int main(int argc, char **argv) +{ + MMAL_STATUS_T status = MMAL_EINVAL; + MMAL_COMPONENT_T *decoder = 0; + MMAL_POOL_T *pool_in = 0, *pool_out = 0; + unsigned int count; + + if (argc < 2) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + + bcm_host_init(); + + vcos_semaphore_create(&context.semaphore, "example", 1); + + SOURCE_OPEN(argv[1]); + + /* Create the decoder component. + * This specific component exposes 2 ports (1 input and 1 output). Like most components + * its expects the format of its input port to be set by the client in order for it to + * know what kind of data it will be fed. */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder); + CHECK_STATUS(status, "failed to create decoder"); + + /* Set format of video decoder input port */ + MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format; + format_in->type = MMAL_ES_TYPE_VIDEO; + format_in->encoding = MMAL_ENCODING_H264; + format_in->es->video.width = 1280; + format_in->es->video.height = 720; + format_in->es->video.frame_rate.num = 30; + format_in->es->video.frame_rate.den = 1; + format_in->es->video.par.num = 1; + format_in->es->video.par.den = 1; + /* If the data is known to be framed then the following flag should be set: + * format_in->flags |= MMAL_ES_FORMAT_FLAG_FRAMED; */ + + SOURCE_READ_CODEC_CONFIG_DATA(codec_header_bytes, codec_header_bytes_size); + status = mmal_format_extradata_alloc(format_in, codec_header_bytes_size); + CHECK_STATUS(status, "failed to allocate extradata"); + format_in->extradata_size = codec_header_bytes_size; + if (format_in->extradata_size) + memcpy(format_in->extradata, codec_header_bytes, format_in->extradata_size); + + status = mmal_port_format_commit(decoder->input[0]); + CHECK_STATUS(status, "failed to commit format"); + + /* Display the output port format */ + MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format; + fprintf(stderr, "%s\n", decoder->output[0]->name); + fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_out->type, (char *)&format_out->encoding); + fprintf(stderr, " bitrate: %i, framed: %i\n", format_out->bitrate, + !!(format_out->flags & MMAL_ES_FORMAT_FLAG_FRAMED)); + fprintf(stderr, " extra data: %i, %p\n", format_out->extradata_size, format_out->extradata); + fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n", + format_out->es->video.width, format_out->es->video.height, + format_out->es->video.crop.x, format_out->es->video.crop.y, + format_out->es->video.crop.width, format_out->es->video.crop.height); + + /* The format of both ports is now set so we can get their buffer requirements and create + * our buffer headers. We use the buffer pool API to create these. */ + decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_min; + decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_min; + decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_min; + decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_min; + pool_in = mmal_pool_create(decoder->input[0]->buffer_num, + decoder->input[0]->buffer_size); + pool_out = mmal_pool_create(decoder->output[0]->buffer_num, + decoder->output[0]->buffer_size); + + /* Create a queue to store our decoded video frames. The callback we will get when + * a frame has been decoded will put the frame into this queue. */ + context.queue = mmal_queue_create(); + + /* Store a reference to our context in each port (will be used during callbacks) */ + decoder->input[0]->userdata = (void *)&context; + decoder->output[0]->userdata = (void *)&context; + + /* Enable all the input port and the output port. + * The callback specified here is the function which will be called when the buffer header + * we sent to the component has been processed. */ + status = mmal_port_enable(decoder->input[0], input_callback); + CHECK_STATUS(status, "failed to enable input port"); + status = mmal_port_enable(decoder->output[0], output_callback); + CHECK_STATUS(status, "failed to enable output port"); + + /* Component won't start processing data until it is enabled. */ + status = mmal_component_enable(decoder); + CHECK_STATUS(status, "failed to enable component"); + + /* Start decoding */ + fprintf(stderr, "start decoding\n"); + + /* This is the main processing loop */ + for (count = 0; count < 500; count++) + { + MMAL_BUFFER_HEADER_T *buffer; + + /* Wait for buffer headers to be available on either of the decoder ports */ + vcos_semaphore_wait(&context.semaphore); + + /* Send data to decode to the input port of the video decoder */ + if ((buffer = mmal_queue_get(pool_in->queue)) != NULL) + { + SOURCE_READ_DATA_INTO_BUFFER(buffer); + if (!buffer->length) + break; + + fprintf(stderr, "sending %i bytes\n", (int)buffer->length); + status = mmal_port_send_buffer(decoder->input[0], buffer); + CHECK_STATUS(status, "failed to send buffer"); + } + + /* Get our decoded frames */ + while ((buffer = mmal_queue_get(context.queue)) != NULL) + { + /* We have a frame, do something with it (why not display it for instance?). + * Once we're done with it, we release it. It will automatically go back + * to its original pool so it can be reused for a new video frame. + */ + fprintf(stderr, "decoded frame\n"); + mmal_buffer_header_release(buffer); + } + + /* Send empty buffers to the output port of the decoder */ + while ((buffer = mmal_queue_get(pool_out->queue)) != NULL) + { + status = mmal_port_send_buffer(decoder->output[0], buffer); + CHECK_STATUS(status, "failed to send buffer"); + } + } + + /* Stop decoding */ + fprintf(stderr, "stop decoding\n"); + + /* Stop everything. Not strictly necessary since mmal_component_destroy() + * will do that anyway */ + mmal_port_disable(decoder->input[0]); + mmal_port_disable(decoder->output[0]); + mmal_component_disable(decoder); + + error: + /* Cleanup everything */ + if (decoder) + mmal_component_destroy(decoder); + if (pool_in) + mmal_pool_destroy(pool_in); + if (pool_out) + mmal_pool_destroy(pool_out); + if (context.queue) + mmal_queue_destroy(context.queue); + + SOURCE_CLOSE(); + vcos_semaphore_delete(&context.semaphore); + return status == MMAL_SUCCESS ? 0 : -1; +} diff --git a/interface/mmal/test/examples/example_basic_2.c b/interface/mmal/test/examples/example_basic_2.c new file mode 100755 index 0000000..cc474f2 --- /dev/null +++ b/interface/mmal/test/examples/example_basic_2.c @@ -0,0 +1,375 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "bcm_host.h" +#include "mmal.h" +#include "util/mmal_default_components.h" +#include "util/mmal_util_params.h" +#include "util/mmal_util.h" +#include "interface/vcos/vcos.h" +#include + +#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); goto error; } + +static uint8_t codec_header_bytes[512]; +static unsigned int codec_header_bytes_size = sizeof(codec_header_bytes); + +static FILE *source_file; + +/* Macros abstracting the I/O, just to make the example code clearer */ +#define SOURCE_OPEN(uri) \ + source_file = fopen(uri, "rb"); if (!source_file) goto error; +#define SOURCE_READ_CODEC_CONFIG_DATA(bytes, size) \ + size = fread(bytes, 1, size, source_file); rewind(source_file) +#define SOURCE_READ_DATA_INTO_BUFFER(a) \ + a->length = fread(a->data, 1, a->alloc_size - 128, source_file); \ + a->offset = 0 +#define SOURCE_CLOSE() \ + if (source_file) fclose(source_file) + +/** Context for our application */ +static struct CONTEXT_T { + VCOS_SEMAPHORE_T semaphore; + MMAL_QUEUE_T *queue; + MMAL_STATUS_T status; +} context; + +static void log_video_format(MMAL_ES_FORMAT_T *format) +{ + if (format->type != MMAL_ES_TYPE_VIDEO) + return; + + fprintf(stderr, "fourcc: %4.4s, width: %i, height: %i, (%i,%i,%i,%i)\n", + (char *)&format->encoding, + format->es->video.width, format->es->video.height, + format->es->video.crop.x, format->es->video.crop.y, + format->es->video.crop.width, format->es->video.crop.height); +} + +/** Callback from the control port. + * Component is sending us an event. */ +static void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata; + + switch (buffer->cmd) + { + case MMAL_EVENT_EOS: + /* Only sink component generate EOS events */ + break; + case MMAL_EVENT_ERROR: + /* Something went wrong. Signal this to the application */ + ctx->status = *(MMAL_STATUS_T *)buffer->data; + break; + default: + break; + } + + /* Done with the event, recycle it */ + mmal_buffer_header_release(buffer); + + /* Kick the processing thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +/** Callback from the input port. + * Buffer has been consumed and is available to be used again. */ +static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata; + + /* The decoder is done with the data, just recycle the buffer header into its pool */ + mmal_buffer_header_release(buffer); + + /* Kick the processing thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +/** Callback from the output port. + * Buffer has been produced by the port and is available for processing. */ +static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata; + + /* Queue the decoded video frame */ + mmal_queue_put(ctx->queue, buffer); + + /* Kick the processing thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +int main(int argc, char **argv) +{ + MMAL_STATUS_T status = MMAL_EINVAL; + MMAL_COMPONENT_T *decoder = 0; + MMAL_POOL_T *pool_in = 0, *pool_out = 0; + MMAL_BOOL_T eos_sent = MMAL_FALSE, eos_received = MMAL_FALSE; + unsigned int count; + + if (argc < 2) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + + bcm_host_init(); + + vcos_semaphore_create(&context.semaphore, "example", 1); + + SOURCE_OPEN(argv[1]); + + /* Create the decoder component. + * This specific component exposes 2 ports (1 input and 1 output). Like most components + * its expects the format of its input port to be set by the client in order for it to + * know what kind of data it will be fed. */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder); + CHECK_STATUS(status, "failed to create decoder"); + + /* Enable control port so we can receive events from the component */ + decoder->control->userdata = (void *)&context; + status = mmal_port_enable(decoder->control, control_callback); + CHECK_STATUS(status, "failed to enable control port"); + + /* Get statistics on the input port */ + MMAL_PARAMETER_CORE_STATISTICS_T stats = {{0}}; + stats.hdr.id = MMAL_PARAMETER_CORE_STATISTICS; + stats.hdr.size = sizeof(MMAL_PARAMETER_CORE_STATISTICS_T); + status = mmal_port_parameter_get(decoder->input[0], &stats.hdr); + CHECK_STATUS(status, "failed to get stats"); + fprintf(stderr, "stats: %i, %i", stats.stats.buffer_count, stats.stats.max_delay); + + /* Set the zero-copy parameter on the input port */ + MMAL_PARAMETER_BOOLEAN_T zc = {{MMAL_PARAMETER_ZERO_COPY, sizeof(zc)}, MMAL_TRUE}; + status = mmal_port_parameter_set(decoder->input[0], &zc.hdr); + fprintf(stderr, "status: %i\n", status); + + /* Set the zero-copy parameter on the output port */ + status = mmal_port_parameter_set_boolean(decoder->output[0], MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + fprintf(stderr, "status: %i\n", status); + + /* Set format of video decoder input port */ + MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format; + format_in->type = MMAL_ES_TYPE_VIDEO; + format_in->encoding = MMAL_ENCODING_H264; + format_in->es->video.width = 1280; + format_in->es->video.height = 720; + format_in->es->video.frame_rate.num = 30; + format_in->es->video.frame_rate.den = 1; + format_in->es->video.par.num = 1; + format_in->es->video.par.den = 1; + /* If the data is known to be framed then the following flag should be set: + * format_in->flags |= MMAL_ES_FORMAT_FLAG_FRAMED; */ + + SOURCE_READ_CODEC_CONFIG_DATA(codec_header_bytes, codec_header_bytes_size); + status = mmal_format_extradata_alloc(format_in, codec_header_bytes_size); + CHECK_STATUS(status, "failed to allocate extradata"); + format_in->extradata_size = codec_header_bytes_size; + if (format_in->extradata_size) + memcpy(format_in->extradata, codec_header_bytes, format_in->extradata_size); + + status = mmal_port_format_commit(decoder->input[0]); + CHECK_STATUS(status, "failed to commit format"); + + /* Our decoder can do internal colour conversion, ask for a conversion to RGB565 */ + MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format; + format_out->encoding = MMAL_ENCODING_RGB16; + status = mmal_port_format_commit(decoder->output[0]); + CHECK_STATUS(status, "failed to commit format"); + + /* Display the output port format */ + fprintf(stderr, "%s\n", decoder->output[0]->name); + fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_out->type, (char *)&format_out->encoding); + fprintf(stderr, " bitrate: %i, framed: %i\n", format_out->bitrate, + !!(format_out->flags & MMAL_ES_FORMAT_FLAG_FRAMED)); + fprintf(stderr, " extra data: %i, %p\n", format_out->extradata_size, format_out->extradata); + fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n", + format_out->es->video.width, format_out->es->video.height, + format_out->es->video.crop.x, format_out->es->video.crop.y, + format_out->es->video.crop.width, format_out->es->video.crop.height); + + /* The format of both ports is now set so we can get their buffer requirements and create + * our buffer headers. We use the buffer pool API to create these. */ + decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_min; + decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_min; + decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_min; + decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_min; + pool_in = mmal_port_pool_create(decoder->output[0], + decoder->input[0]->buffer_num, + decoder->input[0]->buffer_size); + pool_out = mmal_port_pool_create(decoder->output[0], + decoder->output[0]->buffer_num, + decoder->output[0]->buffer_size); + + /* Create a queue to store our decoded video frames. The callback we will get when + * a frame has been decoded will put the frame into this queue. */ + context.queue = mmal_queue_create(); + + /* Store a reference to our context in each port (will be used during callbacks) */ + decoder->input[0]->userdata = (void *)&context; + decoder->output[0]->userdata = (void *)&context; + + /* Enable all the input port and the output port. + * The callback specified here is the function which will be called when the buffer header + * we sent to the component has been processed. */ + status = mmal_port_enable(decoder->input[0], input_callback); + CHECK_STATUS(status, "failed to enable input port"); + status = mmal_port_enable(decoder->output[0], output_callback); + CHECK_STATUS(status, "failed to enable output port"); + + /* Component won't start processing data until it is enabled. */ + status = mmal_component_enable(decoder); + CHECK_STATUS(status, "failed to enable component"); + + /* Start decoding */ + fprintf(stderr, "start decoding\n"); + + /* This is the main processing loop */ + for (count = 0; !eos_received && count < 500; count++) + { + MMAL_BUFFER_HEADER_T *buffer; + + /* Wait for buffer headers to be available on either of the decoder ports */ + vcos_semaphore_wait(&context.semaphore); + + /* Check for errors */ + if (context.status != MMAL_SUCCESS) + { + fprintf(stderr, "Aborting due to error\n"); + break; + } + + /* Send data to decode to the input port of the video decoder */ + if (!eos_sent && (buffer = mmal_queue_get(pool_in->queue)) != NULL) + { + SOURCE_READ_DATA_INTO_BUFFER(buffer); + if(!buffer->length) eos_sent = MMAL_TRUE; + + buffer->flags = buffer->length ? 0 : MMAL_BUFFER_HEADER_FLAG_EOS; + buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN; + fprintf(stderr, "sending %i bytes\n", (int)buffer->length); + status = mmal_port_send_buffer(decoder->input[0], buffer); + CHECK_STATUS(status, "failed to send buffer"); + } + + /* Get our decoded frames */ + while ((buffer = mmal_queue_get(context.queue)) != NULL) + { + /* We have a frame, do something with it (why not display it for instance?). + * Once we're done with it, we release it. It will automatically go back + * to its original pool so it can be reused for a new video frame. + */ + eos_received = buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS; + + if (buffer->cmd) + { + fprintf(stderr, "received event %4.4s\n", (char *)&buffer->cmd); + if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) + { + MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer); + if (event) + { + fprintf(stderr, "----------Port format changed----------\n"); + log_video_format(decoder->output[0]->format); + fprintf(stderr, "-----------------to---------------------\n"); + log_video_format(event->format); + fprintf(stderr, " buffers num (opt %i, min %i), size (opt %i, min: %i)\n", + event->buffer_num_recommended, event->buffer_num_min, + event->buffer_size_recommended, event->buffer_size_min); + fprintf(stderr, "----------------------------------------\n"); + } + + //Assume we can't reuse the buffers, so have to disable, destroy + //pool, create new pool, enable port, feed in buffers. + status = mmal_port_disable(decoder->output[0]); + CHECK_STATUS(status, "failed to disable port"); + + //Clear the queue of all buffers + while(mmal_queue_length(pool_out->queue) != pool_out->headers_num) + { + MMAL_BUFFER_HEADER_T *buf; + fprintf(stderr, "Wait for buffers to be returned. Have %d of %d buffers\n", + mmal_queue_length(pool_out->queue), pool_out->headers_num); + vcos_semaphore_wait(&context.semaphore); + fprintf(stderr, "Got semaphore\n"); + buf = mmal_queue_get(context.queue); + mmal_buffer_header_release(buf); + } + fprintf(stderr, "Got all buffers\n"); + + mmal_port_pool_destroy(decoder->output[0], pool_out); + status = mmal_format_full_copy(decoder->output[0]->format, event->format); + CHECK_STATUS(status, "failed to copy port format"); + status = mmal_port_format_commit(decoder->output[0]); + CHECK_STATUS(status, "failed to commit port format"); + + pool_out = mmal_port_pool_create(decoder->output[0], + decoder->output[0]->buffer_num, + decoder->output[0]->buffer_size); + + status = mmal_port_enable(decoder->output[0], output_callback); + CHECK_STATUS(status, "failed to enable port"); + //Allow the following loop to send all the buffers back to the decoder + } + + } + else + fprintf(stderr, "decoded frame (flags %x)\n", buffer->flags); + mmal_buffer_header_release(buffer); + } + + /* Send empty buffers to the output port of the decoder */ + while ((buffer = mmal_queue_get(pool_out->queue)) != NULL) + { + status = mmal_port_send_buffer(decoder->output[0], buffer); + CHECK_STATUS(status, "failed to send buffer"); + } + } + + /* Stop decoding */ + fprintf(stderr, "stop decoding - count %d, eos_received %d\n", count, eos_received); + + /* Stop everything. Not strictly necessary since mmal_component_destroy() + * will do that anyway */ + mmal_port_disable(decoder->input[0]); + mmal_port_disable(decoder->output[0]); + mmal_component_disable(decoder); + + error: + /* Cleanup everything */ + if (pool_in) + mmal_port_pool_destroy(decoder->input[0], pool_in); + if (pool_out) + mmal_port_pool_destroy(decoder->output[0], pool_out); + if (decoder) + mmal_component_destroy(decoder); + if (context.queue) + mmal_queue_destroy(context.queue); + + SOURCE_CLOSE(); + vcos_semaphore_delete(&context.semaphore); + return status == MMAL_SUCCESS ? 0 : -1; +} diff --git a/interface/mmal/test/examples/example_connections.c b/interface/mmal/test/examples/example_connections.c new file mode 100755 index 0000000..51b585b --- /dev/null +++ b/interface/mmal/test/examples/example_connections.c @@ -0,0 +1,196 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "bcm_host.h" + +#include "mmal.h" +#include "util/mmal_connection.h" +#include "util/mmal_default_components.h" +#include "util/mmal_util_params.h" +#include "interface/vcos/vcos.h" +#include + +#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); goto error; } + +/** Context for our application */ +static struct CONTEXT_T { + VCOS_SEMAPHORE_T semaphore; + MMAL_STATUS_T status; + MMAL_BOOL_T eos; +} context; + +/** Callback from a control port. Error and EOS events stop playback. */ +static void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata; + + if (buffer->cmd == MMAL_EVENT_ERROR) + ctx->status = *(MMAL_STATUS_T *)buffer->data; + else if (buffer->cmd == MMAL_EVENT_EOS) + ctx->eos = MMAL_TRUE; + + mmal_buffer_header_release(buffer); + + /* The processing is done in our main thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +/** Callback from the connection. Buffer is available. */ +static void connection_callback(MMAL_CONNECTION_T *connection) +{ + struct CONTEXT_T *ctx = (struct CONTEXT_T *)connection->user_data; + + /* The processing is done in our main thread */ + vcos_semaphore_post(&ctx->semaphore); +} + +int main(int argc, char **argv) +{ + MMAL_STATUS_T status; + MMAL_COMPONENT_T *reader = 0, *decoder = 0, *renderer = 0; + MMAL_CONNECTION_T *connection[2] = {0}; + unsigned int i, count, connection_num = vcos_countof(connection); + + if (argc < 2) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + + bcm_host_init(); + + vcos_semaphore_create(&context.semaphore, "example", 1); + + /* Create the components */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CONTAINER_READER, &reader); + CHECK_STATUS(status, "failed to create reader"); + reader->control->userdata = (void *)&context; + status = mmal_port_enable(reader->control, control_callback); + CHECK_STATUS(status, "failed to enable control port"); + status = mmal_component_enable(reader); + CHECK_STATUS(status, "failed to enable component"); + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder); + CHECK_STATUS(status, "failed to create decoder"); + decoder->control->userdata = (void *)&context; + status = mmal_port_enable(decoder->control, control_callback); + CHECK_STATUS(status, "failed to enable control port"); + status = mmal_component_enable(decoder); + CHECK_STATUS(status, "failed to enable component"); + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &renderer); + CHECK_STATUS(status, "failed to create renderer"); + renderer->control->userdata = (void *)&context; + status = mmal_port_enable(renderer->control, control_callback); + CHECK_STATUS(status, "failed to enable control port"); + status = mmal_component_enable(renderer); + CHECK_STATUS(status, "failed to enable component"); + + /* Configure the reader using the given URI */ + status = mmal_util_port_set_uri(reader->control, argv[1]); + CHECK_STATUS(status, "failed to set uri"); + + /* Create the connections between the components */ + status = mmal_connection_create(&connection[0], reader->output[0], decoder->input[0], 0); + CHECK_STATUS(status, "failed to create connection between reader / decoder"); + connection[0]->user_data = &context; + connection[0]->callback = connection_callback; + status = mmal_connection_create(&connection[1], decoder->output[0], renderer->input[0], 0); + CHECK_STATUS(status, "failed to create connection between decoder / renderer"); + connection[1]->user_data = &context; + connection[1]->callback = connection_callback; + + /* Enable all our connections */ + for (i = connection_num; i; i--) + { + status = mmal_connection_enable(connection[i-1]); + CHECK_STATUS(status, "failed to enable connection"); + } + + /* Start playback */ + fprintf(stderr, "start playback\n"); + + /* This is the main processing loop */ + for (count = 0; count < 500; count++) + { + MMAL_BUFFER_HEADER_T *buffer; + vcos_semaphore_wait(&context.semaphore); + + /* Check for errors */ + status = context.status; + CHECK_STATUS(status, "error during playback"); + + /* Check for end of stream */ + if (context.eos) + break; + + /* Handle buffers for all our connections */ + for (i = 0; i < connection_num; i++) + { + if (connection[i]->flags & MMAL_CONNECTION_FLAG_TUNNELLING) + continue; /* Nothing else to do in tunnelling mode */ + + /* Send empty buffers to the output port of the connection */ + while ((buffer = mmal_queue_get(connection[i]->pool->queue)) != NULL) + { + status = mmal_port_send_buffer(connection[i]->out, buffer); + CHECK_STATUS(status, "failed to send buffer"); + } + + /* Send any queued buffer to the next component */ + while ((buffer = mmal_queue_get(connection[i]->queue)) != NULL) + { + status = mmal_port_send_buffer(connection[i]->in, buffer); + CHECK_STATUS(status, "failed to send buffer"); + } + } + } + + /* Stop everything */ + fprintf(stderr, "stop playback\n"); + for (i = 0; i < connection_num; i++) + { + mmal_connection_disable(connection[i]); + } + + error: + /* Cleanup everything */ + for (i = 0; i < connection_num; i++) + { + if (connection[i]) + mmal_connection_destroy(connection[i]); + } + if (reader) + mmal_component_destroy(reader); + if (decoder) + mmal_component_destroy(decoder); + if (renderer) + mmal_component_destroy(renderer); + + vcos_semaphore_delete(&context.semaphore); + return status == MMAL_SUCCESS ? 0 : -1; +} diff --git a/interface/mmal/test/examples/example_graph.c b/interface/mmal/test/examples/example_graph.c new file mode 100755 index 0000000..731bbb9 --- /dev/null +++ b/interface/mmal/test/examples/example_graph.c @@ -0,0 +1,98 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "bcm_host.h" +#include "mmal.h" +#include "util/mmal_graph.h" +#include "util/mmal_default_components.h" +#include "util/mmal_util_params.h" +#include + +#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); goto error; } + +int main(int argc, char **argv) +{ + MMAL_STATUS_T status; + MMAL_GRAPH_T *graph = 0; + MMAL_COMPONENT_T *reader = 0, *decoder = 0, *renderer = 0; + + if (argc < 2) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + + bcm_host_init(); + + /* Create the graph */ + status = mmal_graph_create(&graph, 0); + CHECK_STATUS(status, "failed to create graph"); + + /* Add the components */ + status = mmal_graph_new_component(graph, MMAL_COMPONENT_DEFAULT_CONTAINER_READER, &reader); + CHECK_STATUS(status, "failed to create reader"); + + status = mmal_graph_new_component(graph, MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder); + CHECK_STATUS(status, "failed to create decoder"); + + status = mmal_graph_new_component(graph, MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &renderer); + CHECK_STATUS(status, "failed to create renderer"); + + /* Configure the reader using the given URI */ + status = mmal_util_port_set_uri(reader->control, argv[1]); + CHECK_STATUS(status, "failed to set uri"); + + /* connect them up - this propagates port settings from outputs to inputs */ + status = mmal_graph_new_connection(graph, reader->output[0], decoder->input[0], 0, NULL); + CHECK_STATUS(status, "failed to connect reader to decoder"); + status = mmal_graph_new_connection(graph, decoder->output[0], renderer->input[0], 0, NULL); + CHECK_STATUS(status, "failed to connect decoder to renderer"); + + /* Start playback */ + fprintf(stderr, "start playback\n"); + status = mmal_graph_enable(graph, NULL, NULL); + CHECK_STATUS(status, "failed to enable graph"); + + sleep(5); + + /* Stop everything */ + fprintf(stderr, "stop playback\n"); + mmal_graph_disable(graph); + + error: + /* Cleanup everything */ + if (reader) + mmal_component_release(reader); + if (decoder) + mmal_component_release(decoder); + if (renderer) + mmal_component_release(renderer); + if (graph) + mmal_graph_destroy(graph); + + return status == MMAL_SUCCESS ? 0 : -1; +} diff --git a/interface/mmal/util/CMakeLists.txt b/interface/mmal/util/CMakeLists.txt new file mode 100755 index 0000000..b2a6858 --- /dev/null +++ b/interface/mmal/util/CMakeLists.txt @@ -0,0 +1,28 @@ +add_library (mmal_util ${LIBRARY_TYPE} + mmal_il.c + mmal_util.c + mmal_connection.c + mmal_graph.c + mmal_list.c + mmal_param_convert.c + mmal_util_params.c + mmal_component_wrapper.c + mmal_util_rational.c +) + +target_link_libraries (mmal_util vcos) + +install(TARGETS mmal_util DESTINATION lib) +install(FILES + mmal_component_wrapper.h + mmal_connection.h + mmal_default_components.h + mmal_graph.h + mmal_il.h + mmal_list.h + mmal_param_convert.h + mmal_util.h + mmal_util_params.h + mmal_util_rational.h + DESTINATION include/interface/mmal/util +) diff --git a/interface/mmal/util/mmal_component_wrapper.c b/interface/mmal/util/mmal_component_wrapper.c new file mode 100755 index 0000000..daf5e31 --- /dev/null +++ b/interface/mmal/util/mmal_component_wrapper.c @@ -0,0 +1,368 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmal.h" +#include "util/mmal_util.h" +#include "util/mmal_component_wrapper.h" +#include "mmal_logging.h" +#include + +typedef struct +{ + MMAL_WRAPPER_T wrapper; /**< Must be the first member! */ + + VCOS_SEMAPHORE_T sema; + +} MMAL_WRAPPER_PRIVATE_T; + +/** Callback from a control port. Error events will be received there. */ +static void mmal_wrapper_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; + LOG_TRACE("%s(%p),%p,%4.4s", port->name, port, buffer, (char *)&buffer->cmd); + + if (buffer->cmd == MMAL_EVENT_ERROR) + { + private->wrapper.status = *(MMAL_STATUS_T *)buffer->data; + mmal_buffer_header_release(buffer); + + vcos_semaphore_post(&private->sema); + + if (private->wrapper.callback) + private->wrapper.callback(&private->wrapper); + return; + } + + mmal_buffer_header_release(buffer); +} + +/** Callback from an input port. Buffer is released. */ +static void mmal_wrapper_bh_in_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_PARAM_UNUSED(port); + LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length); + + /* We're done with the buffer, just recycle it */ + mmal_buffer_header_release(buffer); +} + +/** Callback from an output port. Buffer is queued for the next component. */ +static void mmal_wrapper_bh_out_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; + LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length); + + /* Queue the buffer produced by the output port */ + mmal_queue_put(private->wrapper.output_queue[port->index], buffer); + vcos_semaphore_post(&private->sema); + + if (private->wrapper.callback) + private->wrapper.callback(&private->wrapper); +} + +/** Callback from the pool. Buffer is available. */ +static MMAL_BOOL_T mmal_wrapper_bh_release_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, + void *userdata) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)userdata; + + mmal_queue_put(pool->queue, buffer); + vcos_semaphore_post(&private->sema); + + if (private->wrapper.callback) + private->wrapper.callback(&private->wrapper); + + return 0; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_wrapper_destroy(MMAL_WRAPPER_T *wrapper) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)wrapper; + unsigned int i; + + LOG_TRACE("%p, %s", wrapper, wrapper->component->name); + + /* Cleanup resources */ + mmal_component_destroy(wrapper->component); + + for (i = 0; i < wrapper->input_num; i++) + { + if (wrapper->input_pool[i]) + mmal_pool_destroy(wrapper->input_pool[i]); + } + + for (i = 0; i < wrapper->output_num; i++) + { + if (wrapper->output_pool[i]) + mmal_pool_destroy(wrapper->output_pool[i]); + if (wrapper->output_queue[i]) + mmal_queue_destroy(wrapper->output_queue[i]); + } + + vcos_semaphore_delete(&private->sema); + vcos_free(private); + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_wrapper_create(MMAL_WRAPPER_T **ctx, const char *name) +{ + MMAL_STATUS_T status; + MMAL_COMPONENT_T *component; + MMAL_WRAPPER_PRIVATE_T *private; + MMAL_WRAPPER_T *wrapper; + int64_t start_time; + unsigned int i, extra_size; + + LOG_TRACE("wrapper %p, name %s", ctx, name); + + /* Sanity checking */ + if (!ctx || !name) + return MMAL_EINVAL; + + start_time = vcos_getmicrosecs(); + + status = mmal_component_create(name, &component); + if (status != MMAL_SUCCESS) + return status; + + extra_size = (component->input_num * sizeof(MMAL_POOL_T*)) + (component->output_num * (sizeof(MMAL_POOL_T*) + sizeof(MMAL_QUEUE_T*))); + private = vcos_calloc(1, sizeof(*private) + extra_size, "mmal wrapper"); + if (!private) + { + mmal_component_destroy(component); + return MMAL_ENOMEM; + } + + if (vcos_semaphore_create(&private->sema, "mmal wrapper", 0) != VCOS_SUCCESS) + { + mmal_component_destroy(component); + vcos_free(private); + return MMAL_ENOMEM; + } + + wrapper = &private->wrapper; + wrapper->component = component; + wrapper->control = component->control; + wrapper->input_num = component->input_num; + wrapper->input = component->input; + wrapper->output_num = component->output_num; + wrapper->output = component->output; + wrapper->input_pool = (MMAL_POOL_T **)&private[1]; + wrapper->output_pool = (MMAL_POOL_T **)&wrapper->input_pool[component->input_num]; + wrapper->output_queue = (MMAL_QUEUE_T **)&wrapper->output_pool[component->output_num]; + + /* Create our pools and queues */ + for (i = 0; i < wrapper->input_num; i++) + { + wrapper->input_pool[i] = mmal_port_pool_create(wrapper->input[i], 0, 0); + if (!wrapper->input_pool[i]) + goto error; + mmal_pool_callback_set(wrapper->input_pool[i], mmal_wrapper_bh_release_cb, (void *)wrapper); + + wrapper->input[i]->userdata = (void *)wrapper; + } + for (i = 0; i < wrapper->output_num; i++) + { + wrapper->output_pool[i] = mmal_port_pool_create(wrapper->output[i], 0, 0); + wrapper->output_queue[i] = mmal_queue_create(); + if (!wrapper->output_pool[i] || !wrapper->output_queue[i]) + goto error; + mmal_pool_callback_set(wrapper->output_pool[i], mmal_wrapper_bh_release_cb, (void *)wrapper); + + wrapper->output[i]->userdata = (void *)wrapper; + } + + /* Setup control port */ + wrapper->control->userdata = (void *)wrapper; + status = mmal_port_enable(wrapper->control, mmal_wrapper_control_cb); + if (status != MMAL_SUCCESS) + goto error; + + wrapper->time_setup = vcos_getmicrosecs() - start_time; + *ctx = wrapper; + return MMAL_SUCCESS; + + error: + mmal_wrapper_destroy(wrapper); + return status == MMAL_SUCCESS ? MMAL_ENOMEM : status; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_wrapper_port_enable(MMAL_PORT_T *port, uint32_t flags) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; + MMAL_WRAPPER_T *wrapper = &private->wrapper; + int64_t start_time = vcos_getmicrosecs(); + uint32_t buffer_size; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + LOG_TRACE("%p, %s", wrapper, port->name); + + if (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT) + return MMAL_EINVAL; + + if (port->is_enabled) + return MMAL_SUCCESS; + + pool = port->type == MMAL_PORT_TYPE_INPUT ? + wrapper->input_pool[port->index] : wrapper->output_pool[port->index]; + buffer_size = (flags & MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) ? port->buffer_size : 0; + + /* FIXME: we don't support switching between shared and non-shared memory. + * We would need to save the flag and force a pool resize when switching. */ + if (flags & MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY) + { + MMAL_PARAMETER_BOOLEAN_T param_zc = + {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1}; + status = mmal_port_parameter_set(port, ¶m_zc.hdr); + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + LOG_ERROR("failed to set zero copy on %s", port->name); + return status; + } + } + + /* Resize the pool */ + status = mmal_pool_resize(pool, port->buffer_num, buffer_size); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("could not resize pool (%i/%i)", (int)port->buffer_num, (int)buffer_size); + return status; + } + + /* Enable port. The callback specified here is the function which + * will be called when a buffer header comes back to the port. */ + status = mmal_port_enable(port, port->type == MMAL_PORT_TYPE_INPUT ? + mmal_wrapper_bh_in_cb : mmal_wrapper_bh_out_cb); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("could not enable port"); + return status; + } + + wrapper->time_enable += vcos_getmicrosecs() - start_time; + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_wrapper_port_disable(MMAL_PORT_T *port) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; + MMAL_WRAPPER_T *wrapper = &private->wrapper; + int64_t start_time = vcos_getmicrosecs(); + MMAL_STATUS_T status; + + LOG_TRACE("%p, %s", wrapper, port->name); + + if (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT) + return MMAL_EINVAL; + + if (!port->is_enabled) + return MMAL_SUCCESS; + + /* Disable port */ + status = mmal_port_disable(port); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("could not disable port"); + return status; + } + + /* Flush the queue */ + if (port->type == MMAL_PORT_TYPE_OUTPUT) + { + MMAL_POOL_T *pool = wrapper->output_pool[port->index]; + MMAL_QUEUE_T *queue = wrapper->output_queue[port->index]; + MMAL_BUFFER_HEADER_T *buffer; + + while ((buffer = mmal_queue_get(queue)) != NULL) + mmal_buffer_header_release(buffer); + + if ( !vcos_verify(mmal_queue_length(pool->queue) == pool->headers_num) ) + { + LOG_ERROR("coul dnot release all buffers"); + } + } + + wrapper->time_disable = vcos_getmicrosecs() - start_time; + return status; +} + +/** Wait for an empty buffer to be available on a port */ +MMAL_STATUS_T mmal_wrapper_buffer_get_empty(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, + uint32_t flags) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; + MMAL_WRAPPER_T *wrapper = &private->wrapper; + MMAL_POOL_T *pool; + + LOG_TRACE("%p, %s", wrapper, port->name); + + if (!buffer || (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT)) + return MMAL_EINVAL; + + pool = port->type == MMAL_PORT_TYPE_INPUT ? + wrapper->input_pool[port->index] : wrapper->output_pool[port->index]; + + while (wrapper->status == MMAL_SUCCESS && + (*buffer = mmal_queue_get(pool->queue)) == NULL) + { + if (!(flags & MMAL_WRAPPER_FLAG_WAIT)) + break; + vcos_semaphore_wait(&private->sema); + } + + return wrapper->status == MMAL_SUCCESS && !*buffer ? MMAL_EAGAIN : wrapper->status; +} + +/** Wait for a full buffer to be available on a port */ +MMAL_STATUS_T mmal_wrapper_buffer_get_full(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, + uint32_t flags) +{ + MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; + MMAL_WRAPPER_T *wrapper = &private->wrapper; + MMAL_QUEUE_T *queue; + + LOG_TRACE("%p, %s", wrapper, port->name); + + if (!buffer || port->type != MMAL_PORT_TYPE_OUTPUT) + return MMAL_EINVAL; + queue = wrapper->output_queue[port->index]; + + while (wrapper->status == MMAL_SUCCESS && + (*buffer = mmal_queue_get(queue)) == NULL) + { + if (!(flags & MMAL_WRAPPER_FLAG_WAIT)) + break; + vcos_semaphore_wait(&private->sema); + } + + return wrapper->status == MMAL_SUCCESS && !*buffer ? MMAL_EAGAIN : wrapper->status; +} diff --git a/interface/mmal/util/mmal_component_wrapper.h b/interface/mmal/util/mmal_component_wrapper.h new file mode 100755 index 0000000..ce29ae6 --- /dev/null +++ b/interface/mmal/util/mmal_component_wrapper.h @@ -0,0 +1,157 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_WRAPPER_H +#define MMAL_WRAPPER_H + +/** \defgroup MmalComponentWrapper utility + * \ingroup MmalUtilities + * The component wrapper utility functions can be used in place of common sequences + * of calls to the MMAL API in order to control a standalone component. It hides some + * of the complexity in using standalone components behind a fully synchronous + * interface. + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Forward type definition for a wrapper */ +typedef struct MMAL_WRAPPER_T MMAL_WRAPPER_T; + +/** Definition of the callback used by a wrapper to signal back to the client + * that a buffer header is available either in the pool or in the output queue. + * + * @param wrapper Pointer to the wrapper + */ +typedef void (*MMAL_WRAPPER_CALLBACK_T)(MMAL_WRAPPER_T *wrapper); + +/** Structure describing a wrapper around a component */ +struct MMAL_WRAPPER_T { + + void *user_data; /**< Field reserved for use by the client. */ + MMAL_WRAPPER_CALLBACK_T callback; /**< Callback set by the client. */ + MMAL_COMPONENT_T *component; + MMAL_STATUS_T status; + + MMAL_PORT_T *control; /**< Control port (Read Only). */ + + uint32_t input_num; /**< Number of input ports (Read Only). */ + MMAL_PORT_T **input; /**< Array of input ports (Read Only). */ + MMAL_POOL_T **input_pool; /**< Array of input pools (Read Only). */ + + uint32_t output_num; /**< Number of output ports (Read Only). */ + MMAL_PORT_T **output; /**< Array of output ports (Read Only). */ + MMAL_POOL_T **output_pool; /**< Array of output pools (Read Only). */ + MMAL_QUEUE_T **output_queue; /**< Array of output queues (Read Only). */ + + /* Used for debug / statistics */ + int64_t time_setup; /**< Time in microseconds taken to setup the connection. */ + int64_t time_enable; /**< Time in microseconds taken to enable the connection. */ + int64_t time_disable; /**< Time in microseconds taken to disable the connection. */ + +}; + +/** Create a wrapper around a component. + * The wrapper shall include a pool of buffer headers for each port. The pools will be suitable + * for the current format of its associated port. + * + * @param wrapper The address of a wrapper pointer that will be set to point to the created + * wrapper. + * @param name The name of the component to create. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_create(MMAL_WRAPPER_T **wrapper, const char *name); + +/** \name MMAL wrapper flags + * \anchor wrapperflags + */ +/* @{ */ +/** The operation should be blocking */ +#define MMAL_WRAPPER_FLAG_WAIT 1 +/** The pool for the port should allocate memory for the payloads */ +#define MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE 2 +/** The port will use shared memory payloads */ +#define MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY 4 +/* @} */ + +/** Enable a port on a component wrapper. + * + * @param port port to enable + * @param flags used to specify payload allocation flags for the pool + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_port_enable(MMAL_PORT_T *port, uint32_t flags); + +/** Disable a port on a component wrapper. + * + * @param port port to disable + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_port_disable(MMAL_PORT_T *port); + +/** Wait for an empty buffer to be available on a port. + * + * @param port port to get an empty buffer from + * @param buffer points to the retreived buffer on return + * @param flags specify MMAL_WRAPPER_FLAG_WAIT for a blocking operation + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_buffer_get_empty(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, uint32_t flags); + +/** Wait for a full buffer to be available on a port. + * + * @param port port to get a full buffer from + * @param buffer points to the retreived buffer on return + * @param flags specify MMAL_WRAPPER_FLAG_WAIT for a blocking operation + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_buffer_get_full(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, uint32_t flags); + +/** Cancel any ongoing blocking operation on a component wrapper. + * + * @param wrapper The wrapper on which to cancel operations. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_cancel(MMAL_WRAPPER_T *wrapper); + +/** Destroy a wrapper. + * Destroys a component wrapper and any resources it owns. + * + * @param wrapper The wrapper to be destroyed. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_wrapper_destroy(MMAL_WRAPPER_T *wrapper); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* MMAL_WRAPPER_H */ diff --git a/interface/mmal/util/mmal_connection.c b/interface/mmal/util/mmal_connection.c new file mode 100755 index 0000000..485ba77 --- /dev/null +++ b/interface/mmal/util/mmal_connection.c @@ -0,0 +1,529 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmal.h" +#include "util/mmal_util.h" +#include "util/mmal_connection.h" +#include "mmal_logging.h" +#include + +#define CONNECTION_NAME_FORMAT "%s:%.2222s:%i/%s:%.2222s:%i" + +typedef struct +{ + MMAL_CONNECTION_T connection; /**< Must be the first member! */ + MMAL_PORT_T *pool_port; /**< Port used to create the pool */ + + /** Reference counting */ + int refcount; + +} MMAL_CONNECTION_PRIVATE_T; + +/** Callback from a clock port. Buffer is immediately sent to next component. */ +static void mmal_connection_bh_clock_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_CONNECTION_T *connection = (MMAL_CONNECTION_T *)port->userdata; + MMAL_PORT_T *other_port = (port == connection->in) ? connection->out : connection->in; + + LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length); + + if (other_port->is_enabled) + { + status = mmal_port_send_buffer(other_port, buffer); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("error sending buffer to clock port (%i)", status); + mmal_buffer_header_release(buffer); + } + } + else + { + mmal_buffer_header_release(buffer); + } +} + +/** Callback from an input port. Buffer is released. */ +static void mmal_connection_bh_in_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length); + + /* We're done with the buffer, just recycle it */ + mmal_buffer_header_release(buffer); +} + +/** Callback from an output port. Buffer is queued for the next component. */ +static void mmal_connection_bh_out_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_CONNECTION_T *connection = (MMAL_CONNECTION_T *)port->userdata; + + LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length); + + /* Queue the buffer produced by the output port */ + mmal_queue_put(connection->queue, buffer); + + if (connection->callback) + connection->callback(connection); +} + +/** Callback from the pool. Buffer is available. */ +static MMAL_BOOL_T mmal_connection_bh_release_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, + void *userdata) +{ + MMAL_CONNECTION_T *connection = (MMAL_CONNECTION_T *)userdata; + MMAL_PARAM_UNUSED(pool); + + /* Queue the buffer produced by the output port */ + mmal_queue_put(pool->queue, buffer); + + if (connection->callback) + connection->callback(connection); + + return 0; +} + +/*****************************************************************************/ +static MMAL_STATUS_T mmal_connection_destroy_internal(MMAL_CONNECTION_T *connection) +{ + MMAL_STATUS_T status; + + if (connection->is_enabled) + { + status = mmal_connection_disable(connection); + if (status != MMAL_SUCCESS) + return status; + } + + /* Special case for tunnelling */ + if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING) + { + status = mmal_port_disconnect(connection->out); + if (status != MMAL_SUCCESS) + LOG_ERROR("connection %s could not be cleared", connection->name); + } + + /* Cleanup resources */ + if (connection->pool) + mmal_pool_destroy(connection->pool); + if (connection->queue) + mmal_queue_destroy(connection->queue); + + vcos_free(connection); + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_connection_destroy(MMAL_CONNECTION_T *connection) +{ + MMAL_CONNECTION_PRIVATE_T *private = (MMAL_CONNECTION_PRIVATE_T *)connection; + + LOG_TRACE("%p, %s", connection, connection->name); + + if (--private->refcount) + { + LOG_DEBUG("delaying destruction of %s (refount %i)", connection->name, + private->refcount); + return MMAL_SUCCESS; + } + + return mmal_connection_destroy_internal(connection); +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_connection_create(MMAL_CONNECTION_T **cx, + MMAL_PORT_T *out, MMAL_PORT_T *in, uint32_t flags) +{ + MMAL_STATUS_T status = MMAL_ENOMEM; + unsigned int name_size = strlen(out->component->name) + strlen(in->component->name) + sizeof(CONNECTION_NAME_FORMAT); + unsigned int size = sizeof(MMAL_CONNECTION_PRIVATE_T) + name_size; + MMAL_CONNECTION_PRIVATE_T *private; + MMAL_CONNECTION_T *connection; + char *name; + + /* Sanity checking */ + if (!cx) + return MMAL_EINVAL; + + private = vcos_malloc(size, "mmal connection"); + if (!private) + return MMAL_ENOMEM; + memset(private, 0, size); + connection = &private->connection; + private->refcount = 1; + name = (char *)&private[1]; + + vcos_snprintf(name, name_size - 1, CONNECTION_NAME_FORMAT, + out->component->name, + mmal_port_type_to_string(out->type), (int)out->index, + in->component->name, + mmal_port_type_to_string(in->type), (int)in->index); + + LOG_TRACE("out %p, in %p, flags %x, %s", out, in, flags, name); + + connection->out = out; + connection->in = in; + connection->flags = flags; + connection->name = name; + + connection->time_setup = vcos_getmicrosecs(); + + /* Set the format of the input port to match the output one */ + status = mmal_format_full_copy(in->format, out->format); + if (status == MMAL_SUCCESS) + status = mmal_port_format_commit(in); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("format not set on input port"); + goto error; + } + + /* In pass-through mode we need to propagate the buffer requirements of the + * connected input port */ + if (out->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH) + { + MMAL_PARAMETER_BUFFER_REQUIREMENTS_T param = + {{MMAL_PARAMETER_BUFFER_REQUIREMENTS, sizeof(MMAL_PARAMETER_BUFFER_REQUIREMENTS_T)}, + in->buffer_num_min, in->buffer_size_min, in->buffer_alignment_min, + in->buffer_num_recommended, in->buffer_size_recommended}; + status = mmal_port_parameter_set(out, ¶m.hdr); + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + LOG_ERROR("failed to propagate buffer requirements"); + goto error; + } + status = MMAL_SUCCESS; + } + + /* Special case for tunnelling */ + if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING) + { + status = mmal_port_connect(out, in); + if (status != MMAL_SUCCESS) + LOG_ERROR("connection could not be made"); + goto done; + } + + /* Create empty pool of buffer headers for now (will be resized later on) */ + private->pool_port = (in->capabilities & MMAL_PORT_CAPABILITY_ALLOCATION) ? in : out; + if (flags & MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT) + private->pool_port = in; + if (flags & MMAL_CONNECTION_FLAG_ALLOCATION_ON_OUTPUT) + private->pool_port = out; + connection->pool = mmal_port_pool_create(private->pool_port, 0, 0); + if (!connection->pool) + goto error; + mmal_pool_callback_set(connection->pool, mmal_connection_bh_release_cb, (void *)connection); + + /* Create a queue to store the buffers from the output port */ + connection->queue = mmal_queue_create(); + if (!connection->queue) + goto error; + + done: + out->userdata = (void *)connection; + in->userdata = (void *)connection; + connection->time_setup = vcos_getmicrosecs() - connection->time_setup; + *cx = connection; + return status; + + error: + /* coverity[var_deref_model] mmal_connection_destroy_internal will check connection->pool correctly */ + mmal_connection_destroy_internal(connection); + return status == MMAL_SUCCESS ? MMAL_ENOMEM : status; +} + +/*****************************************************************************/ +void mmal_connection_acquire(MMAL_CONNECTION_T *connection) +{ + MMAL_CONNECTION_PRIVATE_T *private = (MMAL_CONNECTION_PRIVATE_T *)connection; + LOG_TRACE("connection %s(%p), refcount %i", connection->name, connection, + private->refcount); + private->refcount++; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_connection_release(MMAL_CONNECTION_T *connection) +{ + MMAL_CONNECTION_PRIVATE_T *private = (MMAL_CONNECTION_PRIVATE_T *)connection; + LOG_TRACE("connection %s(%p), refcount %i", connection->name, connection, + private->refcount); + + if (--private->refcount) + return MMAL_SUCCESS; + + LOG_TRACE("destroying connection %s(%p)", connection->name, connection); + return mmal_connection_destroy_internal(connection); +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_connection_enable(MMAL_CONNECTION_T *connection) +{ + MMAL_PORT_T *in = connection->in, *out = connection->out; + uint32_t buffer_num, buffer_size; + MMAL_STATUS_T status; + + LOG_TRACE("%p, %s", connection, connection->name); + + if (connection->is_enabled) + return MMAL_SUCCESS; + + connection->time_enable = vcos_getmicrosecs(); + + /* Override the buffer values with the recommended ones (the port probably knows best) */ + if (!(connection->flags & MMAL_CONNECTION_FLAG_KEEP_BUFFER_REQUIREMENTS)) + { + if (out->buffer_num_recommended) + out->buffer_num = out->buffer_num_recommended; + if (out->buffer_size_recommended) + out->buffer_size = out->buffer_size_recommended; + if (in->buffer_num_recommended) + in->buffer_num = in->buffer_num_recommended; + if (in->buffer_size_recommended) + in->buffer_size = in->buffer_size_recommended; + } + + /* Special case for tunnelling */ + if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING) + { + /* Enable port. No callback because the port is connected. Other end of the connection + * will be enabled automatically. */ + status = mmal_port_enable(out, NULL); + if (status) + LOG_ERROR("output port couldn't be enabled"); + goto done; + } + + /* Set the buffering properties on both ports */ + buffer_num = MMAL_MAX(out->buffer_num, in->buffer_num); + buffer_size = MMAL_MAX(out->buffer_size, in->buffer_size); + out->buffer_num = in->buffer_num = buffer_num; + out->buffer_size = in->buffer_size = buffer_size; + + /* In pass-through mode there isn't any need to allocate memory */ + if (out->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH) + buffer_size = 0; + + /* Resize the output pool */ + status = mmal_pool_resize(connection->pool, buffer_num, buffer_size); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("couldn't resize pool"); + goto done; + } + + /* Enable output port. The callback specified here is the function which + * will be called when an empty buffer header comes back to the port. */ + status = mmal_port_enable(out, (out->type == MMAL_PORT_TYPE_CLOCK) ? + mmal_connection_bh_clock_cb : mmal_connection_bh_out_cb); + if(status) + { + LOG_ERROR("output port couldn't be enabled"); + goto done; + } + + /* Enable input port. The callback specified here is the function which + * will be called when an empty buffer header comes back to the port. */ + status = mmal_port_enable(in, (in->type == MMAL_PORT_TYPE_CLOCK) ? + mmal_connection_bh_clock_cb : mmal_connection_bh_in_cb); + if(status) + { + LOG_ERROR("input port couldn't be enabled"); + mmal_port_disable(out); + goto done; + } + + /* Clock ports need buffers to send clock updates, so + * populate both connected clock ports */ + if ((out->type == MMAL_PORT_TYPE_CLOCK) && (in->type == MMAL_PORT_TYPE_CLOCK)) + { + MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(connection->pool->queue); + while (buffer) + { + mmal_port_send_buffer(out, buffer); + buffer = mmal_queue_get(connection->pool->queue); + if (buffer) + { + mmal_port_send_buffer(in, buffer); + buffer = mmal_queue_get(connection->pool->queue); + } + } + } + + done: + connection->time_enable = vcos_getmicrosecs() - connection->time_enable; + connection->is_enabled = status == MMAL_SUCCESS; + return status; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_connection_disable(MMAL_CONNECTION_T *connection) +{ + MMAL_STATUS_T status; + MMAL_BUFFER_HEADER_T *buffer; + + LOG_TRACE("%p, %s", connection, connection->name); + + if (!connection->is_enabled) + return MMAL_SUCCESS; + + connection->time_disable = vcos_getmicrosecs(); + + /* Special case for tunnelling */ + if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING) + { + /* Disable port. Other end of the connection will be disabled automatically. */ + status = mmal_port_disable(connection->out); + if (status) + LOG_ERROR("output port couldn't be disabled"); + goto done; + } + + /* Disable input port. */ + status = mmal_port_disable(connection->in); + if(status) + { + LOG_ERROR("input port couldn't be disabled"); + goto done; + } + + /* Disable output port */ + status = mmal_port_disable(connection->out); + if(status) + { + LOG_ERROR("output port couldn't be disabled"); + goto done; + } + + /* Flush the queue */ + buffer = mmal_queue_get(connection->queue); + while (buffer) + { + mmal_buffer_header_release(buffer); + buffer = mmal_queue_get(connection->queue); + } + vcos_assert(mmal_queue_length(connection->pool->queue) == connection->pool->headers_num); + + done: + connection->time_disable = vcos_getmicrosecs() - connection->time_disable; + connection->is_enabled = !(status == MMAL_SUCCESS); + return status; +} + +/*****************************************************************************/ +static MMAL_STATUS_T mmal_connection_reconfigure(MMAL_CONNECTION_T *connection, MMAL_ES_FORMAT_T *format) +{ + MMAL_STATUS_T status; + LOG_TRACE("%p, %s", connection, connection->name); + + status = mmal_connection_disable(connection); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("connection couldn't be disabled"); + return status; + } + + /* Set the new format for the output port */ + status = mmal_format_full_copy(connection->out->format, format); + if (status == MMAL_SUCCESS) + status = mmal_port_format_commit(connection->out); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("commit failed on port %s(%p) (%i)", + connection->out->name, connection->out, status); + return status; + } + + /* Set the new format for the input port */ + status = mmal_format_full_copy(connection->in->format, connection->out->format); + if (status == MMAL_SUCCESS) + status = mmal_port_format_commit(connection->in); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("commit failed on port %s(%p) (%i)", + connection->in->name, connection->in, status); + return status; + } + + /* Enable ports */ + status = mmal_connection_enable(connection); + if (status) + { + LOG_ERROR("connection couldn't be enabled"); + return status; + } + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_connection_event_format_changed(MMAL_CONNECTION_T *connection, + MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_EVENT_FORMAT_CHANGED_T *event; + MMAL_STATUS_T status; + + LOG_TRACE("%p, %s", connection, connection->name); + + if (buffer->cmd != MMAL_EVENT_FORMAT_CHANGED) + return MMAL_EINVAL; + + event = mmal_event_format_changed_get(buffer); + if (!event) + return MMAL_EINVAL; + + /* If we don't need to recreate our buffers then we can just forward the event + * to the next component (so it gets configured properly) */ + if ((connection->in->capabilities & MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE) && + event->buffer_size_min <= connection->out->buffer_size && + event->buffer_num_min <= connection->out->buffer_num) + { + status = mmal_format_full_copy(connection->out->format, event->format); + if (status == MMAL_SUCCESS) + status = mmal_port_format_commit(connection->out); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("format commit failed on port %s(%p) (%i)", + connection->out->name, connection->out, status); + return status; + } + + mmal_buffer_header_acquire(buffer); + status = mmal_port_send_buffer(connection->in, buffer); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("buffer send failed on port %s(%p) (%i)", + connection->in->name, connection->in, status); + mmal_buffer_header_release(buffer); + return status; + } + + return MMAL_SUCCESS; + } + + /* Otherwise we have to reconfigure our pipeline */ + return mmal_connection_reconfigure(connection, event->format); +} diff --git a/interface/mmal/util/mmal_connection.h b/interface/mmal/util/mmal_connection.h new file mode 100755 index 0000000..d2a02b1 --- /dev/null +++ b/interface/mmal/util/mmal_connection.h @@ -0,0 +1,230 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_CONNECTION_H +#define MMAL_CONNECTION_H + +/** \defgroup MmalConnectionUtility Port connection utility + * \ingroup MmalUtilities + * The port connection utility functions can be used in place of common sequences + * of calls to the MMAL API in order to process buffers being passed between two + * ports. + * + * \section ProcessingConnectionBufferHeaders Processing connection buffer headers + * Either in response to the client callback function being called, or simply on a + * timer, the client will need to process the buffer headers of the connection + * (unless tunneling is used). + * + * Buffer headers that are in the pool queue will need to be sent to the output port, + * while buffer headers in the connection queue are sent to the input port. The + * buffer headers in the connection queue may contain pixel data (the cmd field is + * zero) or an event (the cmd field is non-zero). In general, pixel data buffer + * headers need to be passed on, while event buffer headers are released. In the + * case of the format changed event, mmal_connection_event_format_changed() can be + * called before the event is released. + * + * Other, specialized use cases may also be implemented, such as getting and + * immediately releasing buffer headers from the connection queue in order to + * prevent their propagation. This could be used to drop out video, for example. + * + * \section TunnellingConnections Tunnelling connections + * If the \ref MMAL_CONNECTION_FLAG_TUNNELLING flag is set when the connection is + * created, MMAL tunneling will be used. This automates the passing of the buffer + * headers between the output port and input port, and back again. It will also do + * this as efficiently as possible, avoiding trips between the ARM and the VideoCore + * if both components are implemented on the VideoCore. The consequence of this is + * that there is no client callback made as buffer headers get transferred. + * + * The client can still monitor the control port of a component (usually a sink + * component, such as video_render) for the end of stream, in order to know when to + * dismantle the connection. + * + * \section ConnectionClientCallback Client callback + * When not using tunnelling, the client callback function is called each time a + * buffer arrives from a port (either input or output). + * + * \note The callback is made on a different thread from the one used by the + * client to set up the connection, so care must be taken with thread safety. + * One option is to raise a signal to the main client thread that queue processing + * needs to be done, another is for the callback to perform the queue processing + * itself. + * + * The client can also store an opaque pointer in the connection object, which is + * never used by the MMAL code and is only meaningful to the client. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \name Connection flags + * \anchor connectionflags + * The following flags describe the properties of the connection. */ +/* @{ */ +/** The connection is tunnelled. Buffer headers do not transit via the client but + * directly from the output port to the input port. */ +#define MMAL_CONNECTION_FLAG_TUNNELLING 0x1 +/** Force the pool of buffer headers used by the connection to be allocated on the input port. */ +#define MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT 0x2 +/** Force the pool of buffer headers used by the connection to be allocated on the output port. */ +#define MMAL_CONNECTION_FLAG_ALLOCATION_ON_OUTPUT 0x4 +/** Specify that the connection should not modify the buffer requirements. */ +#define MMAL_CONNECTION_FLAG_KEEP_BUFFER_REQUIREMENTS 0x8 +/** The connection is flagged as direct. This doesn't change the behaviour of + * the connection itself but is used by the the graph utility to specify that + * the buffer should be sent to the input port from with the port callback. */ +#define MMAL_CONNECTION_FLAG_DIRECT 0x10 +/* @} */ + +/** Forward type definition for a connection */ +typedef struct MMAL_CONNECTION_T MMAL_CONNECTION_T; + +/** Definition of the callback used by a connection to signal back to the client + * that a buffer header is available either in the pool or in the output queue. + * + * @param connection Pointer to the connection + */ +typedef void (*MMAL_CONNECTION_CALLBACK_T)(MMAL_CONNECTION_T *connection); + +/** Structure describing a connection between 2 ports (1 output and 1 input port) */ +struct MMAL_CONNECTION_T { + + void *user_data; /**< Field reserved for use by the client. */ + MMAL_CONNECTION_CALLBACK_T callback; /**< Callback set by the client. */ + + uint32_t is_enabled; /**< Specifies whether the connection is enabled or not (Read Only). */ + + uint32_t flags; /**< Flags passed during the create call (Read Only). A bitwise + * combination of \ref connectionflags "Connection flags" values. + */ + MMAL_PORT_T *in; /**< Input port used for the connection (Read Only). */ + MMAL_PORT_T *out; /**< Output port used for the connection (Read Only). */ + + MMAL_POOL_T *pool; /**< Pool of buffer headers used by the output port (Read Only). */ + MMAL_QUEUE_T *queue; /**< Queue for the buffer headers produced by the output port (Read Only). */ + + const char *name; /**< Connection name (Read Only). Used for debugging purposes. */ + + /* Used for debug / statistics */ + int64_t time_setup; /**< Time in microseconds taken to setup the connection. */ + int64_t time_enable; /**< Time in microseconds taken to enable the connection. */ + int64_t time_disable; /**< Time in microseconds taken to disable the connection. */ +}; + +/** Create a connection between two ports. + * The connection shall include a pool of buffer headers suitable for the current format of + * the output port. The format of the input port shall have been set to the same as that of + * the input port. + * Note that connections are reference counted and creating a connection automatically + * acquires a reference to it (released when \ref mmal_connection_destroy is called). + * + * @param connection The address of a connection pointer that will be set to point to the created + * connection. + * @param out The output port to use for the connection. + * @param in The input port to use for the connection. + * @param flags The flags specifying which type of connection should be created. + * A bitwise combination of \ref connectionflags "Connection flags" values. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_connection_create(MMAL_CONNECTION_T **connection, + MMAL_PORT_T *out, MMAL_PORT_T *in, uint32_t flags); + +/** Acquire a reference on a connection. + * Acquiring a reference on a connection will prevent a connection from being destroyed until + * the acquired reference is released (by a call to \ref mmal_connection_destroy). + * References are internally counted so all acquired references need a matching call to + * release them. + * + * @param connection connection to acquire + */ +void mmal_connection_acquire(MMAL_CONNECTION_T *connection); + +/** Release a reference on a connection + * Release an acquired reference on a connection. Triggers the destruction of the connection when + * the last reference is being released. + * \note This is in fact an alias of \ref mmal_connection_destroy which is added to make client + * code clearer. + * + * @param connection connection to release + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_connection_release(MMAL_CONNECTION_T *connection); + +/** Destroy a connection. + * Release an acquired reference on a connection. Only actually destroys the connection when + * the last reference is being released. + * The actual destruction of the connection will start by disabling it, if necessary. + * Any pool, queue, and so on owned by the connection shall then be destroyed. + * + * @param connection The connection to be destroyed. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_connection_destroy(MMAL_CONNECTION_T *connection); + +/** Enable a connection. + * The format of the two ports must have been committed before calling this function, + * although note that on creation, the connection automatically copies and commits the + * output port's format to the input port. + * + * The MMAL_CONNECTION_T::callback field must have been set if the \ref MMAL_CONNECTION_FLAG_TUNNELLING + * flag was not specified on creation. The client may also set the MMAL_CONNECTION_T::user_data + * in order to get a pointer passed, via the connection, to the callback. + * + * @param connection The connection to be enabled. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_connection_enable(MMAL_CONNECTION_T *connection); + +/** Disable a connection. + * + * @param connection The connection to be disabled. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_connection_disable(MMAL_CONNECTION_T *connection); + +/** Apply a format changed event to the connection. + * This function can be used when the client is processing buffer headers and receives + * a format changed event (\ref MMAL_EVENT_FORMAT_CHANGED). The connection is + * reconfigured, changing the format of the ports, the number of buffer headers and + * the size of the payload buffers as necessary. + * + * @param connection The connection to which the event shall be applied. + * @param buffer The buffer containing a format changed event. + * @return MMAL_SUCCESS on success. + */ +MMAL_STATUS_T mmal_connection_event_format_changed(MMAL_CONNECTION_T *connection, + MMAL_BUFFER_HEADER_T *buffer); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* MMAL_CONNECTION_H */ diff --git a/interface/mmal/util/mmal_default_components.h b/interface/mmal/util/mmal_default_components.h new file mode 100755 index 0000000..fb0ece4 --- /dev/null +++ b/interface/mmal/util/mmal_default_components.h @@ -0,0 +1,92 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_DEFAULT_COMPONENTS_H +#define MMAL_DEFAULT_COMPONENTS_H + +/** \defgroup MmalDefaultComponents List of default components + * This provides a list of default components on a per platform basis. + * @{ + */ + +#define MMAL_COMPONENT_DEFAULT_CONTAINER_READER "container_reader" +#define MMAL_COMPONENT_DEFAULT_CONTAINER_WRITER "container_writer" + +#if defined(ENABLE_MMAL_STANDALONE) +# define MMAL_COMPONENT_DEFAULT_VIDEO_DECODER "avcodec.video_decode" +# define MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER "avcodec.video_encode" +# define MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER "sdl.video_render" +# define MMAL_COMPONENT_DEFAULT_IMAGE_DECODER "avcodec.video_decode" +# define MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER "avcodec.video_encode" +# define MMAL_COMPONENT_DEFAULT_CAMERA "artificial_camera" +# define MMAL_COMPONENT_DEFAULT_VIDEO_CONVERTER "avcodec.video_convert" +# define MMAL_COMPONENT_DEFAULT_SPLITTER "splitter" +# define MMAL_COMPONENT_DEFAULT_SCHEDULER "scheduler" +# define MMAL_COMPONENT_DEFAULT_VIDEO_INJECTER "video_inject" +# define MMAL_COMPONENT_DEFAULT_AUDIO_DECODER "avcodec.audio_decode" +# define MMAL_COMPONENT_DEFAULT_AUDIO_RENDERER "sdl.audio_render" +# define MMAL_COMPONENT_DEFAULT_MIRACAST "miracast" +# define MMAL_COMPONENT_DEFAULT_CLOCK "clock" +#elif defined(__VIDEOCORE__) +# define MMAL_COMPONENT_DEFAULT_VIDEO_DECODER "ril.video_decode" +# define MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER "ril.video_encode" +# define MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER "ril.video_render" +# define MMAL_COMPONENT_DEFAULT_IMAGE_DECODER "ril.image_decode" +# define MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER "ril.image_encode" +# define MMAL_COMPONENT_DEFAULT_CAMERA "ril.camera" +# define MMAL_COMPONENT_DEFAULT_VIDEO_CONVERTER "video_convert" +# define MMAL_COMPONENT_DEFAULT_SPLITTER "splitter" +# define MMAL_COMPONENT_DEFAULT_SCHEDULER "scheduler" +# define MMAL_COMPONENT_DEFAULT_VIDEO_INJECTER "video_inject" +# define MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER "ril.video_splitter" +# define MMAL_COMPONENT_DEFAULT_AUDIO_DECODER "none" +# define MMAL_COMPONENT_DEFAULT_AUDIO_RENDERER "ril.audio_render" +# define MMAL_COMPONENT_DEFAULT_MIRACAST "miracast" +# define MMAL_COMPONENT_DEFAULT_CLOCK "clock" +# define MMAL_COMPONENT_DEFAULT_CAMERA_INFO "camera_info" +#else +# define MMAL_COMPONENT_DEFAULT_VIDEO_DECODER "vc.ril.video_decode" +# define MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER "vc.ril.video_encode" +# define MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER "vc.ril.video_render" +# define MMAL_COMPONENT_DEFAULT_IMAGE_DECODER "vc.ril.image_decode" +# define MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER "vc.ril.image_encode" +# define MMAL_COMPONENT_DEFAULT_CAMERA "vc.ril.camera" +# define MMAL_COMPONENT_DEFAULT_VIDEO_CONVERTER "vc.video_convert" +# define MMAL_COMPONENT_DEFAULT_SPLITTER "vc.splitter" +# define MMAL_COMPONENT_DEFAULT_SCHEDULER "vc.scheduler" +# define MMAL_COMPONENT_DEFAULT_VIDEO_INJECTER "vc.video_inject" +# define MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER "vc.ril.video_splitter" +# define MMAL_COMPONENT_DEFAULT_AUDIO_DECODER "none" +# define MMAL_COMPONENT_DEFAULT_AUDIO_RENDERER "vc.ril.audio_render" +# define MMAL_COMPONENT_DEFAULT_MIRACAST "vc.miracast" +# define MMAL_COMPONENT_DEFAULT_CLOCK "vc.clock" +# define MMAL_COMPONENT_DEFAULT_CAMERA_INFO "vc.camera_info" +#endif + +/** @} */ + +#endif /* MMAL_DEFAULT_COMPONENTS_H */ diff --git a/interface/mmal/util/mmal_graph.c b/interface/mmal/util/mmal_graph.c new file mode 100755 index 0000000..d66527b --- /dev/null +++ b/interface/mmal/util/mmal_graph.c @@ -0,0 +1,1560 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmal.h" +#include "util/mmal_util.h" +#include "util/mmal_graph.h" +#include "core/mmal_component_private.h" +#include "core/mmal_port_private.h" +#include "mmal_logging.h" + +#define GRAPH_CONNECTIONS_MAX 16 +#define PROCESSING_TIME_MAX 20000 + +/*****************************************************************************/ + +/** Private context for our graph. + * This also acts as a MMAL_COMPONENT_MODULE_T for when components are instantiated from graphs */ +typedef struct MMAL_COMPONENT_MODULE_T +{ + MMAL_GRAPH_T graph; /**< Must be the first member! */ + + MMAL_COMPONENT_T *component[GRAPH_CONNECTIONS_MAX]; + MMAL_GRAPH_TOPOLOGY_T topology[GRAPH_CONNECTIONS_MAX]; + unsigned int component_num; + + MMAL_CONNECTION_T *connection[GRAPH_CONNECTIONS_MAX]; + unsigned int connection_num; + unsigned int connection_current; + + MMAL_PORT_T *input[GRAPH_CONNECTIONS_MAX]; + unsigned int input_num; + MMAL_PORT_T *output[GRAPH_CONNECTIONS_MAX]; + unsigned int output_num; + MMAL_PORT_T *clock[GRAPH_CONNECTIONS_MAX]; + unsigned int clock_num; + + MMAL_COMPONENT_T *graph_component; + + MMAL_BOOL_T stop_thread; /**< informs the worker thread to exit */ + VCOS_THREAD_T thread; /**< worker thread which processes all internal connections */ + VCOS_SEMAPHORE_T sema; /**< informs the worker thread that buffers are available */ + + MMAL_GRAPH_EVENT_CB event_cb; /**< callback for sending control port events to the client */ + void *event_cb_data; /**< callback data supplied by the client */ + +} MMAL_GRAPH_PRIVATE_T; + +typedef MMAL_GRAPH_PRIVATE_T MMAL_COMPONENT_MODULE_T; + +/*****************************************************************************/ +static MMAL_STATUS_T mmal_component_create_from_graph(const char *name, MMAL_COMPONENT_T *component); +static MMAL_BOOL_T graph_do_processing(MMAL_GRAPH_PRIVATE_T *graph); +static void graph_process_buffer(MMAL_GRAPH_PRIVATE_T *graph_private, + MMAL_CONNECTION_T *connection, MMAL_BUFFER_HEADER_T *buffer); + +/*****************************************************************************/ +static void graph_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_GRAPH_PRIVATE_T *graph = (MMAL_GRAPH_PRIVATE_T *)port->userdata; + + LOG_TRACE("port: %s(%p), buffer: %p, event: %4.4s", port->name, port, + buffer, (char *)&buffer->cmd); + + if (graph->event_cb) + { + graph->event_cb((MMAL_GRAPH_T *)graph, port, buffer, graph->event_cb_data); + } + else + { + LOG_ERROR("event lost on port %i,%i (event callback not defined)", + (int)port->type, (int)port->index); + mmal_buffer_header_release(buffer); + } +} + +/*****************************************************************************/ +static void graph_connection_cb(MMAL_CONNECTION_T *connection) +{ + MMAL_GRAPH_PRIVATE_T *graph = (MMAL_GRAPH_PRIVATE_T *)connection->user_data; + MMAL_BUFFER_HEADER_T *buffer; + + if (connection->flags == MMAL_CONNECTION_FLAG_DIRECT && + (buffer = mmal_queue_get(connection->queue)) != NULL) + { + graph_process_buffer(graph, connection, buffer); + return; + } + + vcos_semaphore_post(&graph->sema); +} + +/*****************************************************************************/ +static void* graph_worker_thread(void* ctx) +{ + MMAL_GRAPH_PRIVATE_T *graph = (MMAL_GRAPH_PRIVATE_T *)ctx; + + while (1) + { + vcos_semaphore_wait(&graph->sema); + if (graph->stop_thread) + break; + while(graph_do_processing(graph)); + } + + LOG_TRACE("worker thread exit %p", graph); + + return 0; +} + +/*****************************************************************************/ +static void graph_stop_worker_thread(MMAL_GRAPH_PRIVATE_T *graph) +{ + graph->stop_thread = MMAL_TRUE; + vcos_semaphore_post(&graph->sema); + vcos_thread_join(&graph->thread, NULL); +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_create(MMAL_GRAPH_T **graph, unsigned int userdata_size) +{ + MMAL_GRAPH_PRIVATE_T *private; + + LOG_TRACE("graph %p, userdata_size %u", graph, userdata_size); + + /* Sanity checking */ + if (!graph) + return MMAL_EINVAL; + + private = vcos_calloc(1, sizeof(MMAL_GRAPH_PRIVATE_T) + userdata_size, "mmal connection graph"); + if (!private) + return MMAL_ENOMEM; + *graph = &private->graph; + if (userdata_size) + (*graph)->userdata = (struct MMAL_GRAPH_USERDATA_T *)&private[1]; + + if (vcos_semaphore_create(&private->sema, "mmal graph sema", 0) != VCOS_SUCCESS) + { + LOG_ERROR("failed to create semaphore %p", graph); + vcos_free(private); + return MMAL_ENOSPC; + } + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_destroy(MMAL_GRAPH_T *graph) +{ + unsigned i; + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + + if (!graph) + return MMAL_EINVAL; + + LOG_TRACE("%p", graph); + + /* Notify client of destruction */ + if (graph->pf_destroy) + graph->pf_destroy(graph); + + for (i = 0; i < private->connection_num; i++) + mmal_connection_release(private->connection[i]); + + for (i = 0; i < private->component_num; i++) + mmal_component_release(private->component[i]); + + vcos_semaphore_delete(&private->sema); + + vcos_free(graph); + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_add_component(MMAL_GRAPH_T *graph, MMAL_COMPONENT_T *component) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + + LOG_TRACE("graph: %p, component: %s(%p)", graph, component ? component->name: 0, component); + + if (!component) + return MMAL_EINVAL; + + if (private->component_num >= GRAPH_CONNECTIONS_MAX) + { + LOG_ERROR("no space for component %s", component->name); + return MMAL_ENOSPC; + } + + mmal_component_acquire(component); + private->component[private->component_num++] = component; + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_component_topology(MMAL_GRAPH_T *graph, MMAL_COMPONENT_T *component, + MMAL_GRAPH_TOPOLOGY_T topology, int8_t *input, unsigned int input_num, + int8_t *output, unsigned int output_num) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + MMAL_PARAM_UNUSED(input); MMAL_PARAM_UNUSED(input_num); + MMAL_PARAM_UNUSED(output); MMAL_PARAM_UNUSED(output_num); + unsigned int i; + + LOG_TRACE("graph: %p, component: %s(%p)", graph, component ? component->name: 0, component); + + if (!component) + return MMAL_EINVAL; + + for (i = 0; i < private->component_num; i++) + if (component == private->component[i]) + break; + + if (i == private->component_num) + return MMAL_EINVAL; /* Component not found */ + + if (topology > MMAL_GRAPH_TOPOLOGY_STRAIGHT) + return MMAL_ENOSYS; /* Currently not supported */ + + private->topology[i] = topology; + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_add_connection(MMAL_GRAPH_T *graph, MMAL_CONNECTION_T *cx) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + + LOG_TRACE("graph: %p, connection: %s(%p)", graph, cx ? cx->name: 0, cx); + + if (!cx) + return MMAL_EINVAL; + + if (private->connection_num >= GRAPH_CONNECTIONS_MAX) + { + LOG_ERROR("no space for connection %s", cx->name); + return MMAL_ENOSPC; + } + + mmal_connection_acquire(cx); + private->connection[private->connection_num++] = cx; + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_add_port(MMAL_GRAPH_T *graph, MMAL_PORT_T *port) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + MMAL_PORT_T **list; + unsigned int *list_num; + + LOG_TRACE("graph: %p, port: %s(%p)", graph, port ? port->name: 0, port); + + if (!port) + return MMAL_EINVAL; + + switch (port->type) + { + case MMAL_PORT_TYPE_INPUT: + list = private->input; + list_num = &private->input_num; + break; + case MMAL_PORT_TYPE_OUTPUT: + list = private->output; + list_num = &private->output_num; + break; + case MMAL_PORT_TYPE_CLOCK: + list = private->clock; + list_num = &private->clock_num; + break; + default: + return MMAL_EINVAL; + } + + if (*list_num >= GRAPH_CONNECTIONS_MAX) + { + LOG_ERROR("no space for port %s", port->name); + return MMAL_ENOSPC; + } + + list[(*list_num)++] = port; + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_new_component(MMAL_GRAPH_T *graph, const char *name, + MMAL_COMPONENT_T **component) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + MMAL_COMPONENT_T *comp; + MMAL_STATUS_T status; + + LOG_TRACE("graph: %p, name: %s, component: %p", graph, name, component); + + if (private->component_num >= GRAPH_CONNECTIONS_MAX) + { + LOG_ERROR("no space for component %s", name); + return MMAL_ENOSPC; + } + + status = mmal_component_create(name, &comp); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("could not create component %s (%i)", name, status); + return status; + } + + private->component[private->component_num++] = comp; + if (component) + { + mmal_component_acquire(comp); + *component = comp; + } + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_new_connection(MMAL_GRAPH_T *graph, MMAL_PORT_T *out, MMAL_PORT_T *in, + uint32_t flags, MMAL_CONNECTION_T **connection) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + MMAL_CONNECTION_T *cx; + MMAL_STATUS_T status; + + if (!out || !in) + return MMAL_EINVAL; + if (out->type == MMAL_PORT_TYPE_CLOCK && in->type != MMAL_PORT_TYPE_CLOCK) + return MMAL_EINVAL; + if (out->type != MMAL_PORT_TYPE_CLOCK && + (out->type != MMAL_PORT_TYPE_OUTPUT || in->type != MMAL_PORT_TYPE_INPUT)) + return MMAL_EINVAL; + + LOG_TRACE("graph: %p, out: %s(%p), in: %s(%p), flags %x, connection: %p", + graph, out->name, out, in->name, in, (int)flags, connection); + + if (private->connection_num >= GRAPH_CONNECTIONS_MAX) + { + LOG_ERROR("no space for connection %s/%s", out->name, in->name); + return MMAL_ENOSPC; + } + + status = mmal_connection_create(&cx, out, in, flags); + if (status != MMAL_SUCCESS) + return status; + + private->connection[private->connection_num++] = cx; + if (connection) + { + mmal_connection_acquire(cx); + *connection = cx; + } + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_enable(MMAL_GRAPH_T *graph, MMAL_GRAPH_EVENT_CB cb, void *cb_data) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + MMAL_STATUS_T status = MMAL_SUCCESS; + unsigned int i; + + LOG_TRACE("graph: %p", graph); + + if (vcos_thread_create(&private->thread, "mmal graph thread", NULL, + graph_worker_thread, private) != VCOS_SUCCESS) + { + LOG_ERROR("failed to create worker thread %p", graph); + return MMAL_ENOSPC; + } + + private->event_cb = cb; + private->event_cb_data = cb_data; + + /* Enable all control ports */ + for (i = 0; i < private->component_num; i++) + { + private->component[i]->control->userdata = (void *)private; + status = mmal_port_enable(private->component[i]->control, graph_control_cb); + if (status != MMAL_SUCCESS) + LOG_ERROR("could not enable port %s", private->component[i]->control->name); + } + + /* Enable all our connections */ + for (i = 0; i < private->connection_num; i++) + { + MMAL_CONNECTION_T *cx = private->connection[i]; + + cx->callback = graph_connection_cb; + cx->user_data = private; + + status = mmal_connection_enable(cx); + if (status != MMAL_SUCCESS) + goto error; + } + + /* Trigger the worker thread to populate the output ports with empty buffers */ + vcos_semaphore_post(&private->sema); + return status; + + error: + graph_stop_worker_thread(private); + return status; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_disable(MMAL_GRAPH_T *graph) +{ + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + MMAL_STATUS_T status = MMAL_SUCCESS; + unsigned int i; + + LOG_TRACE("graph: %p", graph); + + graph_stop_worker_thread(private); + + /* Disable all our connections */ + for (i = 0; i < private->connection_num; i++) + { + status = mmal_connection_disable(private->connection[i]); + if (status != MMAL_SUCCESS) + break; + } + + return status; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_build(MMAL_GRAPH_T *graph, + const char *name, MMAL_COMPONENT_T **component) +{ + LOG_TRACE("graph: %p, name: %s, component: %p", graph, name, component); + return mmal_component_create_with_constructor(name, mmal_component_create_from_graph, + (MMAL_GRAPH_PRIVATE_T *)graph, component); +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_graph_component_constructor(const char *name, + MMAL_COMPONENT_T *component) +{ + LOG_TRACE("name: %s, component: %p", name, component); + return mmal_component_create_from_graph(name, component); +} + +/*****************************************************************************/ +static void graph_component_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_COMPONENT_T *graph_component = (MMAL_COMPONENT_T *)port->userdata; + MMAL_GRAPH_PRIVATE_T *graph_private = graph_component->priv->module; + MMAL_STATUS_T status; + + LOG_TRACE("%s(%p),%p,%4.4s", port->name, port, buffer, (char *)&buffer->cmd); + + /* Call user defined function first */ + if (graph_private->graph.pf_control_callback) + { + status = graph_private->graph.pf_control_callback(&graph_private->graph, + port, buffer); + if (status != MMAL_ENOSYS) + return; + } + + /* Forward the event on the graph control port */ + mmal_port_event_send(graph_component->control, buffer); +} + +/*****************************************************************************/ +static void graph_component_connection_cb(MMAL_CONNECTION_T *connection) +{ + MMAL_COMPONENT_T *component = (MMAL_COMPONENT_T *)connection->user_data; + MMAL_BUFFER_HEADER_T *buffer; + + if (connection->flags == MMAL_CONNECTION_FLAG_DIRECT && + (buffer = mmal_queue_get(connection->queue)) != NULL) + { + graph_process_buffer((MMAL_GRAPH_PRIVATE_T *)component->priv->module, + connection, buffer); + return; + } + + mmal_component_action_trigger(component); +} + +/*****************************************************************************/ +static void graph_port_event_handler(MMAL_CONNECTION_T *connection, + MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_STATUS_T status; + + LOG_TRACE("port: %s(%p), buffer: %p, event: %4.4s", port->name, port, + buffer, (char *)&buffer->cmd); + + if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED && port->type == MMAL_PORT_TYPE_OUTPUT) + { + MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer); + if (event) + { + LOG_DEBUG("----------Port format changed----------"); + mmal_log_dump_port(port); + LOG_DEBUG("-----------------to---------------------"); + mmal_log_dump_format(event->format); + LOG_DEBUG(" buffers num (opt %i, min %i), size (opt %i, min: %i)", + event->buffer_num_recommended, event->buffer_num_min, + event->buffer_size_recommended, event->buffer_size_min); + LOG_DEBUG("----------------------------------------"); + } + + status = mmal_connection_event_format_changed(connection, buffer); + } + + else + status = MMAL_SUCCESS; /* FIXME: ignore any other event for now */ + + mmal_buffer_header_release(buffer); + + if (status != MMAL_SUCCESS) + mmal_event_error_send(port->component, status); +} + +/*****************************************************************************/ +static void graph_process_buffer(MMAL_GRAPH_PRIVATE_T *graph_private, + MMAL_CONNECTION_T *connection, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_STATUS_T status; + + /* Call user defined function first */ + if (graph_private->graph.pf_connection_buffer) + { + status = graph_private->graph.pf_connection_buffer(&graph_private->graph, connection, buffer); + if (status != MMAL_ENOSYS) + return; + } + + if (buffer->cmd) + { + graph_port_event_handler(connection, connection->out, buffer); + return; + } + + status = mmal_port_send_buffer(connection->in, buffer); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("%s(%p) could not send buffer to %s(%p) (%s)", + connection->out->name, connection->out, + connection->in->name, connection->in, + mmal_status_to_string(status)); + mmal_buffer_header_release(buffer); + mmal_event_error_send(connection->out->component, status); + } +} + +/*****************************************************************************/ +static MMAL_BOOL_T graph_do_processing(MMAL_GRAPH_PRIVATE_T *graph_private) +{ + MMAL_BUFFER_HEADER_T *buffer; + MMAL_BOOL_T run_again = 0; + MMAL_STATUS_T status; + unsigned int i, j; + + /* Process all the empty buffers first */ + for (i = 0, j = graph_private->connection_current; + i < graph_private->connection_num; i++, j++) + { + MMAL_CONNECTION_T *connection = + graph_private->connection[j%graph_private->connection_num]; + + if ((connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING) || + !connection->pool) + continue; /* Nothing else to do in tunnelling mode */ + + /* Send empty buffers to the output port of the connection */ + while ((buffer = mmal_queue_get(connection->pool->queue)) != NULL) + { + run_again = 1; + + status = mmal_port_send_buffer(connection->out, buffer); + if (status != MMAL_SUCCESS) + { + if (connection->out->is_enabled) + LOG_ERROR("mmal_port_send_buffer failed (%i)", status); + mmal_queue_put_back(connection->pool->queue, buffer); + run_again = 0; + break; + } + } + } + + /* Loop through all the connections */ + for (i = 0, j = graph_private->connection_current++; + i < graph_private->connection_num; i++, j++) + { + MMAL_CONNECTION_T *connection = + graph_private->connection[j%graph_private->connection_num]; + int64_t duration = vcos_getmicrosecs64(); + + if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING) + continue; /* Nothing else to do in tunnelling mode */ + if (connection->flags & MMAL_CONNECTION_FLAG_DIRECT) + continue; /* Nothing else to do in direct mode */ + + /* Send any queued buffer to the next component. + * We also make sure no connection can starve the others by + * having a timeout. */ + while (vcos_getmicrosecs64() - duration < PROCESSING_TIME_MAX && + (buffer = mmal_queue_get(connection->queue)) != NULL) + { + run_again = 1; + + graph_process_buffer(graph_private, connection, buffer); + } + } + + return run_again; +} + +/*****************************************************************************/ +static void graph_do_processing_loop(MMAL_COMPONENT_T *component) +{ + while (graph_do_processing((MMAL_GRAPH_PRIVATE_T *)component->priv->module)); +} + +/*****************************************************************************/ +static MMAL_PORT_T *find_port_from_graph(MMAL_GRAPH_PRIVATE_T *graph, MMAL_PORT_T *port) +{ + MMAL_PORT_T **list; + unsigned int *list_num; + + switch (port->type) + { + case MMAL_PORT_TYPE_INPUT: + list = graph->input; + list_num = &graph->input_num; + break; + case MMAL_PORT_TYPE_OUTPUT: + list = graph->output; + list_num = &graph->output_num; + break; + case MMAL_PORT_TYPE_CLOCK: + list = graph->clock; + list_num = &graph->clock_num; + break; + default: + return 0; + } + + if (port->index > *list_num) + return 0; + + return list[port->index]; +} + +static MMAL_PORT_T *find_port_to_graph(MMAL_GRAPH_PRIVATE_T *graph, MMAL_PORT_T *port) +{ + MMAL_COMPONENT_T *component = graph->graph_component; + MMAL_PORT_T **list, **component_list; + unsigned int i, *list_num; + + switch (port->type) + { + case MMAL_PORT_TYPE_INPUT: + list = graph->input; + list_num = &graph->input_num; + component_list = component->input; + break; + case MMAL_PORT_TYPE_OUTPUT: + list = graph->output; + list_num = &graph->output_num; + component_list = component->output; + break; + case MMAL_PORT_TYPE_CLOCK: + list = graph->clock; + list_num = &graph->clock_num; + component_list = component->clock; + break; + default: + return 0; + } + + for (i = 0; i < *list_num; i++) + if (list[i] == port) + break; + + if (i == *list_num) + return 0; + return component_list[i]; +} + +static MMAL_STATUS_T graph_port_update(MMAL_GRAPH_PRIVATE_T *graph, + MMAL_PORT_T *graph_port, MMAL_BOOL_T init) +{ + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + port = find_port_from_graph(graph, graph_port); + if (!port) + { + LOG_ERROR("could not find matching port for %p", graph_port); + return MMAL_EINVAL; + } + + status = mmal_format_full_copy(graph_port->format, port->format); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("format copy failed on port %s", port->name); + return status; + } + + graph_port->buffer_num_min = port->buffer_num_min; + graph_port->buffer_num_recommended = port->buffer_num_recommended; + graph_port->buffer_size_min = port->buffer_size_min; + graph_port->buffer_size_recommended = port->buffer_size_recommended; + graph_port->buffer_alignment_min = port->buffer_alignment_min; + graph_port->capabilities = port->capabilities; + if (init) + { + graph_port->buffer_num = port->buffer_num; + graph_port->buffer_size = port->buffer_size; + } + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T graph_port_update_requirements(MMAL_GRAPH_PRIVATE_T *graph, + MMAL_PORT_T *graph_port) +{ + MMAL_PORT_T *port; + + port = find_port_from_graph(graph, graph_port); + if (!port) + { + LOG_ERROR("could not find matching port for %p", graph_port); + return MMAL_EINVAL; + } + + graph_port->buffer_num_min = port->buffer_num_min; + graph_port->buffer_num_recommended = port->buffer_num_recommended; + graph_port->buffer_size_min = port->buffer_size_min; + graph_port->buffer_size_recommended = port->buffer_size_recommended; + graph_port->buffer_alignment_min = port->buffer_alignment_min; + return MMAL_SUCCESS; +} + +/** Destroy a previously created component */ +static MMAL_STATUS_T graph_component_destroy(MMAL_COMPONENT_T *component) +{ + MMAL_COMPONENT_MODULE_T *graph = component->priv->module; + + /* Notify client of destruction */ + if (graph->graph.pf_destroy) + graph->graph.pf_destroy(&graph->graph); + graph->graph.pf_destroy = NULL; + + if (component->input_num) + mmal_ports_free(component->input, component->input_num); + + if (component->output_num) + mmal_ports_free(component->output, component->output_num); + + if (component->clock_num) + mmal_ports_clock_free(component->clock, component->clock_num); + + /* coverity[address_free] Freeing the first item in the structure is safe */ + mmal_graph_destroy(&graph->graph); + return MMAL_SUCCESS; +} + +/** Enable processing on a component */ +static MMAL_STATUS_T graph_component_enable(MMAL_COMPONENT_T *component) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = component->priv->module; + MMAL_STATUS_T status = MMAL_ENOSYS; + + /* Call user defined function first */ + if (graph_private->graph.pf_graph_enable) + status = graph_private->graph.pf_graph_enable(&graph_private->graph, MMAL_TRUE); + + return status; +} + +/** Disable processing on a component */ +static MMAL_STATUS_T graph_component_disable(MMAL_COMPONENT_T *component) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = component->priv->module; + MMAL_STATUS_T status = MMAL_ENOSYS; + + /* Call user defined function first */ + if (graph_private->graph.pf_graph_enable) + status = graph_private->graph.pf_graph_enable(&graph_private->graph, MMAL_FALSE); + + return status; +} + +/** Callback given to mmal_port_enable() */ +static void graph_port_enable_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = (MMAL_GRAPH_PRIVATE_T *)port->userdata; + MMAL_PORT_T *graph_port; + MMAL_STATUS_T status; + + graph_port = find_port_to_graph(graph_private, port); + if (!graph_port) + { + vcos_assert(0); + mmal_buffer_header_release(buffer); + return; + } + + /* Call user defined function first */ + if (graph_private->graph.pf_return_buffer) + { + status = graph_private->graph.pf_return_buffer(&graph_private->graph, graph_port, buffer); + if (status != MMAL_ENOSYS) + return; + } + + /* Forward the callback */ + if (buffer->cmd) + mmal_port_event_send(graph_port, buffer); + else + mmal_port_buffer_header_callback(graph_port, buffer); +} + +/** Check whether 2 ports of a component are linked */ +static MMAL_BOOL_T graph_component_topology_ports_linked(MMAL_GRAPH_PRIVATE_T *graph, + MMAL_PORT_T *port1, MMAL_PORT_T *port2) +{ + MMAL_COMPONENT_T *component = port1->component; + unsigned int i; + + for (i = 0; i < graph->component_num; i++) + if (component == graph->component[i]) + break; + + if (i == graph->component_num) + return MMAL_FALSE; /* Component not found */ + + if (graph->topology[i] == MMAL_GRAPH_TOPOLOGY_STRAIGHT) + return port1->index == port2->index; + + return MMAL_TRUE; +} + +/** Propagate a port enable */ +static MMAL_STATUS_T graph_port_state_propagate(MMAL_GRAPH_PRIVATE_T *graph, + MMAL_PORT_T *port, MMAL_BOOL_T enable) +{ + MMAL_COMPONENT_T *component = port->component; + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_TYPE_T type = port->type; + unsigned int i, j; + + LOG_TRACE("graph: %p, port %s(%p)", graph, port->name, port); + + if (port->type == MMAL_PORT_TYPE_OUTPUT) + type = MMAL_PORT_TYPE_INPUT; + if (port->type == MMAL_PORT_TYPE_INPUT) + type = MMAL_PORT_TYPE_OUTPUT; + + /* Loop through all the output ports of the component and if they are not enabled and + * match one of the connections we maintain, then we need to propagate the port enable. */ + for (i = 0; i < component->port_num; i++) + { + if (component->port[i]->type != type) + continue; + + if ((component->port[i]->is_enabled && enable) || + (!component->port[i]->is_enabled && !enable)) + continue; + + /* Find the matching connection */ + for (j = 0; j < graph->connection_num; j++) + if (graph->connection[j]->out == component->port[i] || + graph->connection[j]->in == component->port[i]) + break; + + if (j == graph->connection_num) + continue; /* No match */ + + if (!graph_component_topology_ports_linked(graph, port, component->port[i])) + continue; /* Ports are independent */ + + if (enable) + { + status = mmal_connection_enable(graph->connection[j]); + if (status != MMAL_SUCCESS) + break; + + mmal_log_dump_port(graph->connection[j]->out); + mmal_log_dump_port(graph->connection[j]->in); + } + + status = graph_port_state_propagate(graph, graph->connection[j]->in == component->port[i] ? + graph->connection[j]->out : graph->connection[j]->in, enable); + if (status != MMAL_SUCCESS) + break; + + if (!enable) + { + status = mmal_connection_disable(graph->connection[j]); + if (status != MMAL_SUCCESS) + break; + } + } + + return status; +} + +/** Enable processing on a port */ +static MMAL_STATUS_T graph_port_enable(MMAL_PORT_T *graph_port, MMAL_PORT_BH_CB_T cb) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_PORT_T *port; + MMAL_STATUS_T status; + MMAL_PARAM_UNUSED(cb); + + port = find_port_from_graph(graph_private, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Update the buffer requirements */ + port->buffer_num = graph_port->buffer_num; + port->buffer_size = graph_port->buffer_size; + + /* Call user defined function first */ + if (graph_private->graph.pf_enable) + { + status = graph_private->graph.pf_enable(&graph_private->graph, graph_port); + if (status != MMAL_ENOSYS) + return status; + } + + /* We'll intercept the callback */ + port->userdata = (void *)graph_private; + status = mmal_port_enable(port, graph_port_enable_cb); + if (status != MMAL_SUCCESS) + return status; + + /* We need to enable all the connected connections */ + status = graph_port_state_propagate(graph_private, port, 1); + + mmal_component_action_trigger(graph_port->component); + return status; +} + +/** Disable processing on a port */ +static MMAL_STATUS_T graph_port_disable(MMAL_PORT_T *graph_port) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + port = find_port_from_graph(graph_port->component->priv->module, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Call user defined function first */ + if (graph_private->graph.pf_disable) + { + status = graph_private->graph.pf_disable(&graph_private->graph, graph_port); + if (status != MMAL_ENOSYS) + return status; + } + + /* We need to disable all the connected connections. + * Since disable does an implicit flush, we only want to do that if + * we're acting on an input port or we risk discarding buffers along + * the way. */ + if (!graph_private->input_num || port->type == MMAL_PORT_TYPE_INPUT) + { + MMAL_STATUS_T status = graph_port_state_propagate(graph_private, port, 0); + if (status != MMAL_SUCCESS) + return status; + } + + /* Forward the call */ + return mmal_port_disable(port); +} + +/** Propagate a port flush */ +static MMAL_STATUS_T graph_port_flush_propagate(MMAL_GRAPH_PRIVATE_T *graph, + MMAL_PORT_T *port) +{ + MMAL_COMPONENT_T *component = port->component; + MMAL_STATUS_T status; + unsigned int i, j; + + LOG_TRACE("graph: %p, port %s(%p)", graph, port->name, port); + + status = mmal_port_flush(port); + if (status != MMAL_SUCCESS) + return status; + + if (port->type == MMAL_PORT_TYPE_OUTPUT) + return MMAL_SUCCESS; + + /* Loop through all the output ports of the component and if they match one + * of the connections we maintain, then we need to propagate the flush. */ + for (i = 0; i < component->port_num; i++) + { + if (component->port[i]->type != MMAL_PORT_TYPE_OUTPUT) + continue; + if (!component->port[i]->is_enabled) + continue; + + /* Find the matching connection */ + for (j = 0; j < graph->connection_num; j++) + if (graph->connection[j]->out == component->port[i]) + break; + + if (j == graph->connection_num) + continue; /* No match */ + + if (!graph_component_topology_ports_linked(graph, port, component->port[i])) + continue; /* Ports are independent */ + + /* Flush any buffer waiting in the connection queue */ + if (graph->connection[j]->queue) + { + MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(graph->connection[j]->queue); + while(buffer) + { + mmal_buffer_header_release(buffer); + buffer = mmal_queue_get(graph->connection[j]->queue); + } + } + + status = graph_port_flush_propagate(graph, graph->connection[j]->in); + if (status != MMAL_SUCCESS) + break; + } + + return status; +} + +/** Flush a port */ +static MMAL_STATUS_T graph_port_flush(MMAL_PORT_T *graph_port) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + port = find_port_from_graph(graph_private, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Call user defined function first */ + if (graph_private->graph.pf_flush) + { + status = graph_private->graph.pf_flush(&graph_private->graph, graph_port); + if (status != MMAL_ENOSYS) + return status; + } + + /* Forward the call */ + return graph_port_flush_propagate(graph_private, port); +} + +/** Send a buffer header to a port */ +static MMAL_STATUS_T graph_port_send(MMAL_PORT_T *graph_port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + port = find_port_from_graph(graph_port->component->priv->module, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Call user defined function first */ + if (graph_private->graph.pf_send_buffer) + { + status = graph_private->graph.pf_send_buffer(&graph_private->graph, graph_port, buffer); + if (status != MMAL_ENOSYS) + return status; + } + + /* Forward the call */ + return mmal_port_send_buffer(port, buffer); +} + +/** Propagate a format change */ +static MMAL_STATUS_T graph_port_format_commit_propagate(MMAL_GRAPH_PRIVATE_T *graph, + MMAL_PORT_T *port) +{ + MMAL_COMPONENT_T *component = port->component; + MMAL_STATUS_T status = MMAL_SUCCESS; + unsigned int i, j; + + LOG_TRACE("graph: %p, port %s(%p)", graph, port->name, port); + + if (port->type == MMAL_PORT_TYPE_OUTPUT || port->type == MMAL_PORT_TYPE_CLOCK) + return MMAL_SUCCESS; /* Nothing to do */ + + /* Loop through all the output ports of the component and if they are not enabled and + * match one of the connections we maintain, then we need to propagate the format change. */ + for (i = 0; i < component->output_num; i++) + { + MMAL_PORT_T *in, *out; + + if (component->output[i]->is_enabled) + continue; + + /* Find the matching connection */ + for (j = 0; j < graph->connection_num; j++) + if (graph->connection[j]->out == component->output[i]) + break; + + if (j == graph->connection_num) + continue; /* No match */ + + if (!graph_component_topology_ports_linked(graph, port, component->output[i])) + continue; /* Ports are independent */ + + in = graph->connection[j]->in; + out = graph->connection[j]->out; + + /* Apply the format to the input port */ + status = mmal_format_full_copy(in->format, out->format); + if (status != MMAL_SUCCESS) + break; + status = mmal_port_format_commit(in); + if (status != MMAL_SUCCESS) + break; + + mmal_log_dump_port(out); + mmal_log_dump_port(in); + + status = graph_port_format_commit_propagate(graph, in); + if (status != MMAL_SUCCESS) + break; + } + + return status; +} + +/** Set format on a port */ +static MMAL_STATUS_T graph_port_format_commit(MMAL_PORT_T *graph_port) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + unsigned int i; + + /* Call user defined function first */ + if (graph_private->graph.pf_format_commit) + { + status = graph_private->graph.pf_format_commit(&graph_private->graph, graph_port); + if (status == MMAL_SUCCESS) + goto end; + if (status != MMAL_ENOSYS) + return status; + } + + port = find_port_from_graph(graph_private, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Update actual port */ + status = mmal_format_full_copy(port->format, graph_port->format); + if (status != MMAL_SUCCESS) + return status; + port->buffer_num = graph_port->buffer_num; + port->buffer_size = graph_port->buffer_size; + + /* Forward the call */ + status = mmal_port_format_commit(port); + if (status != MMAL_SUCCESS) + return status; + + /* Propagate format changes to the connections */ + status = graph_port_format_commit_propagate(graph_private, port); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("couldn't propagate format commit of port %s(%p)", port->name, port); + return status; + } + + end: + /* Read the values back */ + status = graph_port_update(graph_private, graph_port, MMAL_FALSE); + if (status != MMAL_SUCCESS) + return status; + + /* Get the settings for the output ports in case they have changed */ + if (graph_port->type == MMAL_PORT_TYPE_INPUT) + { + for (i = 0; i < graph_private->output_num; i++) + { + status = graph_port_update(graph_private, graph_port->component->output[i], MMAL_FALSE); + if (status != MMAL_SUCCESS) + return status; + } + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T graph_port_control_parameter_get(MMAL_PORT_T *graph_port, + MMAL_PARAMETER_HEADER_T *param) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status = MMAL_ENOSYS; + unsigned int i; + + /* Call user defined function first */ + if (graph_private->graph.pf_parameter_get) + { + status = graph_private->graph.pf_parameter_get(&graph_private->graph, graph_port, param); + if (status != MMAL_ENOSYS) + return status; + } + + /* By default we do a get parameter on each component until one succeeds */ + for (i = 0; i < graph_private->component_num && status != MMAL_SUCCESS; i++) + status = mmal_port_parameter_get(graph_private->component[i]->control, param); + + return status; +} + +static MMAL_STATUS_T graph_port_parameter_get(MMAL_PORT_T *graph_port, + MMAL_PARAMETER_HEADER_T *param) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + /* Call user defined function first */ + if (graph_private->graph.pf_parameter_get) + { + status = graph_private->graph.pf_parameter_get(&graph_private->graph, graph_port, param); + if (status != MMAL_ENOSYS) + return status; + } + + port = find_port_from_graph(graph_private, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Forward the call */ + return mmal_port_parameter_get(port, param); +} + +static MMAL_STATUS_T graph_port_control_parameter_set(MMAL_PORT_T *graph_port, + const MMAL_PARAMETER_HEADER_T *param) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status = MMAL_ENOSYS; + unsigned int i; + + /* Call user defined function first */ + if (graph_private->graph.pf_parameter_set) + { + status = graph_private->graph.pf_parameter_set(&graph_private->graph, graph_port, param); + if (status != MMAL_ENOSYS) + return status; + } + + /* By default we do a set parameter on each component until one succeeds */ + for (i = 0; i < graph_private->component_num && status != MMAL_SUCCESS; i++) + status = mmal_port_parameter_set(graph_private->component[i]->control, param); + + return status; +} + +static MMAL_STATUS_T graph_port_parameter_set(MMAL_PORT_T *graph_port, + const MMAL_PARAMETER_HEADER_T *param) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + /* Call user defined function first */ + if (graph_private->graph.pf_parameter_set) + { + status = graph_private->graph.pf_parameter_set(&graph_private->graph, graph_port, param); + if (status != MMAL_ENOSYS) + return status; + } + + port = find_port_from_graph(graph_private, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Forward the call */ + status = mmal_port_parameter_set(port, param); + if (status != MMAL_SUCCESS) + goto end; + + if (param->id == MMAL_PARAMETER_BUFFER_REQUIREMENTS) + { + /* This might have changed the buffer requirements of other ports so fetch them all */ + MMAL_COMPONENT_T *component = graph_port->component; + unsigned int i; + for (i = 0; status == MMAL_SUCCESS && i < component->input_num; i++) + status = graph_port_update_requirements(graph_private, component->input[i]); + for (i = 0; status == MMAL_SUCCESS && i < component->output_num; i++) + status = graph_port_update_requirements(graph_private, component->output[i]); + } + + end: + return status; +} + +static MMAL_STATUS_T graph_port_connect(MMAL_PORT_T *graph_port, MMAL_PORT_T *other_port) +{ + MMAL_PORT_T *port; + + LOG_TRACE("%s(%p) %s(%p)", graph_port->name, graph_port, other_port->name, other_port); + + port = find_port_from_graph(graph_port->component->priv->module, graph_port); + if (!port) + return MMAL_EINVAL; + + /* Forward the call */ + return other_port ? mmal_port_connect(port, other_port) : mmal_port_disconnect(port); +} + +static uint8_t *graph_port_payload_alloc(MMAL_PORT_T *graph_port, uint32_t payload_size) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + uint8_t *payload; + + port = find_port_from_graph(graph_port->component->priv->module, graph_port); + if (!port) + return 0; + + /* Call user defined function first */ + if (graph_private->graph.pf_payload_alloc) + { + status = graph_private->graph.pf_payload_alloc(&graph_private->graph, graph_port, + payload_size, &payload); + if (status != MMAL_ENOSYS) + return status == MMAL_SUCCESS ? payload : NULL; + } + + /* Forward the call */ + return mmal_port_payload_alloc(port, payload_size); +} + +static void graph_port_payload_free(MMAL_PORT_T *graph_port, uint8_t *payload) +{ + MMAL_GRAPH_PRIVATE_T *graph_private = graph_port->component->priv->module; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + port = find_port_from_graph(graph_port->component->priv->module, graph_port); + if (!port) + return; + + /* Call user defined function first */ + if (graph_private->graph.pf_payload_free) + { + status = graph_private->graph.pf_payload_free(&graph_private->graph, graph_port, payload); + if (status == MMAL_SUCCESS) + return; + } + + /* Forward the call */ + mmal_port_payload_free(port, payload); +} + +/** Create an instance of a component */ +static MMAL_STATUS_T mmal_component_create_from_graph(const char *name, MMAL_COMPONENT_T *component) +{ + MMAL_STATUS_T status = MMAL_ENOMEM; + /* Our context is already allocated and available */ + MMAL_GRAPH_PRIVATE_T *graph = component->priv->module; + unsigned int i; + MMAL_PARAM_UNUSED(name); + + component->control->priv->pf_parameter_get = graph_port_control_parameter_get; + component->control->priv->pf_parameter_set = graph_port_control_parameter_set; + + /* Allocate the ports for this component */ + if(graph->input_num) + { + component->input = mmal_ports_alloc(component, graph->input_num, MMAL_PORT_TYPE_INPUT, 0); + if(!component->input) + goto error; + } + component->input_num = graph->input_num; + for(i = 0; i < component->input_num; i++) + { + component->input[i]->priv->pf_enable = graph_port_enable; + component->input[i]->priv->pf_disable = graph_port_disable; + component->input[i]->priv->pf_flush = graph_port_flush; + component->input[i]->priv->pf_send = graph_port_send; + component->input[i]->priv->pf_set_format = graph_port_format_commit; + component->input[i]->priv->pf_parameter_get = graph_port_parameter_get; + component->input[i]->priv->pf_parameter_set = graph_port_parameter_set; + if (graph->input[i]->priv->pf_connect && 0 /* FIXME: disabled for now */) + component->input[i]->priv->pf_connect = graph_port_connect; + component->input[i]->priv->pf_payload_alloc = graph_port_payload_alloc; + component->input[i]->priv->pf_payload_free = graph_port_payload_free; + + /* Mirror the port values */ + status = graph_port_update(graph, component->input[i], MMAL_TRUE); + if (status != MMAL_SUCCESS) + goto error; + } + if(graph->output_num) + { + component->output = mmal_ports_alloc(component, graph->output_num, MMAL_PORT_TYPE_OUTPUT, 0); + if(!component->output) + goto error; + } + component->output_num = graph->output_num; + for(i = 0; i < component->output_num; i++) + { + component->output[i]->priv->pf_enable = graph_port_enable; + component->output[i]->priv->pf_disable = graph_port_disable; + component->output[i]->priv->pf_flush = graph_port_flush; + component->output[i]->priv->pf_send = graph_port_send; + component->output[i]->priv->pf_set_format = graph_port_format_commit; + component->output[i]->priv->pf_parameter_get = graph_port_parameter_get; + component->output[i]->priv->pf_parameter_set = graph_port_parameter_set; + if (graph->output[i]->priv->pf_connect && 0 /* FIXME: disabled for now */) + component->output[i]->priv->pf_connect = graph_port_connect; + component->output[i]->priv->pf_payload_alloc = graph_port_payload_alloc; + component->output[i]->priv->pf_payload_free = graph_port_payload_free; + + /* Mirror the port values */ + status = graph_port_update(graph, component->output[i], MMAL_TRUE); + if (status != MMAL_SUCCESS) + goto error; + } + if(graph->clock_num) + { + component->clock = mmal_ports_clock_alloc(component, graph->clock_num, 0, NULL); + if(!component->clock) + { + status = MMAL_ENOMEM; + goto error; + } + } + component->clock_num = graph->clock_num; + for(i = 0; i < component->clock_num; i++) + { + component->clock[i]->priv->pf_enable = graph_port_enable; + component->clock[i]->priv->pf_disable = graph_port_disable; + component->clock[i]->priv->pf_flush = graph_port_flush; + component->clock[i]->priv->pf_send = graph_port_send; + component->clock[i]->priv->pf_set_format = graph_port_format_commit; + component->clock[i]->priv->pf_parameter_get = graph_port_parameter_get; + component->clock[i]->priv->pf_parameter_set = graph_port_parameter_set; + component->clock[i]->priv->pf_connect = NULL; /* FIXME: disabled for now */ + component->clock[i]->priv->pf_payload_alloc = graph_port_payload_alloc; + component->clock[i]->priv->pf_payload_free = graph_port_payload_free; + + /* Mirror the port values */ + status = graph_port_update(graph, component->clock[i], MMAL_TRUE); + if (status != MMAL_SUCCESS) + goto error; + } + + status = mmal_component_action_register(component, graph_do_processing_loop); + if (status != MMAL_SUCCESS) + goto error; + +#if 1 // FIXME + /* Set our connection callback */ + for (i = 0; i < graph->connection_num; i++) + { + graph->connection[i]->callback = graph_component_connection_cb; + graph->connection[i]->user_data = (void *)component; + } +#endif + + component->priv->pf_destroy = graph_component_destroy; + component->priv->pf_enable = graph_component_enable; + component->priv->pf_disable = graph_component_disable; + graph->graph_component = component; + + /* Enable all the control ports */ + for (i = 0; i < graph->component_num; i++) + { + graph->component[i]->control->userdata = (void *)component; + status = mmal_port_enable(graph->component[i]->control, graph_component_control_cb); + if (status != MMAL_SUCCESS) + LOG_ERROR("could not enable port %s", component->control->name); + } + + return MMAL_SUCCESS; + + error: + graph_component_destroy(component); + return status; +} + +MMAL_PORT_T *mmal_graph_find_port(MMAL_GRAPH_T *graph, + const char *name, + MMAL_PORT_TYPE_T type, + unsigned index) +{ + unsigned i; + MMAL_GRAPH_PRIVATE_T *private = (MMAL_GRAPH_PRIVATE_T *)graph; + for (i=0; icomponent_num; i++) + { + MMAL_COMPONENT_T *comp = private->component[i]; + if (vcos_strcasecmp(name, comp->name) == 0) + { + unsigned num; + MMAL_PORT_T **ports; + if (type == MMAL_PORT_TYPE_INPUT) { + num = comp->input_num; + ports = comp->input; + } + else if (type == MMAL_PORT_TYPE_OUTPUT) { + num = comp->output_num; + ports = comp->output; + } + else if (type == MMAL_PORT_TYPE_CLOCK) { + num = comp->clock_num; + ports = comp->clock; + } + else if (type == MMAL_PORT_TYPE_CONTROL) { + num = 1; + ports = &comp->control; + } + else { + vcos_assert(0); + return NULL; + } + if (index < num) + { + /* coverity[ptr_arith] num is 1 at this point */ + return ports[index]; + } + } + } + LOG_INFO("port %s:%d not found", name, index); + return NULL; +} diff --git a/interface/mmal/util/mmal_graph.h b/interface/mmal/util/mmal_graph.h new file mode 100755 index 0000000..e6284ba --- /dev/null +++ b/interface/mmal/util/mmal_graph.h @@ -0,0 +1,243 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_GRAPH_H +#define MMAL_GRAPH_H + +#include "util/mmal_connection.h" + +/** \defgroup MmalGraphUtility Graph Utility + * \ingroup MmalUtilities + * The graph utility functions allows one to easily create graphs of MMAL components. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** List of topology types */ +typedef enum +{ + MMAL_GRAPH_TOPOLOGY_ALL = 0, /**< All input ports and output ports are linked */ + MMAL_GRAPH_TOPOLOGY_STRAIGHT, /**< Input ports and output ports of the same index are linked */ + MMAL_GRAPH_TOPOLOGY_CUSTOM, /**< Custom defined topology */ + MMAL_GRAPH_TOPOLOGY_MAX + +} MMAL_GRAPH_TOPOLOGY_T; + +/** Structure describing a graph */ +typedef struct MMAL_GRAPH_T +{ + /** Pointer to private data of the client */ + struct MMAL_GRAPH_USERDATA_T *userdata; + + /** Optional callback that the client can set to get notified when the graph is going to be destroyed */ + void (*pf_destroy)(struct MMAL_GRAPH_T *); + + /** Optional callback that the client can set to intercept parameter requests on ports exposed by the graph */ + MMAL_STATUS_T (*pf_parameter_set)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param); + /** Optional callback that the client can set to intercept parameter requests on ports exposed by the graph */ + MMAL_STATUS_T (*pf_parameter_get)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, MMAL_PARAMETER_HEADER_T *param); + /** Optional callback that the client can set to intercept format commit calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_format_commit)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port); + /** Optional callback that the client can set to intercept send buffer calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_send_buffer)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); + /** Optional callback that the client can set to intercept buffer callbacks on ports exposed by the graph */ + MMAL_STATUS_T (*pf_return_buffer)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); + /** Optional callback that the client can set to intercept payload alloc calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_payload_alloc)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, uint32_t payload_size, uint8_t **); + /** Optional callback that the client can set to intercept payload free calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_payload_free)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, uint8_t *payload); + /** Optional callback that the client can set to intercept flush calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_flush)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port); + /** Optional callback that the client can set to control callbacks from the internal components of the graph */ + /** Optional callback that the client can set to intercept enable calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_enable)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port); + /** Optional callback that the client can set to intercept disable calls on ports exposed by the graph */ + MMAL_STATUS_T (*pf_disable)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port); + /** Optional callback that the client can set to control callbacks from the internal components of the graph */ + MMAL_STATUS_T (*pf_control_callback)(struct MMAL_GRAPH_T *, MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); + /** Optional callback that the client can set to intercept component_enable/disable calls made to the graph */ + MMAL_STATUS_T (*pf_graph_enable)(struct MMAL_GRAPH_T *, MMAL_BOOL_T enable); + /** Optional callback that the client can set to intercept buffers going through internal connections. + * This will only be triggered if the connection is not tunnelled */ + MMAL_STATUS_T (*pf_connection_buffer)(struct MMAL_GRAPH_T *, MMAL_CONNECTION_T *connection, MMAL_BUFFER_HEADER_T *buffer); + +} MMAL_GRAPH_T; + +/** Create an instance of a graph. + * The newly created graph will need to be populated by the client. + * + * @param graph returned graph + * @param userdata_size size to be allocated for the userdata field + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_create(MMAL_GRAPH_T **graph, unsigned int userdata_size); + +/** Add a component to a graph. + * Allows the client to add a component to the graph. + * + * @param graph instance of the graph + * @param component component to add to a graph + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_add_component(MMAL_GRAPH_T *graph, MMAL_COMPONENT_T *component); + +/** Describe the topology of the ports of a component. + * Allows the client to describe the topology of a component. This information + * is used by the graph to choose which action to perform when + * enabling / disabling / committing / flushing a port exposed by the graph. + * Note that by default the topology of a component is set to MMAL_GRAPH_TOPOLOGY_ALL. + * + * @param graph instance of the graph + * @param component component to describe + * @param topology type of topology used by this component + * @param input output index (or -1 if sink) linked to each input port + * @param input_num number of indexes in the input list + * @param output input index (or -1 if source) linked to each output port + * @param output_num number of indexes in the output list + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_component_topology(MMAL_GRAPH_T *graph, MMAL_COMPONENT_T *component, + MMAL_GRAPH_TOPOLOGY_T topology, int8_t *input, unsigned int input_num, + int8_t *output, unsigned int output_num); + +/** Add a port to a graph. + * Allows the client to add an input or output port to a graph. The given port + * will effectively become an end point for the graph. + * + * @param graph instance of the graph + * @param port port to add to the graph + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_add_port(MMAL_GRAPH_T *graph, MMAL_PORT_T *port); + +/** Add a connection to a graph. + * Allows the client to add an internal connection to a graph. + * + * @param graph instance of the graph + * @param connection connection to add to the graph + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_add_connection(MMAL_GRAPH_T *graph, MMAL_CONNECTION_T *connection); + +/** Create a new component and add it to a graph. + * Allows the client to create and add a component to the graph. + * + * @param graph instance of the graph + * @param name name of the component to create + * @param component if not NULL, will contain a pointer to the created component + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_new_component(MMAL_GRAPH_T *graph, const char *name, + MMAL_COMPONENT_T **component); + +/** Create and add a connection to a graph. + * Allows the client to create and add an internal connection to a graph. + * + * @param graph instance of the graph + * @param out the output port to use for the connection + * @param in the input port to use for the connection + * @param flags the flags specifying which type of connection should be created + * @param connection if not NULL, will contain a pointer to the created connection + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_new_connection(MMAL_GRAPH_T *graph, MMAL_PORT_T *out, MMAL_PORT_T *in, + uint32_t flags, MMAL_CONNECTION_T **connection); + +/** Definition of the callback used by a graph to send events to the client. + * + * @param graph the graph sending the event + * @param port the port which generated the event + * @param buffer the buffer header containing the event data + * @param cb_data data passed back to the client when the callback is invoked + */ +typedef void (*MMAL_GRAPH_EVENT_CB)(MMAL_GRAPH_T *graph, MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer, + void *cb_data); + +/** Enable the graph and start processing. + * + * @param graph the graph to enable + * @param cb the callback to invoke when an event occurs on any of the internal control ports + * @param cb_data data passed back to the client when the callback is invoked + * + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_enable(MMAL_GRAPH_T *graph, MMAL_GRAPH_EVENT_CB cb, void *cb_data); + +MMAL_STATUS_T mmal_graph_disable(MMAL_GRAPH_T *graph); + +/** Find a port in the graph. + * + * @param graph graph instance + * @param name name of the component of interest + * @param type type of port (in/out) + * @param index which port index within the component + * + * @return port, or NULL if not found + */ +MMAL_PORT_T *mmal_graph_find_port(MMAL_GRAPH_T *graph, + const char *name, + MMAL_PORT_TYPE_T type, + unsigned index); + +/** Create an instance of a component from a graph. + * The newly created component will expose input and output ports to the client. + * Not that all the exposed ports will be in a disabled state by default. + * + * @param graph graph to create the component from + * @param name name of the component to create + * @param component returned component + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_build(MMAL_GRAPH_T *ctx, + const char *name, MMAL_COMPONENT_T **component); + +/** Component constructor for a graph. + * FIXME: private function + * + * @param name name of the component to create + * @param component component + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_component_constructor(const char *name, MMAL_COMPONENT_T *component); + +/** Destroy a previously created graph + * @param graph graph to destroy + * @return MMAL_SUCCESS on success + */ +MMAL_STATUS_T mmal_graph_destroy(MMAL_GRAPH_T *ctx); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* MMAL_GRAPH_H */ diff --git a/interface/mmal/util/mmal_il.c b/interface/mmal/util/mmal_il.c new file mode 100755 index 0000000..8e0d8bb --- /dev/null +++ b/interface/mmal/util/mmal_il.c @@ -0,0 +1,1022 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmal.h" +#include "util/mmal_il.h" +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" + +/*****************************************************************************/ +static struct { + MMAL_STATUS_T mmal; + OMX_ERRORTYPE omx; +} mmal_omx_error[] = +{ + {MMAL_SUCCESS, OMX_ErrorNone}, + {MMAL_ENOMEM, OMX_ErrorInsufficientResources}, + {MMAL_ENOSPC, OMX_ErrorInsufficientResources}, + {MMAL_EINVAL, OMX_ErrorBadParameter}, + {MMAL_ENOSYS, OMX_ErrorNotImplemented}, + {(MMAL_STATUS_T)-1, OMX_ErrorUndefined}, +}; + +OMX_ERRORTYPE mmalil_error_to_omx(MMAL_STATUS_T status) +{ + unsigned int i; + for(i = 0; mmal_omx_error[i].mmal != (MMAL_STATUS_T)-1; i++) + if(mmal_omx_error[i].mmal == status) break; + return mmal_omx_error[i].omx; +} + +MMAL_STATUS_T mmalil_error_to_mmal(OMX_ERRORTYPE error) +{ + unsigned int i; + for(i = 0; mmal_omx_error[i].mmal != (MMAL_STATUS_T)-1; i++) + if(mmal_omx_error[i].omx == error) break; + return mmal_omx_error[i].mmal; +} + +/*****************************************************************************/ +OMX_U32 mmalil_buffer_flags_to_omx(uint32_t flags) +{ + OMX_U32 omx_flags = 0; + + if(flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) + omx_flags |= OMX_BUFFERFLAG_SYNCFRAME; + if(flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) + omx_flags |= OMX_BUFFERFLAG_ENDOFFRAME; + if(flags & MMAL_BUFFER_HEADER_FLAG_EOS) + omx_flags |= OMX_BUFFERFLAG_EOS; + if(flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) + omx_flags |= OMX_BUFFERFLAG_CODECCONFIG; + if(flags & MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY) + omx_flags |= OMX_BUFFERFLAG_DISCONTINUITY; + if (flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO) + omx_flags |= OMX_BUFFERFLAG_CODECSIDEINFO; + if (flags & MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT) + omx_flags |= OMX_BUFFERFLAG_CAPTURE_PREVIEW; + if (flags & MMAL_BUFFER_HEADER_FLAG_CORRUPTED) + omx_flags |= OMX_BUFFERFLAG_DATACORRUPT; + if (flags & MMAL_BUFFER_HEADER_FLAG_DECODEONLY) + omx_flags |= OMX_BUFFERFLAG_DECODEONLY; + if (flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) + omx_flags |= OMX_BUFFERFLAG_INTERLACED; + if (flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) + omx_flags |= OMX_BUFFERFLAG_TOP_FIELD_FIRST; + if (flags & MMAL_BUFFER_HEADER_FLAG_USER0) + omx_flags |= 1<<28; + if (flags & MMAL_BUFFER_HEADER_FLAG_USER1) + omx_flags |= 1<<29; + if (flags & MMAL_BUFFER_HEADER_FLAG_USER2) + omx_flags |= 1<<30; + if (flags & MMAL_BUFFER_HEADER_FLAG_USER3) + omx_flags |= 1<<31; + + return omx_flags; +} + +uint32_t mmalil_buffer_flags_to_mmal(OMX_U32 flags) +{ + uint32_t mmal_flags = 0; + + if (flags & OMX_BUFFERFLAG_SYNCFRAME) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME; + if (flags & OMX_BUFFERFLAG_ENDOFFRAME) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; + if (flags & OMX_BUFFERFLAG_EOS) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS; + if (flags & OMX_BUFFERFLAG_CODECCONFIG) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG; + if (flags & OMX_BUFFERFLAG_DISCONTINUITY) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY; + if (flags & OMX_BUFFERFLAG_CODECSIDEINFO) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO; + if (flags & OMX_BUFFERFLAG_CAPTURE_PREVIEW) + mmal_flags |= MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT; + if (flags & OMX_BUFFERFLAG_DATACORRUPT) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; + if (flags & OMX_BUFFERFLAG_DECODEONLY) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_DECODEONLY; + if (flags & OMX_BUFFERFLAG_INTERLACED) + mmal_flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; + if (flags & OMX_BUFFERFLAG_TOP_FIELD_FIRST) + mmal_flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; + if (flags & 1<<28) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_USER0; + if (flags & 1<<29) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_USER1; + if (flags & 1<<30) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_USER2; + if (flags & 1<<31) + mmal_flags |= MMAL_BUFFER_HEADER_FLAG_USER3; + + return mmal_flags; +} + +/*****************************************************************************/ +void mmalil_buffer_header_to_omx(OMX_BUFFERHEADERTYPE *omx, MMAL_BUFFER_HEADER_T *mmal) +{ + omx->pBuffer = mmal->data; + omx->nAllocLen = mmal->alloc_size; + omx->nFilledLen = mmal->length; + omx->nOffset = mmal->offset; + omx->nFlags = mmalil_buffer_flags_to_omx(mmal->flags); + omx->nTimeStamp = omx_ticks_from_s64(mmal->pts); + if (mmal->pts == MMAL_TIME_UNKNOWN) + { + if (mmal->dts == MMAL_TIME_UNKNOWN) + { + omx->nTimeStamp = omx_ticks_from_s64(0); + omx->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN; + } + else + { + omx->nTimeStamp = omx_ticks_from_s64(mmal->dts); + omx->nFlags |= OMX_BUFFERFLAG_TIME_IS_DTS; + } + } +} + +void mmalil_buffer_header_to_mmal(MMAL_BUFFER_HEADER_T *mmal, OMX_BUFFERHEADERTYPE *omx) +{ + mmal->cmd = 0; + mmal->data = omx->pBuffer; + mmal->alloc_size = omx->nAllocLen; + mmal->length = omx->nFilledLen; + mmal->offset = omx->nOffset; + if (omx->nFlags & OMX_BUFFERFLAG_TIME_IS_DTS) + { + mmal->dts = omx_ticks_to_s64(omx->nTimeStamp); + mmal->pts = MMAL_TIME_UNKNOWN; + } + else if (omx->nFlags & OMX_BUFFERFLAG_TIME_UNKNOWN) + { + mmal->dts = MMAL_TIME_UNKNOWN; + mmal->pts = MMAL_TIME_UNKNOWN; + } + else + { + mmal->dts = MMAL_TIME_UNKNOWN; + mmal->pts = omx_ticks_to_s64(omx->nTimeStamp); + } + mmal->flags = mmalil_buffer_flags_to_mmal(omx->nFlags); +} + +/*****************************************************************************/ +static struct { + MMAL_ES_TYPE_T type; + OMX_PORTDOMAINTYPE domain; +} mmal_omx_es_type_table[] = +{ + {MMAL_ES_TYPE_VIDEO, OMX_PortDomainVideo}, + {MMAL_ES_TYPE_VIDEO, OMX_PortDomainImage}, + {MMAL_ES_TYPE_AUDIO, OMX_PortDomainAudio}, + {MMAL_ES_TYPE_UNKNOWN, OMX_PortDomainMax} +}; + +OMX_PORTDOMAINTYPE mmalil_es_type_to_omx_domain(MMAL_ES_TYPE_T type) +{ + unsigned int i; + for(i = 0; mmal_omx_es_type_table[i].type != MMAL_ES_TYPE_UNKNOWN; i++) + if(mmal_omx_es_type_table[i].type == type) break; + return mmal_omx_es_type_table[i].domain; +} + +MMAL_ES_TYPE_T mmalil_omx_domain_to_es_type(OMX_PORTDOMAINTYPE domain) +{ + unsigned int i; + for(i = 0; mmal_omx_es_type_table[i].type != MMAL_ES_TYPE_UNKNOWN; i++) + if(mmal_omx_es_type_table[i].domain == domain) break; + return mmal_omx_es_type_table[i].type; +} + +/*****************************************************************************/ +static struct { + uint32_t encoding; + OMX_AUDIO_CODINGTYPE coding; +} mmal_omx_audio_coding_table[] = +{ + {MMAL_ENCODING_MP4A, OMX_AUDIO_CodingAAC}, + {MMAL_ENCODING_MPGA, OMX_AUDIO_CodingMP3}, + {MMAL_ENCODING_WMA2, OMX_AUDIO_CodingWMA}, + {MMAL_ENCODING_WMA1, OMX_AUDIO_CodingWMA}, + {MMAL_ENCODING_AMRNB, OMX_AUDIO_CodingAMR}, + {MMAL_ENCODING_AMRWB, OMX_AUDIO_CodingAMR}, + {MMAL_ENCODING_AMRWBP, OMX_AUDIO_CodingAMR}, + {MMAL_ENCODING_VORBIS, OMX_AUDIO_CodingVORBIS}, + {MMAL_ENCODING_ALAW, OMX_AUDIO_CodingPCM}, + {MMAL_ENCODING_MULAW, OMX_AUDIO_CodingPCM}, + {MMAL_ENCODING_PCM_SIGNED_LE, OMX_AUDIO_CodingPCM}, + {MMAL_ENCODING_PCM_UNSIGNED_LE,OMX_AUDIO_CodingPCM}, + {MMAL_ENCODING_PCM_SIGNED_BE, OMX_AUDIO_CodingPCM}, + {MMAL_ENCODING_PCM_UNSIGNED_BE,OMX_AUDIO_CodingPCM}, + {MMAL_ENCODING_AC3, OMX_AUDIO_CodingDDP}, + {MMAL_ENCODING_EAC3, OMX_AUDIO_CodingDDP}, + {MMAL_ENCODING_DTS, OMX_AUDIO_CodingDTS}, + {MMAL_ENCODING_UNKNOWN, OMX_AUDIO_CodingUnused} +}; + +uint32_t mmalil_omx_audio_coding_to_encoding(OMX_AUDIO_CODINGTYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_audio_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_audio_coding_table[i].coding == coding) break; + return mmal_omx_audio_coding_table[i].encoding; +} + +OMX_AUDIO_CODINGTYPE mmalil_encoding_to_omx_audio_coding(uint32_t encoding) +{ + unsigned int i; + for(i = 0; mmal_omx_audio_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_audio_coding_table[i].encoding == encoding) break; + return mmal_omx_audio_coding_table[i].coding; +} + +static struct { + OMX_AUDIO_CODINGTYPE coding; + OMX_INDEXTYPE index; + unsigned int size; +} mmal_omx_audio_format_table[] = +{ + {OMX_AUDIO_CodingPCM, OMX_IndexParamAudioPcm, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)}, + {OMX_AUDIO_CodingADPCM, OMX_IndexParamAudioAdpcm, sizeof(OMX_AUDIO_PARAM_ADPCMTYPE)}, + {OMX_AUDIO_CodingAMR, OMX_IndexParamAudioAmr, sizeof(OMX_AUDIO_PARAM_AMRTYPE)}, + {OMX_AUDIO_CodingGSMFR, OMX_IndexParamAudioGsm_FR, sizeof(OMX_AUDIO_PARAM_GSMFRTYPE)}, + {OMX_AUDIO_CodingGSMEFR, OMX_IndexParamAudioGsm_EFR, sizeof(OMX_AUDIO_PARAM_GSMEFRTYPE)}, + {OMX_AUDIO_CodingGSMHR, OMX_IndexParamAudioGsm_HR, sizeof(OMX_AUDIO_PARAM_GSMHRTYPE)}, + {OMX_AUDIO_CodingPDCFR, OMX_IndexParamAudioPdc_FR, sizeof(OMX_AUDIO_PARAM_PDCFRTYPE)}, + {OMX_AUDIO_CodingPDCEFR, OMX_IndexParamAudioPdc_EFR, sizeof(OMX_AUDIO_PARAM_PDCEFRTYPE)}, + {OMX_AUDIO_CodingPDCHR, OMX_IndexParamAudioPdc_HR, sizeof(OMX_AUDIO_PARAM_PDCHRTYPE)}, + {OMX_AUDIO_CodingTDMAFR, OMX_IndexParamAudioTdma_FR, sizeof(OMX_AUDIO_PARAM_TDMAFRTYPE)}, + {OMX_AUDIO_CodingTDMAEFR, OMX_IndexParamAudioTdma_EFR, sizeof(OMX_AUDIO_PARAM_TDMAEFRTYPE)}, + {OMX_AUDIO_CodingQCELP8, OMX_IndexParamAudioQcelp8, sizeof(OMX_AUDIO_PARAM_QCELP8TYPE)}, + {OMX_AUDIO_CodingQCELP13, OMX_IndexParamAudioQcelp13, sizeof(OMX_AUDIO_PARAM_QCELP13TYPE)}, + {OMX_AUDIO_CodingEVRC, OMX_IndexParamAudioEvrc, sizeof(OMX_AUDIO_PARAM_EVRCTYPE)}, + {OMX_AUDIO_CodingSMV, OMX_IndexParamAudioSmv, sizeof(OMX_AUDIO_PARAM_SMVTYPE)}, + {OMX_AUDIO_CodingG723, OMX_IndexParamAudioG723, sizeof(OMX_AUDIO_PARAM_G723TYPE)}, + {OMX_AUDIO_CodingG726, OMX_IndexParamAudioG726, sizeof(OMX_AUDIO_PARAM_G726TYPE)}, + {OMX_AUDIO_CodingG729, OMX_IndexParamAudioG729, sizeof(OMX_AUDIO_PARAM_G729TYPE)}, + {OMX_AUDIO_CodingAAC, OMX_IndexParamAudioAac, sizeof(OMX_AUDIO_PARAM_AACPROFILETYPE)}, + {OMX_AUDIO_CodingMP3, OMX_IndexParamAudioMp3, sizeof(OMX_AUDIO_PARAM_MP3TYPE)}, + {OMX_AUDIO_CodingSBC, OMX_IndexParamAudioSbc, sizeof(OMX_AUDIO_PARAM_SBCTYPE)}, + {OMX_AUDIO_CodingVORBIS, OMX_IndexParamAudioVorbis, sizeof(OMX_AUDIO_PARAM_VORBISTYPE)}, + {OMX_AUDIO_CodingWMA, OMX_IndexParamAudioWma, sizeof(OMX_AUDIO_PARAM_WMATYPE)}, + {OMX_AUDIO_CodingRA, OMX_IndexParamAudioRa, sizeof(OMX_AUDIO_PARAM_RATYPE)}, + {OMX_AUDIO_CodingMIDI, OMX_IndexParamAudioMidi, sizeof(OMX_AUDIO_PARAM_MIDITYPE)}, + {OMX_AUDIO_CodingDDP, OMX_IndexParamAudioDdp, sizeof(OMX_AUDIO_PARAM_DDPTYPE)}, + {OMX_AUDIO_CodingDTS, OMX_IndexParamAudioDts, sizeof(OMX_AUDIO_PARAM_DTSTYPE)}, + {OMX_AUDIO_CodingUnused, 0, 0} +}; + +OMX_AUDIO_CODINGTYPE mmalil_omx_audio_param_index_to_coding(OMX_INDEXTYPE index) +{ + unsigned int i; + for(i = 0; mmal_omx_audio_format_table[i].coding != OMX_AUDIO_CodingUnused; i++) + if(mmal_omx_audio_format_table[i].index == index) break; + + return mmal_omx_audio_format_table[i].coding; +} + +OMX_INDEXTYPE mmalil_omx_audio_param_index(OMX_AUDIO_CODINGTYPE coding, OMX_U32 *size) +{ + unsigned int i; + for(i = 0; mmal_omx_audio_format_table[i].coding != OMX_AUDIO_CodingUnused; i++) + if(mmal_omx_audio_format_table[i].coding == coding) break; + + if(size) *size = mmal_omx_audio_format_table[i].size; + return mmal_omx_audio_format_table[i].index; +} + +MMAL_STATUS_T mmalil_omx_default_channel_mapping(OMX_AUDIO_CHANNELTYPE *channel_mapping, unsigned int nchannels) +{ + static const OMX_AUDIO_CHANNELTYPE default_mapping[][8] = { + {OMX_AUDIO_ChannelNone}, + {OMX_AUDIO_ChannelCF}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, OMX_AUDIO_ChannelCF}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, OMX_AUDIO_ChannelCF, + OMX_AUDIO_ChannelCS}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, OMX_AUDIO_ChannelCF, + OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, OMX_AUDIO_ChannelCF, + OMX_AUDIO_ChannelLFE, OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, OMX_AUDIO_ChannelCF, + OMX_AUDIO_ChannelLFE, OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR, + OMX_AUDIO_ChannelCS}, + {OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF, OMX_AUDIO_ChannelCF, + OMX_AUDIO_ChannelLFE, OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR, + OMX_AUDIO_ChannelLS, OMX_AUDIO_ChannelRS} + }; + + if (!nchannels || nchannels >= MMAL_COUNTOF(default_mapping)) + return MMAL_EINVAL; + + memcpy(channel_mapping, default_mapping[nchannels], + sizeof(default_mapping[0][0]) * nchannels); + return MMAL_SUCCESS; +} + +MMAL_FOURCC_T mmalil_omx_audio_param_to_format(MMAL_ES_FORMAT_T *format, + OMX_AUDIO_CODINGTYPE coding, OMX_FORMAT_PARAM_TYPE *param) +{ + MMAL_AUDIO_FORMAT_T *audio = &format->es->audio; + format->encoding = mmalil_omx_audio_coding_to_encoding(coding); + format->encoding_variant = 0; + + switch(coding) + { + case OMX_AUDIO_CodingPCM: + audio->channels = param->pcm.nChannels; + audio->sample_rate = param->pcm.nSamplingRate; + audio->bits_per_sample = param->pcm.nBitPerSample; + if(param->pcm.ePCMMode == OMX_AUDIO_PCMModeLinear && param->pcm.bInterleaved) + { + if(param->pcm.eEndian == OMX_EndianBig && + param->pcm.eNumData == OMX_NumericalDataSigned) + format->encoding = MMAL_ENCODING_PCM_SIGNED_BE; + else if(param->pcm.eEndian == OMX_EndianLittle && + param->pcm.eNumData == OMX_NumericalDataSigned) + format->encoding = MMAL_ENCODING_PCM_SIGNED_LE; + if(param->pcm.eEndian == OMX_EndianBig && + param->pcm.eNumData == OMX_NumericalDataUnsigned) + format->encoding = MMAL_ENCODING_PCM_UNSIGNED_BE; + if(param->pcm.eEndian == OMX_EndianLittle && + param->pcm.eNumData == OMX_NumericalDataUnsigned) + format->encoding = MMAL_ENCODING_PCM_UNSIGNED_LE; + } + else if(param->pcm.ePCMMode == OMX_AUDIO_PCMModeALaw) + format->encoding = MMAL_ENCODING_ALAW; + else if(param->pcm.ePCMMode == OMX_AUDIO_PCMModeMULaw) + format->encoding = MMAL_ENCODING_MULAW; + break; + case OMX_AUDIO_CodingAAC: + audio->channels = param->aac.nChannels; + audio->sample_rate = param->aac.nSampleRate; + format->bitrate = param->aac.nBitRate; + switch(param->aac.eAACStreamFormat) + { + case OMX_AUDIO_AACStreamFormatMP2ADTS: + case OMX_AUDIO_AACStreamFormatMP4ADTS: + format->encoding = MMAL_ENCODING_MP4A; + format->encoding_variant = MMAL_ENCODING_VARIANT_MP4A_ADTS; + break; + case OMX_AUDIO_AACStreamFormatMP4FF: + case OMX_AUDIO_AACStreamFormatRAW: + format->encoding = MMAL_ENCODING_MP4A; + format->encoding_variant = MMAL_ENCODING_VARIANT_MP4A_DEFAULT; + break; + default: break; + } + break; + case OMX_AUDIO_CodingMP3: + format->encoding = MMAL_ENCODING_MPGA; + audio->channels = param->mp3.nChannels; + audio->sample_rate = param->mp3.nSampleRate; + format->bitrate = param->mp3.nBitRate; + break; + case OMX_AUDIO_CodingWMA: + audio->channels = param->wma.nChannels; + audio->sample_rate = param->wma.nSamplingRate; + audio->block_align = param->wma.nBlockAlign; + format->bitrate = param->wma.nBitRate; + switch(param->wma.eFormat) + { + case OMX_AUDIO_WMAFormat7: + format->encoding = MMAL_ENCODING_WMA1; + break; + case OMX_AUDIO_WMAFormat8: + case OMX_AUDIO_WMAFormat9: + format->encoding = MMAL_ENCODING_WMA2; + break; + default: break; + } + break; + case OMX_AUDIO_CodingVORBIS: + audio->channels = param->vorbis.nChannels; + audio->sample_rate = param->vorbis.nSampleRate; + format->bitrate = param->vorbis.nBitRate; + break; + case OMX_AUDIO_CodingAMR: + audio->channels = param->amr.nChannels; + audio->sample_rate = 8000; + format->bitrate = param->amr.nBitRate; + if(param->amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeNB0 && + param->amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeNB7) + format->encoding = MMAL_ENCODING_AMRNB; + if(param->amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0 && + param->amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeWB8) + format->encoding = MMAL_ENCODING_AMRWB; + break; + case OMX_AUDIO_CodingDDP: + audio->channels = param->ddp.nChannels; + audio->sample_rate = param->ddp.nSampleRate; + if(param->ddp.eBitStreamId > OMX_AUDIO_DDPBitStreamIdAC3) + format->encoding = MMAL_ENCODING_EAC3; + break; + case OMX_AUDIO_CodingDTS: + audio->channels = param->dts.nChannels; + audio->sample_rate = param->dts.nSampleRate; + audio->block_align = param->dts.nDtsFrameSizeBytes; + break; + + case OMX_AUDIO_CodingADPCM: + case OMX_AUDIO_CodingGSMFR: + case OMX_AUDIO_CodingGSMEFR: + case OMX_AUDIO_CodingGSMHR: + case OMX_AUDIO_CodingPDCFR: + case OMX_AUDIO_CodingPDCEFR: + case OMX_AUDIO_CodingPDCHR: + case OMX_AUDIO_CodingTDMAFR: + case OMX_AUDIO_CodingTDMAEFR: + case OMX_AUDIO_CodingQCELP8: + case OMX_AUDIO_CodingQCELP13: + case OMX_AUDIO_CodingEVRC: + case OMX_AUDIO_CodingSMV: + case OMX_AUDIO_CodingG711: + case OMX_AUDIO_CodingG723: + case OMX_AUDIO_CodingG726: + case OMX_AUDIO_CodingG729: + case OMX_AUDIO_CodingSBC: + case OMX_AUDIO_CodingRA: + case OMX_AUDIO_CodingMIDI: + default: + vcos_assert(0); + break; + } + + return format->encoding; +} + +OMX_AUDIO_CODINGTYPE mmalil_format_to_omx_audio_param(OMX_FORMAT_PARAM_TYPE *param, + OMX_INDEXTYPE *param_index, MMAL_ES_FORMAT_T *format) +{ + MMAL_AUDIO_FORMAT_T *audio = &format->es->audio; + OMX_AUDIO_CODINGTYPE coding = mmalil_encoding_to_omx_audio_coding(format->encoding); + OMX_U32 size = 0; + OMX_INDEXTYPE index = mmalil_omx_audio_param_index(coding, &size); + + if(param_index) *param_index = index; + memset(param, 0, size); + param->common.nSize = size; + + switch(coding) + { + case OMX_AUDIO_CodingPCM: + param->pcm.nChannels = audio->channels; + param->pcm.nSamplingRate = audio->sample_rate; + param->pcm.nBitPerSample = audio->bits_per_sample; + mmalil_omx_default_channel_mapping(param->pcm.eChannelMapping, audio->channels); + if(format->encoding == MMAL_ENCODING_PCM_SIGNED_BE || + format->encoding == MMAL_ENCODING_PCM_SIGNED_LE || + format->encoding == MMAL_ENCODING_PCM_UNSIGNED_BE || + format->encoding == MMAL_ENCODING_PCM_UNSIGNED_LE) + { + param->pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; + param->pcm.bInterleaved = OMX_TRUE; + param->pcm.eEndian = OMX_EndianLittle; + param->pcm.eNumData = OMX_NumericalDataSigned; + if(format->encoding == MMAL_ENCODING_PCM_SIGNED_BE || + format->encoding == MMAL_ENCODING_PCM_UNSIGNED_BE) + param->pcm.eEndian = OMX_EndianBig; + if(format->encoding == MMAL_ENCODING_PCM_UNSIGNED_LE || + format->encoding == MMAL_ENCODING_PCM_UNSIGNED_BE) + param->pcm.eNumData = OMX_NumericalDataUnsigned; + } + else if(format->encoding == MMAL_ENCODING_ALAW) + param->pcm.ePCMMode = OMX_AUDIO_PCMModeALaw; + else if(format->encoding == MMAL_ENCODING_MULAW) + param->pcm.ePCMMode = OMX_AUDIO_PCMModeMULaw; + break; + case OMX_AUDIO_CodingAAC: + param->aac.nChannels = audio->channels; + param->aac.nSampleRate = audio->sample_rate; + param->aac.nBitRate = format->bitrate; + switch(format->encoding_variant) + { + case MMAL_ENCODING_VARIANT_MP4A_ADTS: + param->aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; + break; + case MMAL_ENCODING_VARIANT_MP4A_DEFAULT: + param->aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatRAW; + break; + default: break; + } + break; + case OMX_AUDIO_CodingMP3: + param->mp3.nChannels = audio->channels; + param->mp3.nSampleRate = audio->sample_rate; + param->mp3.nBitRate = format->bitrate; + break; + case OMX_AUDIO_CodingWMA: + param->wma.nChannels = audio->channels; + param->wma.nSamplingRate = audio->sample_rate; + param->wma.nBlockAlign = audio->block_align; + param->wma.nBitRate = format->bitrate; + switch(format->encoding) + { + case MMAL_ENCODING_WMA1: + param->wma.eFormat = OMX_AUDIO_WMAFormat7; + break; + case MMAL_ENCODING_WMA2: + param->wma.eFormat = OMX_AUDIO_WMAFormat8; + break; + default: break; + } + break; + case OMX_AUDIO_CodingVORBIS: + param->vorbis.nChannels = audio->channels; + param->vorbis.nSampleRate = audio->sample_rate; + param->vorbis.nBitRate = format->bitrate; + break; + case OMX_AUDIO_CodingAMR: + param->amr.nChannels = audio->channels; + param->amr.nBitRate = format->bitrate; + if(format->encoding == MMAL_ENCODING_AMRNB) + param->amr.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0; + if(format->encoding == MMAL_ENCODING_AMRWB) + param->amr.eAMRBandMode = OMX_AUDIO_AMRBandModeWB0; + break; + case OMX_AUDIO_CodingDDP: + param->ddp.nChannels = audio->channels; + param->ddp.nSampleRate = audio->sample_rate; + param->ddp.eBitStreamId = OMX_AUDIO_DDPBitStreamIdAC3; + if(format->encoding == MMAL_ENCODING_EAC3) + param->ddp.eBitStreamId = OMX_AUDIO_DDPBitStreamIdEAC3; + param->ddp.eBitStreamMode = 0; + param->ddp.eDolbySurroundMode = 0; + mmalil_omx_default_channel_mapping(param->ddp.eChannelMapping, audio->channels); + break; + case OMX_AUDIO_CodingDTS: + param->dts.nChannels = audio->channels; + param->dts.nSampleRate = audio->sample_rate; + param->dts.nDtsFrameSizeBytes = audio->block_align; + param->dts.nDtsType = 1; + param->dts.nFormat = 0; + mmalil_omx_default_channel_mapping(param->dts.eChannelMapping, audio->channels); + break; + case OMX_AUDIO_CodingADPCM: + case OMX_AUDIO_CodingGSMFR: + case OMX_AUDIO_CodingGSMEFR: + case OMX_AUDIO_CodingGSMHR: + case OMX_AUDIO_CodingPDCFR: + case OMX_AUDIO_CodingPDCEFR: + case OMX_AUDIO_CodingPDCHR: + case OMX_AUDIO_CodingTDMAFR: + case OMX_AUDIO_CodingTDMAEFR: + case OMX_AUDIO_CodingQCELP8: + case OMX_AUDIO_CodingQCELP13: + case OMX_AUDIO_CodingEVRC: + case OMX_AUDIO_CodingSMV: + case OMX_AUDIO_CodingG711: + case OMX_AUDIO_CodingG723: + case OMX_AUDIO_CodingG726: + case OMX_AUDIO_CodingG729: + case OMX_AUDIO_CodingSBC: + case OMX_AUDIO_CodingRA: + case OMX_AUDIO_CodingMIDI: + default: + vcos_assert(0); + break; + } + + return coding; +} + +/*****************************************************************************/ +static struct { + uint32_t encoding; + OMX_VIDEO_CODINGTYPE coding; +} mmal_omx_video_coding_table[] = +{ + {MMAL_ENCODING_H264, OMX_VIDEO_CodingAVC}, + {MMAL_ENCODING_MVC, OMX_VIDEO_CodingMVC}, + {MMAL_ENCODING_MP4V, OMX_VIDEO_CodingMPEG4}, + {MMAL_ENCODING_MP2V, OMX_VIDEO_CodingMPEG2}, + {MMAL_ENCODING_MP1V, OMX_VIDEO_CodingMPEG2}, + {MMAL_ENCODING_H263, OMX_VIDEO_CodingH263}, + {MMAL_ENCODING_WVC1, OMX_VIDEO_CodingWMV}, + {MMAL_ENCODING_WMV3, OMX_VIDEO_CodingWMV}, + {MMAL_ENCODING_WMV2, OMX_VIDEO_CodingWMV}, + {MMAL_ENCODING_WMV1, OMX_VIDEO_CodingWMV}, + {MMAL_ENCODING_VP6, OMX_VIDEO_CodingVP6}, + {MMAL_ENCODING_VP7, OMX_VIDEO_CodingVP7}, + {MMAL_ENCODING_VP8, OMX_VIDEO_CodingVP8}, + {MMAL_ENCODING_SPARK, OMX_VIDEO_CodingSorenson}, + {MMAL_ENCODING_THEORA, OMX_VIDEO_CodingTheora}, + {MMAL_ENCODING_MJPEG, OMX_VIDEO_CodingMJPEG}, + {MMAL_ENCODING_UNKNOWN, OMX_VIDEO_CodingUnused} +}; + +uint32_t mmalil_omx_video_coding_to_encoding(OMX_VIDEO_CODINGTYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_video_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_video_coding_table[i].coding == coding) break; + return mmal_omx_video_coding_table[i].encoding; +} + +OMX_VIDEO_CODINGTYPE mmalil_encoding_to_omx_video_coding(uint32_t encoding) +{ + unsigned int i; + for(i = 0; mmal_omx_video_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_video_coding_table[i].encoding == encoding) break; + return mmal_omx_video_coding_table[i].coding; +} + +/*****************************************************************************/ +static struct { + uint32_t encoding; + OMX_IMAGE_CODINGTYPE coding; +} mmal_omx_image_coding_table[] = +{ + {MMAL_ENCODING_JPEG, OMX_IMAGE_CodingJPEG}, + {MMAL_ENCODING_GIF, OMX_IMAGE_CodingGIF}, + {MMAL_ENCODING_PNG, OMX_IMAGE_CodingPNG}, + {MMAL_ENCODING_BMP, OMX_IMAGE_CodingBMP}, + {MMAL_ENCODING_TGA, OMX_IMAGE_CodingTGA}, + {MMAL_ENCODING_PPM, OMX_IMAGE_CodingPPM}, + {MMAL_ENCODING_UNKNOWN, OMX_IMAGE_CodingUnused} +}; + +uint32_t mmalil_omx_image_coding_to_encoding(OMX_IMAGE_CODINGTYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_image_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_image_coding_table[i].coding == coding) break; + return mmal_omx_image_coding_table[i].encoding; +} + +OMX_IMAGE_CODINGTYPE mmalil_encoding_to_omx_image_coding(uint32_t encoding) +{ + unsigned int i; + for(i = 0; mmal_omx_image_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_image_coding_table[i].encoding == encoding) break; + return mmal_omx_image_coding_table[i].coding; +} + +uint32_t mmalil_omx_coding_to_encoding(uint32_t encoding, OMX_PORTDOMAINTYPE domain) +{ + if(domain == OMX_PortDomainVideo) + return mmalil_omx_video_coding_to_encoding((OMX_VIDEO_CODINGTYPE)encoding); + else if(domain == OMX_PortDomainAudio) + return mmalil_omx_audio_coding_to_encoding((OMX_AUDIO_CODINGTYPE)encoding); + else if(domain == OMX_PortDomainImage) + return mmalil_omx_image_coding_to_encoding((OMX_IMAGE_CODINGTYPE)encoding); + else + return MMAL_ENCODING_UNKNOWN; +} + +/*****************************************************************************/ +static struct { + uint32_t encoding; + OMX_COLOR_FORMATTYPE coding; +} mmal_omx_colorformat_coding_table[] = +{ + {MMAL_ENCODING_I420, OMX_COLOR_FormatYUV420PackedPlanar}, + {MMAL_ENCODING_I422, OMX_COLOR_FormatYUV422PackedPlanar}, + {MMAL_ENCODING_I420_SLICE, OMX_COLOR_FormatYUV420PackedPlanar}, + {MMAL_ENCODING_I422_SLICE, OMX_COLOR_FormatYUV422PackedPlanar}, + {MMAL_ENCODING_I420, OMX_COLOR_FormatYUV420Planar}, + {MMAL_ENCODING_YV12, OMX_COLOR_FormatYVU420PackedPlanar}, + {MMAL_ENCODING_NV12, OMX_COLOR_FormatYUV420PackedSemiPlanar}, + {MMAL_ENCODING_NV12, OMX_COLOR_FormatYUV420SemiPlanar}, + {MMAL_ENCODING_NV21, OMX_COLOR_FormatYVU420PackedSemiPlanar}, + {MMAL_ENCODING_YUVUV128, OMX_COLOR_FormatYUVUV128}, + {MMAL_ENCODING_YUYV, OMX_COLOR_FormatYCbYCr}, + {MMAL_ENCODING_YVYU, OMX_COLOR_FormatYCrYCb}, + {MMAL_ENCODING_UYVY, OMX_COLOR_FormatCbYCrY}, + {MMAL_ENCODING_VYUY, OMX_COLOR_FormatCrYCbY}, + {MMAL_ENCODING_RGB16, OMX_COLOR_Format16bitRGB565}, + {MMAL_ENCODING_BGR24, OMX_COLOR_Format24bitRGB888}, + {MMAL_ENCODING_BGRA, OMX_COLOR_Format32bitARGB8888}, + {MMAL_ENCODING_BGR16, OMX_COLOR_Format16bitBGR565}, + {MMAL_ENCODING_RGB24, OMX_COLOR_Format24bitBGR888}, + {MMAL_ENCODING_ARGB, OMX_COLOR_Format32bitBGRA8888}, + {MMAL_ENCODING_RGBA, OMX_COLOR_Format32bitABGR8888}, + {MMAL_ENCODING_RGB16_SLICE, OMX_COLOR_Format16bitRGB565}, + {MMAL_ENCODING_BGR24_SLICE, OMX_COLOR_Format24bitRGB888}, + {MMAL_ENCODING_BGRA_SLICE, OMX_COLOR_Format32bitARGB8888}, + {MMAL_ENCODING_BGR16_SLICE, OMX_COLOR_Format16bitBGR565}, + {MMAL_ENCODING_RGB24_SLICE, OMX_COLOR_Format24bitBGR888}, + {MMAL_ENCODING_ARGB_SLICE, OMX_COLOR_Format32bitBGRA8888}, + {MMAL_ENCODING_RGBA_SLICE, OMX_COLOR_Format32bitABGR8888}, + {MMAL_ENCODING_EGL_IMAGE, OMX_COLOR_FormatBRCMEGL}, + {MMAL_ENCODING_BAYER_SBGGR8, OMX_COLOR_FormatRawBayer8bit}, + {MMAL_ENCODING_BAYER_SBGGR10P, OMX_COLOR_FormatRawBayer10bit}, + {MMAL_ENCODING_BAYER_SGRBG10P, OMX_COLOR_FormatRawBayer10bit}, + {MMAL_ENCODING_BAYER_SGBRG10P, OMX_COLOR_FormatRawBayer10bit}, + {MMAL_ENCODING_BAYER_SRGGB10P, OMX_COLOR_FormatRawBayer10bit}, + {MMAL_ENCODING_BAYER_SBGGR12P, OMX_COLOR_FormatRawBayer12bit}, + {MMAL_ENCODING_BAYER_SGRBG12P, OMX_COLOR_FormatRawBayer12bit}, + {MMAL_ENCODING_BAYER_SGBRG12P, OMX_COLOR_FormatRawBayer12bit}, + {MMAL_ENCODING_BAYER_SRGGB12P, OMX_COLOR_FormatRawBayer12bit}, + {MMAL_ENCODING_BAYER_SBGGR16, OMX_COLOR_FormatRawBayer16bit}, + {MMAL_ENCODING_BAYER_SGBRG16, OMX_COLOR_FormatRawBayer16bit}, + {MMAL_ENCODING_BAYER_SGRBG16, OMX_COLOR_FormatRawBayer16bit}, + {MMAL_ENCODING_BAYER_SRGGB16, OMX_COLOR_FormatRawBayer16bit}, + {MMAL_ENCODING_BAYER_SBGGR10DPCM8,OMX_COLOR_FormatRawBayer8bitcompressed}, + {MMAL_ENCODING_OPAQUE, OMX_COLOR_FormatBRCMOpaque}, + {MMAL_ENCODING_I420_16, OMX_COLOR_FormatYUV420_16PackedPlanar}, + {MMAL_ENCODING_YUVUV64_16, OMX_COLOR_FormatYUVUV64_16}, + {MMAL_ENCODING_I420_10, OMX_COLOR_FormatYUV420_10PackedPlanar}, + {MMAL_ENCODING_YUVUV64_10, OMX_COLOR_FormatYUVUV64_10}, + {MMAL_ENCODING_UNKNOWN, OMX_COLOR_FormatUnused} +}; + +uint32_t mmalil_omx_color_format_to_encoding(OMX_COLOR_FORMATTYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_colorformat_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_colorformat_coding_table[i].coding == coding) break; + return mmal_omx_colorformat_coding_table[i].encoding; +} + +OMX_COLOR_FORMATTYPE mmalil_encoding_to_omx_color_format(uint32_t encoding) +{ + unsigned int i; + for(i = 0; mmal_omx_colorformat_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_colorformat_coding_table[i].encoding == encoding) break; + return mmal_omx_colorformat_coding_table[i].coding; +} + +static struct { + uint32_t encoding; + OMX_COLOR_FORMATTYPE color_format; + OMX_BAYERORDERTYPE bayer_order; +} mmal_omx_bayer_order_coding_table[] = +{ + //Colour format required for conversion from OMX to MMAL. + //Not used for MMAL encoding to OMX color format. + {MMAL_ENCODING_BAYER_SBGGR8, OMX_COLOR_FormatRawBayer8bit, OMX_BayerOrderBGGR}, + {MMAL_ENCODING_BAYER_SGBRG8, OMX_COLOR_FormatRawBayer8bit, OMX_BayerOrderGBRG}, + {MMAL_ENCODING_BAYER_SGRBG8, OMX_COLOR_FormatRawBayer8bit, OMX_BayerOrderGRBG}, + {MMAL_ENCODING_BAYER_SRGGB8, OMX_COLOR_FormatRawBayer8bit, OMX_BayerOrderRGGB}, + + {MMAL_ENCODING_BAYER_SBGGR10P, OMX_COLOR_FormatRawBayer10bit, OMX_BayerOrderBGGR}, + {MMAL_ENCODING_BAYER_SGRBG10P, OMX_COLOR_FormatRawBayer10bit, OMX_BayerOrderGRBG}, + {MMAL_ENCODING_BAYER_SGBRG10P, OMX_COLOR_FormatRawBayer10bit, OMX_BayerOrderGBRG}, + {MMAL_ENCODING_BAYER_SRGGB10P, OMX_COLOR_FormatRawBayer10bit, OMX_BayerOrderRGGB}, + + {MMAL_ENCODING_BAYER_SBGGR12P, OMX_COLOR_FormatRawBayer12bit, OMX_BayerOrderBGGR}, + {MMAL_ENCODING_BAYER_SGRBG12P, OMX_COLOR_FormatRawBayer12bit, OMX_BayerOrderGRBG}, + {MMAL_ENCODING_BAYER_SGBRG12P, OMX_COLOR_FormatRawBayer12bit, OMX_BayerOrderGBRG}, + {MMAL_ENCODING_BAYER_SRGGB12P, OMX_COLOR_FormatRawBayer12bit, OMX_BayerOrderRGGB}, + + {MMAL_ENCODING_BAYER_SBGGR16, OMX_COLOR_FormatRawBayer16bit, OMX_BayerOrderBGGR}, + {MMAL_ENCODING_BAYER_SGRBG16, OMX_COLOR_FormatRawBayer16bit, OMX_BayerOrderGRBG}, + {MMAL_ENCODING_BAYER_SGBRG16, OMX_COLOR_FormatRawBayer16bit, OMX_BayerOrderGBRG}, + {MMAL_ENCODING_BAYER_SRGGB16, OMX_COLOR_FormatRawBayer16bit, OMX_BayerOrderRGGB}, + + {MMAL_ENCODING_BAYER_SBGGR10DPCM8,OMX_COLOR_FormatRawBayer8bitcompressed, OMX_BayerOrderBGGR}, + {MMAL_ENCODING_BAYER_SGRBG10DPCM8,OMX_COLOR_FormatRawBayer8bitcompressed, OMX_BayerOrderGRBG}, + {MMAL_ENCODING_BAYER_SGBRG10DPCM8,OMX_COLOR_FormatRawBayer8bitcompressed, OMX_BayerOrderGBRG}, + {MMAL_ENCODING_BAYER_SRGGB10DPCM8,OMX_COLOR_FormatRawBayer8bitcompressed, OMX_BayerOrderRGGB}, + + {MMAL_ENCODING_UNKNOWN, OMX_COLOR_FormatMax, OMX_BayerOrderMax} +}; + +uint32_t mmalil_omx_bayer_format_order_to_encoding(OMX_BAYERORDERTYPE bayer_order, OMX_COLOR_FORMATTYPE color_format) +{ + unsigned int i; + for(i = 0; mmal_omx_bayer_order_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_bayer_order_coding_table[i].bayer_order == bayer_order && + mmal_omx_bayer_order_coding_table[i].color_format == color_format) + break; + return mmal_omx_bayer_order_coding_table[i].encoding; +} + +OMX_BAYERORDERTYPE mmalil_encoding_to_omx_bayer_order(uint32_t encoding) +{ + unsigned int i; + for(i = 0; mmal_omx_bayer_order_coding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(mmal_omx_bayer_order_coding_table[i].encoding == encoding) break; + return mmal_omx_bayer_order_coding_table[i].bayer_order; +} + +/*****************************************************************************/ +static struct { + uint32_t mmal; + OMX_COLORSPACETYPE omx; +} mmal_omx_colorspace_coding_table[] = +{ + {MMAL_COLOR_SPACE_ITUR_BT601, OMX_COLORSPACE_ITU_R_BT601}, + {MMAL_COLOR_SPACE_ITUR_BT709, OMX_COLORSPACE_ITU_R_BT709}, + {MMAL_COLOR_SPACE_JPEG_JFIF, OMX_COLORSPACE_JPEG_JFIF}, + {MMAL_COLOR_SPACE_FCC, OMX_COLORSPACE_FCC}, + {MMAL_COLOR_SPACE_SMPTE240M, OMX_COLORSPACE_SMPTE240M}, + {MMAL_COLOR_SPACE_BT470_2_M, OMX_COLORSPACE_BT470_2_M}, + {MMAL_COLOR_SPACE_BT470_2_BG, OMX_COLORSPACE_BT470_2_BG}, + {MMAL_COLOR_SPACE_JFIF_Y16_255, OMX_COLORSPACE_JFIF_Y16_255}, + {MMAL_COLOR_SPACE_UNKNOWN, OMX_COLORSPACE_UNKNOWN} +}; + +uint32_t mmalil_omx_color_space_to_mmal(OMX_COLORSPACETYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_colorspace_coding_table[i].mmal != MMAL_COLOR_SPACE_UNKNOWN; i++) + if(mmal_omx_colorspace_coding_table[i].omx == coding) break; + return mmal_omx_colorspace_coding_table[i].mmal; +} + +OMX_COLORSPACETYPE mmalil_color_space_to_omx(uint32_t coding) +{ + unsigned int i; + for(i = 0; mmal_omx_colorspace_coding_table[i].mmal != MMAL_COLOR_SPACE_UNKNOWN; i++) + if(mmal_omx_colorspace_coding_table[i].mmal == coding) break; + return mmal_omx_colorspace_coding_table[i].omx; +} + +/*****************************************************************************/ +static struct { + uint32_t mmal; + OMX_U32 omx; + OMX_VIDEO_CODINGTYPE omx_coding; +} mmal_omx_video_profile_table[] = +{ + { MMAL_VIDEO_PROFILE_H263_BASELINE, OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_H320CODING, OMX_VIDEO_H263ProfileH320Coding, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_BACKWARDCOMPATIBLE, OMX_VIDEO_H263ProfileBackwardCompatible, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_ISWV2, OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_ISWV3, OMX_VIDEO_H263ProfileISWV3, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_HIGHCOMPRESSION, OMX_VIDEO_H263ProfileHighCompression, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_INTERNET, OMX_VIDEO_H263ProfileInternet, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_INTERLACE, OMX_VIDEO_H263ProfileInterlace, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_H263_HIGHLATENCY, OMX_VIDEO_H263ProfileHighLatency, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_PROFILE_MP4V_SIMPLE, OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_SIMPLESCALABLE, OMX_VIDEO_MPEG4ProfileSimpleScalable, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_CORE, OMX_VIDEO_MPEG4ProfileCore, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_MAIN, OMX_VIDEO_MPEG4ProfileMain, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_NBIT, OMX_VIDEO_MPEG4ProfileNbit, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_SCALABLETEXTURE, OMX_VIDEO_MPEG4ProfileScalableTexture, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_SIMPLEFACE, OMX_VIDEO_MPEG4ProfileSimpleFace, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_SIMPLEFBA, OMX_VIDEO_MPEG4ProfileSimpleFBA, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_BASICANIMATED, OMX_VIDEO_MPEG4ProfileBasicAnimated, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_HYBRID, OMX_VIDEO_MPEG4ProfileHybrid, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_ADVANCEDREALTIME, OMX_VIDEO_MPEG4ProfileAdvancedRealTime, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_CORESCALABLE, OMX_VIDEO_MPEG4ProfileCoreScalable, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCODING, OMX_VIDEO_MPEG4ProfileAdvancedCoding, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCORE, OMX_VIDEO_MPEG4ProfileAdvancedCore, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSCALABLE, OMX_VIDEO_MPEG4ProfileAdvancedScalable, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSIMPLE, OMX_VIDEO_MPEG4ProfileAdvancedSimple, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_PROFILE_H264_BASELINE, OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_MAIN, OMX_VIDEO_AVCProfileMain, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_EXTENDED, OMX_VIDEO_AVCProfileExtended, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_HIGH, OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_HIGH10, OMX_VIDEO_AVCProfileHigh10, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_HIGH422, OMX_VIDEO_AVCProfileHigh422, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_HIGH444, OMX_VIDEO_AVCProfileHigh444, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_PROFILE_DUMMY, OMX_VIDEO_AVCProfileMax, OMX_VIDEO_CodingAVC}, +}; + +uint32_t mmalil_omx_video_profile_to_mmal(OMX_U32 profile, OMX_VIDEO_CODINGTYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_video_profile_table[i].mmal != MMAL_VIDEO_PROFILE_DUMMY; i++) + if(mmal_omx_video_profile_table[i].omx == profile + && mmal_omx_video_profile_table[i].omx_coding == coding) break; + return mmal_omx_video_profile_table[i].mmal; +} + +OMX_U32 mmalil_video_profile_to_omx(uint32_t profile) +{ + unsigned int i; + for(i = 0; mmal_omx_video_profile_table[i].mmal != MMAL_VIDEO_PROFILE_DUMMY; i++) + if(mmal_omx_video_profile_table[i].mmal == profile) break; + return mmal_omx_video_profile_table[i].omx; +} + +/*****************************************************************************/ +static struct { + uint32_t mmal; + OMX_U32 omx; + OMX_VIDEO_CODINGTYPE omx_coding; +} mmal_omx_video_level_table[] = +{ + { MMAL_VIDEO_LEVEL_H263_10, OMX_VIDEO_H263Level10, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_20, OMX_VIDEO_H263Level20, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_30, OMX_VIDEO_H263Level30, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_40, OMX_VIDEO_H263Level40, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_45, OMX_VIDEO_H263Level45, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_50, OMX_VIDEO_H263Level50, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_60, OMX_VIDEO_H263Level60, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_H263_70, OMX_VIDEO_H263Level70, OMX_VIDEO_CodingH263}, + { MMAL_VIDEO_LEVEL_MP4V_0, OMX_VIDEO_MPEG4Level0, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_0b, OMX_VIDEO_MPEG4Level0b, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_1, OMX_VIDEO_MPEG4Level1, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_2, OMX_VIDEO_MPEG4Level2, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_3, OMX_VIDEO_MPEG4Level3, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_4, OMX_VIDEO_MPEG4Level4, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_4a, OMX_VIDEO_MPEG4Level4a, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_5, OMX_VIDEO_MPEG4Level5, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_MP4V_6, OMX_VIDEO_MPEG4Level6, OMX_VIDEO_CodingMPEG4}, + { MMAL_VIDEO_LEVEL_H264_1, OMX_VIDEO_AVCLevel1, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_1b, OMX_VIDEO_AVCLevel1b, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_11, OMX_VIDEO_AVCLevel11, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_12, OMX_VIDEO_AVCLevel12, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_13, OMX_VIDEO_AVCLevel13, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_2, OMX_VIDEO_AVCLevel2, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_21, OMX_VIDEO_AVCLevel21, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_22, OMX_VIDEO_AVCLevel22, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_3, OMX_VIDEO_AVCLevel3, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_31, OMX_VIDEO_AVCLevel31, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_32, OMX_VIDEO_AVCLevel32, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_4, OMX_VIDEO_AVCLevel4, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_41, OMX_VIDEO_AVCLevel41, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_42, OMX_VIDEO_AVCLevel42, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_5, OMX_VIDEO_AVCLevel5, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_H264_51, OMX_VIDEO_AVCLevel51, OMX_VIDEO_CodingAVC}, + { MMAL_VIDEO_LEVEL_DUMMY, OMX_VIDEO_AVCLevelMax, OMX_VIDEO_CodingMax}, +}; + +uint32_t mmalil_omx_video_level_to_mmal(OMX_U32 level, OMX_VIDEO_CODINGTYPE coding) +{ + unsigned int i; + for(i = 0; mmal_omx_video_level_table[i].mmal != MMAL_VIDEO_LEVEL_DUMMY; i++) + if(mmal_omx_video_level_table[i].omx == level + && mmal_omx_video_level_table[i].omx_coding == coding) break; + return mmal_omx_video_level_table[i].mmal; +} + +OMX_U32 mmalil_video_level_to_omx(uint32_t level) +{ + unsigned int i; + for(i = 0; mmal_omx_video_level_table[i].mmal != MMAL_VIDEO_LEVEL_DUMMY; i++) + if(mmal_omx_video_level_table[i].mmal == level) break; + return mmal_omx_video_level_table[i].omx; +} + +/*****************************************************************************/ +static struct { + MMAL_VIDEO_RATECONTROL_T mmal; + OMX_VIDEO_CONTROLRATETYPE omx; +} mmal_omx_video_ratecontrol_table[] = +{ + { MMAL_VIDEO_RATECONTROL_DEFAULT, OMX_Video_ControlRateDisable}, + { MMAL_VIDEO_RATECONTROL_VARIABLE, OMX_Video_ControlRateVariable}, + { MMAL_VIDEO_RATECONTROL_CONSTANT, OMX_Video_ControlRateConstant}, + { MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, OMX_Video_ControlRateVariableSkipFrames}, + { MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES, OMX_Video_ControlRateConstantSkipFrames}, + { MMAL_VIDEO_RATECONTROL_DUMMY, OMX_Video_ControlRateMax}, +}; + +MMAL_VIDEO_RATECONTROL_T mmalil_omx_video_ratecontrol_to_mmal(OMX_VIDEO_CONTROLRATETYPE omx) +{ + unsigned int i; + for(i = 0; mmal_omx_video_ratecontrol_table[i].mmal != MMAL_VIDEO_RATECONTROL_DUMMY; i++) + if(mmal_omx_video_ratecontrol_table[i].omx == omx) break; + return mmal_omx_video_ratecontrol_table[i].mmal; +} + +OMX_VIDEO_CONTROLRATETYPE mmalil_video_ratecontrol_to_omx(MMAL_VIDEO_RATECONTROL_T mmal) +{ + unsigned int i; + for(i = 0; mmal_omx_video_ratecontrol_table[i].mmal != MMAL_VIDEO_RATECONTROL_DUMMY; i++) + if(mmal_omx_video_ratecontrol_table[i].mmal == mmal) break; + return mmal_omx_video_ratecontrol_table[i].omx; +} + +/*****************************************************************************/ +static struct { + MMAL_VIDEO_INTRA_REFRESH_T mmal; + OMX_VIDEO_INTRAREFRESHTYPE omx; +} mmal_omx_video_intrarefresh_table[] = +{ + { MMAL_VIDEO_INTRA_REFRESH_CYCLIC, OMX_VIDEO_IntraRefreshCyclic}, + { MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE, OMX_VIDEO_IntraRefreshAdaptive}, + { MMAL_VIDEO_INTRA_REFRESH_BOTH, OMX_VIDEO_IntraRefreshBoth}, + { MMAL_VIDEO_INTRA_REFRESH_KHRONOSEXTENSIONS, OMX_VIDEO_IntraRefreshKhronosExtensions}, + { MMAL_VIDEO_INTRA_REFRESH_VENDORSTARTUNUSED, OMX_VIDEO_IntraRefreshVendorStartUnused}, + { MMAL_VIDEO_INTRA_REFRESH_DUMMY, OMX_VIDEO_IntraRefreshMax}, +}; + +MMAL_VIDEO_INTRA_REFRESH_T mmalil_omx_video_intrarefresh_to_mmal(OMX_VIDEO_INTRAREFRESHTYPE omx) +{ + unsigned int i; + for(i = 0; mmal_omx_video_intrarefresh_table[i].mmal != MMAL_VIDEO_INTRA_REFRESH_DUMMY; i++) + if(mmal_omx_video_intrarefresh_table[i].omx == omx) break; + return mmal_omx_video_intrarefresh_table[i].mmal; +} diff --git a/interface/mmal/util/mmal_il.h b/interface/mmal/util/mmal_il.h new file mode 100755 index 0000000..b047a28 --- /dev/null +++ b/interface/mmal/util/mmal_il.h @@ -0,0 +1,214 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_IL_H +#define MMAL_IL_H + +/** \defgroup MmalILUtility MMAL to OMX IL conversion utilities + * \ingroup MmalUtilities + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vmcs_host/khronos/IL/OMX_Core.h" +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/khronos/IL/OMX_Video.h" +#include "interface/vmcs_host/khronos/IL/OMX_Audio.h" +#include "interface/vmcs_host/khronos/IL/OMX_Broadcom.h" + +/** Convert MMAL status codes into OMX error codes. + * + * @param status MMAL status code. + * @return OMX error code. + */ +OMX_ERRORTYPE mmalil_error_to_omx(MMAL_STATUS_T status); + +/** Convert OMX error codes into MMAL status codes. + * + * @param error OMX error code. + * @return MMAL status code. + */ +MMAL_STATUS_T mmalil_error_to_mmal(OMX_ERRORTYPE error); + +/** Convert MMAL buffer header flags into OMX buffer header flags. + * + * @param flags OMX buffer header flags. + * @return MMAL buffer header flags. + */ +uint32_t mmalil_buffer_flags_to_mmal(OMX_U32 flags); + +/** Convert OMX buffer header flags into MMAL buffer header flags. + * + * @param flags MMAL buffer header flags. + * @return OMX buffer header flags. + */ +OMX_U32 mmalil_buffer_flags_to_omx(uint32_t flags); + +/** Convert a MMAL buffer header into an OMX buffer header. + * Note that only the fields which have a direct mapping between OMX and MMAL are converted. + * + * @param omx Pointer to the destination OMX buffer header. + * @param mmal Pointer to the source MMAL buffer header. + */ +void mmalil_buffer_header_to_omx(OMX_BUFFERHEADERTYPE *omx, MMAL_BUFFER_HEADER_T *mmal); + +/** Convert an OMX buffer header into a MMAL buffer header. + * + * @param mmal Pointer to the destination MMAL buffer header. + * @param omx Pointer to the source OMX buffer header. + */ +void mmalil_buffer_header_to_mmal(MMAL_BUFFER_HEADER_T *mmal, OMX_BUFFERHEADERTYPE *omx); + + +OMX_PORTDOMAINTYPE mmalil_es_type_to_omx_domain(MMAL_ES_TYPE_T type); +MMAL_ES_TYPE_T mmalil_omx_domain_to_es_type(OMX_PORTDOMAINTYPE domain); +uint32_t mmalil_omx_audio_coding_to_encoding(OMX_AUDIO_CODINGTYPE coding); +OMX_AUDIO_CODINGTYPE mmalil_encoding_to_omx_audio_coding(uint32_t encoding); +uint32_t mmalil_omx_video_coding_to_encoding(OMX_VIDEO_CODINGTYPE coding); +OMX_VIDEO_CODINGTYPE mmalil_encoding_to_omx_video_coding(uint32_t encoding); +uint32_t mmalil_omx_image_coding_to_encoding(OMX_IMAGE_CODINGTYPE coding); +OMX_IMAGE_CODINGTYPE mmalil_encoding_to_omx_image_coding(uint32_t encoding); +uint32_t mmalil_omx_coding_to_encoding(uint32_t encoding, OMX_PORTDOMAINTYPE domain); +uint32_t mmalil_omx_color_format_to_encoding(OMX_COLOR_FORMATTYPE coding); +OMX_COLOR_FORMATTYPE mmalil_encoding_to_omx_color_format(uint32_t encoding); +uint32_t mmalil_omx_bayer_format_order_to_encoding(OMX_BAYERORDERTYPE bayer_order, OMX_COLOR_FORMATTYPE color_format); +OMX_BAYERORDERTYPE mmalil_encoding_to_omx_bayer_order(uint32_t encoding); +uint32_t mmalil_omx_color_space_to_mmal(OMX_COLORSPACETYPE coding); +OMX_COLORSPACETYPE mmalil_color_space_to_omx(uint32_t coding); +uint32_t mmalil_omx_video_profile_to_mmal(OMX_U32 level, OMX_VIDEO_CODINGTYPE coding); +OMX_U32 mmalil_video_profile_to_omx(uint32_t profile); +uint32_t mmalil_omx_video_level_to_mmal(OMX_U32 level, OMX_VIDEO_CODINGTYPE coding); +OMX_U32 mmalil_video_level_to_omx(uint32_t level); +MMAL_VIDEO_RATECONTROL_T mmalil_omx_video_ratecontrol_to_mmal(OMX_VIDEO_CONTROLRATETYPE omx); +OMX_VIDEO_CONTROLRATETYPE mmalil_video_ratecontrol_to_omx(MMAL_VIDEO_RATECONTROL_T mmal); +MMAL_VIDEO_INTRA_REFRESH_T mmalil_omx_video_intrarefresh_to_mmal(OMX_VIDEO_INTRAREFRESHTYPE omx); + +/** Union of all the OMX_VIDEO/AUDIO_PARAM types */ +typedef union OMX_FORMAT_PARAM_TYPE { + OMX_PARAM_U32TYPE common; + + /* Video */ + OMX_VIDEO_PARAM_AVCTYPE avc; + OMX_VIDEO_PARAM_H263TYPE h263; + OMX_VIDEO_PARAM_MPEG2TYPE mpeg2; + OMX_VIDEO_PARAM_MPEG4TYPE mpeg4; + OMX_VIDEO_PARAM_WMVTYPE wmv; + OMX_VIDEO_PARAM_RVTYPE rv; + + /* Audio */ + OMX_AUDIO_PARAM_PCMMODETYPE pcm; + OMX_AUDIO_PARAM_MP3TYPE mp3; + OMX_AUDIO_PARAM_AACPROFILETYPE aac; + OMX_AUDIO_PARAM_VORBISTYPE vorbis; + OMX_AUDIO_PARAM_WMATYPE wma; + OMX_AUDIO_PARAM_RATYPE ra; + OMX_AUDIO_PARAM_SBCTYPE sbc; + OMX_AUDIO_PARAM_ADPCMTYPE adpcm; + OMX_AUDIO_PARAM_G723TYPE g723; + OMX_AUDIO_PARAM_G726TYPE g726; + OMX_AUDIO_PARAM_G729TYPE g729; + OMX_AUDIO_PARAM_AMRTYPE amr; + OMX_AUDIO_PARAM_GSMFRTYPE gsmfr; + OMX_AUDIO_PARAM_GSMHRTYPE gsmhr; + OMX_AUDIO_PARAM_GSMEFRTYPE gsmefr; + OMX_AUDIO_PARAM_TDMAFRTYPE tdmafr; + OMX_AUDIO_PARAM_TDMAEFRTYPE tdmaefr; + OMX_AUDIO_PARAM_PDCFRTYPE pdcfr; + OMX_AUDIO_PARAM_PDCEFRTYPE pdcefr; + OMX_AUDIO_PARAM_PDCHRTYPE pdchr; + OMX_AUDIO_PARAM_QCELP8TYPE qcelp8; + OMX_AUDIO_PARAM_QCELP13TYPE qcelp13; + OMX_AUDIO_PARAM_EVRCTYPE evrc; + OMX_AUDIO_PARAM_SMVTYPE smv; + OMX_AUDIO_PARAM_MIDITYPE midi; +#ifdef OMX_AUDIO_CodingDDP_Supported + OMX_AUDIO_PARAM_DDPTYPE ddp; +#endif +#ifdef OMX_AUDIO_CodingDTS_Supported + OMX_AUDIO_PARAM_DTSTYPE dts; +#endif + +} OMX_FORMAT_PARAM_TYPE; + +/** Get the OMX_IndexParamAudio index corresponding to a specified audio coding type. + * + * @param coding Audio coding type. + * @param size Pointer used to return the size of the parameter. + * + * @return OMX index or 0 if no match was found. + */ +OMX_INDEXTYPE mmalil_omx_audio_param_index(OMX_AUDIO_CODINGTYPE coding, OMX_U32 *size); + +/** Get the audio coding corresponding to a specified OMX_IndexParamAudio index. + * + * @param index Audio coding type. + * + * @return Audio coding type. + */ +OMX_AUDIO_CODINGTYPE mmalil_omx_audio_param_index_to_coding(OMX_INDEXTYPE index); + +/** Setup a default channel mapping based on the number of channels + * @param channel_mapping The output channel mapping + * @param nchannels Number of channels + * + * @return MMAL_SUCCESS if we managed to produce a channel mapping + */ +MMAL_STATUS_T mmalil_omx_default_channel_mapping(OMX_AUDIO_CHANNELTYPE *channel_mapping, unsigned int nchannels); + +/** Convert an OMX_IndexParamAudio into a MMAL elementary stream format. + * + * @param format Format structure to update. + * @param coding Audio coding type. + * @param param Source OMX_IndexParamAudio structure. + * + * @return The MMAL encoding if a match was found or MMAL_ENCODING_UNKNOWN otherwise. + */ +MMAL_FOURCC_T mmalil_omx_audio_param_to_format(MMAL_ES_FORMAT_T *format, + OMX_AUDIO_CODINGTYPE coding, OMX_FORMAT_PARAM_TYPE *param); + +/** Convert a MMAL elementary stream format into a OMX_IndexParamAudio structure. + * + * @param param OMX_IndexParamAudio structure to update. + * @param param_index returns the OMX_IndexParamAudio index corresponding to the format. + * @param format Source format structure. + * + * @return The OMX aduio coding type if a match was found or OMX_AUDIO_CodingUnused otherwise. + */ +OMX_AUDIO_CODINGTYPE mmalil_format_to_omx_audio_param(OMX_FORMAT_PARAM_TYPE *param, + OMX_INDEXTYPE *param_index, MMAL_ES_FORMAT_T *format); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* MMAL_IL_H */ diff --git a/interface/mmal/util/mmal_list.c b/interface/mmal/util/mmal_list.c new file mode 100755 index 0000000..6f45d78 --- /dev/null +++ b/interface/mmal/util/mmal_list.c @@ -0,0 +1,221 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vcos/vcos.h" +#include "interface/mmal/util/mmal_list.h" + + +/* Private list context */ +typedef struct MMAL_LIST_PRIVATE_T +{ + MMAL_LIST_T list; /* must be first */ + VCOS_MUTEX_T lock; +} MMAL_LIST_PRIVATE_T; + + +/* Lock the list. */ +static inline void mmal_list_lock(MMAL_LIST_T *list) +{ + vcos_mutex_lock(&((MMAL_LIST_PRIVATE_T*)list)->lock); +} + +/* Unlock the list. */ +static inline void mmal_list_unlock(MMAL_LIST_T *list) +{ + vcos_mutex_unlock(&((MMAL_LIST_PRIVATE_T*)list)->lock); +} + +/* Create a new linked list. */ +MMAL_LIST_T* mmal_list_create(void) +{ + MMAL_LIST_PRIVATE_T *private; + + private = vcos_malloc(sizeof(MMAL_LIST_PRIVATE_T), "mmal-list"); + if (private == NULL) + goto error; + + if (vcos_mutex_create(&private->lock, "mmal-list lock") != VCOS_SUCCESS) + goto error; + + /* lock to keep coverity happy */ + vcos_mutex_lock(&private->lock); + private->list.first = NULL; + private->list.last = NULL; + private->list.length = 0; + vcos_mutex_unlock(&private->lock); + + return &private->list; + +error: + vcos_free(private); + return NULL; +} + +/* Destroy a linked list. */ +void mmal_list_destroy(MMAL_LIST_T *list) +{ + MMAL_LIST_PRIVATE_T *private = (MMAL_LIST_PRIVATE_T*)list; + + vcos_mutex_delete(&private->lock); + vcos_free(private); +} + +/* Remove the last element in the list. */ +MMAL_LIST_ELEMENT_T* mmal_list_pop_back(MMAL_LIST_T *list) +{ + MMAL_LIST_ELEMENT_T *element; + + mmal_list_lock(list); + + element = list->last; + if (element != NULL) + { + list->length--; + + list->last = element->prev; + if (list->last) + list->last->next = NULL; + else + list->first = NULL; /* list is now empty */ + + element->prev = NULL; + element->next = NULL; + } + + mmal_list_unlock(list); + + return element; +} + +/* Remove the first element in the list. */ +MMAL_LIST_ELEMENT_T* mmal_list_pop_front(MMAL_LIST_T *list) +{ + MMAL_LIST_ELEMENT_T *element; + + mmal_list_lock(list); + + element = list->first; + if (element != NULL) + { + list->length--; + + list->first = element->next; + if (list->first) + list->first->prev = NULL; + else + list->last = NULL; /* list is now empty */ + + element->prev = NULL; + element->next = NULL; + } + + mmal_list_unlock(list); + + return element; +} + +/* Add an element to the front of the list. */ +void mmal_list_push_front(MMAL_LIST_T *list, MMAL_LIST_ELEMENT_T *element) +{ + mmal_list_lock(list); + + list->length++; + + element->prev = NULL; + element->next = list->first; + + if (list->first) + list->first->prev = element; + else + list->last = element; /* list was empty */ + + list->first = element; + + mmal_list_unlock(list); +} + +/* Add an element to the back of the list. */ +void mmal_list_push_back(MMAL_LIST_T *list, MMAL_LIST_ELEMENT_T *element) +{ + mmal_list_lock(list); + + list->length++; + + element->next = NULL; + element->prev = list->last; + + if (list->last) + list->last->next = element; + else + list->first = element; /* list was empty */ + + list->last = element; + + mmal_list_unlock(list); +} + +/* Insert an element into the list. */ +void mmal_list_insert(MMAL_LIST_T *list, MMAL_LIST_ELEMENT_T *element, MMAL_LIST_COMPARE_T compare) +{ + MMAL_LIST_ELEMENT_T *cur; + + mmal_list_lock(list); + + if (list->first == NULL) + { + /* List empty */ + mmal_list_unlock(list); + mmal_list_push_front(list, element); + return; + } + + cur = list->first; + while (cur) + { + if (compare(element, cur)) + { + /* Slot found! */ + list->length++; + if (cur == list->first) + list->first = element; + else + cur->prev->next = element; + element->prev = cur->prev; + element->next = cur; + cur->prev = element; + mmal_list_unlock(list); + return; + } + + cur = cur->next; + } + + /* If we get here, none of the existing elements are greater + * than the new on, so just add it to the back of the list */ + mmal_list_unlock(list); + mmal_list_push_back(list, element); +} diff --git a/interface/mmal/util/mmal_list.h b/interface/mmal/util/mmal_list.h new file mode 100755 index 0000000..967cc7b --- /dev/null +++ b/interface/mmal/util/mmal_list.h @@ -0,0 +1,127 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_LIST_H +#define MMAL_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup MmalList Generic Linked List + * This provides a thread-safe implementation of a linked list which can be used + * with any data type. */ +/* @{ */ + +/** Single element in the list */ +typedef struct MMAL_LIST_ELEMENT_T +{ + struct MMAL_LIST_ELEMENT_T *next; + struct MMAL_LIST_ELEMENT_T *prev; +} MMAL_LIST_ELEMENT_T; + +/** Linked list type. + * Clients shouldn't modify this directly. Use the provided API functions to + * add new elements. The public members are only for debug purposes. + * */ +typedef struct MMAL_LIST_T +{ + unsigned int length; /**< Number of elements in the list (read-only) */ + MMAL_LIST_ELEMENT_T *first; /**< First element in the list (read-only) */ + MMAL_LIST_ELEMENT_T *last; /**< Last element in the list (read-only) */ +} MMAL_LIST_T; + +/** Create a new linked list. + * + * @return Pointer to new queue (NULL on failure). + */ +MMAL_LIST_T* mmal_list_create(void); + +/** Destroy a linked list. + * + * @param list List to destroy + */ +void mmal_list_destroy(MMAL_LIST_T *list); + +/** Remove the last element in the list. + * + * @param list List to remove from + * + * @return Pointer to the last element (or NULL if empty) + */ +MMAL_LIST_ELEMENT_T* mmal_list_pop_back(MMAL_LIST_T *list); + +/** Remove the first element in the list. + * + * @param list List to remove from + * + * @return Pointer to the first element (or NULL if empty) + */ +MMAL_LIST_ELEMENT_T* mmal_list_pop_front(MMAL_LIST_T *list); + +/** Add an element to the front of the list. + * + * @param list List to add to + * @param element The element to add + */ +void mmal_list_push_front(MMAL_LIST_T *list, MMAL_LIST_ELEMENT_T *element); + +/** Add an element to the back of the list. + * + * @param list List to add to + * @param element The element to add + */ +void mmal_list_push_back(MMAL_LIST_T *list, MMAL_LIST_ELEMENT_T *element); + +/** List comparison function. + * This is supplied by a client when inserting an element in + * the middle of the list. The list will always insert a smaller + * element in front of a larger element. + * + * @return TRUE: lhs < rhs + * FALSE: lhs >= rhs + */ +typedef int (*MMAL_LIST_COMPARE_T)(MMAL_LIST_ELEMENT_T *lhs, MMAL_LIST_ELEMENT_T *rhs); + +/** Insert an element into the list. + * The location where the element is inserted is determined using + * the supplied comparison function. Smaller elements are inserted + * in front of larger elements. + * + * @param list List to add to + * @param element The element to insert + * @param compare Comparison function supplied by the client + */ +void mmal_list_insert(MMAL_LIST_T *list, MMAL_LIST_ELEMENT_T *element, MMAL_LIST_COMPARE_T compare); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MMAL_LIST_H */ diff --git a/interface/mmal/util/mmal_param_convert.c b/interface/mmal/util/mmal_param_convert.c new file mode 100755 index 0000000..ec48a5b --- /dev/null +++ b/interface/mmal/util/mmal_param_convert.c @@ -0,0 +1,176 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "mmal_param_convert.h" +#include +#include + +typedef struct string_pair_t +{ + const char *string; + int value; +} string_pair_t; + +static MMAL_STATUS_T parse_enum(int *dest, string_pair_t *pairs, size_t n_pairs, const char *str) +{ + size_t i; + for (i=0; inum = num; + dest->den = den; + return ret; +} + +MMAL_STATUS_T mmal_parse_int(int *dest, const char *str) +{ + char *endptr; + long i = strtol(str, &endptr, 0); + if (endptr[0] == '\0') + { + *dest = i; + return MMAL_SUCCESS; + } + else + { + return MMAL_EINVAL; + } +} + +MMAL_STATUS_T mmal_parse_uint(unsigned int *dest, const char *str) +{ + char *endptr; + unsigned long i = strtoul(str, &endptr, 0); + if (endptr[0] == '\0') + { + *dest = i; + return MMAL_SUCCESS; + } + else + { + return MMAL_EINVAL; + } +} + +MMAL_STATUS_T mmal_parse_video_codec(uint32_t *dest, const char *str) +{ + static string_pair_t video_codec_enums[] = { + { "h264", MMAL_ENCODING_H264 }, + { "h263", MMAL_ENCODING_H263 }, + { "mpeg4", MMAL_ENCODING_MP4V }, + { "mpeg2", MMAL_ENCODING_MP2V }, + { "vp8", MMAL_ENCODING_VP8 }, + { "vp7", MMAL_ENCODING_VP7 }, + { "vp6", MMAL_ENCODING_VP6 }, + }; + int i = 0; + MMAL_STATUS_T ret; + + ret = parse_enum(&i, video_codec_enums, vcos_countof(video_codec_enums), str); + *dest = i; + return ret; +} + +MMAL_STATUS_T mmal_parse_geometry(MMAL_RECT_T *dest, const char *str) +{ + MMAL_STATUS_T ret; + uint32_t w, h, x, y; + x = y = w = h = 0; + /* coverity[secure_coding] */ + if (sscanf(str, "%d*%d+%d+%d", &w,&h,&x,&y) == 4 || + sscanf(str, "%d*%d", &w,&h) == 2) + { + dest->x = x; + dest->y = y; + dest->width = w; + dest->height = h; + ret = MMAL_SUCCESS; + } + else + { + ret = MMAL_EINVAL; + } + return ret; +} + diff --git a/interface/mmal/util/mmal_param_convert.h b/interface/mmal/util/mmal_param_convert.h new file mode 100755 index 0000000..1d653c2 --- /dev/null +++ b/interface/mmal/util/mmal_param_convert.h @@ -0,0 +1,92 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Support for setting/getting parameters as string values. + */ + +#ifndef MMAL_PARAM_CONVERT_H +#define MMAL_PARAM_CONVERT_H + +#include "interface/mmal/mmal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Parse a video size. e.g. "1080p" gives 1920x1080. + * + * @param w width result + * @param h height result + * @param str string to convert + * @return MMAL_SUCCESS or error code + */ +MMAL_STATUS_T mmal_parse_video_size(uint32_t *w, uint32_t *h, const char *str); + +/** Parse a rational number. e.g. "30000/1001", "30", etc. + * @param dest filled in with result + * @param str string to convert + * @return MMAL_SUCCESS or error code + */ +MMAL_STATUS_T mmal_parse_rational(MMAL_RATIONAL_T *dest, const char *str); + +/** Parse an integer, e.g. -10, 0x1A, etc. + * @param dest filled in with result + * @param str string to convert + * @return MMAL_SUCCESS or error code + */ +MMAL_STATUS_T mmal_parse_int(int *dest, const char *str); + +/** Parse an unsigned integer, e.g. 10, 0x1A, etc. + * @param dest filled in with result + * @param str string to convert + * @return MMAL_SUCCESS or error code + */ +MMAL_STATUS_T mmal_parse_uint(unsigned int *dest, const char *str); + +/** Parse a geometry for a rectangle + * + * e.g. 100*100+50+75 + * or 200*150 + * @param dest filled in with result + * @param str string to convert + * @return MMAL_SUCCESS or error code + */ +MMAL_STATUS_T mmal_parse_geometry(MMAL_RECT_T *dest, const char *str); + +/** Parse a video codec name (something that can be encoded/decoded) + * @param str string to convert + * @param dest filled in with result + * @return MMAL_SUCCESS or error code + */ +MMAL_STATUS_T mmal_parse_video_codec(uint32_t *dest, const char *str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/interface/mmal/util/mmal_util.c b/interface/mmal/util/mmal_util.c new file mode 100755 index 0000000..aa1f3f9 --- /dev/null +++ b/interface/mmal/util/mmal_util.c @@ -0,0 +1,473 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "interface/mmal/mmal.h" +#include "mmal_encodings.h" +#include "mmal_util.h" +#include "mmal_logging.h" +#include +#include + +#define STATUS_TO_STR(x) { MMAL_##x, #x } + +static struct { + MMAL_STATUS_T status; + const char *str; +} status_to_string_map[] = +{ + STATUS_TO_STR(SUCCESS), + STATUS_TO_STR(ENOMEM), + STATUS_TO_STR(ENOSPC), + STATUS_TO_STR(EINVAL), + STATUS_TO_STR(ENOSYS), + STATUS_TO_STR(ENOENT), + STATUS_TO_STR(ENXIO), + STATUS_TO_STR(EIO), + STATUS_TO_STR(ESPIPE), + STATUS_TO_STR(ECORRUPT), + STATUS_TO_STR(ENOTREADY), + STATUS_TO_STR(ECONFIG), + {0, 0} +}; + +const char *mmal_status_to_string(MMAL_STATUS_T status) +{ + unsigned i; + + for (i=0; status_to_string_map[i].str; i++) + if (status_to_string_map[i].status == status) + break; + + return status_to_string_map[i].str ? status_to_string_map[i].str : "UNKNOWN"; +} + +static struct { + uint32_t encoding; + uint32_t pitch_num; + uint32_t pitch_den; + uint32_t alignment; +} pixel_pitch[] = +{ + {MMAL_ENCODING_I420, 1, 1, 1}, + {MMAL_ENCODING_YV12, 1, 1, 1}, + {MMAL_ENCODING_I422, 1, 1, 1}, + {MMAL_ENCODING_NV21, 1, 1, 1}, + {MMAL_ENCODING_NV12, 1, 1, 1}, + {MMAL_ENCODING_ARGB, 4, 1, 1}, + {MMAL_ENCODING_RGBA, 4, 1, 1}, + {MMAL_ENCODING_RGB32, 4, 1, 1}, + {MMAL_ENCODING_ABGR, 4, 1, 1}, + {MMAL_ENCODING_BGRA, 4, 1, 1}, + {MMAL_ENCODING_BGR32, 4, 1, 1}, + {MMAL_ENCODING_RGB16, 2, 1, 1}, + {MMAL_ENCODING_RGB24, 3, 1, 1}, + {MMAL_ENCODING_BGR16, 2, 1, 1}, + {MMAL_ENCODING_BGR24, 3, 1, 1}, + {MMAL_ENCODING_I420_16, 2, 1, 1}, + {MMAL_ENCODING_I420_10, 2, 1, 1}, + + {MMAL_ENCODING_I420_SLICE, 1, 1, 1}, + {MMAL_ENCODING_I422_SLICE, 1, 1, 1}, + {MMAL_ENCODING_ARGB_SLICE, 4, 1, 1}, + {MMAL_ENCODING_RGBA_SLICE, 4, 1, 1}, + {MMAL_ENCODING_RGB32_SLICE, 4, 1, 1}, + {MMAL_ENCODING_ABGR_SLICE, 4, 1, 1}, + {MMAL_ENCODING_BGRA_SLICE, 4, 1, 1}, + {MMAL_ENCODING_BGR32_SLICE, 4, 1, 1}, + {MMAL_ENCODING_RGB16_SLICE, 2, 1, 1}, + {MMAL_ENCODING_RGB24_SLICE, 3, 1, 1}, + {MMAL_ENCODING_BGR16_SLICE, 2, 1, 1}, + {MMAL_ENCODING_BGR24_SLICE, 3, 1, 1}, + + {MMAL_ENCODING_YUYV, 2, 1, 1}, + {MMAL_ENCODING_YVYU, 2, 1, 1}, + {MMAL_ENCODING_UYVY, 2, 1, 1}, + {MMAL_ENCODING_VYUY, 2, 1, 1}, + + // Bayer formats, the resulting alignment must also be a multiple of 16. + // Camplus padded to a multiple of 32, so let's copy that. + {MMAL_ENCODING_BAYER_SBGGR8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SGBRG8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SGRBG8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SRGGB8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SBGGR10DPCM8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SGBRG10DPCM8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SGRBG10DPCM8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SRGGB10DPCM8, 1, 1, 32}, + {MMAL_ENCODING_BAYER_SBGGR10P, 10,8, 32}, + {MMAL_ENCODING_BAYER_SGRBG10P, 10,8, 32}, + {MMAL_ENCODING_BAYER_SGBRG10P, 10,8, 32}, + {MMAL_ENCODING_BAYER_SRGGB10P, 10,8, 32}, + {MMAL_ENCODING_BAYER_SBGGR12P, 12,8, 32}, + {MMAL_ENCODING_BAYER_SGRBG12P, 12,8, 32}, + {MMAL_ENCODING_BAYER_SGBRG12P, 12,8, 32}, + {MMAL_ENCODING_BAYER_SRGGB12P, 12,8, 32}, + {MMAL_ENCODING_BAYER_SBGGR16, 2, 1, 32}, + {MMAL_ENCODING_BAYER_SGBRG16, 2, 1, 32}, + {MMAL_ENCODING_BAYER_SGRBG16, 2, 1, 32}, + {MMAL_ENCODING_BAYER_SRGGB16, 2, 1, 32}, + + /* {MMAL_ENCODING_YUVUV128, 1, 1}, That's a special case which must not be included */ + /* {MMAL_ENCODING_YUVUV64_16, 1, 1}, That's a special case which must not be included */ + /* {MMAL_ENCODING_YUVUV64_10, 1, 1}, That's a special case which must not be included */ + {MMAL_ENCODING_UNKNOWN, 0, 0} +}; + +static struct { + uint32_t encoding; + uint32_t sliced_encoding; +} slice_equivalents[] = +{ + { MMAL_ENCODING_I420, MMAL_ENCODING_I420_SLICE }, + { MMAL_ENCODING_I422, MMAL_ENCODING_I422_SLICE }, + { MMAL_ENCODING_ARGB, MMAL_ENCODING_ARGB_SLICE }, + { MMAL_ENCODING_RGBA, MMAL_ENCODING_RGBA_SLICE }, + { MMAL_ENCODING_RGB32, MMAL_ENCODING_RGB32_SLICE }, + { MMAL_ENCODING_ABGR, MMAL_ENCODING_ABGR_SLICE }, + { MMAL_ENCODING_BGRA, MMAL_ENCODING_BGRA_SLICE }, + { MMAL_ENCODING_BGR32, MMAL_ENCODING_BGR32_SLICE }, + { MMAL_ENCODING_RGB16, MMAL_ENCODING_RGB16_SLICE }, + { MMAL_ENCODING_RGB24, MMAL_ENCODING_RGB24_SLICE }, + { MMAL_ENCODING_BGR16, MMAL_ENCODING_BGR16_SLICE }, + { MMAL_ENCODING_BGR24, MMAL_ENCODING_BGR24_SLICE }, + { MMAL_ENCODING_UNKNOWN, MMAL_ENCODING_UNKNOWN }, +}; + + +uint32_t mmal_encoding_stride_to_width(uint32_t encoding, uint32_t stride) +{ + unsigned int i; + + for(i = 0; pixel_pitch[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(pixel_pitch[i].encoding == encoding) break; + + if(pixel_pitch[i].encoding == MMAL_ENCODING_UNKNOWN) + return 0; + + return pixel_pitch[i].pitch_den * stride / pixel_pitch[i].pitch_num; +} + +uint32_t mmal_encoding_width_to_stride(uint32_t encoding, uint32_t width) +{ + unsigned int i; + + for(i = 0; pixel_pitch[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(pixel_pitch[i].encoding == encoding) break; + + if(pixel_pitch[i].encoding == MMAL_ENCODING_UNKNOWN) + return 0; + + return VCOS_ALIGN_UP(pixel_pitch[i].pitch_num * width / pixel_pitch[i].pitch_den, pixel_pitch[i].alignment); +} + +uint32_t mmal_encoding_get_slice_variant(uint32_t encoding) +{ + unsigned int i; + + for(i = 0; slice_equivalents[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if(slice_equivalents[i].encoding == encoding) break; + + return slice_equivalents[i].sliced_encoding; +} + +const char* mmal_port_type_to_string(MMAL_PORT_TYPE_T type) +{ + const char *str; + + switch (type) + { + case MMAL_PORT_TYPE_INPUT: str = "in"; break; + case MMAL_PORT_TYPE_OUTPUT: str = "out"; break; + case MMAL_PORT_TYPE_CLOCK: str = "clk"; break; + case MMAL_PORT_TYPE_CONTROL: str = "ctr"; break; + default: str = "invalid"; break; + } + + return str; +} + +MMAL_PARAMETER_HEADER_T *mmal_port_parameter_alloc_get(MMAL_PORT_T *port, + uint32_t id, uint32_t size, MMAL_STATUS_T *p_status) +{ + MMAL_PARAMETER_HEADER_T *param = NULL; + MMAL_STATUS_T status = MMAL_ENOSYS; + + if (size < sizeof(MMAL_PARAMETER_HEADER_T)) + size = sizeof(MMAL_PARAMETER_HEADER_T); + + if ((param = vcos_calloc(1, size, "mmal_port_param_get")) == NULL) + { + status = MMAL_ENOMEM; + goto error; + } + + param->id = id; + param->size = size; + + if ((status = mmal_port_parameter_get(port, param)) == MMAL_ENOSPC) + { + /* We need to reallocate to get enough space for all parameter data */ + size = param->size; + vcos_free(param); + if ((param = vcos_calloc(1, size, "mmal_port_param_get")) == NULL) + { + status = MMAL_ENOMEM; + goto error; + } + + /* Now retrieve it again */ + param->id = id; + param->size = size; + status = mmal_port_parameter_get(port, param); + } + + if (status != MMAL_SUCCESS) + goto error; + +end: + if (p_status) *p_status = status; + return param; +error: + if (param) vcos_free(param); + param = NULL; + goto end; +} + +void mmal_port_parameter_free(MMAL_PARAMETER_HEADER_T *param) +{ + vcos_free(param); +} + +/** Copy buffer header metadata from source to dest + */ +void mmal_buffer_header_copy_header(MMAL_BUFFER_HEADER_T *dest, const MMAL_BUFFER_HEADER_T *src) +{ + dest->cmd = src->cmd; + dest->offset = src->offset; + dest->length = src->length; + dest->flags = src->flags; + dest->pts = src->pts; + dest->dts = src->dts; + *dest->type = *src->type; +} + +/** Create a pool of MMAL_BUFFER_HEADER_T */ +MMAL_POOL_T *mmal_port_pool_create(MMAL_PORT_T *port, unsigned int headers, uint32_t payload_size) +{ + if (!port || !port->priv) + return NULL; + + LOG_TRACE("%s(%i:%i) port %p, headers %u, size %i", port->component->name, + (int)port->type, (int)port->index, port, headers, (int)payload_size); + + /* Create a pool and ask the port for some memory */ + return mmal_pool_create_with_allocator(headers, payload_size, (void *)port, + (mmal_pool_allocator_alloc_t)mmal_port_payload_alloc, + (mmal_pool_allocator_free_t)mmal_port_payload_free); +} + +/** Destroy a pool of MMAL_BUFFER_HEADER_T */ +void mmal_port_pool_destroy(MMAL_PORT_T *port, MMAL_POOL_T *pool) +{ + if (!port || !port->priv || !pool) + return; + + LOG_TRACE("%s(%i:%i) port %p, pool %p", port->component->name, + (int)port->type, (int)port->index, port, pool); + + if (!vcos_verify(!port->is_enabled)) + { + LOG_ERROR("port %p, pool %p destroyed while port enabled", port, pool); + mmal_port_disable(port); + } + + mmal_pool_destroy(pool); +} + +/*****************************************************************************/ +void mmal_log_dump_port(MMAL_PORT_T *port) +{ + if (!port) + return; + + LOG_DEBUG("%s(%p)", port->name, port); + + mmal_log_dump_format(port->format); + + LOG_DEBUG(" buffers num: %i(opt %i, min %i), size: %i(opt %i, min: %i), align: %i", + port->buffer_num, port->buffer_num_recommended, port->buffer_num_min, + port->buffer_size, port->buffer_size_recommended, port->buffer_size_min, + port->buffer_alignment_min); +} + +/*****************************************************************************/ +void mmal_log_dump_format(MMAL_ES_FORMAT_T *format) +{ + const char *name_type; + + if (!format) + return; + + switch(format->type) + { + case MMAL_ES_TYPE_AUDIO: name_type = "audio"; break; + case MMAL_ES_TYPE_VIDEO: name_type = "video"; break; + case MMAL_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; + default: name_type = "unknown"; break; + } + + LOG_DEBUG("type: %s, fourcc: %4.4s", name_type, (char *)&format->encoding); + LOG_DEBUG(" bitrate: %i, framed: %i", format->bitrate, + !!(format->flags & MMAL_ES_FORMAT_FLAG_FRAMED)); + LOG_DEBUG(" extra data: %i, %p", format->extradata_size, format->extradata); + switch(format->type) + { + case MMAL_ES_TYPE_AUDIO: + LOG_DEBUG(" samplerate: %i, channels: %i, bps: %i, block align: %i", + format->es->audio.sample_rate, format->es->audio.channels, + format->es->audio.bits_per_sample, format->es->audio.block_align); + break; + + case MMAL_ES_TYPE_VIDEO: + LOG_DEBUG(" width: %i, height: %i, (%i,%i,%i,%i)", + format->es->video.width, format->es->video.height, + format->es->video.crop.x, format->es->video.crop.y, + format->es->video.crop.width, format->es->video.crop.height); + LOG_DEBUG(" pixel aspect ratio: %i/%i, frame rate: %i/%i", + format->es->video.par.num, format->es->video.par.den, + format->es->video.frame_rate.num, format->es->video.frame_rate.den); + break; + + case MMAL_ES_TYPE_SUBPICTURE: + break; + + default: break; + } +} + +MMAL_PORT_T *mmal_util_get_port(MMAL_COMPONENT_T *comp, MMAL_PORT_TYPE_T type, unsigned index) +{ + unsigned num; + MMAL_PORT_T **list; + + switch (type) + { + case MMAL_PORT_TYPE_INPUT: + num = comp->input_num; + list = comp->input; + break; + + case MMAL_PORT_TYPE_OUTPUT: + num = comp->output_num; + list = comp->output; + break; + + case MMAL_PORT_TYPE_CLOCK: + num = comp->clock_num; + list = comp->clock; + break; + + case MMAL_PORT_TYPE_CONTROL: + num = 1; + list = &comp->control; + break; + + default: + vcos_assert(0); + return NULL; + } + if (index < num) + /* coverity[ptr_arith] num is 1 here */ + return list[index]; + else + return NULL; +} + +char *mmal_4cc_to_string(char *buf, size_t len, uint32_t fourcc) +{ + char *src = (char*)&fourcc; + vcos_assert(len >= 5); + if (len < 5) + { + buf[0] = '\0'; + } + else if (fourcc) + { + memcpy(buf, src, 4); + buf[4] = '\0'; + } + else + { + snprintf(buf, len, "<0>"); + } + return buf; +} + +#define MAX_ENCODINGS_NUM 25 +typedef struct { + MMAL_PARAMETER_HEADER_T header; + MMAL_FOURCC_T encodings[MAX_ENCODINGS_NUM]; +} MMAL_SUPPORTED_ENCODINGS_T; + + +int mmal_util_rgb_order_fixed(MMAL_PORT_T *port) +{ + int new_fw = 0; + MMAL_STATUS_T ret; + //Firmware support of RGB24 vs BGR24 colour ordering from camera + //and video splitter components has been corrected as of June 2016. + //New firmwares always report MMAL_ENCODING_RGB24 before BGR24, and + //that is the format we want. + //Old firmware reported BGR24 first, and also returned an error on + //the still port on querying MMAL_PARAMETER_SUPPORTED_ENCODINGS. + + MMAL_SUPPORTED_ENCODINGS_T sup_encodings = {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(sup_encodings)}, {0}}; + ret = mmal_port_parameter_get(port, &sup_encodings.header); + if (ret == MMAL_SUCCESS || ret == MMAL_ENOSPC) + { + //Allow ENOSPC error and hope that the desired formats are in the first + //MAX_ENCODINGS_NUM entries. + int i; + int num_encodings = (sup_encodings.header.size - sizeof(sup_encodings.header)) / + sizeof(sup_encodings.encodings[0]); + if(num_encodings > MAX_ENCODINGS_NUM) + num_encodings = MAX_ENCODINGS_NUM; + for (i=0; iFIELD - (uint8_t *)0)) + +#ifdef __cplusplus +extern "C" { +#endif + +/** Convert a status to a statically-allocated string. + * + * @param status The MMAL status code. + * @return A C string describing the status code. + */ +const char *mmal_status_to_string(MMAL_STATUS_T status); + +/** Convert stride to pixel width for a given pixel encoding. + * + * @param encoding The pixel encoding (such as one of the \ref MmalEncodings "pre-defined encodings") + * @param stride The stride in bytes. + * @return The width in pixels. + */ +uint32_t mmal_encoding_stride_to_width(uint32_t encoding, uint32_t stride); + +/** Convert pixel width to stride for a given pixel encoding + * + * @param encoding The pixel encoding (such as one of the \ref MmalEncodings "pre-defined encodings") + * @param width The width in pixels. + * @return The stride in bytes. + */ +uint32_t mmal_encoding_width_to_stride(uint32_t encoding, uint32_t width); + +/** Return the 16 line high sliced version of a given pixel encoding + * + * @param encoding The pixel encoding (such as one of the \ref MmalEncodings "pre-defined encodings") + * @return The sliced equivalent, or MMAL_ENCODING_UNKNOWN if not supported. + */ +uint32_t mmal_encoding_get_slice_variant(uint32_t encoding); + +/** Convert a port type to a string. + * + * @param type The MMAL port type. + * @return A NULL-terminated string describing the port type. + */ +const char* mmal_port_type_to_string(MMAL_PORT_TYPE_T type); + +/** Get a parameter from a port allocating the required amount of memory + * for the parameter (i.e. for variable length parameters like URI or arrays). + * The size field will be set on output to the actual size of the + * parameter allocated and retrieved. + * + * The pointer returned must be released by a call to \ref mmal_port_parameter_free(). + * + * @param port port to send request to + * @param id parameter id + * @param size initial size hint for allocation (can be 0) + * @param status status of the parameter get operation (can be 0) + * @return pointer to the header of the parameter or NULL on failure. + */ +MMAL_PARAMETER_HEADER_T *mmal_port_parameter_alloc_get(MMAL_PORT_T *port, + uint32_t id, uint32_t size, MMAL_STATUS_T *status); + +/** Free a parameter structure previously allocated via + * \ref mmal_port_parameter_alloc_get(). + * + * @param param pointer to header of the parameter + */ +void mmal_port_parameter_free(MMAL_PARAMETER_HEADER_T *param); + +/** Copy buffer header metadata from source to destination. + * + * @param dest The destination buffer header. + * @param src The source buffer header. + */ +void mmal_buffer_header_copy_header(MMAL_BUFFER_HEADER_T *dest, const MMAL_BUFFER_HEADER_T *src); + +/** Create a pool of MMAL_BUFFER_HEADER_T associated with a specific port. + * This allows a client to allocate memory for the payload buffers based on the preferences + * of a port. This for instance will allow the port to allocate memory which can be shared + * between the host processor and videocore. + * After allocation, all allocated buffer headers will have been added to the queue. + * + * It is valid to create a pool with no buffer headers, or with zero size payload buffers. + * The mmal_pool_resize() function can be used to increase or decrease the number of buffer + * headers, or the size of the payload buffers, after creation of the pool. + * + * @param port Port responsible for creating the pool. + * @param headers Number of buffers which will be allocated with the pool. + * @param payload_size Size of the payload buffer which will be allocated in + * each of the buffer headers. + * @return Pointer to the newly created pool or NULL on failure. + */ +MMAL_POOL_T *mmal_port_pool_create(MMAL_PORT_T *port, + unsigned int headers, uint32_t payload_size); + +/** Destroy a pool of MMAL_BUFFER_HEADER_T associated with a specific port. + * This will also deallocate all of the memory which was allocated when creating or + * resizing the pool. + * + * @param port Pointer to the port responsible for creating the pool. + * @param pool Pointer to the pool to be destroyed. + */ +void mmal_port_pool_destroy(MMAL_PORT_T *port, MMAL_POOL_T *pool); + +/** Log the content of a \ref MMAL_PORT_T structure. + * + * @param port Pointer to the port to dump. + */ +void mmal_log_dump_port(MMAL_PORT_T *port); + +/** Log the content of a \ref MMAL_ES_FORMAT_T structure. + * + * @param format Pointer to the format to dump. + */ +void mmal_log_dump_format(MMAL_ES_FORMAT_T *format); + +/** Return the nth port. + * + * @param comp component to query + * @param index port index + * @param type port type + * + * @return port or NULL if not found + */ +MMAL_PORT_T *mmal_util_get_port(MMAL_COMPONENT_T *comp, MMAL_PORT_TYPE_T type, unsigned index); + +/** Convert a 4cc into a string. + * + * @param buf Destination for result + * @param len Size of result buffer + * @param fourcc 4cc to be converted + * @return converted string (buf) + * + */ +char *mmal_4cc_to_string(char *buf, size_t len, uint32_t fourcc); + + +/** On FW prior to June 2016, camera and video_splitter + * had BGR24 and RGB24 support reversed. + * This is now fixed, and this function will return whether the + * FW has the fix or not. + * + * @param port MMAL port to check (on camera or video_splitter) + * @return 0 if old firmware, 1 if new. + * + */ +int mmal_util_rgb_order_fixed(MMAL_PORT_T *port); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif diff --git a/interface/mmal/util/mmal_util_params.c b/interface/mmal/util/mmal_util_params.c new file mode 100755 index 0000000..6e24791 --- /dev/null +++ b/interface/mmal/util/mmal_util_params.c @@ -0,0 +1,223 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmal_util_params.h" + +/** Helper function to set the value of a boolean parameter */ +MMAL_STATUS_T mmal_port_parameter_set_boolean(MMAL_PORT_T *port, uint32_t id, MMAL_BOOL_T value) +{ + MMAL_PARAMETER_BOOLEAN_T param = {{id, sizeof(param)}, value}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +/** Helper function to get the value of a boolean parameter */ +MMAL_STATUS_T mmal_port_parameter_get_boolean(MMAL_PORT_T *port, uint32_t id, MMAL_BOOL_T *value) +{ + MMAL_PARAMETER_BOOLEAN_T param = {{id, sizeof(param)}, 0}; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + MMAL_STATUS_T status = mmal_port_parameter_get(port, ¶m.hdr); + if (status == MMAL_SUCCESS) + *value = param.enable; + return status; +} + +/** Helper function to set the value of a 64 bits unsigned integer parameter */ +MMAL_STATUS_T mmal_port_parameter_set_uint64(MMAL_PORT_T *port, uint32_t id, uint64_t value) +{ + MMAL_PARAMETER_UINT64_T param = {{id, sizeof(param)}, value}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +/** Helper function to get the value of a 64 bits unsigned integer parameter */ +MMAL_STATUS_T mmal_port_parameter_get_uint64(MMAL_PORT_T *port, uint32_t id, uint64_t *value) +{ + MMAL_PARAMETER_UINT64_T param = {{id, sizeof(param)}, 0LL}; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + MMAL_STATUS_T status = mmal_port_parameter_get(port, ¶m.hdr); + if (status == MMAL_SUCCESS) + *value = param.value; + return status; +} + +/** Helper function to set the value of a 64 bits signed integer parameter */ +MMAL_STATUS_T mmal_port_parameter_set_int64(MMAL_PORT_T *port, uint32_t id, int64_t value) +{ + MMAL_PARAMETER_INT64_T param = {{id, sizeof(param)}, value}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +/** Helper function to get the value of a 64 bits signed integer parameter */ +MMAL_STATUS_T mmal_port_parameter_get_int64(MMAL_PORT_T *port, uint32_t id, int64_t *value) +{ + MMAL_PARAMETER_INT64_T param = {{id, sizeof(param)}, 0LL}; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + MMAL_STATUS_T status = mmal_port_parameter_get(port, ¶m.hdr); + if (status == MMAL_SUCCESS) + *value = param.value; + return status; +} + +/** Helper function to set the value of a 32 bits unsigned integer parameter */ +MMAL_STATUS_T mmal_port_parameter_set_uint32(MMAL_PORT_T *port, uint32_t id, uint32_t value) +{ + MMAL_PARAMETER_UINT32_T param = {{id, sizeof(param)}, value}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +/** Helper function to get the value of a 32 bits unsigned integer parameter */ +MMAL_STATUS_T mmal_port_parameter_get_uint32(MMAL_PORT_T *port, uint32_t id, uint32_t *value) +{ + MMAL_PARAMETER_UINT32_T param = {{id, sizeof(param)}, 0}; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + MMAL_STATUS_T status = mmal_port_parameter_get(port, ¶m.hdr); + if (status == MMAL_SUCCESS) + *value = param.value; + return status; +} + +/** Helper function to set the value of a 32 bits signed integer parameter */ +MMAL_STATUS_T mmal_port_parameter_set_int32(MMAL_PORT_T *port, uint32_t id, int32_t value) +{ + MMAL_PARAMETER_INT32_T param = {{id, sizeof(param)}, value}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +/** Helper function to get the value of a 32 bits signed integer parameter */ +MMAL_STATUS_T mmal_port_parameter_get_int32(MMAL_PORT_T *port, uint32_t id, int32_t *value) +{ + MMAL_PARAMETER_INT32_T param = {{id, sizeof(param)}, 0}; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + MMAL_STATUS_T status = mmal_port_parameter_get(port, ¶m.hdr); + if (status == MMAL_SUCCESS) + *value = param.value; + return status; +} + +/** Helper function to set the value of a rational parameter */ +MMAL_STATUS_T mmal_port_parameter_set_rational(MMAL_PORT_T *port, uint32_t id, MMAL_RATIONAL_T value) +{ + MMAL_PARAMETER_RATIONAL_T param = {{id, sizeof(param)}, {value.num, value.den}}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +/** Helper function to get the value of a rational parameter */ +MMAL_STATUS_T mmal_port_parameter_get_rational(MMAL_PORT_T *port, uint32_t id, MMAL_RATIONAL_T *value) +{ + MMAL_PARAMETER_RATIONAL_T param = {{id, sizeof(param)}, {0,0}}; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + MMAL_STATUS_T status = mmal_port_parameter_get(port, ¶m.hdr); + if (status == MMAL_SUCCESS) + *value = param.value; + return status; +} + +/** Helper function to set the value of a string parameter */ +MMAL_STATUS_T mmal_port_parameter_set_string(MMAL_PORT_T *port, uint32_t id, const char *value) +{ + MMAL_PARAMETER_STRING_T *param = 0; + MMAL_STATUS_T status; + size_t param_size = sizeof(param->hdr) + strlen(value) + 1; + + param = calloc(1, param_size); + if (!param) + return MMAL_ENOMEM; + + param->hdr.id = id; + param->hdr.size = param_size; + memcpy(param->str, value, strlen(value)+1); + status = mmal_port_parameter_set(port, ¶m->hdr); + free(param); + return status; +} + +/** Helper function to set a MMAL_PARAMETER_URI_T parameter on a port */ +MMAL_STATUS_T mmal_util_port_set_uri(MMAL_PORT_T *port, const char *uri) +{ + return mmal_port_parameter_set_string(port, MMAL_PARAMETER_URI, uri); +} + +/** Helper function to set the value of an array of bytes parameter */ +MMAL_STATUS_T mmal_port_parameter_set_bytes(MMAL_PORT_T *port, uint32_t id, + const uint8_t *data, unsigned int size) +{ + MMAL_PARAMETER_BYTES_T *param = 0; + MMAL_STATUS_T status; + size_t param_size = sizeof(param->hdr) + size; + + param = calloc(1, param_size); + if (!param) + return MMAL_ENOMEM; + + param->hdr.id = id; + param->hdr.size = param_size; + memcpy(param->data, data, size); + status = mmal_port_parameter_set(port, ¶m->hdr); + free(param); + return status; +} + +/** Set the display region. + * @param port port to configure + * @param region region + * + * @return MMAL_SUCCESS or error + */ + +MMAL_STATUS_T mmal_util_set_display_region(MMAL_PORT_T *port, + MMAL_DISPLAYREGION_T *region) +{ + region->hdr.id = MMAL_PARAMETER_DISPLAYREGION; + region->hdr.size = sizeof(*region); + return mmal_port_parameter_set(port, ®ion->hdr); +} + +MMAL_STATUS_T mmal_util_camera_use_stc_timestamp(MMAL_PORT_T *port, MMAL_CAMERA_STC_MODE_T mode) +{ + MMAL_PARAMETER_CAMERA_STC_MODE_T param = + {{MMAL_PARAMETER_USE_STC, sizeof(MMAL_PARAMETER_CAMERA_STC_MODE_T)},mode}; + return mmal_port_parameter_set(port, ¶m.hdr); +} + +MMAL_STATUS_T mmal_util_get_core_port_stats(MMAL_PORT_T *port, + MMAL_CORE_STATS_DIR dir, + MMAL_BOOL_T reset, + MMAL_CORE_STATISTICS_T *stats) +{ + MMAL_PARAMETER_CORE_STATISTICS_T param; + MMAL_STATUS_T ret; + + memset(¶m, 0, sizeof(param)); + param.hdr.id = MMAL_PARAMETER_CORE_STATISTICS; + param.hdr.size = sizeof(param); + param.dir = dir; + param.reset = reset; + // coverity[overrun-buffer-val] Structure accessed correctly via size field + ret = mmal_port_parameter_get(port, ¶m.hdr); + if (ret == MMAL_SUCCESS) + *stats = param.stats; + return ret; +} diff --git a/interface/mmal/util/mmal_util_params.h b/interface/mmal/util/mmal_util_params.h new file mode 100755 index 0000000..9379e03 --- /dev/null +++ b/interface/mmal/util/mmal_util_params.h @@ -0,0 +1,210 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_UTIL_PARAMS_H +#define MMAL_UTIL_PARAMS_H + +#include "interface/mmal/mmal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * Utility functions to set some common parameters. + */ + +/** Helper function to set the value of a boolean parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value value to set the parameter to + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_boolean(MMAL_PORT_T *port, uint32_t id, MMAL_BOOL_T value); + +/** Helper function to get the value of a boolean parameter. + * @param port port on which to get the parameter + * @param id parameter id + * @param value pointer to where the value will be returned + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_get_boolean(MMAL_PORT_T *port, uint32_t id, MMAL_BOOL_T *value); + +/** Helper function to set the value of a 64 bits unsigned integer parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value value to set the parameter to + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_uint64(MMAL_PORT_T *port, uint32_t id, uint64_t value); + +/** Helper function to get the value of a 64 bits unsigned integer parameter. + * @param port port on which to get the parameter + * @param id parameter id + * @param value pointer to where the value will be returned + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_get_uint64(MMAL_PORT_T *port, uint32_t id, uint64_t *value); + +/** Helper function to set the value of a 64 bits signed integer parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value value to set the parameter to + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_int64(MMAL_PORT_T *port, uint32_t id, int64_t value); + +/** Helper function to get the value of a 64 bits signed integer parameter. + * @param port port on which to get the parameter + * @param id parameter id + * @param value pointer to where the value will be returned + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_get_int64(MMAL_PORT_T *port, uint32_t id, int64_t *value); + +/** Helper function to set the value of a 32 bits unsigned integer parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value value to set the parameter to + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_uint32(MMAL_PORT_T *port, uint32_t id, uint32_t value); + +/** Helper function to get the value of a 32 bits unsigned integer parameter. + * @param port port on which to get the parameter + * @param id parameter id + * @param value pointer to where the value will be returned + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_get_uint32(MMAL_PORT_T *port, uint32_t id, uint32_t *value); + +/** Helper function to set the value of a 32 bits signed integer parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value value to set the parameter to + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_int32(MMAL_PORT_T *port, uint32_t id, int32_t value); + +/** Helper function to get the value of a 32 bits signed integer parameter. + * @param port port on which to get the parameter + * @param id parameter id + * @param value pointer to where the value will be returned + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_get_int32(MMAL_PORT_T *port, uint32_t id, int32_t *value); + +/** Helper function to set the value of a rational parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value value to set the parameter to + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_rational(MMAL_PORT_T *port, uint32_t id, MMAL_RATIONAL_T value); + +/** Helper function to get the value of a rational parameter. + * @param port port on which to get the parameter + * @param id parameter id + * @param value pointer to where the value will be returned + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_get_rational(MMAL_PORT_T *port, uint32_t id, MMAL_RATIONAL_T *value); + +/** Helper function to set the value of a string parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param value null-terminated string value + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_string(MMAL_PORT_T *port, uint32_t id, const char *value); + +/** Helper function to set the value of an array of bytes parameter. + * @param port port on which to set the parameter + * @param id parameter id + * @param data pointer to the array of bytes + * @param size size of the array of bytes + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_port_parameter_set_bytes(MMAL_PORT_T *port, uint32_t id, + const uint8_t *data, unsigned int size); + +/** Helper function to set a MMAL_PARAMETER_URI_T parameter on a port. + * @param port port on which to set the parameter + * @param uri URI string + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_util_port_set_uri(MMAL_PORT_T *port, const char *uri); + +/** Set the display region. + * @param port port to configure + * @param region region + * + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_util_set_display_region(MMAL_PORT_T *port, + MMAL_DISPLAYREGION_T *region); + +/** Tell the camera to use the STC for timestamps rather than the clock. + * + * @param port port to configure + * @param mode STC mode to use + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_util_camera_use_stc_timestamp(MMAL_PORT_T *port, MMAL_CAMERA_STC_MODE_T mode); + +/** Get the MMAL core statistics for a given port. + * + * @param port port to query + * @param dir port direction + * @param reset reset the stats as well + * @param stats filled in with results + * @return MMAL_SUCCESS or error + */ +MMAL_STATUS_T mmal_util_get_core_port_stats(MMAL_PORT_T *port, MMAL_CORE_STATS_DIR dir, MMAL_BOOL_T reset, + MMAL_CORE_STATISTICS_T *stats); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/interface/mmal/util/mmal_util_rational.c b/interface/mmal/util/mmal_util_rational.c new file mode 100755 index 0000000..288b27b --- /dev/null +++ b/interface/mmal/util/mmal_util_rational.c @@ -0,0 +1,158 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "interface/mmal/util/mmal_util_rational.h" + +#define Q16_ONE (1 << 16) + +#define ABS(v) (((v) < 0) ? -(v) : (v)) + +/** Calculate the greatest common denominator between 2 integers. + * Avoids division. */ +static int32_t gcd(int32_t a, int32_t b) +{ + int shift; + + if (a == 0 || b == 0) + return 1; + + a = ABS(a); + b = ABS(b); + for (shift = 0; !((a | b) & 0x01); shift++) + a >>= 1, b >>= 1; + + while (a > 0) + { + while (!(a & 0x01)) + a >>= 1; + while (!(b & 0x01)) + b >>= 1; + if (a >= b) + a = (a - b) >> 1; + else + b = (b - a) >> 1; + } + return b << shift; +} + +/** Calculate a + b. */ +MMAL_RATIONAL_T mmal_rational_add(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b) +{ + MMAL_RATIONAL_T result; + int32_t g = gcd(a.den, b.den); + a.den /= g; + a.num = a.num * (b.den / g) + b.num * a.den; + g = gcd(a.num, g); + a.num /= g; + a.den *= b.den / g; + + result.num = a.num; + result.den = a.den; + return result; +} + +/** Calculate a - b. */ +MMAL_RATIONAL_T mmal_rational_subtract(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b) +{ + b.num = -b.num; + return mmal_rational_add(a, b); +} + +/** Calculate a * b */ +MMAL_RATIONAL_T mmal_rational_multiply(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b) +{ + MMAL_RATIONAL_T result; + int32_t gcd1 = gcd(a.num, b.den); + int32_t gcd2 = gcd(b.num, a.den); + result.num = (a.num / gcd1) * (b.num / gcd2); + result.den = (a.den / gcd2) * (b.den / gcd1); + + return result; +} + +/** Calculate a / b */ +MMAL_RATIONAL_T mmal_rational_divide(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b) +{ + MMAL_RATIONAL_T result; + int32_t gcd1, gcd2; + + if (b.num == 0) + { + vcos_assert(0); + return a; + } + + if (a.num == 0) + return a; + + gcd1 = gcd(a.num, b.num); + gcd2 = gcd(b.den, a.den); + result.num = (a.num / gcd1) * (b.den / gcd2); + result.den = (a.den / gcd2) * (b.num / gcd1); + + return result; +} + +/** Convert a rational number to a signed 32-bit Q16 number. */ +int32_t mmal_rational_to_fixed_16_16(MMAL_RATIONAL_T rational) +{ + int64_t result = (int64_t)rational.num << 16; + if (rational.den) + result /= rational.den; + + if (result > INT_MAX) + result = INT_MAX; + else if (result < INT_MIN) + result = INT_MIN; + + return (int32_t)result; +} + +/** Convert a rational number to a signed 32-bit Q16 number. */ +MMAL_RATIONAL_T mmal_rational_from_fixed_16_16(int32_t fixed) +{ + MMAL_RATIONAL_T result = { fixed, Q16_ONE }; + mmal_rational_simplify(&result); + return result; +} + +/** Reduce a rational number to it's simplest form. */ +void mmal_rational_simplify(MMAL_RATIONAL_T *rational) +{ + int g = gcd(rational->num, rational->den); + rational->num /= g; + rational->den /= g; +} + +/** Tests for equality */ +MMAL_BOOL_T mmal_rational_equal(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b) +{ + if (a.num != b.num && a.num * (int64_t)b.num == 0) + return MMAL_FALSE; + return a.num * (int64_t)b.den == b.num * (int64_t)a.den; +} diff --git a/interface/mmal/util/mmal_util_rational.h b/interface/mmal/util/mmal_util_rational.h new file mode 100755 index 0000000..f459f51 --- /dev/null +++ b/interface/mmal/util/mmal_util_rational.h @@ -0,0 +1,127 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_UTIL_RATIONAL_H +#define MMAL_UTIL_RATIONAL_H + +#include "interface/mmal/mmal_types.h" + +/** \defgroup MmalRationalUtilities Rational Utility Functions + * \ingroup MmalUtilities + * The rational utility functions allow easy manipulation of rational numbers. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Add 2 rational numbers. + * It is assumed that both input rational numbers are in + * their simplest form. + * + * @param a First operand + * @param b Second operand + * + * @return a + b + */ +MMAL_RATIONAL_T mmal_rational_add(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b); + +/** Subtract 2 rational numbers. + * It is assumed that both input rational numbers are in + * their simplest form. + * + * @param a First operand + * @param b Second operand + * + * @return a - b + */ +MMAL_RATIONAL_T mmal_rational_subtract(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b); + +/** Multiply 2 rational numbers. + * It is assumed that both input rational numbers are in + * their simplest form. + * + * @param a First operand + * @param b Second operand + * + * @return a * b + */ +MMAL_RATIONAL_T mmal_rational_multiply(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b); + +/** Divide 2 rational numbers. + * It is assumed that both input rational numbers are in + * their simplest form. + * + * @param a First operand + * @param b Second operand + * + * @return a / b + */ +MMAL_RATIONAL_T mmal_rational_divide(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b); + +/** Convert a rational number to a 32-bit signed Q16 number. + * Saturation will occur for rational numbers with an absolute + * value greater than 32768. + * + * @param rational Rational number to convert + * + * @return 32-bit signed Q16 number + */ +int32_t mmal_rational_to_fixed_16_16(MMAL_RATIONAL_T rational); + +/** Convert a signed 32-bit Q16 number to a rational number. + * + * @param fixed Signed 32-bit Q16 number to convert + * + * @return Rational number + */ +MMAL_RATIONAL_T mmal_rational_from_fixed_16_16(int32_t fixed); + +/** Reduce a rational number to it's simplest form. + * + * @param rational Rational number to simplify + */ +void mmal_rational_simplify(MMAL_RATIONAL_T *rational); + +/** Test 2 rational numbers for equality. + * + * @param a First operand + * @param b Second operand + * + * @return true if equal + */ +MMAL_BOOL_T mmal_rational_equal(MMAL_RATIONAL_T a, MMAL_RATIONAL_T b); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif diff --git a/interface/mmal/vc/CMakeLists.txt b/interface/mmal/vc/CMakeLists.txt new file mode 100755 index 0000000..d6e80db --- /dev/null +++ b/interface/mmal/vc/CMakeLists.txt @@ -0,0 +1,26 @@ +add_definitions(-DENABLE_MMAL_VCSM) + +add_library(mmal_vc_client ${LIBRARY_TYPE} mmal_vc_client.c mmal_vc_shm.c mmal_vc_api.c mmal_vc_opaque_alloc.c mmal_vc_msgnames.c mmal_vc_api_drm.c) +#target_link_libraries(mmal_vc_client vchiq_arm vcos) + +target_link_libraries(mmal_vc_client vchiq_arm vcos vcsm) + +if(BUILD_MMAL_APPS) +add_executable(mmal_vc_diag mmal_vc_diag.c) +target_link_libraries(mmal_vc_diag mmal mmal_vc_client debug_sym vcos) +install(TARGETS mmal_vc_diag RUNTIME DESTINATION bin) +endif(BUILD_MMAL_APPS) + +include_directories ( ../../../host_applications/linux/libs/sm ) + +install(TARGETS mmal_vc_client DESTINATION lib) +install(FILES + mmal_vc_api.h + mmal_vc_api_drm.h + mmal_vc_client_priv.h + mmal_vc_msgnames.h + mmal_vc_msgs.h + mmal_vc_opaque_alloc.h + mmal_vc_shm.h + DESTINATION include/interface/mmal/vc +) diff --git a/interface/mmal/vc/mmal_vc_api.c b/interface/mmal/vc/mmal_vc_api.c new file mode 100755 index 0000000..31502a0 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_api.c @@ -0,0 +1,1505 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal.h" +#include "mmal_vc_api.h" +#include "mmal_vc_msgs.h" +#include "mmal_vc_client_priv.h" +#include "mmal_vc_opaque_alloc.h" +#include "mmal_vc_shm.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/core/mmal_component_private.h" +#include "interface/mmal/core/mmal_port_private.h" +#include "interface/mmal/core/mmal_buffer_private.h" +#include "interface/vcos/vcos.h" + +/** Private information for MMAL VC components + */ + +typedef enum MMAL_ZEROLEN_CHECK_T +{ + ZEROLEN_NOT_INITIALIZED, + ZEROLEN_COMPATIBLE, + ZEROLEN_INCOMPATIBLE +} MMAL_ZEROLEN_CHECK_T; + +typedef enum MMAL_PORT_FLUSH_CHECK_T +{ + PORT_FLUSH_NOT_INITIALIZED, + PORT_FLUSH_COMPATIBLE, + PORT_FLUSH_INCOMPATIBLE +} MMAL_PORT_FLUSH_CHECK_T; + +typedef struct MMAL_PORT_MODULE_T +{ + uint32_t magic; + uint32_t component_handle; + MMAL_PORT_T *port; + uint32_t port_handle; + + MMAL_BOOL_T has_pool; + VCOS_BLOCKPOOL_T pool; + + MMAL_BOOL_T is_zero_copy; + MMAL_BOOL_T zero_copy_workaround; + uint32_t opaque_allocs; + + MMAL_BOOL_T sent_data_on_port; + + MMAL_PORT_T *connected; /**< Connected port if any */ +} MMAL_PORT_MODULE_T; + +typedef struct MMAL_COMPONENT_MODULE_T +{ + uint32_t component_handle; + + MMAL_PORT_MODULE_T **ports; + uint32_t ports_num; + + MMAL_QUEUE_T *callback_queue; /**< Used to queue the callbacks we need to make to the client */ + + MMAL_BOOL_T event_ctx_initialised; + MMAL_VC_CLIENT_BUFFER_CONTEXT_T event_ctx; /**< Used as the ctx for event buffers */ +} MMAL_COMPONENT_MODULE_T; + +/***************************************************************************** + * Local function prototypes + *****************************************************************************/ +static void mmal_vc_do_callback(MMAL_COMPONENT_T *component); +static MMAL_STATUS_T mmal_vc_port_info_get(MMAL_PORT_T *port); + +/*****************************************************************************/ +MMAL_STATUS_T mmal_vc_get_version(uint32_t *major, uint32_t *minor, uint32_t *minimum) +{ + mmal_worker_version msg; + size_t len = sizeof(msg); + MMAL_STATUS_T status; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_GET_VERSION, &msg, &len, MMAL_FALSE); + + if (status != MMAL_SUCCESS) + return status; + + if (!vcos_verify(len == sizeof(msg))) + return MMAL_EINVAL; + + *major = msg.major; + *minor = msg.minor; + *minimum = msg.minimum; + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +MMAL_STATUS_T mmal_vc_get_stats(MMAL_VC_STATS_T *stats, int reset) +{ + mmal_worker_stats msg; + size_t len = sizeof(msg); + msg.reset = reset; + + MMAL_STATUS_T status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &msg.header, sizeof(msg), + MMAL_WORKER_GET_STATS, + &msg, &len, MMAL_FALSE); + + + if (status == MMAL_SUCCESS) + { + vcos_assert(len == sizeof(msg)); + *stats = msg.stats; + } + return status; +} + +/** Set port buffer requirements. */ +static MMAL_STATUS_T mmal_vc_port_requirements_set(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_port_action msg; + size_t replylen = sizeof(reply); + + msg.component_handle = module->component_handle; + msg.action = MMAL_WORKER_PORT_ACTION_SET_REQUIREMENTS; + msg.port_handle = module->port_handle; + msg.param.enable.port = *port; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + LOG_ERROR("failed to set port requirements (%i/%i,%i/%i)", + port->buffer_num, port->buffer_num_min, + port->buffer_size, port->buffer_size_min); + + return status; +} + +/** Get port buffer requirements. */ +static MMAL_STATUS_T mmal_vc_port_requirements_get(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + mmal_worker_port_info_get msg; + mmal_worker_port_info reply; + size_t replylen = sizeof(reply); + MMAL_STATUS_T status; + + msg.component_handle = module->component_handle; + msg.port_type = port->type; + msg.index = port->index; + + LOG_TRACE("get port requirements (%i:%i)", port->type, port->index); + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_INFO_GET, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to get port requirements (%i:%i)", port->type, port->index); + return status; + } + + port->buffer_num_min = reply.port.buffer_num_min; + port->buffer_num_recommended = reply.port.buffer_num_recommended; + port->buffer_size_min = reply.port.buffer_size_min; + port->buffer_size_recommended = reply.port.buffer_size_recommended; + port->buffer_alignment_min = reply.port.buffer_alignment_min; + + return MMAL_SUCCESS; +} + +/** Enable processing on a port */ +static MMAL_STATUS_T mmal_vc_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_port_action msg; + size_t replylen = sizeof(reply); + MMAL_PARAM_UNUSED(cb); + + if (!port->component->priv->module->event_ctx_initialised) + { + MMAL_POOL_T *pool = port->component->priv->event_pool; + MMAL_DRIVER_BUFFER_T *drv; + unsigned int i; + + /* We need to associate our vc client context to all our event buffers. + * This only needs to be done when the first port is enabled because no event + * can be received on disabled ports. */ + for (i = 0; i < pool->headers_num; i++) + { + drv = mmal_buffer_header_driver_data(pool->header[i]); + drv->client_context = &port->component->priv->module->event_ctx; + drv->magic = MMAL_MAGIC; + } + + port->component->priv->module->event_ctx_initialised = MMAL_TRUE; + } + + if (!module->connected) + { + if (vcos_blockpool_create_on_heap(&module->pool, port->buffer_num, + sizeof(MMAL_VC_CLIENT_BUFFER_CONTEXT_T), + VCOS_BLOCKPOOL_ALIGN_DEFAULT, VCOS_BLOCKPOOL_FLAG_NONE, "mmal vc port pool") != VCOS_SUCCESS) + { + LOG_ERROR("failed to create port pool"); + return MMAL_ENOMEM; + } + module->has_pool = 1; + } + + if (module->connected) + { + /* The connected port won't be enabled explicitly so make sure we apply + * the buffer requirements now. */ + status = mmal_vc_port_requirements_set(module->connected); + if (status != MMAL_SUCCESS) + goto error; + } + + msg.component_handle = module->component_handle; + msg.action = MMAL_WORKER_PORT_ACTION_ENABLE; + msg.port_handle = module->port_handle; + msg.param.enable.port = *port; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to enable port %s: %s", + port->name, mmal_status_to_string(status)); + goto error; + } + + if (module->connected) + mmal_vc_port_info_get(module->connected); + + return MMAL_SUCCESS; + + error: + if (module->has_pool) + vcos_blockpool_delete(&module->pool); + return status; +} + +/** Disable processing on a port */ +static MMAL_STATUS_T mmal_vc_port_disable(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_port_action msg; + size_t replylen = sizeof(reply); + + msg.component_handle = module->component_handle; + msg.action = MMAL_WORKER_PORT_ACTION_DISABLE; + msg.port_handle = module->port_handle; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + LOG_ERROR("failed to disable port - reason %d", status); + + if (module->has_pool) + { + /* MMAL server should make sure that all buffers are sent back before it + * disables the port. */ + vcos_assert(vcos_blockpool_available_count(&module->pool) == port->buffer_num); + vcos_blockpool_delete(&module->pool); + module->has_pool = 0; + } + + /* We need to make sure all the queued callbacks have been done */ + while (mmal_queue_length(port->component->priv->module->callback_queue)) + mmal_vc_do_callback(port->component); + + if (module->connected) + mmal_vc_port_info_get(module->connected); + + return status; +} + +/** Flush a port using MMAL_WORKER_PORT_ACTION - when the port is zero-copy or no data has been sent */ +static MMAL_STATUS_T mmal_vc_port_flush_normal(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_port_action msg; + size_t replylen = sizeof(reply); + + msg.component_handle = module->component_handle; + msg.action = MMAL_WORKER_PORT_ACTION_FLUSH; + msg.port_handle = module->port_handle; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + LOG_ERROR("failed to disable port - reason %d", status); + + return status; +} + + +/** Flush a port using PORT_FLUSH - generates a dummy bulk transfer to keep it in sync + * with buffers being passed using bulk transfer */ +static MMAL_STATUS_T mmal_vc_port_flush_sync(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_reply reply; + MMAL_VC_CLIENT_BUFFER_CONTEXT_T client_context; + mmal_worker_buffer_from_host *msg; + + size_t replylen = sizeof(reply); + + msg = &client_context.msg; + + client_context.magic = MMAL_MAGIC; + client_context.port = port; + + msg->drvbuf.client_context = &client_context; + msg->drvbuf.component_handle = module->component_handle; + msg->drvbuf.port_handle = module->port_handle; + msg->drvbuf.magic = MMAL_MAGIC; + + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg->header, sizeof(*msg), + MMAL_WORKER_PORT_FLUSH, &reply, &replylen, MMAL_TRUE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + LOG_ERROR("failed to disable port - reason %d", status); + + return status; +} + +/** Flush a port */ +static MMAL_STATUS_T mmal_vc_port_flush(MMAL_PORT_T *port) +{ + static MMAL_PORT_FLUSH_CHECK_T is_port_flush_compatible = PORT_FLUSH_NOT_INITIALIZED; + uint32_t major = 0, minor = 0, minimum = 0; + MMAL_STATUS_T status; + /* Buffers sent to videocore, if not zero-copy, use vchiq bulk transfers to copy the data. + A flush could be sent while one of these buffers is being copied. If the normal flushing method + is used, the flush can arrive before the buffer, which causes confusion when a pre-flush buffer + arrives after the flush. So use a special flush mode that uses a dummy vchiq transfer to synchronise + things. + If data has never been sent on the port, then we don't need to worry about a flush overtaking data. + In that case, the port may not actually be set up on the other end to receive bulk transfers, so use + the normal flushing mechanism in that case. + */ + + if (port->priv->module->is_zero_copy || !port->priv->module->sent_data_on_port) + return mmal_vc_port_flush_normal(port); + + if (is_port_flush_compatible == PORT_FLUSH_NOT_INITIALIZED) + { + status = mmal_vc_get_version(&major, &minor, &minimum); + if (major >= 15) + { + is_port_flush_compatible = PORT_FLUSH_COMPATIBLE; + } + else + { + LOG_ERROR("Version number of MMAL Server incompatible. Required Major:14 Minor: 2 \ + or Greater. Current Major %d , Minor %d",major,minor); + is_port_flush_compatible = PORT_FLUSH_INCOMPATIBLE; + } + } + + if (is_port_flush_compatible == PORT_FLUSH_COMPATIBLE) + return mmal_vc_port_flush_sync(port); + else + return mmal_vc_port_flush_normal(port); +} + + +/** Connect 2 ports together */ +static MMAL_STATUS_T mmal_vc_port_connect(MMAL_PORT_T *port, MMAL_PORT_T *other_port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_port_action msg; + size_t replylen = sizeof(reply); + + /* We only support connecting vc components together */ + if (other_port && port->priv->pf_enable != other_port->priv->pf_enable) + return MMAL_ENOSYS; + + /* Send the request to the video side */ + msg.component_handle = module->component_handle; + msg.action = other_port ? MMAL_WORKER_PORT_ACTION_CONNECT : MMAL_WORKER_PORT_ACTION_DISCONNECT; + msg.port_handle = module->port_handle; + if (other_port) + { + msg.param.connect.component_handle = other_port->priv->module->component_handle; + msg.param.connect.port_handle = other_port->priv->module->port_handle; + } + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to connect ports: %s", mmal_status_to_string(status)); + return status; + } + + if (other_port) + { + /* Connection */ + module->connected = other_port; + other_port->priv->module->connected = port; + } + else + { + /* Disconnection */ + if (module->connected) + module->connected->priv->module->connected = NULL; + module->connected = NULL; + } + + return MMAL_SUCCESS; +} + +/*****************************************************************************/ +static void mmal_vc_do_callback(MMAL_COMPONENT_T *component) +{ + MMAL_COMPONENT_MODULE_T *module = component->priv->module; + MMAL_BUFFER_HEADER_T *buffer; + MMAL_PORT_T *port; + + /* Get a buffer from this port */ + buffer = mmal_queue_get(module->callback_queue); + if (!buffer) + return; /* Will happen when a port gets disabled */ + + port = (MMAL_PORT_T *)buffer->priv->component_data; + + /* Catch and report any transmission error */ + if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED) + mmal_event_error_send(port->component, MMAL_EIO); + + /* Events generated by this component are handled differently */ + if (mmal_buffer_header_driver_data(buffer)->client_context == + &component->priv->module->event_ctx) + { + mmal_port_event_send(port, buffer); + return; + } + + buffer->data = mmal_vc_shm_lock(buffer->data, port->priv->module->zero_copy_workaround); + mmal_port_buffer_header_callback(port, buffer); +} + +static void mmal_vc_do_callback_loop(MMAL_COMPONENT_T *component) +{ + while (mmal_queue_length(component->priv->module->callback_queue)) + mmal_vc_do_callback(component); +} + +/** Called back from VCHI(Q) event handler when buffers come back from the copro. + * + * The message points to the message sent by videocore, and which should have + * a pointer back to our original client side context. + * + */ +static void mmal_vc_port_send_callback(mmal_worker_buffer_from_host *msg) +{ + MMAL_BUFFER_HEADER_T *buffer; + MMAL_PORT_T *port; + MMAL_VC_CLIENT_BUFFER_CONTEXT_T *client_context = msg->drvbuf.client_context; + + vcos_assert(client_context); + vcos_assert(client_context->magic == MMAL_MAGIC); + + buffer = client_context->buffer; + port = client_context->port; + vcos_blockpool_free(msg->drvbuf.client_context); + + vcos_assert(port->priv->module->magic == MMAL_MAGIC); + mmal_vc_msg_to_buffer_header(buffer, msg); + + /* Queue the callback so it is delivered by the action thread */ + buffer->priv->component_data = (void *)port; + mmal_queue_put(port->component->priv->module->callback_queue, buffer); + mmal_component_action_trigger(port->component); +} + +static void mmal_vc_port_send_event_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + /* Queue the event to be delivered by the action thread */ + buffer->priv->component_data = (void *)port; + mmal_queue_put(port->component->priv->module->callback_queue, buffer); + mmal_component_action_trigger(port->component); +} + +/** Called from the client to send a buffer (empty or full) to + * the copro. + */ +static MMAL_STATUS_T mmal_vc_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + MMAL_VC_CLIENT_BUFFER_CONTEXT_T *client_context; + mmal_worker_buffer_from_host *msg; + uint32_t length; + uint32_t msgid = MMAL_WORKER_BUFFER_FROM_HOST; + uint32_t major = 0, minor = 0, minimum = 0; + static MMAL_ZEROLEN_CHECK_T is_vc_zerolength_compatible = ZEROLEN_NOT_INITIALIZED; + + vcos_assert(port); + vcos_assert(module); + vcos_assert(module->magic == MMAL_MAGIC); + + /* Handle event buffers */ + if (buffer->cmd) + { + MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer); + if (event) + { + mmal_format_copy(port->format, event->format); + status = port->priv->pf_set_format(port); + if(status != MMAL_SUCCESS) + LOG_ERROR("format not set on port %p", port); + } + else + { + LOG_ERROR("discarding event %i on port %p", (int)buffer->cmd, port); + } + + buffer->length = 0; + mmal_port_buffer_header_callback(port, buffer); + return MMAL_SUCCESS; + } + + /* We can only send buffers if we have a pool */ + if (!module->has_pool) + { + LOG_ERROR("no pool on port %p", port); + return MMAL_EINVAL; + } + + client_context = vcos_blockpool_alloc(&module->pool); + if(!client_context) + { + LOG_INFO("couldn't allocate client buffer context from pool"); + return MMAL_ENOMEM; + } + msg = &client_context->msg; + + client_context->magic = MMAL_MAGIC; + client_context->buffer = buffer; + client_context->callback = mmal_vc_port_send_callback; + client_context->callback_event = NULL; + client_context->port = port; + + msg->drvbuf.client_context = client_context; + msg->drvbuf.component_handle = module->component_handle; + msg->drvbuf.port_handle = module->port_handle; + msg->drvbuf.magic = MMAL_MAGIC; + + length = buffer->length; + + if (length <= MMAL_VC_SHORT_DATA && !port->priv->module->is_zero_copy && + (port->format->encoding == MMAL_ENCODING_OPAQUE || + port->type == MMAL_PORT_TYPE_CLOCK)) + { + memcpy(msg->short_data, buffer->data + buffer->offset, buffer->length); + msg->payload_in_message = length; + length = 0; + } + else + { + msg->payload_in_message = 0; + } + + buffer->data = + mmal_vc_shm_unlock(buffer->data, &length, port->priv->module->zero_copy_workaround); + mmal_vc_buffer_header_to_msg(msg, buffer); + + if (!VCOS_BLOCKPOOL_IS_VALID_HANDLE_FORMAT(msg->drvbuf.component_handle, 256)) + { + LOG_ERROR("bad component handle 0x%x", msg->drvbuf.component_handle); + return MMAL_EINVAL; + } + + if (msg->drvbuf.port_handle > 255) + { + LOG_ERROR("bad port handle 0x%x", msg->drvbuf.port_handle); + return MMAL_EINVAL; + } + + if (module->is_zero_copy) + length = 0; + + if (is_vc_zerolength_compatible == ZEROLEN_NOT_INITIALIZED) + { + status = mmal_vc_get_version(&major, &minor, &minimum); + if ((major > 12 ) || ((major == 12) && (minor >= 2))) + { + is_vc_zerolength_compatible = ZEROLEN_COMPATIBLE; + } + else + { + LOG_ERROR("Version number of MMAL Server incompatible. Required Major:12 Minor: 2 \ + or Greater. Current Major %d , Minor %d",major,minor); + is_vc_zerolength_compatible = ZEROLEN_INCOMPATIBLE; + } + } + + if ((is_vc_zerolength_compatible == ZEROLEN_COMPATIBLE) && !(module->is_zero_copy) && !length + && (msg->buffer_header.flags & MMAL_BUFFER_HEADER_FLAG_EOS)) + { + length = 8; + msgid = MMAL_WORKER_BUFFER_FROM_HOST_ZEROLEN; + } + + if (length) + { + // We're doing a bulk transfer. Note this so that flushes know + // they need to use the more cumbersome fake-bulk-transfer mechanism + // to guarantee correct ordering. + port->priv->module->sent_data_on_port = MMAL_TRUE; + + // Data will be received at the start of the destination buffer, so fixup + // the offset in the destination buffer header. + msg->buffer_header.offset = 0; + } + + status = mmal_vc_send_message(mmal_vc_get_client(), &msg->header, sizeof(*msg), + buffer->data + buffer->offset, length, + msgid); + if (status != MMAL_SUCCESS) + { + LOG_INFO("failed %d", status); + vcos_blockpool_free(msg->drvbuf.client_context); + buffer->data = mmal_vc_shm_lock(buffer->data, port->priv->module->zero_copy_workaround); + } + + return status; +} + +static MMAL_STATUS_T mmal_vc_component_disable(MMAL_COMPONENT_T *component) +{ + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_component_disable msg; + size_t replylen = sizeof(reply); + + vcos_assert(component && component->priv && component->priv->module); + + msg.component_handle = component->priv->module->component_handle; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_COMPONENT_DISABLE, + &reply, &replylen, MMAL_FALSE); + + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + LOG_ERROR("failed to disable component - reason %d", status); + goto fail; + } + + return status; +fail: + return status; +} + +static MMAL_STATUS_T mmal_vc_component_enable(MMAL_COMPONENT_T *component) +{ + MMAL_STATUS_T status; + mmal_worker_reply reply; + mmal_worker_component_enable msg; + size_t replylen = sizeof(reply); + + vcos_assert(component && component->priv && component->priv->module); + + msg.component_handle = component->priv->module->component_handle; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_COMPONENT_ENABLE, &reply, &replylen, MMAL_FALSE); + + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + LOG_ERROR("failed to enable component: %s", mmal_status_to_string(status)); + return status; + } + + return MMAL_SUCCESS; +} + +static MMAL_STATUS_T mmal_vc_component_destroy(MMAL_COMPONENT_T *component) +{ + MMAL_STATUS_T status; + mmal_worker_component_destroy msg; + mmal_worker_reply reply; + size_t replylen = sizeof(reply); + + vcos_assert(component && component->priv && component->priv->module); + + msg.component_handle = component->priv->module->component_handle; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_COMPONENT_DESTROY, + &reply, &replylen, MMAL_FALSE); + + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to destroy component - reason %d", status ); + goto fail; + } + + if(component->input_num) + mmal_ports_free(component->input, component->input_num); + if(component->output_num) + mmal_ports_free(component->output, component->output_num); + if(component->clock_num) + mmal_ports_free(component->clock, component->clock_num); + + mmal_queue_destroy(component->priv->module->callback_queue); + + vcos_free(component->priv->module); + component->priv->module = NULL; + +fail: + // no longer require videocore + mmal_vc_release(); + mmal_vc_deinit(); + return status; +} + +MMAL_STATUS_T mmal_vc_consume_mem(size_t size, uint32_t *handle) +{ + MMAL_STATUS_T status; + mmal_worker_consume_mem req; + mmal_worker_consume_mem reply; + size_t len = sizeof(reply); + + req.size = (uint32_t) size; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &req.header, sizeof(req), + MMAL_WORKER_CONSUME_MEM, + &reply, &len, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(len == sizeof(reply)); + status = reply.status; + *handle = reply.handle; + } + return status; +} + +MMAL_STATUS_T mmal_vc_compact(MMAL_VC_COMPACT_MODE_T mode, uint32_t *duration) +{ + MMAL_STATUS_T status; + mmal_worker_compact req; + mmal_worker_compact reply; + size_t len = sizeof(reply); + + req.mode = (uint32_t)mode; + status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &req.header, sizeof(req), + MMAL_WORKER_COMPACT, + &reply, &len, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(len == sizeof(reply)); + status = reply.status; + *duration = reply.duration; + } + return status; +} + +MMAL_STATUS_T mmal_vc_lmk(uint32_t alloc_size) +{ + MMAL_STATUS_T status; + mmal_worker_lmk req; + mmal_worker_lmk reply; + size_t len = sizeof(reply); + + req.alloc_size = alloc_size; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &req.header, sizeof(req), + MMAL_WORKER_LMK, + &reply, &len, MMAL_FALSE); + return status; +} + +MMAL_STATUS_T mmal_vc_host_log(const char *msg) +{ + MMAL_STATUS_T status = MMAL_EINVAL; + if (msg) + { + mmal_worker_host_log req; + mmal_worker_reply reply; + size_t replylen = sizeof(reply); + size_t msg_len = vcos_safe_strcpy(req.msg, msg, sizeof(req.msg), 0); + + /* Reduce the length if it is shorter than the max message length */ + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &req.header, + sizeof(req) - sizeof(req.msg) + vcos_min(sizeof(req.msg), msg_len + 1), + MMAL_WORKER_HOST_LOG, + &reply, &replylen, MMAL_FALSE); + + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + } + return status; +} + +MMAL_STATUS_T mmal_vc_get_core_stats(MMAL_CORE_STATISTICS_T *stats, + MMAL_STATS_RESULT_T *result, + char *name, + size_t namelen, + MMAL_PORT_TYPE_T type, + unsigned component_index, + unsigned port_index, + MMAL_CORE_STATS_DIR dir, + MMAL_BOOL_T reset) +{ + mmal_worker_get_core_stats_for_port req; + mmal_worker_get_core_stats_for_port_reply reply; + MMAL_STATUS_T status; + size_t len = sizeof(reply); + + req.component_index = component_index; + req.port_index = port_index; + req.type = type; + req.reset = reset; + req.dir = dir; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &req.header, sizeof(req), + MMAL_WORKER_GET_CORE_STATS_FOR_PORT, + &reply, &len, MMAL_FALSE); + + if (status == MMAL_SUCCESS) + { + vcos_assert(len == sizeof(reply)); + *stats = reply.stats; + *result = reply.result; + strncpy(name, reply.component_name, namelen); + name[namelen-1] = '\0'; + } + return status; +} + + +/** Get port context data. */ +static MMAL_STATUS_T mmal_vc_port_info_get(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + mmal_worker_port_info_get msg; + mmal_worker_port_info reply; + size_t replylen = sizeof(reply); + MMAL_STATUS_T status; + + msg.component_handle = module->component_handle; + msg.port_type = port->type; + msg.index = port->index; + + LOG_TRACE("get port info (%i:%i)", port->type, port->index); + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_INFO_GET, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to get port info (%i:%i): %s", port->type, port->index, + mmal_status_to_string(status)); + return status; + } + + module->port_handle = reply.port_handle; + port->buffer_num_min = reply.port.buffer_num_min; + port->buffer_num_recommended = reply.port.buffer_num_recommended; + port->buffer_num = reply.port.buffer_num; + port->buffer_size_min = reply.port.buffer_size_min; + port->buffer_size_recommended = reply.port.buffer_size_recommended; + port->buffer_size = reply.port.buffer_size; + port->buffer_alignment_min = reply.port.buffer_alignment_min; + port->is_enabled = reply.port.is_enabled; + port->capabilities = reply.port.capabilities; + reply.format.extradata = port->format->extradata; + reply.format.es = port->format->es; + *port->format = reply.format; + *port->format->es = reply.es; + if(port->format->extradata_size) + { + status = mmal_format_extradata_alloc(port->format, port->format->extradata_size); + if(status != MMAL_SUCCESS) + { + vcos_assert(0); + port->format->extradata_size = 0; + LOG_ERROR("couldn't allocate extradata %i", port->format->extradata_size); + return MMAL_ENOMEM; + } + memcpy(port->format->extradata, reply.extradata, port->format->extradata_size); + } + + return MMAL_SUCCESS; +} + +/** Set port context data. */ +static MMAL_STATUS_T mmal_vc_port_info_set(MMAL_PORT_T *port) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + mmal_worker_port_info_set msg; + mmal_worker_port_info reply; + size_t replylen = sizeof(reply); + MMAL_STATUS_T status; + + msg.component_handle = module->component_handle; + msg.port_type = port->type; + msg.index = port->index; + msg.port = *port; + msg.format = *port->format; + msg.es = *port->format->es; + if(msg.format.extradata_size > MMAL_FORMAT_EXTRADATA_MAX_SIZE) + { + vcos_assert(0); + msg.format.extradata_size = MMAL_FORMAT_EXTRADATA_MAX_SIZE; + } + memcpy(msg.extradata, msg.format.extradata, msg.format.extradata_size); + + LOG_TRACE("set port info (%i:%i)", port->type, port->index); + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_PORT_INFO_SET, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to set port info (%i:%i): %s", port->type, port->index, + mmal_status_to_string(status)); + return status; + } + + port->buffer_num_min = reply.port.buffer_num_min; + port->buffer_num_recommended = reply.port.buffer_num_recommended; + port->buffer_num = reply.port.buffer_num; + port->buffer_size_min = reply.port.buffer_size_min; + port->buffer_size_recommended = reply.port.buffer_size_recommended; + port->buffer_size = reply.port.buffer_size; + port->buffer_alignment_min = reply.port.buffer_alignment_min; + port->is_enabled = reply.port.is_enabled; + port->capabilities = reply.port.capabilities; + reply.format.extradata = port->format->extradata; + reply.format.es = port->format->es; + *port->format = reply.format; + *port->format->es = reply.es; + if(port->format->extradata_size) + { + status = mmal_format_extradata_alloc(port->format, port->format->extradata_size); + if(status != MMAL_SUCCESS) + { + vcos_assert(0); + port->format->extradata_size = 0; + LOG_ERROR("couldn't allocate extradata %i", port->format->extradata_size); + return MMAL_ENOMEM; + } + memcpy(port->format->extradata, reply.extradata, port->format->extradata_size); + } + + return MMAL_SUCCESS; +} + +/** Set format on a port */ +static MMAL_STATUS_T mmal_vc_port_set_format(MMAL_PORT_T *port) +{ + MMAL_COMPONENT_T *component = port->component; + MMAL_COMPONENT_MODULE_T *module = component->priv->module; + MMAL_STATUS_T status; + unsigned int i; + + status = mmal_vc_port_info_set(port); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("mmal_vc_port_info_set failed %p (%s)", port, + mmal_status_to_string(status)); + return status; + } + + /* Get the setting back for this port */ + status = mmal_vc_port_info_get(port); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("mmal_vc_port_info_get failed %p (%s)", port, + mmal_status_to_string(status)); + return status; + } + + /* Get the settings for the output ports in case they have changed */ + if (port->type == MMAL_PORT_TYPE_INPUT) + { + for (i = 0; i < module->ports_num; i++) + { + if (module->ports[i]->port->type != MMAL_PORT_TYPE_OUTPUT) + continue; + + status = mmal_vc_port_info_get(module->ports[i]->port); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("mmal_vc_port_info_get failed %p (%i)", + module->ports[i]->port, status); + return status; + } + } + } + + return MMAL_SUCCESS; +} + +/** Set parameter on a port */ +static MMAL_STATUS_T mmal_vc_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_port_param_set msg; + size_t msglen = MMAL_OFFSET(mmal_worker_port_param_set, param) + param->size; + mmal_worker_reply reply; + size_t replylen = sizeof(reply); + + if(param->size > MMAL_WORKER_PORT_PARAMETER_SET_MAX) + { + LOG_ERROR("parameter too large (%u > %u)", param->size, MMAL_WORKER_PORT_PARAMETER_SET_MAX); + return MMAL_ENOSPC; + } + + /* Intercept the zero copy parameter */ + if (param->id == MMAL_PARAMETER_ZERO_COPY && + param->size >= sizeof(MMAL_PARAMETER_BOOLEAN_T) ) + { + module->is_zero_copy = !!((MMAL_PARAMETER_BOOLEAN_T *)param)->enable; + module->zero_copy_workaround = ((MMAL_PARAMETER_BOOLEAN_T *)param)->enable == 0xBEEF; + LOG_DEBUG("%s zero copy on port %p", module->is_zero_copy ? "enable" : "disable", port); + } + + msg.component_handle = module->component_handle; + msg.port_handle = module->port_handle; + /* coverity[overrun-buffer-arg] */ + memcpy(&msg.param, param, param->size); + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, msglen, + MMAL_WORKER_PORT_PARAMETER_SET, &reply, &replylen, MMAL_FALSE); + + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + if (status != MMAL_SUCCESS) + { + LOG_WARN("failed to set port parameter %u:%u %u:%u %s", msg.component_handle, msg.port_handle, + param->id, param->size, mmal_status_to_string(status)); + return status; + } + + if (param->id == MMAL_PARAMETER_BUFFER_REQUIREMENTS) + { + /* This might have changed the buffer requirements of other ports so fetch them all */ + MMAL_COMPONENT_T *component = port->component; + unsigned int i; + for (i = 0; status == MMAL_SUCCESS && i < component->input_num; i++) + status = mmal_vc_port_requirements_get(component->input[i]); + for (i = 0; status == MMAL_SUCCESS && i < component->output_num; i++) + status = mmal_vc_port_requirements_get(component->output[i]); + } + + return status; +} + +/** Get parameter on a port */ +static MMAL_STATUS_T mmal_vc_port_parameter_get(MMAL_PORT_T *port, MMAL_PARAMETER_HEADER_T *param) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_STATUS_T status; + mmal_worker_port_param_get msg; + size_t msglen = MMAL_OFFSET(mmal_worker_port_param_get, param) + param->size; + mmal_worker_port_param_get_reply reply; + size_t replylen = MMAL_OFFSET(mmal_worker_port_param_get_reply, param) + param->size; + + if(param->size > MMAL_WORKER_PORT_PARAMETER_GET_MAX) + { + LOG_ERROR("parameter too large (%u > %u) id %u", param->size, + MMAL_WORKER_PORT_PARAMETER_GET_MAX, param->id); + return MMAL_ENOMEM; + } + + msg.component_handle = module->component_handle; + msg.port_handle = module->port_handle; + memcpy(&msg.param, param, param->size); + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, msglen, + MMAL_WORKER_PORT_PARAMETER_GET, &reply, &replylen, MMAL_FALSE); + if (status == MMAL_SUCCESS) + { + status = reply.status; + /* Reply must include the parameter header */ + vcos_assert(replylen >= MMAL_OFFSET(mmal_worker_port_param_get_reply, space)); + + /* If the call fails with MMAL_ENOSPC then reply.param.size is set to the size required for + * the call to succeed, and that may be bigger than the buffers, so only check these asserts + * if the call succeeded. + */ + if ( status == MMAL_SUCCESS ) + { + /* Reply mustn't be bigger than the parameter given */ + vcos_assert(replylen <= (MMAL_OFFSET(mmal_worker_port_param_get_reply, param) + param->size)); + /* Reply must be consistent with the parameter size embedded in it */ + vcos_assert(replylen == (MMAL_OFFSET(mmal_worker_port_param_get_reply, param) + reply.param.size)); + } + } + + if (status != MMAL_SUCCESS && status != MMAL_ENOSPC) + { + LOG_WARN("failed to get port parameter %u:%u %u:%u %s", msg.component_handle, msg.port_handle, + param->id, param->size, mmal_status_to_string(status)); + return status; + } + + if (status == MMAL_ENOSPC) + { + /* Copy only as much as we have space for but report true size of parameter */ + /* coverity[overrun-buffer-arg] */ + memcpy(param, &reply.param, param->size); + param->size = reply.param.size; + } + else + { + memcpy(param, &reply.param, reply.param.size); + } + + return status; +} + +static uint8_t *mmal_vc_port_payload_alloc(MMAL_PORT_T *port, uint32_t payload_size) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + MMAL_BOOL_T can_deref = MMAL_TRUE; + char buf[5]; + void *ret; + (void)buf; + + LOG_TRACE("%s: allocating %d bytes, format %s, is_zero_copy %d", + port->name, + payload_size, + mmal_4cc_to_string(buf, sizeof(buf), port->format->encoding), + module->is_zero_copy); + + if (port->format->encoding == MMAL_ENCODING_OPAQUE && + module->is_zero_copy) + { + MMAL_OPAQUE_IMAGE_HANDLE_T h = mmal_vc_opaque_alloc_desc(port->name); + can_deref = MMAL_FALSE; + ret = (void*)h; + if (!ret) + { + LOG_ERROR("%s: failed to allocate %d bytes opaque memory", + port->name, payload_size); + return NULL; + } + module->opaque_allocs++; + } + + else if (module->is_zero_copy) + { + ret = mmal_vc_shm_alloc(payload_size); + if (!ret) + { + LOG_ERROR("%s: failed to allocate %d bytes of shared memory", + port->name, payload_size); + return NULL; + } + } + + else + { + /* Allocate conventional memory */ + ret = vcos_malloc(payload_size, "mmal_vc_port payload"); + if (!ret) + { + LOG_ERROR("could not allocate %i bytes", (int)payload_size); + return NULL; + } + } + + /* Ensure that newly minted opaque buffers are always in a sensible + * state, and don't have random garbage in them. + */ + if (can_deref && port->format->encoding == MMAL_ENCODING_OPAQUE) + memset(ret, 0, payload_size); + + LOG_DEBUG("%s: allocated at %p", port->name, ret); + return ret; +} + +static void mmal_vc_port_payload_free(MMAL_PORT_T *port, uint8_t *payload) +{ + MMAL_PORT_MODULE_T *module = port->priv->module; + + if (module->opaque_allocs) + { + module->opaque_allocs--; + mmal_vc_opaque_release((MMAL_OPAQUE_IMAGE_HANDLE_T)payload); + return; + } + + else if (mmal_vc_shm_free(payload) == MMAL_SUCCESS) + return; + + /* We're dealing with conventional memory */ + vcos_free(payload); +} + +/** Create a component given its name. */ +static MMAL_STATUS_T mmal_vc_component_create(const char *name, MMAL_COMPONENT_T *component) +{ + MMAL_STATUS_T status; + const char *basename; + mmal_worker_component_create msg; + mmal_worker_component_create_reply reply; + size_t replylen = sizeof(reply); + MMAL_COMPONENT_MODULE_T *module = NULL; + unsigned int ports_num, i; + + LOG_TRACE("%s", name); + + if (strstr(name, VIDEOCORE_PREFIX ".") != name) + return MMAL_ENOSYS; + + basename = name + sizeof(VIDEOCORE_PREFIX ".") - 1; + if (strlen(basename) >= sizeof(msg.name)-1) + { + vcos_assert(0); + return MMAL_EINVAL; + } + + msg.client_component = component; + /* coverity[secure_coding] Length tested above */ + strcpy(msg.name, basename); +#ifdef __linux__ + msg.pid = getpid(); +#endif + + status = mmal_vc_init(); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to initialise mmal ipc for '%s' (%i:%s)", + name, status, mmal_status_to_string(status)); + return status; + } + // claim VC for entire duration of component. + status = mmal_vc_use(); + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_COMPONENT_CREATE, &reply, &replylen, MMAL_FALSE); + + vcos_log_info("%s: %s: handle 0x%x status %d reply status %d", + __FUNCTION__, name, reply.component_handle, status, reply.status); + + if (status == MMAL_SUCCESS) + { + vcos_assert(replylen == sizeof(reply)); + status = reply.status; + } + + if (status != MMAL_SUCCESS) + { + LOG_ERROR("failed to create component '%s' (%i:%s)", name, status, + mmal_status_to_string(status)); + mmal_vc_release(); + mmal_vc_deinit(); + return status; + } + + /* Component has been created, allocate our context. */ + status = MMAL_ENOMEM; + ports_num = 1 + reply.input_num + reply.output_num + reply.clock_num; + module = vcos_calloc(1, sizeof(*module) + ports_num * sizeof(*module->ports), "mmal_vc_module"); + if (!module) + { + mmal_worker_component_destroy msg; + mmal_worker_reply reply; + size_t replylen = sizeof(reply); + MMAL_STATUS_T destroy_status; + + destroy_status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg), + MMAL_WORKER_COMPONENT_DESTROY, &reply, &replylen, MMAL_FALSE); + vcos_assert(destroy_status == MMAL_SUCCESS); + mmal_vc_release(); + mmal_vc_deinit(); + return status; + } + module->ports = (MMAL_PORT_MODULE_T **)&module[1]; + module->component_handle = reply.component_handle; + component->priv->module = module; + + /* Allocate our local ports. Control port reallocated to set module size. */ + mmal_port_free(component->control); + component->control = mmal_port_alloc(component, MMAL_PORT_TYPE_CONTROL, + sizeof(MMAL_PORT_MODULE_T)); + if (!component->control) + goto fail; + + if (reply.input_num) + { + component->input = mmal_ports_alloc(component, reply.input_num, MMAL_PORT_TYPE_INPUT, + sizeof(MMAL_PORT_MODULE_T)); + if (!component->input) + goto fail; + } + component->input_num = reply.input_num; + + if (reply.output_num) + { + component->output = mmal_ports_alloc(component, reply.output_num, MMAL_PORT_TYPE_OUTPUT, + sizeof(MMAL_PORT_MODULE_T)); + if (!component->output) + goto fail; + } + component->output_num = reply.output_num; + + if (reply.clock_num) + { + component->clock = mmal_ports_alloc(component, reply.clock_num, MMAL_PORT_TYPE_CLOCK, + sizeof(MMAL_PORT_MODULE_T)); + if (!component->clock) + goto fail; + } + component->clock_num = reply.clock_num; + + /* We want to do the buffer callbacks to the client into a separate thread. + * We'll need to queue these callbacks and have an action which does the actual callback. */ + module->callback_queue = mmal_queue_create(); + if (!module->callback_queue) + goto fail; + status = mmal_component_action_register(component, mmal_vc_do_callback_loop); + if (status != MMAL_SUCCESS) + goto fail; + + LOG_TRACE(" handle %i", reply.component_handle); + + module->ports[module->ports_num] = component->control->priv->module; + module->ports[module->ports_num]->port = component->control; + module->ports[module->ports_num]->component_handle = module->component_handle; + module->ports_num++; + + for (i = 0; i < component->input_num; i++, module->ports_num++) + { + module->ports[module->ports_num] = component->input[i]->priv->module; + module->ports[module->ports_num]->port = component->input[i]; + module->ports[module->ports_num]->component_handle = module->component_handle; + } + + for (i = 0; i < component->output_num; i++, module->ports_num++) + { + module->ports[module->ports_num] = component->output[i]->priv->module; + module->ports[module->ports_num]->port = component->output[i]; + module->ports[module->ports_num]->component_handle = module->component_handle; + } + + for (i = 0; i < component->clock_num; i++, module->ports_num++) + { + module->ports[module->ports_num] = component->clock[i]->priv->module; + module->ports[module->ports_num]->port = component->clock[i]; + module->ports[module->ports_num]->component_handle = module->component_handle; + } + + /* Get the ports info */ + for (i = 0; i < module->ports_num; i++) + { + MMAL_PORT_T *port = module->ports[i]->port; + port->priv->pf_set_format = mmal_vc_port_set_format; + port->priv->pf_enable = mmal_vc_port_enable; + port->priv->pf_disable = mmal_vc_port_disable; + port->priv->pf_send = mmal_vc_port_send; + port->priv->pf_flush = mmal_vc_port_flush; + port->priv->pf_connect = mmal_vc_port_connect; + port->priv->pf_parameter_set = mmal_vc_port_parameter_set; + port->priv->pf_parameter_get = mmal_vc_port_parameter_get; + port->priv->pf_payload_alloc = mmal_vc_port_payload_alloc; + port->priv->pf_payload_free = mmal_vc_port_payload_free; + port->priv->module->component_handle = module->component_handle; + port->priv->module->magic = MMAL_MAGIC; + + status = mmal_vc_port_info_get(port); + if (status != MMAL_SUCCESS) + goto fail; + } + + /* Initialise the vc client context which will be used for our event buffers */ + module->event_ctx_initialised = MMAL_FALSE; + module->event_ctx.magic = MMAL_MAGIC; + module->event_ctx.callback_event = mmal_vc_port_send_event_callback; + + /* populate component structure */ + component->priv->pf_enable = mmal_vc_component_enable; + component->priv->pf_disable = mmal_vc_component_disable; + component->priv->pf_destroy = mmal_vc_component_destroy; + return MMAL_SUCCESS; + +fail: + mmal_vc_component_destroy(component); + return status; +} + +MMAL_CONSTRUCTOR(mmal_register_component_videocore); +void mmal_register_component_videocore(void) +{ + mmal_vc_shm_init(); + mmal_component_supplier_register(VIDEOCORE_PREFIX, mmal_vc_component_create); +} + diff --git a/interface/mmal/vc/mmal_vc_api.h b/interface/mmal/vc/mmal_vc_api.h new file mode 100755 index 0000000..cf9dd27 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_api.h @@ -0,0 +1,239 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_VC_API_H +#define MMAL_VC_API_H + +/** @file + * + * Public API for MMAL VC client. Most functionality is exposed + * via MMAL itself. + */ + +#include "interface/mmal/mmal_types.h" +#include "interface/mmal/mmal_parameters.h" +#include "interface/mmal/mmal_port.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** State of components created by the VC adaptation layer, used for + * statistics reporting. + */ +typedef enum { + MMAL_STATS_COMP_IDLE, + MMAL_STATS_COMP_CREATED, + MMAL_STATS_COMP_DESTROYING, + MMAL_STATS_COMP_DESTROYED, + MMAL_STATS_COMP_UNUSED = 0xffffffff /* force 32bit */ +} MMAL_STATS_COMP_STATE_T; + +/** Per-component statistics collected by the VC adaptation layer. + */ +struct MMAL_VC_COMP_STATS_T { + struct MMAL_DRIVER_COMPONENT_T *comp; + MMAL_STATS_COMP_STATE_T state; + uint32_t pid; + uint32_t pool_mem_alloc_size; + char name[20]; +}; + +/** VC adaptation layer statistics. + */ +struct MMAL_VC_STATS_T +{ + struct + { + uint32_t rx; /**< Count of data buffers received */ + uint32_t rx_zero_copy; /**< Count of zero-copy data buffers received */ + uint32_t rx_empty; /**< Empty data buffers (to be filled) */ + uint32_t rx_fails; /**< Gave up partway through */ + uint32_t tx; /**< Count of data buffers sent */ + uint32_t tx_zero_copy; /**< Count of zero-copy data buffers sent */ + uint32_t tx_empty; /**< Count of empty data buffers sent */ + uint32_t tx_fails; /**< Gave up partway through */ + uint32_t tx_short_msg; /**< Messages sent directly in the control message */ + uint32_t rx_short_msg; /**< Messages received directly in the control message */ + } buffers; + struct service + { + uint32_t created; /**< How many services created */ + uint32_t pending_destroy; /**< How many destroyed */ + uint32_t destroyed; /**< How many destroyed */ + uint32_t failures; /**< Failures to create a service */ + } service; + struct commands + { + uint32_t bad_messages; + uint32_t executed; + uint32_t failed; + uint32_t replies; + uint32_t reply_fails; + } commands; + struct + { + uint32_t tx; /**< Count of events sent */ + uint32_t tx_fails; /**< Count of events not fully sent */ + } events; + struct + { + uint32_t created; + uint32_t destroyed; + uint32_t destroying; + uint32_t failed; + uint32_t list_size; + struct MMAL_VC_COMP_STATS_T component_list[8]; + } components; + struct + { + uint32_t enqueued_messages; + uint32_t dequeued_messages; + uint32_t max_parameter_set_delay; + uint32_t max_messages_waiting; + } worker; + +}; +typedef struct MMAL_VC_STATS_T MMAL_VC_STATS_T; + +/* Simple circular text buffer used to store 'interesting' data + * from MMAL clients. e.g. settings for each picture taken */ +struct MMAL_VC_HOST_LOG_T +{ + /** Simple circular buffer of plain text log messages separated by NUL */ + char buffer[16 << 10]; + /** For VCDBG validation and to help detect buffer overflow */ + uint32_t magic; + /** Write offset into buffer */ + int32_t offset; + /** Counter of host messages logged since boot */ + unsigned count; +}; +typedef struct MMAL_VC_HOST_LOG_T MMAL_VC_HOST_LOG_T; + +/** Status from querying MMAL core statistics. + */ +typedef enum +{ + MMAL_STATS_FOUND, + MMAL_STATS_COMPONENT_NOT_FOUND, + MMAL_STATS_PORT_NOT_FOUND, + MMAL_STATS_INVALID = 0x7fffffff +} MMAL_STATS_RESULT_T; + +/* If opening dev_vchiq outside mmal/vchiq this is the file path and mode */ +#define MMAL_DEV_VCHIQ_PATH "/dev/vchiq" +#define MMAL_DEV_VCHIQ_MODE O_RDWR + +MMAL_STATUS_T mmal_vc_init(void); +MMAL_STATUS_T mmal_vc_init_fd(int dev_vchiq_fd); +void mmal_vc_deinit(void); + +MMAL_STATUS_T mmal_vc_use(void); +MMAL_STATUS_T mmal_vc_release(void); + +MMAL_STATUS_T mmal_vc_get_version(uint32_t *major, uint32_t *minor, uint32_t *minimum); +MMAL_STATUS_T mmal_vc_get_stats(MMAL_VC_STATS_T *stats, int reset); + +/** Return the MMAL core statistics for a given component/port. + * + * @param stats Updated with given port statistics + * @param result Whether the port/component was found + * @param name Filled in with the name of the port + * @param namelen Length of name + * @param component Which component (indexed from zero) + * @param port_type Which type of port + * @param port Which port (index from zero) + * @param reset Reset the stats. + */ +MMAL_STATUS_T mmal_vc_get_core_stats(MMAL_CORE_STATISTICS_T *stats, + MMAL_STATS_RESULT_T *result, + char *name, + size_t namelen, + MMAL_PORT_TYPE_T type, + unsigned component, + unsigned port, + MMAL_CORE_STATS_DIR dir, + MMAL_BOOL_T reset); +/** + * Stores an arbitrary text message in a circular buffer inside the MMAL VC server. + * The purpose of this message is to log high level events from the host in order + * to diagnose problems that require multiple actions to reproduce. e.g. taking + * multiple pictures with different settings. + * + * @param msg The message text. + * @return MMAL_SUCCESS if the message was logged or MMAL_ENOSYS if the API + * if not supported. + */ +MMAL_STATUS_T mmal_vc_host_log(const char *msg); + +/* For backwards compatibility in builds */ +#define MMAL_VC_API_HAVE_HOST_LOG + +/* VC DEBUG ONLY ************************************************************/ +/** Consumes memory in the relocatable heap. + * + * The existing reserved memory is freed first then the new chunk is allocated. + * If zero is specified for the size then the previously reserved memory + * is freed and no allocation occurs. + * + * At startup no memory is reserved. + * + * @param size Size of memory to consume in bytes. + * @param handle Set to the mem handle for the reserved memory or zero + * if no memory was allocated. + * @return MMAL_SUCCESS if memory was reserved (or size zero requested), + * MMAL_ENOSPC if the allocation failed or MMAL_ENOSYS if the + * API is not supported e.g in release mode VC images. + * @internal + */ +MMAL_STATUS_T mmal_vc_consume_mem(size_t size, uint32_t *handle); + +typedef enum +{ + MMAL_VC_COMPACT_NONE = 0, + MMAL_VC_COMPACT_NORMAL = 1, + MMAL_VC_COMPACT_DISCARD = 2, + MMAL_VC_COMPACT_AGGRESSIVE = 4, + MMAL_VC_COMPACT_SHUFFLE = 0x80, + MMAL_VC_COMPACT_ALL = MMAL_VC_COMPACT_NORMAL | MMAL_VC_COMPACT_DISCARD | MMAL_VC_COMPACT_AGGRESSIVE, +} MMAL_VC_COMPACT_MODE_T; + +/** Trigger relocatable heap compaction. + * @internal + */ +MMAL_STATUS_T mmal_vc_compact(MMAL_VC_COMPACT_MODE_T mode, uint32_t *duration); + +/** Trigger LMK action from VC, for diagnostics. + * @internal + */ +MMAL_STATUS_T mmal_vc_lmk(uint32_t alloc_size); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/mmal/vc/mmal_vc_api_drm.c b/interface/mmal/vc/mmal_vc_api_drm.c new file mode 100755 index 0000000..a926a86 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_api_drm.c @@ -0,0 +1,77 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "mmal_vc_api_drm.h" +#include "mmal_vc_api.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal.h" +#include "mmal_vc_api.h" +#include "mmal_vc_msgs.h" +#include "mmal_vc_client_priv.h" +#include "mmal_vc_opaque_alloc.h" +#include "mmal_vc_shm.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/core/mmal_component_private.h" +#include "interface/mmal/core/mmal_port_private.h" +#include "interface/mmal/core/mmal_buffer_private.h" +#include "interface/vcos/vcos.h" + + +int mmal_vc_drm_get_time(unsigned int * time) +{ + MMAL_STATUS_T status; + mmal_worker_msg_header req; + mmal_worker_drm_get_time_reply reply; + size_t len = sizeof(reply); + status = mmal_vc_init(); + if (status != MMAL_SUCCESS) return status; + status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &req, sizeof(req), + MMAL_WORKER_DRM_GET_TIME, + &reply, &len, MMAL_FALSE); + *time = reply.time; + mmal_vc_deinit(); + return status; +} + + +int mmal_vc_drm_get_lhs32(unsigned char * into) +{ + MMAL_STATUS_T status; + mmal_worker_msg_header req; + mmal_worker_drm_get_lhs32_reply reply; + size_t len = sizeof(reply); + status = mmal_vc_init(); + if (status != MMAL_SUCCESS) return status; + + status = mmal_vc_sendwait_message(mmal_vc_get_client(), + &req, sizeof(req), + MMAL_WORKER_DRM_GET_LHS32, + &reply, &len, MMAL_FALSE); + memcpy(into, reply.secret, 32); + mmal_vc_deinit(); + return status; +} diff --git a/interface/mmal/vc/mmal_vc_api_drm.h b/interface/mmal/vc/mmal_vc_api_drm.h new file mode 100755 index 0000000..04688b6 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_api_drm.h @@ -0,0 +1,55 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MMAL_VC_API_DRM_H +#define MMAL_VC_API_DRM_H + +/** @file + * + * Public API for MMAL VC client. (Divx DRM part) + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +// Reads the current clock (in microseconds) into the "time" variable. +// Returns zero on success, nonszero on failure +int mmal_vc_drm_get_time(unsigned int * time); + +// Reads the local hardware secret into the "into" variable (needs to be 32 bytes of space for this) +// Returns 0 on success, nonzero on failure +// Usage: +// unsigned char buffer[32]; +// success = mmal_vc_divx_drm_get_lhs(buffer); +int mmal_vc_drm_get_lhs32(unsigned char * into); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/mmal/vc/mmal_vc_client.c b/interface/mmal/vc/mmal_vc_client.c new file mode 100755 index 0000000..db64dc6 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_client.c @@ -0,0 +1,827 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "mmal.h" +#include "mmal_vc_msgs.h" +#include "mmal_vc_api.h" +#include "mmal_vc_client_priv.h" +#include "interface/vcos/vcos.h" +#include "vchiq_util.h" +#include "interface/mmal/core/mmal_buffer_private.h" +#include "interface/mmal/core/mmal_component_private.h" +#include "interface/mmal/core/mmal_port_private.h" +#include "interface/mmal/util/mmal_list.h" +#include "interface/mmal/util/mmal_util.h" + +#define VCOS_LOG_CATEGORY (&mmal_ipc_log_category) +#include "interface/mmal/mmal_logging.h" + +#include + +#define MAX_WAITERS 16 +static VCOS_ONCE_T once = VCOS_ONCE_INIT; +static VCHIQ_INSTANCE_T mmal_vchiq_instance; +static VCOS_LOG_CAT_T mmal_ipc_log_category; + +/** Client threads use one of these to wait for + * a reply from VideoCore. + */ +typedef struct MMAL_WAITER_T +{ + VCOS_SEMAPHORE_T sem; + unsigned inuse; + void *dest; /**< Where to write reply */ + size_t destlen; /**< Max length for reply */ +} MMAL_WAITER_T; + +/** We have an array of waiters and allocate them to waiting + * threads. They can be released back to the pool in any order. + * If there are none free, the calling thread will block until + * one becomes available. + */ +typedef struct +{ + MMAL_WAITER_T waiters[MAX_WAITERS]; + VCOS_SEMAPHORE_T sem; +} MMAL_WAITPOOL_T; + +struct MMAL_CLIENT_T +{ + int refcount; + int usecount; + VCOS_MUTEX_T lock; + VCHIQ_SERVICE_HANDLE_T service; + MMAL_WAITPOOL_T waitpool; + VCOS_MUTEX_T bulk_lock; + + MMAL_BOOL_T inited; +}; + +/* One client per process/VC connection. Multiple threads may + * be using a single client. + */ +static MMAL_CLIENT_T client; + +static void init_once(void) +{ + vcos_mutex_create(&client.lock, VCOS_FUNCTION); +} + +/** Create a pool of wait-structures. + */ +static MMAL_STATUS_T create_waitpool(MMAL_WAITPOOL_T *waitpool) +{ + VCOS_STATUS_T status; + int i; + + status = vcos_semaphore_create(&waitpool->sem, VCOS_FUNCTION, + MAX_WAITERS); + if (status != VCOS_SUCCESS) + return status==VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOSPC; + + for (i=0; iwaiters[i].inuse = 0; + status = vcos_semaphore_create(&waitpool->waiters[i].sem, + "mmal waiter", 0); + if (status != VCOS_SUCCESS) + break; + } + + if (status != VCOS_SUCCESS) + { + /* clean up */ + i--; + while (i>=0) + { + vcos_semaphore_delete(&waitpool->waiters[i].sem); + i--; + } + vcos_semaphore_delete(&waitpool->sem); + } + return status==VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOSPC; +} + +static void destroy_waitpool(MMAL_WAITPOOL_T *waitpool) +{ + int i; + for (i=0; iwaiters[i].sem); + + vcos_semaphore_delete(&waitpool->sem); +} + +/** Grab a waiter from the pool. Return immediately if one already + * available, or wait for one to become available. + */ +static MMAL_WAITER_T *get_waiter(MMAL_CLIENT_T *client) +{ + int i; + MMAL_WAITER_T *waiter = NULL; + vcos_semaphore_wait(&client->waitpool.sem); + vcos_mutex_lock(&client->lock); + for (i=0; iwaitpool.waiters[i].inuse == 0) + break; + } + /* If this fails, the semaphore is not working */ + if (vcos_verify(i != MAX_WAITERS)) + { + waiter = client->waitpool.waiters+i; + waiter->inuse = 1; + } + vcos_mutex_unlock(&client->lock); + + return waiter; +} + +/** Return a waiter to the pool. + */ +static void release_waiter(MMAL_CLIENT_T *client, MMAL_WAITER_T *waiter) +{ + LOG_TRACE("at %p", waiter); + vcos_assert(waiter); + vcos_assert(waiter->inuse); + waiter->inuse = 0; + vcos_semaphore_post(&client->waitpool.sem); +} + +static MMAL_PORT_T *mmal_vc_port_by_number(MMAL_COMPONENT_T *component, uint32_t type, uint32_t number) +{ + switch (type) + { + case MMAL_PORT_TYPE_CONTROL: + vcos_assert(number == 0); + return component->control; + case MMAL_PORT_TYPE_INPUT: + vcos_assert(number < component->input_num); + return component->input[number]; + case MMAL_PORT_TYPE_OUTPUT: + vcos_assert(number < component->output_num); + return component->output[number]; + case MMAL_PORT_TYPE_CLOCK: + vcos_assert(number < component->clock_num); + return component->clock[number]; + } + + return NULL; +} + +static void mmal_vc_handle_event_msg(VCHIQ_HEADER_T *vchiq_header, + VCHIQ_SERVICE_HANDLE_T service, + void *context) +{ + mmal_worker_event_to_host *msg = (mmal_worker_event_to_host *)vchiq_header->data; + MMAL_COMPONENT_T *component = msg->client_component; + MMAL_BUFFER_HEADER_T *buffer; + MMAL_STATUS_T status; + MMAL_PORT_T *port; + + LOG_DEBUG("event to host, cmd 0x%08x len %d to component %p port (%d,%d)", + msg->cmd, msg->length, msg->client_component, msg->port_type, msg->port_num); + (void)context; + + port = mmal_vc_port_by_number(component, msg->port_type, msg->port_num); + if (!vcos_verify(port)) + { + LOG_ERROR("port (%i,%i) doesn't exist", (int)msg->port_type, (int)msg->port_num); + goto error; + } + + status = mmal_port_event_get(port, &buffer, msg->cmd); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("no event buffer available to receive event (%i)", (int)status); + goto error; + } + + if (!vcos_verify(msg->length <= buffer->alloc_size)) + { + LOG_ERROR("event buffer to small to receive event (%i/%i)", + (int)buffer->alloc_size, (int)msg->length); + goto error; + } + buffer->length = msg->length; + + /* Sanity check that the event buffers have the proper vc client context */ + if (!vcos_verify(mmal_buffer_header_driver_data(buffer)->magic == MMAL_MAGIC && + mmal_buffer_header_driver_data(buffer)->client_context && + mmal_buffer_header_driver_data(buffer)->client_context->magic == MMAL_MAGIC && + mmal_buffer_header_driver_data(buffer)->client_context->callback_event)) + { + LOG_ERROR("event buffers not configured properly by component"); + goto error; + } + + if (buffer->length > MMAL_WORKER_EVENT_SPACE) + { + /* a buffer full of data for us to process */ + int len = buffer->length; + len = (len+3) & (~3); + LOG_DEBUG("queue event bulk rx: %p, %d", buffer->data, buffer->length); + msg->delayed_buffer = buffer; + + VCHIQ_STATUS_T vst = vchiq_queue_bulk_receive(service, buffer->data, len, vchiq_header); + if (vst != VCHIQ_SUCCESS) + { + LOG_TRACE("queue event bulk rx len %d failed to start", buffer->length); + mmal_buffer_header_release(buffer); + goto error; + } + } + else + { + if (msg->length) + memcpy(buffer->data, msg->data, msg->length); + + mmal_buffer_header_driver_data(buffer)->client_context->callback_event(port, buffer); + LOG_DEBUG("done callback back to client"); + vchiq_release_message(service, vchiq_header); + } + + return; + +error: + /* FIXME: How to abort bulk receive if necessary? */ + msg->length = 0; /* FIXME: set a buffer flag to signal error */ + vchiq_release_message(service, vchiq_header); +} + +static MMAL_STATUS_T mmal_vc_use_internal(MMAL_CLIENT_T *client) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + vcos_mutex_lock(&client->lock); + if(client->usecount++ == 0) + { + if(vchiq_use_service(client->service) != VCHIQ_SUCCESS) + { + client->usecount--; + status = MMAL_EIO; + } + } + vcos_mutex_unlock(&client->lock); + return status; +} + +static MMAL_STATUS_T mmal_vc_release_internal(MMAL_CLIENT_T *client) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + vcos_mutex_lock(&client->lock); + if(--client->usecount == 0) + { + if(vchiq_release_service(client->service) != VCHIQ_SUCCESS) + { + client->usecount++; + status = MMAL_EIO; + } + } + vcos_mutex_unlock(&client->lock); + return status; +} + + +/** Callback invoked by VCHIQ + */ +static VCHIQ_STATUS_T mmal_vc_vchiq_callback(VCHIQ_REASON_T reason, + VCHIQ_HEADER_T *vchiq_header, + VCHIQ_SERVICE_HANDLE_T service, + void *context) +{ + LOG_TRACE("reason %d", reason); + + switch (reason) + { + case VCHIQ_MESSAGE_AVAILABLE: + { + mmal_worker_msg_header *msg = (mmal_worker_msg_header*)vchiq_header->data; + vcos_assert(msg->magic == MMAL_MAGIC); + + if (msg->msgid == MMAL_WORKER_BUFFER_TO_HOST) + { + LOG_TRACE("buffer to host"); + mmal_worker_buffer_from_host *msg = (mmal_worker_buffer_from_host *)vchiq_header->data; + LOG_TRACE("len %d context %p", msg->buffer_header.length, msg->drvbuf.client_context); + vcos_assert(msg->drvbuf.client_context); + vcos_assert(msg->drvbuf.client_context->magic == MMAL_MAGIC); + + /* If the buffer is referencing another, need to replicate it here + * in order to use the reference buffer's payload and ensure the + * reference is not released prematurely */ + if (msg->has_reference) + mmal_buffer_header_replicate(msg->drvbuf.client_context->buffer, + msg->drvbuf_ref.client_context->buffer); + + /* Sanity check the size of the transfer so we don't overrun our buffer */ + if (!vcos_verify(msg->buffer_header.offset + msg->buffer_header.length <= + msg->drvbuf.client_context->buffer->alloc_size)) + { + LOG_TRACE("buffer too small (%i, %i)", + msg->buffer_header.offset + msg->buffer_header.length, + msg->drvbuf.client_context->buffer->alloc_size); + msg->buffer_header.length = 0; /* FIXME: set a buffer flag to signal error */ + msg->drvbuf.client_context->callback(msg); + vchiq_release_message(service, vchiq_header); + break; + } + /*To handle VC to HOST filled buffer callback of EOS buffer to receive in sync with data buffers*/ + if (!msg->is_zero_copy && + (msg->buffer_header.length != 0 || + (msg->buffer_header.flags & MMAL_BUFFER_HEADER_FLAG_EOS))) + { + /* a buffer full of data for us to process */ + VCHIQ_STATUS_T vst = VCHIQ_SUCCESS; + LOG_TRACE("queue bulk rx: %p, %d", msg->drvbuf.client_context->buffer->data + + msg->buffer_header.offset, msg->buffer_header.length); + int len = msg->buffer_header.length; + len = (len+3) & (~3); + + if (!len && (msg->buffer_header.flags & MMAL_BUFFER_HEADER_FLAG_EOS)) + { + len = 8; + } + if (!msg->payload_in_message) + { + /* buffer transferred using vchiq bulk xfer */ + vst = vchiq_queue_bulk_receive(service, + msg->drvbuf.client_context->buffer->data + msg->buffer_header.offset, + len, vchiq_header); + + if (vst != VCHIQ_SUCCESS) + { + LOG_TRACE("queue bulk rx len %d failed to start", msg->buffer_header.length); + msg->buffer_header.length = 0; /* FIXME: set a buffer flag to signal error */ + msg->drvbuf.client_context->callback(msg); + vchiq_release_message(service, vchiq_header); + } + } + else if (msg->payload_in_message <= MMAL_VC_SHORT_DATA) + { + /* we have already received the buffer data in the message! */ + MMAL_BUFFER_HEADER_T *dst = msg->drvbuf.client_context->buffer; + LOG_TRACE("short data: dst = %p, dst->data = %p, len %d short len %d", dst, dst? dst->data : 0, msg->buffer_header.length, msg->payload_in_message); + memcpy(dst->data, msg->short_data, msg->payload_in_message); + dst->offset = 0; + dst->length = msg->payload_in_message; + vchiq_release_message(service, vchiq_header); + msg->drvbuf.client_context->callback(msg); + } + else + { + /* impossible short data length */ + LOG_ERROR("Message with invalid short payload length %d", + msg->payload_in_message); + vcos_assert(0); + } + } + else + { + + /* Message received from videocore; the client_context should have + * been passed all the way through by videocore back to us, and will + * be picked up in the callback to complete the sequence. + */ + LOG_TRACE("doing cb (%p) context %p", + msg->drvbuf.client_context, msg->drvbuf.client_context ? + msg->drvbuf.client_context->callback : 0); + msg->drvbuf.client_context->callback(msg); + LOG_TRACE("done callback back to client"); + vchiq_release_message(service, vchiq_header); + } + } + else if (msg->msgid == MMAL_WORKER_EVENT_TO_HOST) + { + mmal_vc_handle_event_msg(vchiq_header, service, context); + } + else + { + MMAL_WAITER_T *waiter = msg->u.waiter; + LOG_TRACE("waking up waiter at %p", waiter); + vcos_assert(waiter->inuse); + int len = vcos_min(waiter->destlen, vchiq_header->size); + waiter->destlen = len; + LOG_TRACE("copying payload @%p to %p len %d", waiter->dest, msg, len); + memcpy(waiter->dest, msg, len); + vchiq_release_message(service, vchiq_header); + vcos_semaphore_post(&waiter->sem); + } + } + break; + case VCHIQ_BULK_TRANSMIT_DONE: + { + /* nothing to do here, need to wait for the copro to tell us it + * has emptied the buffer before we can recycle it, otherwise we + * end up feeding the copro with buffers it cannot handle. + */ +#ifdef VCOS_LOGGING_ENABLED + mmal_worker_buffer_from_host *msg = (mmal_worker_buffer_from_host *)context; +#endif + LOG_TRACE("bulk tx done: %p, %d", msg->buffer_header.data, msg->buffer_header.length); + } + break; + case VCHIQ_BULK_RECEIVE_DONE: + { + VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)context; + mmal_worker_msg_header *msg_hdr = (mmal_worker_msg_header*)header->data; + if (msg_hdr->msgid == MMAL_WORKER_BUFFER_TO_HOST) + { + mmal_worker_buffer_from_host *msg = (mmal_worker_buffer_from_host *)msg_hdr; + vcos_assert(msg->drvbuf.client_context->magic == MMAL_MAGIC); + msg->drvbuf.client_context->callback(msg); + LOG_TRACE("bulk rx done: %p, %d", msg->buffer_header.data, msg->buffer_header.length); + } + else + { + mmal_worker_event_to_host *msg = (mmal_worker_event_to_host *)msg_hdr; + MMAL_PORT_T *port = mmal_vc_port_by_number(msg->client_component, msg->port_type, msg->port_num); + + vcos_assert(port); + mmal_buffer_header_driver_data(msg->delayed_buffer)-> + client_context->callback_event(port, msg->delayed_buffer); + LOG_DEBUG("event bulk rx done, length %d", msg->length); + } + vchiq_release_message(service, header); + } + break; + case VCHIQ_BULK_RECEIVE_ABORTED: + { + VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)context; + mmal_worker_msg_header *msg_hdr = (mmal_worker_msg_header*)header->data; + if (msg_hdr->msgid == MMAL_WORKER_BUFFER_TO_HOST) + { + mmal_worker_buffer_from_host *msg = (mmal_worker_buffer_from_host *)msg_hdr; + LOG_TRACE("bulk rx aborted: %p, %d", msg->buffer_header.data, msg->buffer_header.length); + vcos_assert(msg->drvbuf.client_context->magic == MMAL_MAGIC); + msg->buffer_header.flags |= MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED; + msg->drvbuf.client_context->callback(msg); + } + else + { + mmal_worker_event_to_host *msg = (mmal_worker_event_to_host *)msg_hdr; + MMAL_PORT_T *port = mmal_vc_port_by_number(msg->client_component, msg->port_type, msg->port_num); + + vcos_assert(port); + LOG_DEBUG("event bulk rx aborted"); + msg->delayed_buffer->flags |= MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED; + mmal_buffer_header_driver_data(msg->delayed_buffer)-> + client_context->callback_event(port, msg->delayed_buffer); + } + vchiq_release_message(service, header); + } + break; + case VCHIQ_BULK_TRANSMIT_ABORTED: + { + mmal_worker_buffer_from_host *msg = (mmal_worker_buffer_from_host *)context; + LOG_INFO("bulk tx aborted: %p, %d", msg->buffer_header.data, msg->buffer_header.length); + vcos_assert(msg->drvbuf.client_context->magic == MMAL_MAGIC); + /* Nothing to do as the VC side will release the buffer and notify us of the error */ + } + break; + default: + break; + } + + return VCHIQ_SUCCESS; +} + +/** Send a message and wait for a reply. + * + * @param client client to send message for + * @param msg_header message vchiq_header to send + * @param size length of message, including header + * @param msgid message id + * @param dest destination for reply + * @param destlen size of destination, updated with actual length + * @param send_dummy_bulk whether to send a dummy bulk transfer + */ +MMAL_STATUS_T mmal_vc_sendwait_message(struct MMAL_CLIENT_T *client, + mmal_worker_msg_header *msg_header, + size_t size, + uint32_t msgid, + void *dest, + size_t *destlen, + MMAL_BOOL_T send_dummy_bulk) +{ + MMAL_STATUS_T ret; + MMAL_WAITER_T *waiter; + VCHIQ_STATUS_T vst; + VCHIQ_ELEMENT_T elems[] = {{msg_header, size}}; + + vcos_assert(size >= sizeof(mmal_worker_msg_header)); + vcos_assert(dest); + + if (!client->inited) + { + vcos_assert(0); + return MMAL_EINVAL; + } + + if (send_dummy_bulk) + vcos_mutex_lock(&client->bulk_lock); + + waiter = get_waiter(client); + msg_header->msgid = msgid; + msg_header->u.waiter = waiter; + msg_header->magic = MMAL_MAGIC; + + waiter->dest = dest; + waiter->destlen = *destlen; + LOG_TRACE("wait %p, reply to %p", waiter, dest); + mmal_vc_use_internal(client); + + vst = vchiq_queue_message(client->service, elems, 1); + + if (vst != VCHIQ_SUCCESS) + { + ret = MMAL_EIO; + if (send_dummy_bulk) + vcos_mutex_unlock(&client->bulk_lock); + goto fail_msg; + } + + if (send_dummy_bulk) + { + uint32_t data_size = 8; + /* The data is just some dummy bytes so it's fine for it to be static */ + static uint8_t data[8]; + vst = vchiq_queue_bulk_transmit(client->service, data, data_size, msg_header); + + vcos_mutex_unlock(&client->bulk_lock); + + if (!vcos_verify(vst == VCHIQ_SUCCESS)) + { + LOG_ERROR("failed bulk transmit"); + /* This really should not happen and if it does, things will go wrong as + * we've already queued the vchiq message above. */ + vcos_assert(0); + ret = MMAL_EIO; + goto fail_msg; + } + } + + /* now wait for the reply... + * + * FIXME: we could do with a timeout here. Need to be careful to cancel + * the semaphore on a timeout. + */ + /* coverity[lock] This semaphore isn't being used as a mutex */ + vcos_semaphore_wait(&waiter->sem); + + mmal_vc_release_internal(client); + LOG_TRACE("got reply (len %i/%i)", (int)*destlen, (int)waiter->destlen); + *destlen = waiter->destlen; + + release_waiter(client, waiter); + return MMAL_SUCCESS; + +fail_msg: + mmal_vc_release_internal(client); + + release_waiter(client, waiter); + return ret; +} + +/** Send a message and do not wait for a reply. + * + * @note + * This function should only be called from within a mmal component, so + * vchiq_use/release_service calls aren't required (dealt with at higher level). + * + * @param client client to send message for + * @param msg_header message header to send + * @param size length of message, including header + * @param msgid message id + */ +MMAL_STATUS_T mmal_vc_send_message(MMAL_CLIENT_T *client, + mmal_worker_msg_header *msg_header, size_t size, + uint8_t *data, size_t data_size, + uint32_t msgid) +{ + VCHIQ_STATUS_T vst; + VCHIQ_ELEMENT_T elems[] = {{msg_header, size}}; + MMAL_BOOL_T using_bulk_transfer = (data_size != 0); + + LOG_TRACE("len %d", data_size); + vcos_assert(size >= sizeof(mmal_worker_msg_header)); + + if (!client->inited) + { + vcos_assert(0); + return MMAL_EINVAL; + } + + if (using_bulk_transfer) + vcos_mutex_lock(&client->bulk_lock); + + msg_header->msgid = msgid; + msg_header->magic = MMAL_MAGIC; + + vst = vchiq_queue_message(client->service, elems, 1); + + if (vst != VCHIQ_SUCCESS) + { + if (using_bulk_transfer) + vcos_mutex_unlock(&client->bulk_lock); + + LOG_ERROR("failed"); + goto error; + } + + if (using_bulk_transfer) + { + LOG_TRACE("bulk transmit: %p, %i", data, data_size); + + data_size = (data_size + 3) & ~3; + vst = vchiq_queue_bulk_transmit(client->service, data, data_size, msg_header); + + vcos_mutex_unlock(&client->bulk_lock); + + if (!vcos_verify(vst == VCHIQ_SUCCESS)) + { + LOG_ERROR("failed bulk transmit"); + /* This really should not happen and if it does, things will go wrong as + * we've already queued the vchiq message above. */ + vcos_assert(0); + goto error; + } + } + + return MMAL_SUCCESS; + + error: + return MMAL_EIO; +} + +MMAL_STATUS_T mmal_vc_use(void) +{ + MMAL_STATUS_T status = MMAL_ENOTCONN; + if(client.inited) + status = mmal_vc_use_internal(&client); + return status; +} + +MMAL_STATUS_T mmal_vc_release(void) +{ + MMAL_STATUS_T status = MMAL_ENOTCONN; + if(client.inited) + status = mmal_vc_release_internal(&client); + return status; +} + +MMAL_STATUS_T mmal_vc_init_fd(int dev_vchiq_fd) +{ + VCHIQ_SERVICE_PARAMS_T vchiq_params; + MMAL_BOOL_T vchiq_initialised = 0, waitpool_initialised = 0; + MMAL_BOOL_T service_initialised = 0; + MMAL_STATUS_T status = MMAL_EIO; + VCHIQ_STATUS_T vchiq_status; + int count; + + vcos_once(&once, init_once); + + vcos_mutex_lock(&client.lock); + + count = client.refcount++; + if (count > 0) + { + /* Already initialised so nothing to do */ + vcos_mutex_unlock(&client.lock); + return MMAL_SUCCESS; + } + + vcos_log_register("mmalipc", VCOS_LOG_CATEGORY); + + /* Initialise a VCHIQ instance */ + vchiq_status = vchiq_initialise_fd(&mmal_vchiq_instance, dev_vchiq_fd); + if (vchiq_status != VCHIQ_SUCCESS) + { + LOG_ERROR("failed to initialise vchiq"); + status = MMAL_EIO; + goto error; + } + vchiq_initialised = 1; + + vchiq_status = vchiq_connect(mmal_vchiq_instance); + if (vchiq_status != VCHIQ_SUCCESS) + { + LOG_ERROR("failed to connect to vchiq"); + status = MMAL_EIO; + goto error; + } + + memset(&vchiq_params,0,sizeof(vchiq_params)); + vchiq_params.fourcc = MMAL_CONTROL_FOURCC(); + vchiq_params.callback = mmal_vc_vchiq_callback; + vchiq_params.userdata = &client; + vchiq_params.version = WORKER_VER_MAJOR; + vchiq_params.version_min = WORKER_VER_MINIMUM; + + vchiq_status = vchiq_open_service(mmal_vchiq_instance, &vchiq_params, &client.service); + if (vchiq_status != VCHIQ_SUCCESS) + { + LOG_ERROR("could not open vchiq service"); + status = MMAL_EIO; + goto error; + } + client.usecount = 1; /* usecount set to 1 by the open call. */ + service_initialised = 1; + + status = create_waitpool(&client.waitpool); + if (status != MMAL_SUCCESS) + { + LOG_ERROR("could not create wait pool"); + goto error; + } + waitpool_initialised = 1; + + if (vcos_mutex_create(&client.bulk_lock, "mmal client bulk lock") != VCOS_SUCCESS) + { + LOG_ERROR("could not create bulk lock"); + status = MMAL_ENOSPC; + goto error; + } + + client.inited = 1; + + vcos_mutex_unlock(&client.lock); + /* assume we're not using VC immediately. Do this outside the lock */ + mmal_vc_release(); + + + return MMAL_SUCCESS; + + error: + if (waitpool_initialised) + destroy_waitpool(&client.waitpool); + if (service_initialised) + { + client.usecount = 0; + vchiq_close_service(client.service); + } + if (vchiq_initialised) + vchiq_shutdown(mmal_vchiq_instance); + vcos_log_unregister(VCOS_LOG_CATEGORY); + client.refcount--; + + vcos_mutex_unlock(&client.lock); + return status; +} + +MMAL_STATUS_T mmal_vc_init(void) +{ + return mmal_vc_init_fd(-1); +} + +void mmal_vc_deinit(void) +{ + int count; + + vcos_mutex_lock(&client.lock); + count = --client.refcount; + if (count != 0) + { + /* Still in use so don't do anything */ + vcos_mutex_unlock(&client.lock); + return; + } + + vcos_mutex_delete(&client.bulk_lock); + destroy_waitpool(&client.waitpool); + vchiq_close_service(client.service); + vchiq_shutdown(mmal_vchiq_instance); + vcos_log_unregister(VCOS_LOG_CATEGORY); + + client.service = VCHIQ_SERVICE_HANDLE_INVALID; + client.inited = 0; + vcos_mutex_unlock(&client.lock); +} + +MMAL_CLIENT_T *mmal_vc_get_client(void) +{ + return &client; +} diff --git a/interface/mmal/vc/mmal_vc_client_priv.h b/interface/mmal/vc/mmal_vc_client_priv.h new file mode 100755 index 0000000..0fc3aaa --- /dev/null +++ b/interface/mmal/vc/mmal_vc_client_priv.h @@ -0,0 +1,80 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_VC_CLIENT_H +#define MMAL_VC_CLIENT_H + +/** @file mmal_vc_client_priv.h + * + * Internal API for vchiq_arm MMAL client. + */ + +struct MMAL_CLIENT_T; +typedef struct MMAL_CLIENT_T MMAL_CLIENT_T; + +void mmal_vc_client_init(void); + +/** Hold the context required when sending a buffer to the copro. + */ +typedef struct MMAL_VC_CLIENT_BUFFER_CONTEXT_T +{ + uint32_t magic; + + /** Called when VC is done with the buffer */ + void (*callback)(struct mmal_worker_buffer_from_host *); + + /** Called when VC sends an event */ + void (*callback_event)(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *event); + + /** The port this buffer was sent to */ + MMAL_PORT_T *port; + + /** The original buffer from the host. */ + MMAL_BUFFER_HEADER_T *buffer; + + /** The actual message sent to the host */ + struct mmal_worker_buffer_from_host msg; +} MMAL_VC_CLIENT_BUFFER_CONTEXT_T; + + +MMAL_CLIENT_T *mmal_vc_get_client(void); + +MMAL_STATUS_T mmal_vc_sendwait_message(MMAL_CLIENT_T *client, + mmal_worker_msg_header *header, + size_t size, + uint32_t msgid, + void *dest, + size_t *destlen, + MMAL_BOOL_T send_dummy_bulk); + +MMAL_STATUS_T mmal_vc_send_message(MMAL_CLIENT_T *client, + mmal_worker_msg_header *header, size_t size, + uint8_t *data, size_t data_size, + uint32_t msgid); + +#endif + diff --git a/interface/mmal/vc/mmal_vc_dbglog.h b/interface/mmal/vc/mmal_vc_dbglog.h new file mode 100755 index 0000000..0008bc4 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_dbglog.h @@ -0,0 +1,160 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Multi-Media Abstraction Layer API + */ +#ifndef MMAL_VC_DBGLOG_H +#define MMAL_VC_DBGLOG_H + +#include "interface/mmal/mmal.h" +#include "mmal_vc_msgs.h" + +/* Debug log for MMAL messages going past */ + +typedef enum { + MMAL_DBG_MSG, + MMAL_DBG_BULK, + MMAL_DBG_OPENED, + MMAL_DBG_CLOSED, + MMAL_DBG_BULK_ACK, + MMAL_DBG_BULK_TX, + MMAL_DBG_BULK_RX, +} MMAL_DBG_EVENT_TYPE_T; + + +/** Debug log data. */ +typedef union +{ + struct { + mmal_worker_msg_header header; + uint32_t msg[4]; /**< Snarf this much message data */ + } msg; + + struct { + uint32_t len; /**< Length of transfer */ + uint32_t data[4]; /**< Snarf this much payload data */ + } bulk; + + uint32_t uint; + + uint32_t arr[15]; /** Pad complete DBG_ENTRY_T to 64 bytes per line */ + +} MMAL_DBG_DATA_T; + +/** One entry in the debug log */ +typedef struct +{ + uint32_t time; + uint32_t event_type; + MMAL_DBG_DATA_T u; +} MMAL_DBG_ENTRY_T; + +#define MMAL_DBG_ENTRIES_MAX 64 +#define MMAL_DBG_ENTRIES_MASK (MMAL_DBG_ENTRIES_MAX-1) +#define MMAL_DBG_VERSION 1 + +/** The debug log itself. This is currently allocated in uncached + * memory so that the ARM can easily access it. + */ +typedef struct +{ + uint32_t version; + uint32_t magic; + uint32_t num_entries; + uint32_t index; + uint32_t size; + uint32_t elemsize; + uint32_t pad[2]; + MMAL_DBG_ENTRY_T entries[MMAL_DBG_ENTRIES_MAX]; + +} MMAL_DBG_LOG_T; + +extern VCOS_MUTEX_T mmal_dbg_lock; +extern MMAL_DBG_LOG_T *mmal_dbg_log; + +/** Get the next event and hold the lock. Should only be + * accessed by the macros below. + */ +static inline MMAL_DBG_ENTRY_T *mmal_log_lock_event(MMAL_DBG_EVENT_TYPE_T event_type ) { + uint32_t index; + MMAL_DBG_ENTRY_T *entry; + vcos_mutex_lock(&mmal_dbg_lock); + index = mmal_dbg_log->index++; + entry = mmal_dbg_log->entries + (index & MMAL_DBG_ENTRIES_MASK); + entry->time = vcos_getmicrosecs(); + entry->event_type = event_type; + return entry; +} + +/** Release the lock. Should only be accessed by the macros below. */ +static inline void mmal_log_unlock_event(void) { + vcos_mutex_unlock(&mmal_dbg_lock); +} + +/** Initialise the logging module. */ +MMAL_STATUS_T mmal_vc_dbglog_init(void); + +/** Deinitialise the logging module. */ +void mmal_vc_dbglog_deinit(void); + +/** Put an entry into the log. + * + * @param short_type type of event, e.g. OPENED + * @param name union entry name, e.g. uint. + * @param event event data + */ +#define MMAL_LOG_EVENT(short_type, name, value) {\ + MMAL_DBG_ENTRY_T *entry = mmal_log_lock_event(MMAL_DBG_##short_type); \ + entry->u.name = value;\ + mmal_log_unlock_event(); \ +} + +/** Log an uint event (i.e. all just the fact that it occurred */ +#define LOG_EVENT_UINT(short_type,u) MMAL_LOG_EVENT(short_type, uint, u) + +#define LOG_EVENT_OPENED() LOG_EVENT_UINT(OPENED,0) +#define LOG_EVENT_CLOSED() LOG_EVENT_UINT(CLOSED,0) +#define LOG_EVENT_BULK_ACK(reason) LOG_EVENT_UINT(BULK_ACK,reason) + +/** Log a message. Grabs part of the message data. + */ +#define LOG_EVENT_MSG(header) {\ + MMAL_DBG_ENTRY_T *entry = mmal_log_lock_event(MMAL_DBG_MSG); \ + memcpy(&entry->u.msg, header, sizeof(entry->u.msg)); \ + mmal_log_unlock_event(); \ +} + +/** Log bulk data. For now, do not grab the actual data itself */ +#define LOG_EVENT_BULK(type, len, data) {\ + MMAL_DBG_ENTRY_T *entry = mmal_log_lock_event(MMAL_DBG_BULK_##type); \ + entry->u.bulk.len = len; \ + mmal_log_unlock_event(); \ +} + + +#endif diff --git a/interface/mmal/vc/mmal_vc_diag.c b/interface/mmal/vc/mmal_vc_diag.c new file mode 100755 index 0000000..e9712af --- /dev/null +++ b/interface/mmal/vc/mmal_vc_diag.c @@ -0,0 +1,880 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/mmal/mmal.h" +#include "interface/mmal/util/mmal_util.h" +#include "mmal_vc_api.h" +#include +#include +#include +#include +#ifdef __ANDROID__ +#include +#endif +#include "host_applications/linux/libs/debug_sym/debug_sym.h" +#include "mmal_vc_msgnames.h" +#include "mmal_vc_dbglog.h" +#include "vchiq.h" +#include "interface/vmcs_host/vc_imageconv_defs.h" + +/** Command-line diagnostics at the VC MMAL API level. + * + * @fixme: how does this work with multiple videocores? + */ + +struct cmd { + const char *name; + int (*pfn)(int argc, const char **argv); + const char *descr; + int flags; +}; + +#define CONNECT 1 + +static int do_commands(int argc, const char **argv); +static int do_version(int argc, const char **argv); +static int do_stats(int argc, const char **argv); +static int do_usage(int argc, const char **argv); +static int do_create(int argc, const char **argv); +static int do_eventlog(int argc, const char **argv); +static int do_components(int argc, const char **argv); +static int do_mmal_stats(int argc, const char **argv); +static int do_imageconv_stats(int argc, const char **argv); +static int do_compact(int argc, const char **argv); +static int do_autosusptest(int argc, const char **argv); +static int do_camerainfo(int argc, const char **argv); +static int do_host_log(int argc, const char **argv); +static int do_host_log_write(int argc, const char **argv); + +static struct cmd cmds[] = { + { "help", do_usage, "give this help", 0 }, + { "version", do_version, "report VC MMAL server version number", CONNECT }, + { "stats", do_stats, "report VC MMAL statistics", CONNECT }, + { "reset", do_stats, "reset VC MMAL statistics", CONNECT }, + { "commands", do_commands, "list available commands", CONNECT }, + { "create", do_create, "create a component", CONNECT }, + { "eventlog", do_eventlog, "display event log", 0 }, + { "components", do_components, "[update] list components", 0 }, + { "mmal-stats", do_mmal_stats, "list mmal core stats", CONNECT }, + { "ic-stats", do_imageconv_stats, "[reset] list imageconv stats", CONNECT }, + { "compact", do_compact, "trigger memory compaction", CONNECT }, + { "autosusp", do_autosusptest, "test out auto-suspend/resume", CONNECT }, + { "camerainfo", do_camerainfo, "get camera info", CONNECT }, + { "host_log", do_host_log, "dumps the MMAL VC log", CONNECT }, + { "host_log_write", do_host_log_write, "appends a message to the MMAL VC log of host messages", CONNECT }, + { NULL, NULL, NULL, 0}, +}; + +static void do_connect(void) +{ + /* this command needs a vchiq connection */ + MMAL_STATUS_T st; + if ((st = mmal_vc_init()) != MMAL_SUCCESS) + { + fprintf(stderr, "failed to initialize mmal vc library (%i:%s)\n", + st, mmal_status_to_string(st)); + exit(1); + } +} + +int main(int argc, const char **argv) +{ + int i; + + if (argc < 2) + { + do_usage(argc, argv); + exit(1); + } + + for (i = 0; cmds[i].name; i++) + { + if (strcasecmp(cmds[i].name, argv[1]) == 0) + { + int rc; + if (cmds[i].flags & CONNECT) + { + do_connect(); + } + rc = cmds[i].pfn(argc, argv); + + if (cmds[i].flags & CONNECT) + mmal_vc_deinit(); + return rc; + } + } + fprintf(stderr,"unknown command %s\n", argv[1]); + return -1; +} + +static int do_commands(int argc, const char **argv) +{ + int i = 0; + (void)argc; (void)argv; + while (cmds[i].name) + { + printf("%-20s %s\n", cmds[i].name, cmds[i].descr); + i++; + } + return 0; +} + +static int do_create(int argc, const char **argv) +{ + MMAL_COMPONENT_T *comp; + MMAL_STATUS_T st; + if (argc != 3) + { + printf("usage: mmal-vc-diag create \n"); + printf(" e.g. vc.camera\n"); + exit(1); + } + st = mmal_component_create(argv[2], &comp); + if (comp) + printf("Created component\n"); + else + printf("Failed to create %s: %d\n", argv[2], st); + + return 0; +} + +static int do_version(int argc, const char **argv) +{ + uint32_t maj = UINT_MAX, min = UINT_MAX, minimum; + MMAL_STATUS_T st = mmal_vc_get_version(&maj, &min, &minimum); + (void)argc; (void)argv; + if (st == MMAL_SUCCESS) + { + printf("version %d.%02d (min %d)\n", maj, min, minimum); + return 0; + } + else + { + fprintf(stderr, "error getting version (%i:%s)\n", st, mmal_status_to_string(st)); + return -1; + } +} + +#define STATS_FIELD(x) { #x, offsetof(MMAL_VC_STATS_T, x) } + +static struct { + const char *name; + unsigned offset; +} stats_fields [] = { + STATS_FIELD(buffers.rx), + STATS_FIELD(buffers.rx_zero_copy), + STATS_FIELD(buffers.rx_empty), + STATS_FIELD(buffers.rx_fails), + STATS_FIELD(buffers.tx), + STATS_FIELD(buffers.tx_zero_copy), + STATS_FIELD(buffers.tx_empty), + STATS_FIELD(buffers.tx_fails), + STATS_FIELD(buffers.tx_short_msg), + STATS_FIELD(buffers.rx_short_msg), + STATS_FIELD(service.created), + STATS_FIELD(service.pending_destroy), + STATS_FIELD(service.destroyed), + STATS_FIELD(service.failures), + STATS_FIELD(commands.bad_messages), + STATS_FIELD(commands.executed), + STATS_FIELD(commands.failed), + STATS_FIELD(commands.replies), + STATS_FIELD(commands.reply_fails), + STATS_FIELD(events.tx), + STATS_FIELD(events.tx_fails), + STATS_FIELD(worker.enqueued_messages), + STATS_FIELD(worker.dequeued_messages), + STATS_FIELD(worker.max_parameter_set_delay), + STATS_FIELD(worker.max_messages_waiting) +}; + +static int do_stats(int argc, const char **argv) +{ + MMAL_VC_STATS_T stats; + int reset_stats = strcasecmp(argv[1], "reset") == 0; + MMAL_STATUS_T st = mmal_vc_get_stats(&stats, reset_stats); + int ret; + (void)argc; (void)argv; + if (st != MMAL_SUCCESS) + { + fprintf(stderr, "error getting status (%i,%s)\n", st, mmal_status_to_string(st)); + ret = -1; + } + else + { + unsigned i; + uint32_t *ptr = (uint32_t*)&stats; + for (i=0; ievent_type) { + case MMAL_DBG_OPENED: snprintf(buf,buflen,"opened"); break; + case MMAL_DBG_CLOSED: snprintf(buf,buflen,"closed"); break; + default: break; + } +} + +static void on_bulk_ack(MMAL_DBG_ENTRY_T *entry, + char *buf, + size_t buflen) +{ + switch (entry->u.uint) + { + case VCHIQ_BULK_RECEIVE_ABORTED: snprintf(buf,buflen,"vchiq bulk rx abort"); break; + case VCHIQ_BULK_TRANSMIT_ABORTED: snprintf(buf,buflen,"vchiq bulk tx abort"); break; + case VCHIQ_BULK_TRANSMIT_DONE: snprintf(buf,buflen,"vchiq bulk tx done"); break; + case VCHIQ_BULK_RECEIVE_DONE: snprintf(buf,buflen,"vchiq bulk rx done"); break; + default: snprintf(buf,buflen,"vchiq unknown reason %d", entry->u.uint); break; + } +} + +static void on_msg(MMAL_DBG_ENTRY_T *entry, + char *buf, + size_t buflen) +{ + uint32_t id = entry->u.msg.header.msgid; + snprintf(buf,buflen,"msgid %d (%s)", id, mmal_msgname(id)); +} + +static void on_bulk(MMAL_DBG_ENTRY_T *entry, + char *buf, + size_t buflen) +{ + const char *name = entry->event_type == MMAL_DBG_BULK_TX ? "tx" : "rx"; + snprintf(buf,buflen,"bulk %s len %d", name, entry->u.bulk.len); +} + +static struct event_handler handlers[] = { + { MMAL_DBG_OPENED, on_openclose }, + { MMAL_DBG_CLOSED, on_openclose }, + { MMAL_DBG_BULK_ACK, on_bulk_ack }, + { MMAL_DBG_MSG, on_msg }, + { MMAL_DBG_BULK_TX, on_bulk }, + { MMAL_DBG_BULK_RX, on_bulk }, +}; +static int n_handlers = sizeof(handlers)/sizeof(handlers[0]); + +static void print_mmal_event_log(VC_MEM_ACCESS_HANDLE_T vc, MMAL_DBG_LOG_T *log) +{ + uint32_t i; + uint32_t n = vcos_min(log->num_entries, log->index); + uint32_t mask = log->num_entries-1; + uint32_t start = log->index < log->num_entries ? + 0 : log->index & mask; + uint32_t last_t = 0; + (void)vc; + + for (i=0; ientries[(start+i) & mask]; + char buf[256]; + int j; + uint32_t t = e->time; + printf("[%08u]: ", t-last_t); + last_t = t; + for (j=0; jevent_type) + { + handlers[j].handler(e, buf, sizeof(buf)); + printf("%s\n", buf); + break; + } + } + if (j == n_handlers ) + printf("Unknown event type %d\n", e->event_type); + } +} + +static int do_eventlog(int argc, const char **argv) +{ + VC_MEM_ACCESS_HANDLE_T vc; + VC_MEM_ADDR_T addr; /** The address of the pointer to the log */ + size_t size; + VC_MEM_ADDR_T logaddr; /** The address of the log itself */ + MMAL_DBG_LOG_T log; + + (void)argc; (void)argv; + int rc; + if ((rc = OpenVideoCoreMemory(&vc)) < 0) + { + fprintf(stderr,"Unable to open videocore memory: %d\n", rc); + return -1; + } + if (!LookupVideoCoreSymbol(vc, "mmal_dbg_log", &addr, &size)) + { + fprintf(stderr,"Could not get MMAL log address\n"); + goto fail; + } + if (!ReadVideoCoreUInt32(vc, &logaddr, addr)) + { + fprintf(stderr,"Could not read MMAL log pointer at address 0x%x\n", + addr); + goto fail; + } + if (!ReadVideoCoreMemory(vc, &log, logaddr, sizeof(log))) + { + fprintf(stderr,"Could not read MMAL log at address 0x%x\n", + logaddr); + goto fail; + } + if (log.magic != MMAL_MAGIC) + { + fprintf(stderr,"Bad magic 0x%08x in log at 0x%x\n", log.magic, logaddr); + goto fail; + } + if (log.size != sizeof(log)) + { + fprintf(stderr,"MMAL Log size mismatch (got %d, expected %d)\n", + log.size, sizeof(log)); + goto fail; + } + if (log.elemsize != sizeof(MMAL_DBG_ENTRY_T)) + { + fprintf(stderr,"MMAL log element size mismatch (got %d, expected %d)\n", + log.elemsize, sizeof(MMAL_DBG_ENTRY_T)); + goto fail; + } + + printf("reading MMAL log at 0x%x version %d magic %x\n", + logaddr, log.version, log.magic); + printf("%d events, %d entries each size %d\n", log.index, log.num_entries, + log.elemsize); + print_mmal_event_log(vc, &log); + + CloseVideoCoreMemory(vc); + return 0; +fail: + CloseVideoCoreMemory(vc); + return -1; + +} + +static int print_component_stats(const MMAL_VC_STATS_T *stats) +{ + size_t i; + if (stats->components.list_size > 64) + { + fprintf(stderr,"component array looks corrupt (list size %d\n", + stats->components.list_size); + goto fail; + } + printf("%d created, %d destroyed (%d destroying), %d create failures\n", + stats->components.created, + stats->components.destroyed, + stats->components.destroying, + stats->components.failed); + + for (i=0; i < stats->components.list_size; i++) + { + const struct MMAL_VC_COMP_STATS_T *cs = stats->components.component_list+i; + const char *state; + /* coverity[overrun-local] */ + if (cs->state != MMAL_STATS_COMP_IDLE) + { + switch (cs->state) + { + case MMAL_STATS_COMP_CREATED: state = "created"; break; + case MMAL_STATS_COMP_DESTROYING: state = "destroying"; break; + case MMAL_STATS_COMP_DESTROYED: state = "destroyed"; break; + default: state = "corrupt"; break; + } + printf("%-32s: %s: pid %d address %p pool mem alloc size %d\n", + cs->name, state, cs->pid, cs->comp, cs->pool_mem_alloc_size); + } + } + return 0; +fail: + return -1; +} + +static int do_components(int argc, const char **argv) +{ + VC_MEM_ACCESS_HANDLE_T vc; + VC_MEM_ADDR_T addr, statsaddr; + size_t size; + MMAL_VC_STATS_T stats; + int rc; + + + if (argc > 2 && (strcasecmp(argv[2], "update") == 0)) + { + MMAL_STATUS_T status; + do_connect(); + status = mmal_vc_get_stats(&stats, 0); + if (status != MMAL_SUCCESS) + { + fprintf(stderr, "Failed to update MMAL stats. error %s", + mmal_status_to_string(status)); + return -1; + } + } + else + { + if ((rc = OpenVideoCoreMemory(&vc)) < 0) + { + fprintf(stderr,"Unable to open videocore memory: %d\n", rc); + return -1; + } + if (!LookupVideoCoreSymbol(vc, "mmal_vc_stats", &addr, &size)) + { + fprintf(stderr,"Could not get MMAL stats address\n"); + goto fail; + } + if (!ReadVideoCoreUInt32(vc, &statsaddr, addr)) + { + fprintf(stderr,"Could not read MMAL stats pointer at address 0x%x\n", + addr); + goto fail; + } + if (!ReadVideoCoreMemory(vc, &stats, statsaddr, sizeof(stats))) + { + fprintf(stderr,"Could not read MMAL stats at address 0x%x\n", addr); + goto fail; + } + CloseVideoCoreMemory(vc); + } + rc = print_component_stats(&stats); + return rc; + +fail: + CloseVideoCoreMemory(vc); + return -1; +} + +static int do_mmal_stats(int argc, const char **argv) +{ + unsigned comp_index = 0; + MMAL_BOOL_T more_ports = MMAL_TRUE, reset = MMAL_FALSE; + unsigned port_index = 0; + MMAL_PORT_TYPE_T type = MMAL_PORT_TYPE_INPUT; + + if (argc >= 3) + reset = strcasecmp(argv[2], "reset") == 0; + + printf("component\t\tport\t\tbuffers\t\tfps\tdelay\n"); + while (more_ports) + { + int dir; + const char *dirnames[] = {"rx","tx"}; + MMAL_STATS_RESULT_T result; + + for (dir = MMAL_CORE_STATS_RX; dir <= MMAL_CORE_STATS_TX; dir++) + { + double framerate; + MMAL_CORE_STATISTICS_T stats; + char name[32]; + MMAL_STATUS_T status = mmal_vc_get_core_stats(&stats, + &result, + name, sizeof(name), + type, + comp_index, + port_index, + dir, + reset); + if (status != MMAL_SUCCESS) + { + fprintf(stderr, "could not get core stats: %s\n", + mmal_status_to_string(status)); + exit(1); + } + + if (result == MMAL_STATS_FOUND) + { + if (stats.first_buffer_time == stats.last_buffer_time) + framerate = 0; + else + framerate = 1.0e6*(1+stats.buffer_count)/(stats.last_buffer_time-stats.first_buffer_time); + + printf("%-20s\t%d [%s]%2s\t%-10d\t%4.1f\t%d\n", + name, port_index, + type == MMAL_PORT_TYPE_INPUT ? "in " : "out", + dirnames[dir], + stats.buffer_count, framerate, stats.max_delay); + + } + } + + switch (result) + { + case MMAL_STATS_FOUND: + port_index++; + break; + + case MMAL_STATS_COMPONENT_NOT_FOUND: + more_ports = MMAL_FALSE; + break; + + case MMAL_STATS_PORT_NOT_FOUND: + port_index = 0; + if (type == MMAL_PORT_TYPE_INPUT) + { + type = MMAL_PORT_TYPE_OUTPUT; + } + else + { + type = MMAL_PORT_TYPE_INPUT; + comp_index++; + } + break; + default: + fprintf(stderr, "bad result from query: %d\n", result); + vcos_assert(0); + exit(1); + } + } + return 0; +} + +static int do_imageconv_stats(int argc, const char **argv) +{ + VC_MEM_ACCESS_HANDLE_T vc; + VC_MEM_ADDR_T addr, statsaddr; + size_t size; + IMAGECONV_STATS_T stats; + long convert_time; + double frame_rate; + int rc; + int reset_stats = 0; + + if (argc > 2) + reset_stats = strcasecmp(argv[2], "reset") == 0; + + if ((rc = OpenVideoCoreMemory(&vc)) < 0) + { + fprintf(stderr,"Unable to open videocore memory: %d\n", rc); + return -1; + } + if (!LookupVideoCoreSymbol(vc, "imageconv_stats", &addr, &size)) + { + fprintf(stderr,"Could not get imageconv stats address\n"); + goto fail; + } + if (!ReadVideoCoreUInt32(vc, &statsaddr, addr)) + { + fprintf(stderr, "Could not read imageconv stats address\n"); + goto fail; + } + + if (reset_stats) + { + memset(&stats, 0, sizeof(stats)); + stats.magic = IMAGECONV_STATS_MAGIC; + if (!WriteVideoCoreMemory(vc, &stats, statsaddr, sizeof(stats))) + { + fprintf(stderr, "Could not write stats at 0x%x\n", statsaddr); + goto fail; + } + } + + if (!ReadVideoCoreMemory(vc, &stats, statsaddr, sizeof(stats))) + { + fprintf(stderr, "Could not read stats at 0x%x\n", statsaddr); + goto fail; + } + + if (stats.magic != IMAGECONV_STATS_MAGIC) + { + fprintf(stderr, "Bad magic 0x%x\n", stats.magic); + goto fail; + } + + if (stats.conversions) + convert_time = stats.time_spent / stats.conversions; + else + convert_time = 0; + + if (stats.conversions) + frame_rate = 1000000.0 * stats.conversions / + (stats.last_image_ts - stats.first_image_ts); + else + frame_rate = 0; + + printf("%-25s:\t%d\n", "conversions", stats.conversions); + printf("%-25s:\t%d\n", "size requests", stats.size_requests); + printf("%-25s:\t%d\n", "max vrf delay", stats.max_vrf_delay); + printf("%-25s:\t%d\n", "vrf wait time", stats.vrf_wait_time); + printf("%-25s:\t%d\n", "duplicate conversions", stats.duplicate_conversions); + printf("%-25s:\t%d\n", "failures", stats.failures); + printf("%-25s:\t%ld\n", "convert time / image (us)", convert_time); + printf("%-25s:\t%.1f\n", "client frame_rate", frame_rate); + printf("%-25s:\t%d us\n", "max delay to consume", stats.max_delay); + + CloseVideoCoreMemory(vc); + return 0; +fail: + CloseVideoCoreMemory(vc); + return -1; + +} + +static int do_compact(int argc, const char **argv) +{ + uint32_t duration; + + if (argc > 2) + { + if (strcmp(argv[2], "a") == 0) + { + mmal_vc_compact(MMAL_VC_COMPACT_AGGRESSIVE, &duration); + printf("Triggered aggressive compaction on VC - duration %u us.\n", duration); + } + else if (strcmp(argv[2], "d") == 0) + { + mmal_vc_compact(MMAL_VC_COMPACT_DISCARD, &duration); + printf("Triggered discard compaction on VC - duration %u us.\n", duration); + } + else if (strcmp(argv[2], "n") == 0) + { + mmal_vc_compact(MMAL_VC_COMPACT_NORMAL, &duration); + printf("Triggered normal compaction on VC - duration %u us.\n", duration); + } + else + { + printf("Invalid memory compaction option %s\n.", argv[2]); + exit(1); + } + } + else + { + printf("Invalid memory compaction arguments. Need to specify 'a', 'n' or 't'.\n"); + exit(1); + } + return 0; +} + +/* Autosuspend test. Create a component, but kill process + * shortly after startup. + */ + +static int autosusp_signal; + +static void autosusp_timeout_handler(int cause, siginfo_t *how, void *ucontext) +{ + (void)how; (void)ucontext; (void)cause; + printf("Sending signal %d\n", autosusp_signal); + kill(getpid(), autosusp_signal); +} + +static int do_autosusptest(int argc, const char **argv) +{ + long timeout; + struct timeval interval; + MMAL_STATUS_T status; + + if (argc != 4) + { + printf("usage: %s autosusp \n", + argv[0]); + printf(" e.g. 650 9\n"); + exit(1); + } + timeout = 1000 * atoi(argv[2]); + autosusp_signal = atoi(argv[3]); + + if ((status=mmal_vc_use()) != MMAL_SUCCESS) + { + fprintf(stderr,"mmal_vc_use failed: %d\n", status); + exit(1); + } + + /* install a signal handler for the alarm */ + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction = autosusp_timeout_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGALRM, &sa, 0)) + { + perror("sigaction"); + exit(1); + } + + /* when to expire */ + interval.tv_sec = timeout / 1000000; + interval.tv_usec = timeout % 1000000; + + struct itimerval alarm_spec = { + .it_interval = {0,0}, + .it_value = interval + }; + + int rc = setitimer(ITIMER_REAL, &alarm_spec, NULL); + if (rc < 0) + { + perror("setitimer failed"); + exit(1); + } + + usleep(timeout + 1000000); + printf("%s: not killed by timer\n", argv[0]); + mmal_vc_release(); + + return 0; +} + +static int do_camerainfo(int argc, const char **argv) +{ + MMAL_COMPONENT_T *comp; + MMAL_STATUS_T st; + MMAL_PARAMETER_CAMERA_INFO_T mmal_camera_config_info; + unsigned i; + (void)argc; + (void)argv; + + st = mmal_component_create("vc.camera_info", &comp); + if (st != MMAL_SUCCESS) + { + fprintf(stderr, "Failed to create camera_info: %d\n", st); + exit(1); + } + + mmal_camera_config_info.hdr.id = MMAL_PARAMETER_CAMERA_INFO; + mmal_camera_config_info.hdr.size = sizeof(mmal_camera_config_info); + st = mmal_port_parameter_get(comp->control, &mmal_camera_config_info.hdr); + if (st != MMAL_SUCCESS) + { + fprintf(stderr, "%s: get param failed:%d", __FUNCTION__, st); + exit(1); + } + + printf("cameras : %d\n", mmal_camera_config_info.num_cameras); + printf("flashes : %d\n", mmal_camera_config_info.num_flashes); + + for (i=0; i 2) + mmal_vc_host_log(argv[2]); + return 0; +} diff --git a/interface/mmal/vc/mmal_vc_msgnames.c b/interface/mmal/vc/mmal_vc_msgnames.c new file mode 100755 index 0000000..052b392 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_msgnames.c @@ -0,0 +1,78 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mmal_vc_msgnames.h" +#include "mmal_vc_msgs.h" + +/** Convert a message id to a name. + */ +const char *mmal_msgname(uint32_t id) +{ +#define MSGNAME(x) { MMAL_WORKER_##x, #x } + static struct { + uint32_t id; + const char *name; + } msgnames[] = { + MSGNAME(QUIT), + MSGNAME(SERVICE_CLOSED), + MSGNAME(GET_VERSION), + MSGNAME(COMPONENT_CREATE), + MSGNAME(COMPONENT_DESTROY), + MSGNAME(COMPONENT_ENABLE), + MSGNAME(COMPONENT_DISABLE), + MSGNAME(PORT_INFO_GET), + MSGNAME(PORT_INFO_SET), + MSGNAME(PORT_ACTION), + MSGNAME(BUFFER_FROM_HOST), + MSGNAME(BUFFER_TO_HOST), + MSGNAME(GET_STATS), + MSGNAME(PORT_PARAMETER_SET), + MSGNAME(PORT_PARAMETER_GET), + MSGNAME(EVENT_TO_HOST), + MSGNAME(GET_CORE_STATS_FOR_PORT), + MSGNAME(OPAQUE_ALLOCATOR), + MSGNAME(CONSUME_MEM), + MSGNAME(LMK), + MSGNAME(OPAQUE_ALLOCATOR_DESC), + MSGNAME(DRM_GET_LHS32), + MSGNAME(DRM_GET_TIME), + MSGNAME(BUFFER_FROM_HOST_ZEROLEN), + MSGNAME(PORT_FLUSH), + MSGNAME(HOST_LOG), + MSGNAME(COMPACT), + { 0, NULL }, + }; + vcos_static_assert(sizeof(msgnames)/sizeof(msgnames[0]) == MMAL_WORKER_MSG_LAST); + int i = 0; + while (msgnames[i].name) + { + if (msgnames[i].id == id) + return msgnames[i].name; + i++; + } + return "unknown-message"; +} diff --git a/interface/mmal/vc/mmal_vc_msgnames.h b/interface/mmal/vc/mmal_vc_msgnames.h new file mode 100755 index 0000000..12a97c7 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_msgnames.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_VC_MSGNAMES_H +#define MMAL_VC_MSGNAMES_H + +#include "interface/vcos/vcos.h" + +/** Convert a message id to a name. + */ +const char *mmal_msgname(uint32_t id); + +#endif diff --git a/interface/mmal/vc/mmal_vc_msgs.h b/interface/mmal/vc/mmal_vc_msgs.h new file mode 100755 index 0000000..343922b --- /dev/null +++ b/interface/mmal/vc/mmal_vc_msgs.h @@ -0,0 +1,535 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_VC_MSGS_H +#define MMAL_VC_MSGS_H + +/** @file mmal_vc_msgs.h + * + * Private message definitions, defining the message API between + * the host and VideoCore. + */ +#include "interface/vcos/vcos.h" +#include "interface/mmal/mmal.h" +#include "mmal_vc_api.h" + +#define MMAL_CONTROL_FOURCC() VCHIQ_MAKE_FOURCC('m','m','a','l') + +/* Major version indicates binary backwards compatibility */ +#define WORKER_VER_MAJOR 16 +#define WORKER_VER_MINIMUM 10 +/* Minor version is not used normally. + */ +#define WORKER_VER_MINOR 1 +#ifndef WORKER_VER_MINIMUM +#endif + +#define VIDEOCORE_PREFIX "vc" + +#define MMAL_MAX_PORTS 8 /**< Max ports per component */ + +#define MMAL_WORKER_MAX_MSG_LEN 512 +#define MMAL_VC_CORE_STATS_NAME_MAX 32 /**< Length of the name in the core stats message */ + +/** A MMAL_CONTROL_SERVICE_T gets space for a single message. This + * is the space allocated for these messages. + */ +#define MMAL_WORKER_MSG_LEN 28 + +/** Maximum size of the format extradata. + * FIXME: should probably be made bigger and maybe be passed separately from the info. + */ +#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 + +/** Size of space reserved in a buffer message for short messages. + */ +#define MMAL_VC_SHORT_DATA 128 + +/** Message ids sent to worker thread. + */ + +/* Please update the array in mmal_vc_msgnames.c if this is updated. + */ +typedef enum { + MMAL_WORKER_QUIT = 1, + MMAL_WORKER_SERVICE_CLOSED, + MMAL_WORKER_GET_VERSION, + MMAL_WORKER_COMPONENT_CREATE, + MMAL_WORKER_COMPONENT_DESTROY, + MMAL_WORKER_COMPONENT_ENABLE, + MMAL_WORKER_COMPONENT_DISABLE, + MMAL_WORKER_PORT_INFO_GET, + MMAL_WORKER_PORT_INFO_SET, + MMAL_WORKER_PORT_ACTION, + MMAL_WORKER_BUFFER_FROM_HOST, + MMAL_WORKER_BUFFER_TO_HOST, + MMAL_WORKER_GET_STATS, + MMAL_WORKER_PORT_PARAMETER_SET, + MMAL_WORKER_PORT_PARAMETER_GET, + MMAL_WORKER_EVENT_TO_HOST, + MMAL_WORKER_GET_CORE_STATS_FOR_PORT, + MMAL_WORKER_OPAQUE_ALLOCATOR, + /* VC debug mode only - due to security, denial of service implications */ + MMAL_WORKER_CONSUME_MEM, + MMAL_WORKER_LMK, + MMAL_WORKER_OPAQUE_ALLOCATOR_DESC, + MMAL_WORKER_DRM_GET_LHS32, + MMAL_WORKER_DRM_GET_TIME, + MMAL_WORKER_BUFFER_FROM_HOST_ZEROLEN, + MMAL_WORKER_PORT_FLUSH, + MMAL_WORKER_HOST_LOG, + MMAL_WORKER_COMPACT, + MMAL_WORKER_MSG_LAST +} MMAL_WORKER_CMD_T; + +/** Every message has one of these at the start. + */ +typedef struct +{ + uint32_t magic; + uint32_t msgid; + struct MMAL_CONTROL_SERVICE_T *control_service; /** Handle to the control service */ + + union { + struct MMAL_WAITER_T *waiter; /** User-land wait structure, passed back */ + } u; + + MMAL_STATUS_T status; /** Result code, passed back */ + /* Make sure this structure is 64 bit aligned */ + uint32_t dummy; +} mmal_worker_msg_header; + +/* Make sure mmal_worker_msg_header will preserve 64 bits alignment */ +vcos_static_assert(!(sizeof(mmal_worker_msg_header) & 0x7)); + +/* Message structures sent to worker thread. + */ + +/** Tell the worker a service has closed. It should start to delete + * the associated components. + */ +typedef struct +{ + mmal_worker_msg_header header; +} mmal_worker_service_closed; +vcos_static_assert(sizeof(mmal_worker_service_closed) <= MMAL_WORKER_MSG_LEN); + +/** Send from VC to host to report our version */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t flags; + uint32_t major; + uint32_t minor; + uint32_t minimum; +} mmal_worker_version; + +/** Request component creation */ +typedef struct +{ + mmal_worker_msg_header header; + void *client_component; /** Client component */ + char name[128]; + uint32_t pid; /**< For debug */ +} mmal_worker_component_create; + +/** Reply to component-creation message. Reports back + * the number of ports. + */ +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; + uint32_t component_handle; /** Handle on VideoCore for component */ + uint32_t input_num; /**< Number of input ports */ + uint32_t output_num; /**< Number of output ports */ + uint32_t clock_num; /**< Number of clock ports */ +} mmal_worker_component_create_reply; +vcos_static_assert(sizeof(mmal_worker_component_create_reply) <= MMAL_WORKER_MAX_MSG_LEN); + +/** Destroys a component + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< which component */ +} mmal_worker_component_destroy; + +/** Enables a component + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< which component */ +} mmal_worker_component_enable; + +/** Disable a component + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< Which component */ +} mmal_worker_component_disable; + +/** Component port info. Used to get port info. + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< Which component */ + MMAL_PORT_TYPE_T port_type; /**< Type of port */ + uint32_t index; /**< Which port of given type to get */ +} mmal_worker_port_info_get; +vcos_static_assert(sizeof(mmal_worker_port_info_get) <= MMAL_WORKER_MAX_MSG_LEN); + +/** Component port info. Used to set port info. + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< Which component */ + MMAL_PORT_TYPE_T port_type; /**< Type of port */ + uint32_t index; /**< Which port of given type to get */ + MMAL_PORT_T port; + MMAL_ES_FORMAT_T format; + MMAL_ES_SPECIFIC_FORMAT_T es; + uint8_t extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; +} mmal_worker_port_info_set; +vcos_static_assert(sizeof(mmal_worker_port_info_set) <= MMAL_WORKER_MAX_MSG_LEN); + +/** Report port info back in response to a get / set. */ +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; /**< Result of query */ + uint32_t component_handle; /**< Which component */ + MMAL_PORT_TYPE_T port_type; /**< Type of port */ + uint32_t index; /**< Which port of given type to get */ + int32_t found; /**< Did we find anything? */ + uint32_t port_handle; /**< Handle to use for this port */ + MMAL_PORT_T port; + MMAL_ES_FORMAT_T format; + MMAL_ES_SPECIFIC_FORMAT_T es; + uint8_t extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; +} mmal_worker_port_info; +vcos_static_assert(sizeof(mmal_worker_port_info) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; +} mmal_worker_reply; + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; + uint8_t secret[32]; +} mmal_worker_drm_get_lhs32_reply; +vcos_static_assert(sizeof(mmal_worker_drm_get_lhs32_reply) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; + uint32_t time; +} mmal_worker_drm_get_time_reply; +vcos_static_assert(sizeof(mmal_worker_drm_get_time_reply) <= MMAL_WORKER_MAX_MSG_LEN); + +/** List of actions for a port */ +enum MMAL_WORKER_PORT_ACTIONS +{ + MMAL_WORKER_PORT_ACTION_UNKNOWN = 0, /**< Unknown action */ + MMAL_WORKER_PORT_ACTION_ENABLE, /**< Enable a port */ + MMAL_WORKER_PORT_ACTION_DISABLE, /**< Disable a port */ + MMAL_WORKER_PORT_ACTION_FLUSH, /**< Flush a port */ + MMAL_WORKER_PORT_ACTION_CONNECT, /**< Connect 2 ports together */ + MMAL_WORKER_PORT_ACTION_DISCONNECT, /**< Disconnect 2 ports connected together */ + MMAL_WORKER_PORT_ACTION_SET_REQUIREMENTS, /**< Set buffer requirements */ + MMAL_WORKER_PORT_ACTION_MAX = 0x7fffffff /**< Make the enum 32bits */ +}; + +/** Trigger an action on a port. + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; + uint32_t port_handle; + enum MMAL_WORKER_PORT_ACTIONS action; + + /** Action parameter */ + union { + struct { + MMAL_PORT_T port; + } enable; + struct { + uint32_t component_handle; + uint32_t port_handle; + } connect; + } param; + +} mmal_worker_port_action; +vcos_static_assert(sizeof(mmal_worker_port_action) <= MMAL_WORKER_MAX_MSG_LEN); + +#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 + +#define MMAL_WORKER_PORT_PARAMETER_SET_MAX \ + (MMAL_WORKER_PORT_PARAMETER_SPACE*sizeof(uint32_t)+sizeof(MMAL_PARAMETER_HEADER_T)) + +#define MMAL_WORKER_PORT_PARAMETER_GET_MAX MMAL_WORKER_PORT_PARAMETER_SET_MAX + +/** Component port parameter set. Doesn't include space for the parameter data. + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< Which component */ + uint32_t port_handle; /**< Which port */ + MMAL_PARAMETER_HEADER_T param; /**< Parameter ID and size */ + uint32_t space[MMAL_WORKER_PORT_PARAMETER_SPACE]; +} mmal_worker_port_param_set; +vcos_static_assert(sizeof(mmal_worker_port_param_set) <= MMAL_WORKER_MAX_MSG_LEN); + +/** Component port parameter get. + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< Which component */ + uint32_t port_handle; /**< Which port */ + MMAL_PARAMETER_HEADER_T param; /**< Parameter ID and size */ + uint32_t space[MMAL_WORKER_PORT_PARAMETER_SPACE]; +} mmal_worker_port_param_get; +vcos_static_assert(sizeof(mmal_worker_port_param_get) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_handle; /**< Which component */ + uint32_t port_handle; /**< Which port */ + MMAL_PARAMETER_HEADER_T param; /**< Parameter ID and size */ +} mmal_worker_port_param_get_old; + +/** Component port parameter get reply. Doesn't include space for the parameter data. + */ +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; /**< Status of mmal_port_parameter_get call */ + MMAL_PARAMETER_HEADER_T param; /**< Parameter ID and size */ + uint32_t space[MMAL_WORKER_PORT_PARAMETER_SPACE]; +} mmal_worker_port_param_get_reply; +vcos_static_assert(sizeof(mmal_worker_port_param_get_reply) <= MMAL_WORKER_MAX_MSG_LEN); + +/** Buffer header driver area structure. In the private area + * of a buffer header there is a driver area where we can + * put values. This structure defines the layout of that. + */ +struct MMAL_DRIVER_BUFFER_T +{ + uint32_t magic; + uint32_t component_handle; /**< The component this buffer is from */ + uint32_t port_handle; /**< Index into array of ports for this component */ + + /** Client side uses this to get back to its context structure. */ + struct MMAL_VC_CLIENT_BUFFER_CONTEXT_T *client_context; +}; + +/** Receive a buffer from the host. + * + * @sa mmal_port_send_buffer() + */ + +typedef struct mmal_worker_buffer_from_host +{ + mmal_worker_msg_header header; + + /** Our control data, copied from the buffer header "driver area" + * @sa mmal_buffer_header_driver_data(). + */ + struct MMAL_DRIVER_BUFFER_T drvbuf; + + /** Referenced buffer control data. + * This is set if the buffer is referencing another + * buffer as is the case with passthrough ports where + * buffers on the output port reference buffers on the + * input port. */ + struct MMAL_DRIVER_BUFFER_T drvbuf_ref; + + /** the buffer header itself */ + MMAL_BUFFER_HEADER_T buffer_header; + MMAL_BUFFER_HEADER_TYPE_SPECIFIC_T buffer_header_type_specific; + + MMAL_BOOL_T is_zero_copy; + MMAL_BOOL_T has_reference; + + /** If the data is short enough, then send it in the control message rather + * than using a separate VCHIQ bulk transfer. + */ + uint32_t payload_in_message; + uint8_t short_data[MMAL_VC_SHORT_DATA]; + +} mmal_worker_buffer_from_host; +vcos_static_assert(sizeof(mmal_worker_buffer_from_host) <= MMAL_WORKER_MAX_MSG_LEN); + +/** Maximum number of event data bytes that can be passed in the message. + * More than this and the data is passed in a bulk message. + */ +#define MMAL_WORKER_EVENT_SPACE 256 + +/** Send an event buffer from the host. + * + * @sa mmal_port_send_event() + */ + +typedef struct mmal_worker_event_to_host +{ + mmal_worker_msg_header header; + + struct MMAL_COMPONENT_T *client_component; + uint32_t port_type; + uint32_t port_num; + + uint32_t cmd; + uint32_t length; + uint8_t data[MMAL_WORKER_EVENT_SPACE]; + MMAL_BUFFER_HEADER_T *delayed_buffer; /* Only used to remember buffer for bulk rx */ +} mmal_worker_event_to_host; +vcos_static_assert(sizeof(mmal_worker_event_to_host) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_VC_STATS_T stats; + uint32_t reset; +} mmal_worker_stats; +vcos_static_assert(sizeof(mmal_worker_stats) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef enum { + MMAL_WORKER_OPAQUE_MEM_ALLOC, + MMAL_WORKER_OPAQUE_MEM_RELEASE, + MMAL_WORKER_OPAQUE_MEM_ACQUIRE, + MMAL_WORKER_OPAQUE_MEM_MAX = 0x7fffffff, +} MMAL_WORKER_OPAQUE_MEM_OP; + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_WORKER_OPAQUE_MEM_OP op; + uint32_t handle; + MMAL_STATUS_T status; + char description[32]; +} mmal_worker_opaque_allocator; + +/* + * Per-port core statistics + */ +typedef struct +{ + mmal_worker_msg_header header; + uint32_t component_index; + uint32_t port_index; + MMAL_PORT_TYPE_T type; + MMAL_CORE_STATS_DIR dir; + MMAL_BOOL_T reset; +} mmal_worker_get_core_stats_for_port; + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; + MMAL_STATS_RESULT_T result; + MMAL_CORE_STATISTICS_T stats; + char component_name[MMAL_VC_CORE_STATS_NAME_MAX]; +} mmal_worker_get_core_stats_for_port_reply; + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; + /* The amount of memory to reserve */ + uint32_t size; + /* Handle to newly allocated memory or MEM_HANDLE_INVALD on failure */ + uint32_t handle; +} mmal_worker_consume_mem; +vcos_static_assert(sizeof(mmal_worker_consume_mem) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + MMAL_STATUS_T status; + uint32_t mode; + uint32_t duration; +} mmal_worker_compact; +vcos_static_assert(sizeof(mmal_worker_compact) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + /* Message text to add to the circular buffer */ + char msg[MMAL_WORKER_MAX_MSG_LEN - sizeof(mmal_worker_msg_header)]; +} mmal_worker_host_log; +vcos_static_assert(sizeof(mmal_worker_host_log) <= MMAL_WORKER_MAX_MSG_LEN); + +typedef struct +{ + mmal_worker_msg_header header; + /* The memory allocation size to pass to lmk, as if in a response to an + * allocation for this amount of memory. */ + uint32_t alloc_size; +} mmal_worker_lmk; +vcos_static_assert(sizeof(mmal_worker_lmk) <= MMAL_WORKER_MAX_MSG_LEN); + +static inline void mmal_vc_buffer_header_to_msg(mmal_worker_buffer_from_host *msg, + MMAL_BUFFER_HEADER_T *header) +{ + msg->buffer_header.cmd = header->cmd; + msg->buffer_header.offset = header->offset; + msg->buffer_header.length = header->length; + msg->buffer_header.flags = header->flags; + msg->buffer_header.pts = header->pts; + msg->buffer_header.dts = header->dts; + msg->buffer_header.alloc_size = header->alloc_size; + msg->buffer_header.data = header->data; + msg->buffer_header_type_specific = *header->type; +} + +static inline void mmal_vc_msg_to_buffer_header(MMAL_BUFFER_HEADER_T *header, + mmal_worker_buffer_from_host *msg) +{ + header->cmd = msg->buffer_header.cmd; + header->offset = msg->buffer_header.offset; + header->length = msg->buffer_header.length; + header->flags = msg->buffer_header.flags; + header->pts = msg->buffer_header.pts; + header->dts = msg->buffer_header.dts; + *header->type = msg->buffer_header_type_specific; +} + +#endif + diff --git a/interface/mmal/vc/mmal_vc_opaque_alloc.c b/interface/mmal/vc/mmal_vc_opaque_alloc.c new file mode 100755 index 0000000..b5b5ba9 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_opaque_alloc.c @@ -0,0 +1,87 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/mmal/vc/mmal_vc_opaque_alloc.h" +#include "mmal_vc_msgs.h" +#include "mmal_vc_client_priv.h" + +MMAL_OPAQUE_IMAGE_HANDLE_T mmal_vc_opaque_alloc_desc(const char *description) +{ + MMAL_STATUS_T ret; + MMAL_OPAQUE_IMAGE_HANDLE_T h = 0; + mmal_worker_opaque_allocator msg; + size_t len = sizeof(msg); + msg.op = MMAL_WORKER_OPAQUE_MEM_ALLOC; + vcos_safe_strcpy(msg.description, description, sizeof(msg.description), 0); + ret = mmal_vc_sendwait_message(mmal_vc_get_client(), + &msg.header, sizeof(msg), + MMAL_WORKER_OPAQUE_ALLOCATOR_DESC, + &msg, &len, MMAL_FALSE); + if (ret == MMAL_SUCCESS) + { + h = msg.handle; + } + return h; +} + +MMAL_OPAQUE_IMAGE_HANDLE_T mmal_vc_opaque_alloc(void) +{ + return mmal_vc_opaque_alloc_desc("?"); +} + +MMAL_STATUS_T mmal_vc_opaque_acquire(unsigned int handle) +{ + MMAL_STATUS_T ret; + mmal_worker_opaque_allocator msg; + size_t len = sizeof(msg); + msg.handle = handle; + msg.op = MMAL_WORKER_OPAQUE_MEM_ACQUIRE; + ret = mmal_vc_sendwait_message(mmal_vc_get_client(), + &msg.header, sizeof(msg), + MMAL_WORKER_OPAQUE_ALLOCATOR, + &msg, &len, MMAL_FALSE); + if (ret == MMAL_SUCCESS) + ret = msg.status; + return ret; +} + +MMAL_STATUS_T mmal_vc_opaque_release(unsigned int handle) +{ + MMAL_STATUS_T ret; + mmal_worker_opaque_allocator msg; + size_t len = sizeof(msg); + msg.handle = handle; + msg.op = MMAL_WORKER_OPAQUE_MEM_RELEASE; + ret = mmal_vc_sendwait_message(mmal_vc_get_client(), + &msg.header, sizeof(msg), + MMAL_WORKER_OPAQUE_ALLOCATOR, + &msg, &len, MMAL_FALSE); + if (ret == MMAL_SUCCESS) + ret = msg.status; + return ret; +} + diff --git a/interface/mmal/vc/mmal_vc_opaque_alloc.h b/interface/mmal/vc/mmal_vc_opaque_alloc.h new file mode 100755 index 0000000..60a5e56 --- /dev/null +++ b/interface/mmal/vc/mmal_vc_opaque_alloc.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MMAL_VC_OPAQUE_ALLOC_H +#define MMAL_VC_OPAQUE_ALLOC_H + + +#include +#include "interface/mmal/mmal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t MMAL_OPAQUE_IMAGE_HANDLE_T; + +/** Allocate an opaque image on VideoCore. + * + * @return allocated handle, or zero if allocation failed. + */ +MMAL_OPAQUE_IMAGE_HANDLE_T mmal_vc_opaque_alloc(void); + +/** Allocate an opaque image on VideoCore, providing a description. + * @return allocated handle, or zero if allocation failed. + */ +MMAL_OPAQUE_IMAGE_HANDLE_T mmal_vc_opaque_alloc_desc(const char *description); + +/** Release an opaque image. + * + * @param handle handle allocated earlier + * @return MMAL_SUCCESS or error code if handle not found + */ +MMAL_STATUS_T mmal_vc_opaque_release(MMAL_OPAQUE_IMAGE_HANDLE_T h); + +/** Acquire an additional reference to an opaque image. + * + * @param handle handle allocated earlier + * @return MMAL_SUCCESS or error code if handle not found + */ +MMAL_STATUS_T mmal_vc_opaque_acquire(MMAL_OPAQUE_IMAGE_HANDLE_T h); + + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/interface/mmal/vc/mmal_vc_shm.c b/interface/mmal/vc/mmal_vc_shm.c new file mode 100755 index 0000000..e22a53a --- /dev/null +++ b/interface/mmal/vc/mmal_vc_shm.c @@ -0,0 +1,241 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/vc/mmal_vc_shm.h" + +#ifdef ENABLE_MMAL_VCSM +# include "user-vcsm.h" +#endif /* ENABLE_MMAL_VCSM */ + +#define MMAL_VC_PAYLOAD_ELEM_MAX 512 + +typedef struct MMAL_VC_PAYLOAD_ELEM_T +{ + struct MMAL_VC_PAYLOAD_ELEM_T *next; + void *handle; + void *vc_handle; + uint8_t *mem; + MMAL_BOOL_T in_use; +} MMAL_VC_PAYLOAD_ELEM_T; + +typedef struct MMAL_VC_PAYLOAD_LIST_T +{ + MMAL_VC_PAYLOAD_ELEM_T list[MMAL_VC_PAYLOAD_ELEM_MAX]; + VCOS_MUTEX_T lock; +} MMAL_VC_PAYLOAD_LIST_T; + +static MMAL_VC_PAYLOAD_LIST_T mmal_vc_payload_list; + +static void mmal_vc_payload_list_init() +{ + vcos_mutex_create(&mmal_vc_payload_list.lock, "mmal_vc_payload_list"); +} + +static MMAL_VC_PAYLOAD_ELEM_T *mmal_vc_payload_list_get() +{ + MMAL_VC_PAYLOAD_ELEM_T *elem = 0; + unsigned int i; + + vcos_mutex_lock(&mmal_vc_payload_list.lock); + for (i = 0; i < MMAL_VC_PAYLOAD_ELEM_MAX; i++) + { + if (mmal_vc_payload_list.list[i].in_use) + continue; + elem = &mmal_vc_payload_list.list[i]; + elem->in_use = 1; + break; + } + vcos_mutex_unlock(&mmal_vc_payload_list.lock); + + return elem; +} + +static void mmal_vc_payload_list_release(MMAL_VC_PAYLOAD_ELEM_T *elem) +{ + vcos_mutex_lock(&mmal_vc_payload_list.lock); + elem->handle = elem->vc_handle = 0; + elem->mem = 0; + elem->in_use = 0; + vcos_mutex_unlock(&mmal_vc_payload_list.lock); +} + +static MMAL_VC_PAYLOAD_ELEM_T *mmal_vc_payload_list_find_mem(uint8_t *mem) +{ + MMAL_VC_PAYLOAD_ELEM_T *elem = 0; + unsigned int i; + + vcos_mutex_lock(&mmal_vc_payload_list.lock); + for (i = 0; i < MMAL_VC_PAYLOAD_ELEM_MAX; i++) + { + if (!mmal_vc_payload_list.list[i].in_use) + continue; + if (mmal_vc_payload_list.list[i].mem != mem) + continue; + elem = &mmal_vc_payload_list.list[i]; + break; + } + vcos_mutex_unlock(&mmal_vc_payload_list.lock); + + return elem; +} + +static MMAL_VC_PAYLOAD_ELEM_T *mmal_vc_payload_list_find_handle(uint8_t *mem) +{ + MMAL_VC_PAYLOAD_ELEM_T *elem = 0; + unsigned int i; + + vcos_mutex_lock(&mmal_vc_payload_list.lock); + for (i = 0; i < MMAL_VC_PAYLOAD_ELEM_MAX; i++) + { + if (!mmal_vc_payload_list.list[i].in_use) + continue; + if (mmal_vc_payload_list.list[i].vc_handle != (void *)mem) + continue; + elem = &mmal_vc_payload_list.list[i]; + break; + } + vcos_mutex_unlock(&mmal_vc_payload_list.lock); + + return elem; +} + +/** Initialise the shared memory system */ +MMAL_STATUS_T mmal_vc_shm_init(void) +{ +#ifdef ENABLE_MMAL_VCSM + if (vcsm_init() != 0) + { + LOG_ERROR("could not initialize vc shared memory service"); + return MMAL_EIO; + } +#endif /* ENABLE_MMAL_VCSM */ + + mmal_vc_payload_list_init(); + return MMAL_SUCCESS; +} + +/** Allocate a shared memory buffer */ +uint8_t *mmal_vc_shm_alloc(uint32_t size) +{ + uint8_t *mem = NULL; + + MMAL_VC_PAYLOAD_ELEM_T *payload_elem = mmal_vc_payload_list_get(); + if (!payload_elem) + { + LOG_ERROR("could not get a free slot in the payload list"); + return NULL; + } + +#ifdef ENABLE_MMAL_VCSM + unsigned int vcsm_handle = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST, "mmal_vc_port buffer"); + unsigned int vc_handle = vcsm_vc_hdl_from_hdl(vcsm_handle); + mem = (uint8_t *)vcsm_lock( vcsm_handle ); + if (!mem || !vc_handle) + { + LOG_ERROR("could not allocate %i bytes of shared memory (handle %x)", + (int)size, vcsm_handle); + if (mem) + vcsm_unlock_hdl(vcsm_handle); + if (vcsm_handle) + vcsm_free(vcsm_handle); + mmal_vc_payload_list_release(payload_elem); + return NULL; + } + + /* The memory area is automatically mem-locked by vcsm's fault + * handler when it is next used. So leave it unlocked until it + * is needed. + */ + vcsm_unlock_hdl(vcsm_handle); + + payload_elem->mem = mem; + payload_elem->handle = (void *)vcsm_handle; + payload_elem->vc_handle = (void *)vc_handle; +#else /* ENABLE_MMAL_VCSM */ + MMAL_PARAM_UNUSED(size); + mmal_vc_payload_list_release(payload_elem); +#endif /* ENABLE_MMAL_VCSM */ + + return mem; +} + +/** Free a shared memory buffer */ +MMAL_STATUS_T mmal_vc_shm_free(uint8_t *mem) +{ + MMAL_VC_PAYLOAD_ELEM_T *payload_elem = mmal_vc_payload_list_find_mem(mem); + if (payload_elem) + { +#ifdef ENABLE_MMAL_VCSM + vcsm_free((unsigned int)payload_elem->handle); +#endif /* ENABLE_MMAL_VCSM */ + mmal_vc_payload_list_release(payload_elem); + return MMAL_SUCCESS; + } + + return MMAL_EINVAL; +} + +/** Lock a shared memory buffer */ +uint8_t *mmal_vc_shm_lock(uint8_t *mem, uint32_t workaround) +{ + /* Zero copy stuff */ + MMAL_VC_PAYLOAD_ELEM_T *elem = mmal_vc_payload_list_find_handle(mem); + MMAL_PARAM_UNUSED(workaround); + + if (elem) { + mem = elem->mem; +#ifdef ENABLE_MMAL_VCSM + void *p = vcsm_lock((unsigned int)elem->handle); + if (!p) + assert(0); +#endif /* ENABLE_MMAL_VCSM */ + } + + return mem; +} + +/** Unlock a shared memory buffer */ +uint8_t *mmal_vc_shm_unlock(uint8_t *mem, uint32_t *length, uint32_t workaround) +{ + /* Zero copy stuff */ + MMAL_VC_PAYLOAD_ELEM_T *elem = mmal_vc_payload_list_find_mem(mem); + MMAL_PARAM_UNUSED(workaround); + + if (elem) + { + *length = 0; + mem = (uint8_t *)elem->vc_handle; +#ifdef ENABLE_MMAL_VCSM + vcsm_unlock_ptr(elem->mem); +#endif /* ENABLE_MMAL_VCSM */ + } + + return mem; +} diff --git a/interface/mmal/vc/mmal_vc_shm.h b/interface/mmal/vc/mmal_vc_shm.h new file mode 100755 index 0000000..0aacb5b --- /dev/null +++ b/interface/mmal/vc/mmal_vc_shm.h @@ -0,0 +1,62 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MMAL_VC_SHM_H +#define MMAL_VC_SHM_H + +/** @file + * + * Abstraction layer for MMAL VC shared memory. + * This API is only used by the MMAL VC component. + */ + +#include "mmal_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialise the shared memory system */ +MMAL_STATUS_T mmal_vc_shm_init(void); + +/** Allocate a shared memory buffer */ +uint8_t *mmal_vc_shm_alloc(uint32_t size); + +/** Free a shared memory buffer */ +MMAL_STATUS_T mmal_vc_shm_free(uint8_t *mem); + +/** Lock a shared memory buffer */ +uint8_t *mmal_vc_shm_lock(uint8_t *mem, uint32_t workaround); + +/** Unlock a shared memory buffer */ +uint8_t *mmal_vc_shm_unlock(uint8_t *mem, uint32_t *length, uint32_t workaround); + + +#ifdef __cplusplus +} +#endif + +#endif /* MMAL_VC_SHM_H */ diff --git a/interface/peer/vc_vchi_dispmanx_common.h b/interface/peer/vc_vchi_dispmanx_common.h new file mode 100755 index 0000000..3ccea84 --- /dev/null +++ b/interface/peer/vc_vchi_dispmanx_common.h @@ -0,0 +1,85 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_VCHI_DISPMANX_COMMON_H +#define VC_VCHI_DISPMANX_COMMON_H + +typedef enum { + // IMPORTANT - DO NOT ALTER THE ORDER OF COMMANDS IN THIS ENUMERATION + // NEW FUNCTIONS SHOULD BE ADDED TO THE END, AND MUST ALSO BE ADDED TO + // THE HOST SIDE FUNCTION TABLE IN display_server.c. + + // No function configured - do not use + EDispmanNoFunction = 0, + + // Dispman pre-configure functions + EDispmanGetDevices, + EDispmanGetModes, + + // Dispman resource-related functions + EDispmanResourceCreate, + EDispmanResourceCreateFromImage, + EDispmanResourceDelete, + EDispmanResourceGetData, + EDispmanResourceGetImage, + + // Dispman display-related functions + EDispmanDisplayOpen, + EDispmanDisplayOpenMode, + EDispmanDisplayOpenOffscreen, + EDispmanDisplayReconfigure, + EDispmanDisplaySetDestination, + EDispmanDisplaySetBackground, + EDispmanDisplayGetInfo, + EDispmanDisplayClose, + + // Dispman update-related functions + EDispmanUpdateStart, + EDispmanUpdateSubmit, + EDispmanUpdateSubmitSync, + + // Dispman element-related functions + EDispmanElementAdd, + EDispmanElementModified, + EDispmanElementRemove, + EDispmanElementChangeSource, + EDispmanElementChangeLayer, + EDispmanElementChangeAttributes, + + //More commands go here... + EDispmanResourceFill, //Comes from uideck + EDispmanQueryImageFormats, + EDispmanBulkWrite, + EDispmanBulkRead, + EDispmanDisplayOrientation, + EDispmanSnapshot, + EDispmanSetPalette, + EDispmanVsyncCallback, + + EDispmanMaxFunction +} DISPMANX_COMMAND_T; + +#endif diff --git a/interface/vchi/common/endian.h b/interface/vchi/common/endian.h new file mode 100755 index 0000000..3f1b68e --- /dev/null +++ b/interface/vchi/common/endian.h @@ -0,0 +1,44 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VCHI_ENDIAN_H_ +#define _VCHI_ENDIAN_H_ + +#include "interface/vcos/vcos.h" + +int16_t vchi_readbuf_int16 ( const void *ptr ); +uint16_t vchi_readbuf_uint16( const void *ptr ); +uint32_t vchi_readbuf_uint32( const void *ptr ); +vcos_fourcc_t vchi_readbuf_fourcc( const void *ptr ); + +void vchi_writebuf_uint16( void *ptr, uint16_t value ); +void vchi_writebuf_uint32( void *ptr, uint32_t value ); +void vchi_writebuf_fourcc( void *ptr, vcos_fourcc_t value ); + +#endif /* _VCHI_ENDIAN_H_ */ + +/********************************** End of file ******************************************/ diff --git a/interface/vchi/connections/connection.h b/interface/vchi/connections/connection.h new file mode 100755 index 0000000..91bc91d --- /dev/null +++ b/interface/vchi/connections/connection.h @@ -0,0 +1,324 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief Contains the protypes for the interface functions. +*/ + +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +#include "interface/vchi/vchi_cfg_internal.h" +#include "interface/vchi/vchi_common.h" +#include "interface/vchi/message_drivers/message.h" + +/****************************************************************************** + Global defs + *****************************************************************************/ + +// Opaque handle for a connection / service pair +typedef struct opaque_vchi_connection_connected_service_handle_t *VCHI_CONNECTION_SERVICE_HANDLE_T; + +// opaque handle to the connection state information +typedef struct opaque_vchi_connection_info_t VCHI_CONNECTION_STATE_T; + +typedef struct vchi_connection_t VCHI_CONNECTION_T; + + +/****************************************************************************** + API + *****************************************************************************/ + +// Routine to init a connection with a particular low level driver +typedef VCHI_CONNECTION_STATE_T * (*VCHI_CONNECTION_INIT_T)( struct vchi_connection_t * connection, + const VCHI_MESSAGE_DRIVER_T * driver ); + +// Routine to control CRC enabling at a connection level +typedef int32_t (*VCHI_CONNECTION_CRC_CONTROL_T)( VCHI_CONNECTION_STATE_T *state_handle, + VCHI_CRC_CONTROL_T control ); + +// Routine to create a service +typedef int32_t (*VCHI_CONNECTION_SERVICE_CONNECT_T)( VCHI_CONNECTION_STATE_T *state_handle, + vcos_fourcc_t service_id, + uint32_t rx_fifo_size, + uint32_t tx_fifo_size, + int server, + VCHI_CALLBACK_T callback, + void *callback_param, + vcos_bool_t want_crc, + vcos_bool_t want_unaligned_bulk_rx, + vcos_bool_t want_unaligned_bulk_tx, + VCHI_CONNECTION_SERVICE_HANDLE_T *service_handle ); + +// Routine to close a service +typedef int32_t (*VCHI_CONNECTION_SERVICE_DISCONNECT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle ); + +// Routine to queue a message +typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + const void *data, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *msg_handle ); + +// scatter-gather (vector) message queueing +typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + VCHI_MSG_VECTOR_T *vector, + uint32_t count, + VCHI_FLAGS_T flags, + void *msg_handle ); + +// Routine to dequeue a message +typedef int32_t (*VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + void *data, + uint32_t max_data_size_to_read, + uint32_t *actual_msg_size, + VCHI_FLAGS_T flags ); + +// Routine to peek at a message +typedef int32_t (*VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags ); + +// Routine to hold a message +typedef int32_t (*VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags, + void **message_handle ); + +// Routine to initialise a received message iterator +typedef int32_t (*VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + VCHI_MSG_ITER_T *iter, + VCHI_FLAGS_T flags ); + +// Routine to release a held message +typedef int32_t (*VCHI_CONNECTION_HELD_MSG_RELEASE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + void *message_handle ); + +// Routine to get info on a held message +typedef int32_t (*VCHI_CONNECTION_HELD_MSG_INFO_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + void *message_handle, + void **data, + int32_t *msg_size, + uint32_t *tx_timestamp, + uint32_t *rx_timestamp ); + +// Routine to check whether the iterator has a next message +typedef vcos_bool_t (*VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, + const VCHI_MSG_ITER_T *iter ); + +// Routine to advance the iterator +typedef int32_t (*VCHI_CONNECTION_MSG_ITER_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, + VCHI_MSG_ITER_T *iter, + void **data, + uint32_t *msg_size ); + +// Routine to remove the last message returned by the iterator +typedef int32_t (*VCHI_CONNECTION_MSG_ITER_REMOVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, + VCHI_MSG_ITER_T *iter ); + +// Routine to hold the last message returned by the iterator +typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HOLD_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, + VCHI_MSG_ITER_T *iter, + void **msg_handle ); + +// Routine to transmit bulk data +typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + const void *data_src, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *bulk_handle ); + +// Routine to receive data +typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, + void *data_dst, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *bulk_handle ); + +// Routine to report if a server is available +typedef int32_t (*VCHI_CONNECTION_SERVER_PRESENT)( VCHI_CONNECTION_STATE_T *state, vcos_fourcc_t service_id, int32_t peer_flags ); + +// Routine to report the number of RX slots available +typedef int (*VCHI_CONNECTION_RX_SLOTS_AVAILABLE)( const VCHI_CONNECTION_STATE_T *state ); + +// Routine to report the RX slot size +typedef uint32_t (*VCHI_CONNECTION_RX_SLOT_SIZE)( const VCHI_CONNECTION_STATE_T *state ); + +// Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO +typedef void (*VCHI_CONNECTION_RX_BULK_BUFFER_ADDED)(VCHI_CONNECTION_STATE_T *state, + vcos_fourcc_t service, + uint32_t length, + MESSAGE_TX_CHANNEL_T channel, + uint32_t channel_params, + uint32_t data_length, + uint32_t data_offset); + +// Callback to inform a service that a Xon or Xoff message has been received +typedef void (*VCHI_CONNECTION_FLOW_CONTROL)(VCHI_CONNECTION_STATE_T *state, vcos_fourcc_t service_id, int32_t xoff); + +// Callback to inform a service that a server available reply message has been received +typedef void (*VCHI_CONNECTION_SERVER_AVAILABLE_REPLY)(VCHI_CONNECTION_STATE_T *state, vcos_fourcc_t service_id, uint32_t flags); + +// Callback to indicate that bulk auxiliary messages have arrived +typedef void (*VCHI_CONNECTION_BULK_AUX_RECEIVED)(VCHI_CONNECTION_STATE_T *state); + +// Callback to indicate that bulk auxiliary messages have arrived +typedef void (*VCHI_CONNECTION_BULK_AUX_TRANSMITTED)(VCHI_CONNECTION_STATE_T *state, void *handle); + +// Callback with all the connection info you require +typedef void (*VCHI_CONNECTION_INFO)(VCHI_CONNECTION_STATE_T *state, uint32_t protocol_version, uint32_t slot_size, uint32_t num_slots, uint32_t min_bulk_size); + +// Callback to inform of a disconnect +typedef void (*VCHI_CONNECTION_DISCONNECT)(VCHI_CONNECTION_STATE_T *state, uint32_t flags); + +// Callback to inform of a power control request +typedef void (*VCHI_CONNECTION_POWER_CONTROL)(VCHI_CONNECTION_STATE_T *state, MESSAGE_TX_CHANNEL_T channel, vcos_bool_t enable); + +// allocate memory suitably aligned for this connection +typedef void * (*VCHI_BUFFER_ALLOCATE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, uint32_t * length); + +// free memory allocated by buffer_allocate +typedef void (*VCHI_BUFFER_FREE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, void * address); + + +/****************************************************************************** + System driver struct + *****************************************************************************/ + +struct opaque_vchi_connection_api_t +{ + // Routine to init the connection + VCHI_CONNECTION_INIT_T init; + + // Connection-level CRC control + VCHI_CONNECTION_CRC_CONTROL_T crc_control; + + // Routine to connect to or create service + VCHI_CONNECTION_SERVICE_CONNECT_T service_connect; + + // Routine to disconnect from a service + VCHI_CONNECTION_SERVICE_DISCONNECT_T service_disconnect; + + // Routine to queue a message + VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T service_queue_msg; + + // scatter-gather (vector) message queue + VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T service_queue_msgv; + + // Routine to dequeue a message + VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T service_dequeue_msg; + + // Routine to peek at a message + VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T service_peek_msg; + + // Routine to hold a message + VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T service_hold_msg; + + // Routine to initialise a received message iterator + VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T service_look_ahead_msg; + + // Routine to release a message + VCHI_CONNECTION_HELD_MSG_RELEASE_T held_msg_release; + + // Routine to get information on a held message + VCHI_CONNECTION_HELD_MSG_INFO_T held_msg_info; + + // Routine to check for next message on iterator + VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T msg_iter_has_next; + + // Routine to get next message on iterator + VCHI_CONNECTION_MSG_ITER_NEXT_T msg_iter_next; + + // Routine to remove the last message returned by iterator + VCHI_CONNECTION_MSG_ITER_REMOVE_T msg_iter_remove; + + // Routine to hold the last message returned by iterator + VCHI_CONNECTION_MSG_ITER_HOLD_T msg_iter_hold; + + // Routine to transmit bulk data + VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T bulk_queue_transmit; + + // Routine to receive data + VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T bulk_queue_receive; + + // Routine to report the available servers + VCHI_CONNECTION_SERVER_PRESENT server_present; + + // Routine to report the number of RX slots available + VCHI_CONNECTION_RX_SLOTS_AVAILABLE connection_rx_slots_available; + + // Routine to report the RX slot size + VCHI_CONNECTION_RX_SLOT_SIZE connection_rx_slot_size; + + // Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO + VCHI_CONNECTION_RX_BULK_BUFFER_ADDED rx_bulk_buffer_added; + + // Callback to inform a service that a Xon or Xoff message has been received + VCHI_CONNECTION_FLOW_CONTROL flow_control; + + // Callback to inform a service that a server available reply message has been received + VCHI_CONNECTION_SERVER_AVAILABLE_REPLY server_available_reply; + + // Callback to indicate that bulk auxiliary messages have arrived + VCHI_CONNECTION_BULK_AUX_RECEIVED bulk_aux_received; + + // Callback to indicate that a bulk auxiliary message has been transmitted + VCHI_CONNECTION_BULK_AUX_TRANSMITTED bulk_aux_transmitted; + + // Callback to provide information about the connection + VCHI_CONNECTION_INFO connection_info; + + // Callback to notify that peer has requested disconnect + VCHI_CONNECTION_DISCONNECT disconnect; + + // Callback to notify that peer has requested power change + VCHI_CONNECTION_POWER_CONTROL power_control; + + // allocate memory suitably aligned for this connection + VCHI_BUFFER_ALLOCATE buffer_allocate; + + // free memory allocated by buffer_allocate + VCHI_BUFFER_FREE buffer_free; + +}; + +struct vchi_connection_t { + const VCHI_CONNECTION_API_T *api; + VCHI_CONNECTION_STATE_T *state; +#ifdef VCHI_COARSE_LOCKING + VCOS_SEMAPHORE_T sem; +#endif +}; + + +#endif /* CONNECTION_H_ */ + +/****************************** End of file **********************************/ diff --git a/interface/vchi/message_drivers/message.h b/interface/vchi/message_drivers/message.h new file mode 100755 index 0000000..4dc4e58 --- /dev/null +++ b/interface/vchi/message_drivers/message.h @@ -0,0 +1,197 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// MPHI videocore message driver + +#ifndef _VCHI_MESSAGE_H_ +#define _VCHI_MESSAGE_H_ + +#include "interface/vchi/vchi_cfg_internal.h" +#include "interface/vcos/vcos.h" +#include "interface/vchi/vchi_common.h" + + +typedef enum message_event_type { + MESSAGE_EVENT_NONE, + MESSAGE_EVENT_NOP, + MESSAGE_EVENT_MESSAGE, + MESSAGE_EVENT_SLOT_COMPLETE, + MESSAGE_EVENT_RX_BULK_PAUSED, + MESSAGE_EVENT_RX_BULK_COMPLETE, + MESSAGE_EVENT_TX_COMPLETE, + MESSAGE_EVENT_MSG_DISCARDED +} MESSAGE_EVENT_TYPE_T; + +typedef enum vchi_msg_flags +{ + VCHI_MSG_FLAGS_NONE = 0x0, + VCHI_MSG_FLAGS_TERMINATE_DMA = 0x1 +} VCHI_MSG_FLAGS_T; + +typedef enum message_tx_channel +{ + MESSAGE_TX_CHANNEL_MESSAGE = 0, + MESSAGE_TX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards +} MESSAGE_TX_CHANNEL_T; + +// Macros used for cycling through bulk channels +#define MESSAGE_TX_CHANNEL_BULK_PREV(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION-1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) +#define MESSAGE_TX_CHANNEL_BULK_NEXT(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) + +typedef enum message_rx_channel +{ + MESSAGE_RX_CHANNEL_MESSAGE = 0, + MESSAGE_RX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards +} MESSAGE_RX_CHANNEL_T; + +// Message receive slot information +typedef struct rx_msg_slot_info { + + struct rx_msg_slot_info *next; + //struct slot_info *prev; +#if !defined VCHI_COARSE_LOCKING + VCOS_SEMAPHORE_T sem; +#endif + + uint8_t *addr; // base address of slot + uint32_t len; // length of slot in bytes + + uint32_t write_ptr; // hardware causes this to advance + uint32_t read_ptr; // this module does the reading + int active; // is this slot in the hardware dma fifo? + uint32_t msgs_parsed; // count how many messages are in this slot + uint32_t msgs_released; // how many messages have been released + void *state; // connection state information + uint8_t ref_count[VCHI_MAX_SERVICES_PER_CONNECTION]; // reference count for slots held by services +} RX_MSG_SLOTINFO_T; + +// The message driver no longer needs to know about the fields of RX_BULK_SLOTINFO_T - sort this out. +// In particular, it mustn't use addr and len - they're the client buffer, but the message +// driver will be tasked with sending the aligned core section. +typedef struct rx_bulk_slotinfo_t { + struct rx_bulk_slotinfo_t *next; + + VCOS_SEMAPHORE_T *blocking; + + // needed by DMA + void *addr; + uint32_t len; + + // needed for the callback + void *service; + void *handle; + VCHI_FLAGS_T flags; +} RX_BULK_SLOTINFO_T; + + +/* ---------------------------------------------------------------------- + * each connection driver will have a pool of the following struct. + * + * the pool will be managed by vchi_qman_* + * this means there will be multiple queues (single linked lists) + * a given struct message_info will be on exactly one of these queues + * at any one time + * -------------------------------------------------------------------- */ +typedef struct rx_message_info { + + struct message_info *next; + //struct message_info *prev; + + uint8_t *addr; + uint32_t len; + RX_MSG_SLOTINFO_T *slot; // points to whichever slot contains this message + uint32_t tx_timestamp; + uint32_t rx_timestamp; + +} RX_MESSAGE_INFO_T; + +typedef struct { + MESSAGE_EVENT_TYPE_T type; + + struct { + // for messages + void *addr; // address of message + uint16_t slot_delta; // whether this message indicated slot delta + uint32_t len; // length of message + RX_MSG_SLOTINFO_T *slot; // slot this message is in + vcos_fourcc_t service; // service id this message is destined for + uint32_t tx_timestamp; // timestamp from the header + uint32_t rx_timestamp; // timestamp when we parsed it + } message; + + // FIXME: cleanup slot reporting... + RX_MSG_SLOTINFO_T *rx_msg; + RX_BULK_SLOTINFO_T *rx_bulk; + void *tx_handle; + MESSAGE_TX_CHANNEL_T tx_channel; + +} MESSAGE_EVENT_T; + + +// callbacks +typedef void VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T( void *state ); + +typedef struct { + VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T *event_callback; +} VCHI_MESSAGE_DRIVER_OPEN_T; + + +// handle to this instance of message driver (as returned by ->open) +typedef struct opaque_mhandle_t *VCHI_MDRIVER_HANDLE_T; + +struct opaque_vchi_message_driver_t { + VCHI_MDRIVER_HANDLE_T *(*open)( VCHI_MESSAGE_DRIVER_OPEN_T *params, void *state ); + int32_t (*suspending)( VCHI_MDRIVER_HANDLE_T *handle ); + int32_t (*resumed)( VCHI_MDRIVER_HANDLE_T *handle ); + int32_t (*power_control)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T, vcos_bool_t enable ); + int32_t (*add_msg_rx_slot)( VCHI_MDRIVER_HANDLE_T *handle, RX_MSG_SLOTINFO_T *slot ); // rx message + int32_t (*add_bulk_rx)( VCHI_MDRIVER_HANDLE_T *handle, void *data, uint32_t len, RX_BULK_SLOTINFO_T *slot ); // rx data (bulk) + int32_t (*send)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, VCHI_MSG_FLAGS_T flags, void *send_handle ); // tx (message & bulk) + void (*next_event)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_EVENT_T *event ); // get the next event from message_driver + int32_t (*enable)( VCHI_MDRIVER_HANDLE_T *handle ); + int32_t (*form_message)( VCHI_MDRIVER_HANDLE_T *handle, vcos_fourcc_t service_id, VCHI_MSG_VECTOR_T *vector, uint32_t count, void + *address, uint32_t length_avail, uint32_t max_total_length, vcos_bool_t pad_to_fill, vcos_bool_t allow_partial ); + + int32_t (*update_message)( VCHI_MDRIVER_HANDLE_T *handle, void *dest, int16_t *slot_count ); + int32_t (*buffer_aligned)( VCHI_MDRIVER_HANDLE_T *handle, int tx, int uncached, const void *address, const uint32_t length ); + void * (*allocate_buffer)( VCHI_MDRIVER_HANDLE_T *handle, uint32_t *length ); + void (*free_buffer)( VCHI_MDRIVER_HANDLE_T *handle, void *address ); + int (*rx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); + int (*tx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); + + vcos_bool_t (*tx_supports_terminate)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); + uint32_t (*tx_bulk_chunk_size)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); + int (*tx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); + int (*rx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_RX_CHANNEL_T channel ); + void (*form_bulk_aux)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, uint32_t chunk_size, const void **aux_data, int32_t *aux_len ); + void (*debug)( VCHI_MDRIVER_HANDLE_T *handle ); +}; + + +#endif // _VCHI_MESSAGE_H_ + +/****************************** End of file ***********************************/ diff --git a/interface/vchi/vchi.h b/interface/vchi/vchi.h new file mode 100755 index 0000000..8775d07 --- /dev/null +++ b/interface/vchi/vchi.h @@ -0,0 +1,382 @@ +/* +Copyright (c) 2012-2014, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Contains the protypes for the vchi functions. + +#ifndef VCHI_H_ +#define VCHI_H_ + +#include "interface/vcos/vcos.h" +#include "interface/vchi/vchi_cfg.h" +#include "interface/vchi/vchi_common.h" +#include "interface/vchi/connections/connection.h" +#include "vchi_mh.h" + + +/****************************************************************************** + Global defs + *****************************************************************************/ + +#define VCHI_SERVICE_HANDLE_INVALID 0 + +#define VCHI_BULK_ROUND_UP(x) ((((unsigned long)(x))+VCHI_BULK_ALIGN-1) & ~(VCHI_BULK_ALIGN-1)) +#define VCHI_BULK_ROUND_DOWN(x) (((unsigned long)(x)) & ~(VCHI_BULK_ALIGN-1)) +#define VCHI_BULK_ALIGN_NBYTES(x) (VCHI_BULK_ALIGNED(x) ? 0 : (VCHI_BULK_ALIGN - ((unsigned long)(x) & (VCHI_BULK_ALIGN-1)))) + +#ifdef USE_VCHIQ_ARM +#define VCHI_BULK_ALIGNED(x) 1 +#else +#define VCHI_BULK_ALIGNED(x) (((unsigned long)(x) & (VCHI_BULK_ALIGN-1)) == 0) +#endif + +typedef struct +{ + uint32_t version; + uint32_t version_min; +} VCHI_VERSION_T; +#define VCHI_VERSION(v_) { v_, v_ } +#define VCHI_VERSION_EX(v_,m_) { v_, m_ } + +typedef enum +{ + VCHI_VEC_POINTER, + VCHI_VEC_HANDLE, + VCHI_VEC_LIST +} VCHI_MSG_VECTOR_TYPE_T; + +typedef struct vchi_msg_vector_ex { + + VCHI_MSG_VECTOR_TYPE_T type; + union + { + // a memory handle + struct + { + VCHI_MEM_HANDLE_T handle; + uint32_t offset; + int32_t vec_len; + } handle; + + // an ordinary data pointer + struct + { + const void *vec_base; + int32_t vec_len; + } ptr; + + // a nested vector list + struct + { + struct vchi_msg_vector_ex *vec; + uint32_t vec_len; + } list; + } u; +} VCHI_MSG_VECTOR_EX_T; + + +// Construct an entry in a msg vector for a pointer (p) of length (l) +#define VCHI_VEC_POINTER(p,l) VCHI_VEC_POINTER, { { (VCHI_MEM_HANDLE_T)(p), (l) } } + +// Construct an entry in a msg vector for a message handle (h), starting at offset (o) of length (l) +#define VCHI_VEC_HANDLE(h,o,l) VCHI_VEC_HANDLE, { { (h), (o), (l) } } + +// Macros to manipulate fourcc_t values +#define MAKE_FOURCC(x) ((fourcc_t)( (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] )) +#define FOURCC_TO_CHAR(x) (x >> 24) & 0xFF,(x >> 16) & 0xFF,(x >> 8) & 0xFF, x & 0xFF + + +// Opaque service information +struct opaque_vchi_service_t; + +// Descriptor for a held message. Allocated by client, initialised by vchi_msg_hold, +// vchi_msg_iter_hold or vchi_msg_iter_hold_next. Fields are for internal VCHI use only. +typedef struct +{ + struct opaque_vchi_service_t *service; + void *message; +} VCHI_HELD_MSG_T; + + + +// structure used to provide the information needed to open a server or a client +typedef struct { + VCHI_VERSION_T version; + vcos_fourcc_t service_id; + VCHI_CONNECTION_T *connection; + uint32_t rx_fifo_size; + uint32_t tx_fifo_size; + VCHI_CALLBACK_T callback; + void *callback_param; + vcos_bool_t want_unaligned_bulk_rx; // client intends to receive bulk transfers of odd lengths or into unaligned buffers + vcos_bool_t want_unaligned_bulk_tx; // client intends to transmit bulk transfers of odd lengths or out of unaligned buffers + vcos_bool_t want_crc; // client wants to check CRCs on (bulk) transfers. Only needs to be set at 1 end - will do both directions. +} SERVICE_CREATION_T; + +// Opaque handle for a VCHI instance +typedef struct opaque_vchi_instance_handle_t *VCHI_INSTANCE_T; + +// Opaque handle for a server or client +typedef unsigned int VCHI_SERVICE_HANDLE_T; + +// Service registration & startup +typedef void (*VCHI_SERVICE_INIT)(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); + +typedef struct service_info_tag { + const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */ + VCHI_SERVICE_INIT init; /* Service initialisation function */ + void *vll_handle; /* VLL handle; NULL when unloaded or a "static VLL" in build */ +} SERVICE_INFO_T; + +// Pagelist structure for copy callback +struct pagelist_struct; + +/****************************************************************************** + Global funcs - implementation is specific to which side you are on (local / remote) + *****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern /*@observer@*/ VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, + const VCHI_MESSAGE_DRIVER_T * low_level); + + +// Routine used to initialise the vchi on both local + remote connections +extern int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ); + +extern int32_t vchi_connect( VCHI_CONNECTION_T **connections, + const uint32_t num_connections, + VCHI_INSTANCE_T instance_handle ); + +//When this is called, ensure that all services have no data pending. +//Bulk transfers can remain 'queued' +extern int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ); + +// Global control over bulk CRC checking +extern int32_t vchi_crc_control( VCHI_CONNECTION_T *connection, + VCHI_CRC_CONTROL_T control ); + +// helper functions +extern void * vchi_allocate_buffer(VCHI_SERVICE_HANDLE_T handle, uint32_t *length); +extern void vchi_free_buffer(VCHI_SERVICE_HANDLE_T handle, void *address); +extern uint32_t vchi_current_time(VCHI_INSTANCE_T instance_handle); + + +/****************************************************************************** + Global service API + *****************************************************************************/ +// Routine to create a named service +extern int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, + SERVICE_CREATION_T *setup, + VCHI_SERVICE_HANDLE_T *handle ); + +// Routine to destroy a service +extern int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ); + +// Routine to open a named service +extern int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, + SERVICE_CREATION_T *setup, + VCHI_SERVICE_HANDLE_T *handle); + +extern int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, + short *peer_version ); + +// Routine to close a named service +extern int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ); + +// Routine to increment ref count on a named service +extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); + +// Routine to decrement ref count on a named service +extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); + +// Routine to set a control option for a named service +extern int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, + VCHI_SERVICE_OPTION_T option, + int value); + +// Routine to send a message across a service +extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, + const void *data, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *msg_handle ); + +// scatter-gather (vector) and send message +int32_t vchi_msg_queuev_ex( VCHI_SERVICE_HANDLE_T handle, + VCHI_MSG_VECTOR_EX_T *vector, + uint32_t count, + VCHI_FLAGS_T flags, + void *msg_handle ); + +// legacy scatter-gather (vector) and send message, only handles pointers +int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, + VCHI_MSG_VECTOR_T *vector, + uint32_t count, + VCHI_FLAGS_T flags, + void *msg_handle ); + +// Routine to receive a msg from a service +// Dequeue is equivalent to hold, copy into client buffer, release +extern int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, + void *data, + uint32_t max_data_size_to_read, + uint32_t *actual_msg_size, + VCHI_FLAGS_T flags ); + +// Routine to look at a message in place. +// The message is not dequeued, so a subsequent call to peek or dequeue +// will return the same message. +extern int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags ); + +// Routine to remove a message after it has been read in place with peek +// The first message on the queue is dequeued. +extern int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ); + +// Routine to look at a message in place. +// The message is dequeued, so the caller is left holding it; the descriptor is +// filled in and must be released when the user has finished with the message. +extern int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, + void **data, // } may be NULL, as info can be + uint32_t *msg_size, // } obtained from HELD_MSG_T + VCHI_FLAGS_T flags, + VCHI_HELD_MSG_T *message_descriptor ); + +// Initialise an iterator to look through messages in place +extern int32_t vchi_msg_look_ahead( VCHI_SERVICE_HANDLE_T handle, + VCHI_MSG_ITER_T *iter, + VCHI_FLAGS_T flags ); + +/****************************************************************************** + Global service support API - operations on held messages and message iterators + *****************************************************************************/ + +// Routine to get the address of a held message +extern void *vchi_held_msg_ptr( const VCHI_HELD_MSG_T *message ); + +// Routine to get the size of a held message +extern int32_t vchi_held_msg_size( const VCHI_HELD_MSG_T *message ); + +// Routine to get the transmit timestamp as written into the header by the peer +extern uint32_t vchi_held_msg_tx_timestamp( const VCHI_HELD_MSG_T *message ); + +// Routine to get the reception timestamp, written as we parsed the header +extern uint32_t vchi_held_msg_rx_timestamp( const VCHI_HELD_MSG_T *message ); + +// Routine to release a held message after it has been processed +extern int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ); + +// Indicates whether the iterator has a next message. +extern vcos_bool_t vchi_msg_iter_has_next( const VCHI_MSG_ITER_T *iter ); + +// Return the pointer and length for the next message and advance the iterator. +extern int32_t vchi_msg_iter_next( VCHI_MSG_ITER_T *iter, + void **data, + uint32_t *msg_size ); + +// Remove the last message returned by vchi_msg_iter_next. +// Can only be called once after each call to vchi_msg_iter_next. +extern int32_t vchi_msg_iter_remove( VCHI_MSG_ITER_T *iter ); + +// Hold the last message returned by vchi_msg_iter_next. +// Can only be called once after each call to vchi_msg_iter_next. +extern int32_t vchi_msg_iter_hold( VCHI_MSG_ITER_T *iter, + VCHI_HELD_MSG_T *message ); + +// Return information for the next message, and hold it, advancing the iterator. +extern int32_t vchi_msg_iter_hold_next( VCHI_MSG_ITER_T *iter, + void **data, // } may be NULL + uint32_t *msg_size, // } + VCHI_HELD_MSG_T *message ); + + +/****************************************************************************** + Global bulk API + *****************************************************************************/ + +// Routine to prepare interface for a transfer from the other side +extern int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, + void *data_dst, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *transfer_handle ); + + +// Prepare interface for a transfer from the other side into relocatable memory. +int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T h_dst, + uint32_t offset, + uint32_t data_size, + const VCHI_FLAGS_T flags, + void * const bulk_handle ); + +// Prepare interface for a transfer from the other side into relocatable memory. +int32_t vchi_bulk_queue_receive_reloc_func( const VCHI_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T h_dst, + uint32_t offset, + uint32_t data_size, + const VCHI_FLAGS_T flags, + void * const bulk_handle, + int copy_pagelist(char *vcptr, const struct pagelist_struct *pagelist)); + +// Routine to queue up data ready for transfer to the other (once they have signalled they are ready) +extern int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, + const void *data_src, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *transfer_handle ); + + +/****************************************************************************** + Configuration plumbing + *****************************************************************************/ + +// function prototypes for the different mid layers (the state info gives the different physical connections) +extern const VCHI_CONNECTION_API_T *single_get_func_table( void ); +//extern const VCHI_CONNECTION_API_T *local_server_get_func_table( void ); +//extern const VCHI_CONNECTION_API_T *local_client_get_func_table( void ); + +// declare all message drivers here +const VCHI_MESSAGE_DRIVER_T *vchi_mphi_message_driver_func_table( void ); + +#ifdef __cplusplus +} +#endif + +extern int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T h_src, + uint32_t offset, + uint32_t data_size, + VCHI_FLAGS_T flags, + void *transfer_handle ); +#endif /* VCHI_H_ */ + +/****************************** End of file **********************************/ diff --git a/interface/vchi/vchi_cfg.h b/interface/vchi/vchi_cfg.h new file mode 100755 index 0000000..d179f8a --- /dev/null +++ b/interface/vchi/vchi_cfg.h @@ -0,0 +1,222 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Contains the #defines for the number of servers / clients etc, these can be +// over-ridden from the platform makefile if needed + + +#ifndef VCHI_CFG_H_ +#define VCHI_CFG_H_ + +/**************************************************************************************** + * Defines in this first section are part of the VCHI API and may be examined by VCHI + * services. + ***************************************************************************************/ + +/* Required alignment of base addresses for bulk transfer, if unaligned transfers are not enabled */ +/* Really determined by the message driver, and should be available from a run-time call. */ +#ifndef VCHI_BULK_ALIGN +# if __VCCOREVER__ >= 0x04000000 +# define VCHI_BULK_ALIGN 32 // Allows for the need to do cache cleans +# else +# define VCHI_BULK_ALIGN 16 +# endif +#endif + +/* Required length multiple for bulk transfers, if unaligned transfers are not enabled */ +/* May be less than or greater than VCHI_BULK_ALIGN */ +/* Really determined by the message driver, and should be available from a run-time call. */ +#ifndef VCHI_BULK_GRANULARITY +# if __VCCOREVER__ >= 0x04000000 +# define VCHI_BULK_GRANULARITY 32 // Allows for the need to do cache cleans +# else +# define VCHI_BULK_GRANULARITY 16 +# endif +#endif + +/* The largest possible message to be queued with vchi_msg_queue. */ +#ifndef VCHI_MAX_MSG_SIZE +# if defined VCHI_LOCAL_HOST_PORT +# define VCHI_MAX_MSG_SIZE 16384 // makes file transfers fast, but should they be using bulk? +# else +# define VCHI_MAX_MSG_SIZE 4096 // NOTE: THIS MUST BE LARGER THAN OR EQUAL TO THE SIZE OF THE KHRONOS MERGE BUFFER!! +# endif +#endif + +/****************************************************************************************** + * Defines below are system configuration options, and should not be used by VCHI services. + *****************************************************************************************/ + +/* How many connections can we support? A localhost implementation uses 2 connections, + * 1 for host-app, 1 for VMCS, and these are hooked together by a loopback MPHI VCFW + * driver. */ +#ifndef VCHI_MAX_NUM_CONNECTIONS +# define VCHI_MAX_NUM_CONNECTIONS 3 +#endif + +/* How many services can we open per connection? Extending this doesn't cost processing time, just a small + * amount of static memory. */ +#ifndef VCHI_MAX_SERVICES_PER_CONNECTION +# define VCHI_MAX_SERVICES_PER_CONNECTION 36 +#endif + +/* Adjust if using a message driver that supports more logical TX channels */ +#ifndef VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION +# define VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION 9 // 1 MPHI + 8 CCP2 logical channels +#endif + +/* Adjust if using a message driver that supports more logical RX channels */ +#ifndef VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION +# define VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION 1 // 1 MPHI +#endif + +/* How many receive slots do we use. This times VCHI_MAX_MSG_SIZE gives the effective + * receive queue space, less message headers. */ +#ifndef VCHI_NUM_READ_SLOTS +# if defined(VCHI_LOCAL_HOST_PORT) +# define VCHI_NUM_READ_SLOTS 4 +# else +# define VCHI_NUM_READ_SLOTS 48 +# endif +#endif + +/* Do we utilise overrun facility for receive message slots? Can aid peer transmit + * performance. Only define on VideoCore end, talking to host. + */ +//#define VCHI_MSG_RX_OVERRUN + +/* How many transmit slots do we use. Generally don't need many, as the hardware driver + * underneath VCHI will usually have its own buffering. */ +#ifndef VCHI_NUM_WRITE_SLOTS +# define VCHI_NUM_WRITE_SLOTS 4 +#endif + +/* If a service has held or queued received messages in VCHI_XOFF_THRESHOLD or more slots, + * then it's taking up too much buffer space, and the peer service will be told to stop + * transmitting with an XOFF message. For this to be effective, the VCHI_NUM_READ_SLOTS + * needs to be considerably bigger than VCHI_NUM_WRITE_SLOTS, or the transmit latency + * is too high. */ +#ifndef VCHI_XOFF_THRESHOLD +# define VCHI_XOFF_THRESHOLD (VCHI_NUM_READ_SLOTS / 2) +#endif + +/* After we've sent an XOFF, the peer will be told to resume transmission once the local + * service has dequeued/released enough messages that it's now occupying + * VCHI_XON_THRESHOLD slots or fewer. */ +#ifndef VCHI_XON_THRESHOLD +# define VCHI_XON_THRESHOLD (VCHI_NUM_READ_SLOTS / 4) +#endif + +/* A size below which a bulk transfer omits the handshake completely and always goes + * via the message channel, if bulk auxiliary is being sent on that service. (The user + * can guarantee this by enabling unaligned transmits). + * Not API. */ +#ifndef VCHI_MIN_BULK_SIZE +# define VCHI_MIN_BULK_SIZE ( VCHI_MAX_MSG_SIZE / 2 < 4096 ? VCHI_MAX_MSG_SIZE / 2 : 4096 ) +#endif + +/* Maximum size of bulk transmission chunks, for each interface type. A trade-off between + * speed and latency; the smaller the chunk size the better change of messages and other + * bulk transmissions getting in when big bulk transfers are happening. Set to 0 to not + * break transmissions into chunks. + */ +#ifndef VCHI_MAX_BULK_CHUNK_SIZE_MPHI +# define VCHI_MAX_BULK_CHUNK_SIZE_MPHI (16 * 1024) +#endif + +/* NB Chunked CCP2 transmissions violate the letter of the CCP2 spec by using "JPEG8" mode + * with multiple-line frames. Only use if the receiver can cope. */ +#ifndef VCHI_MAX_BULK_CHUNK_SIZE_CCP2 +# define VCHI_MAX_BULK_CHUNK_SIZE_CCP2 0 +#endif + +/* How many TX messages can we have pending in our transmit slots. Once exhausted, + * vchi_msg_queue will be blocked. */ +#ifndef VCHI_TX_MSG_QUEUE_SIZE +# define VCHI_TX_MSG_QUEUE_SIZE 256 +#endif + +/* How many RX messages can we have parsed in the receive slots. Once exhausted, parsing + * will be suspended until older messages are dequeued/released. */ +#ifndef VCHI_RX_MSG_QUEUE_SIZE +# define VCHI_RX_MSG_QUEUE_SIZE 256 +#endif + +/* Really should be able to cope if we run out of received message descriptors, by + * suspending parsing as the comment above says, but we don't. This sweeps the issue + * under the carpet. */ +#if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS +# undef VCHI_RX_MSG_QUEUE_SIZE +# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS +#endif + +/* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit + * will be blocked. */ +#ifndef VCHI_TX_BULK_QUEUE_SIZE +# define VCHI_TX_BULK_QUEUE_SIZE 64 +#endif + +/* How many bulk receives can we have pending. Once exhausted, vchi_bulk_queue_receive + * will be blocked. */ +#ifndef VCHI_RX_BULK_QUEUE_SIZE +# define VCHI_RX_BULK_QUEUE_SIZE 64 +#endif + +/* A limit on how many outstanding bulk requests we expect the peer to give us. If + * the peer asks for more than this, VCHI will fail and assert. The number is determined + * by the peer's hardware - it's the number of outstanding requests that can be queued + * on all bulk channels. VC3's MPHI peripheral allows 16. */ +#ifndef VCHI_MAX_PEER_BULK_REQUESTS +# define VCHI_MAX_PEER_BULK_REQUESTS 32 +#endif + +/* Define VCHI_CCP2TX_MANUAL_POWER if the host tells us when to turn the CCP2 + * transmitter on and off. + */ +/*#define VCHI_CCP2TX_MANUAL_POWER*/ + +#ifndef VCHI_CCP2TX_MANUAL_POWER + +/* Timeout (in milliseconds) for putting the CCP2TX interface into IDLE state. Set + * negative for no IDLE. + */ +# ifndef VCHI_CCP2TX_IDLE_TIMEOUT +# define VCHI_CCP2TX_IDLE_TIMEOUT 5 +# endif + +/* Timeout (in milliseconds) for putting the CCP2TX interface into OFF state. Set + * negative for no OFF. + */ +# ifndef VCHI_CCP2TX_OFF_TIMEOUT +# define VCHI_CCP2TX_OFF_TIMEOUT 1000 +# endif + +#endif /* VCHI_CCP2TX_MANUAL_POWER */ + +#endif /* VCHI_CFG_H_ */ + +/****************************** End of file **********************************/ diff --git a/interface/vchi/vchi_cfg_internal.h b/interface/vchi/vchi_cfg_internal.h new file mode 100755 index 0000000..13a0b83 --- /dev/null +++ b/interface/vchi/vchi_cfg_internal.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCHI_CFG_INTERNAL_H_ +#define VCHI_CFG_INTERNAL_H_ + +/**************************************************************************************** + * Control optimisation attempts. + ***************************************************************************************/ + +// Don't use lots of short-term locks - use great long ones, reducing the overall locks-per-second +#define VCHI_COARSE_LOCKING + +// Avoid lock then unlock on exit from blocking queue operations (msg tx, bulk rx/tx) +// (only relevant if VCHI_COARSE_LOCKING) +#define VCHI_ELIDE_BLOCK_EXIT_LOCK + +// Avoid lock on non-blocking peek +// (only relevant if VCHI_COARSE_LOCKING) +#define VCHI_AVOID_PEEK_LOCK + +// Use one slot-handler thread per connection, rather than 1 thread dealing with all connections in rotation. +#define VCHI_MULTIPLE_HANDLER_THREADS + +// Put free descriptors onto the head of the free queue, rather than the tail, so that we don't thrash +// our way through the pool of descriptors. +#define VCHI_PUSH_FREE_DESCRIPTORS_ONTO_HEAD + +// Don't issue a MSG_AVAILABLE callback for every single message. Possibly only safe if VCHI_COARSE_LOCKING. +#define VCHI_FEWER_MSG_AVAILABLE_CALLBACKS + +// Don't use message descriptors for TX messages that don't need them +#define VCHI_MINIMISE_TX_MSG_DESCRIPTORS + +// Nano-locks for multiqueue +//#define VCHI_MQUEUE_NANOLOCKS + +// Lock-free(er) dequeuing +//#define VCHI_RX_NANOLOCKS + +#endif /*VCHI_CFG_INTERNAL_H_*/ diff --git a/interface/vchi/vchi_common.h b/interface/vchi/vchi_common.h new file mode 100755 index 0000000..4942669 --- /dev/null +++ b/interface/vchi/vchi_common.h @@ -0,0 +1,170 @@ +/* +Copyright (c) 2012-2014, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Contains global defs used by submodules within vchi + +#ifndef VCHI_COMMON_H_ +#define VCHI_COMMON_H_ + + +//flags used when sending messages (must be bitmapped) +typedef enum +{ + VCHI_FLAGS_NONE = 0x0, + VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE = 0x1, // waits for message to be received, or sent (NB. not the same as being seen on other side) + VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE = 0x2, // run a callback when message sent + VCHI_FLAGS_BLOCK_UNTIL_QUEUED = 0x4, // return once the transfer is in a queue ready to go + VCHI_FLAGS_ALLOW_PARTIAL = 0x8, + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ = 0x10, + VCHI_FLAGS_CALLBACK_WHEN_DATA_READ = 0x20, + + VCHI_FLAGS_ALIGN_SLOT = 0x000080, // internal use only + VCHI_FLAGS_BULK_AUX_QUEUED = 0x010000, // internal use only + VCHI_FLAGS_BULK_AUX_COMPLETE = 0x020000, // internal use only + VCHI_FLAGS_BULK_DATA_QUEUED = 0x040000, // internal use only + VCHI_FLAGS_BULK_DATA_COMPLETE = 0x080000, // internal use only + VCHI_FLAGS_INTERNAL = 0xFF0000 +} VCHI_FLAGS_T; + +// constants for vchi_crc_control() +typedef enum { + VCHI_CRC_NOTHING = -1, + VCHI_CRC_PER_SERVICE = 0, + VCHI_CRC_EVERYTHING = 1, +} VCHI_CRC_CONTROL_T; + +//callback reasons when an event occurs on a service +typedef enum +{ + VCHI_CALLBACK_REASON_MIN, + + //This indicates that there is data available + //handle is the msg id that was transmitted with the data + // When a message is received and there was no FULL message available previously, send callback + // Tasks get kicked by the callback, reset their event and try and read from the fifo until it fails + VCHI_CALLBACK_MSG_AVAILABLE, + VCHI_CALLBACK_MSG_SENT, + VCHI_CALLBACK_MSG_SPACE_AVAILABLE, // XXX not yet implemented + + // This indicates that a transfer from the other side has completed + VCHI_CALLBACK_BULK_RECEIVED, + //This indicates that data queued up to be sent has now gone + //handle is the msg id that was used when sending the data + VCHI_CALLBACK_BULK_SENT, + VCHI_CALLBACK_BULK_RX_SPACE_AVAILABLE, // XXX not yet implemented + VCHI_CALLBACK_BULK_TX_SPACE_AVAILABLE, // XXX not yet implemented + + VCHI_CALLBACK_SERVICE_CLOSED, + + // this side has sent XOFF to peer due to lack of data consumption by service + // (suggests the service may need to take some recovery action if it has + // been deliberately holding off consuming data) + VCHI_CALLBACK_SENT_XOFF, + VCHI_CALLBACK_SENT_XON, + + // indicates that a bulk transfer has finished reading the source buffer + VCHI_CALLBACK_BULK_DATA_READ, + + // power notification events (currently host side only) + VCHI_CALLBACK_PEER_OFF, + VCHI_CALLBACK_PEER_SUSPENDED, + VCHI_CALLBACK_PEER_ON, + VCHI_CALLBACK_PEER_RESUMED, + VCHI_CALLBACK_FORCED_POWER_OFF, + +#ifdef USE_VCHIQ_ARM + // some extra notifications provided by vchiq_arm + VCHI_CALLBACK_SERVICE_OPENED, + VCHI_CALLBACK_BULK_RECEIVE_ABORTED, + VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, +#endif + + VCHI_CALLBACK_REASON_MAX +} VCHI_CALLBACK_REASON_T; + +// service control options +typedef enum +{ + VCHI_SERVICE_OPTION_MIN, + + VCHI_SERVICE_OPTION_TRACE, + VCHI_SERVICE_OPTION_SYNCHRONOUS, + + VCHI_SERVICE_OPTION_MAX +} VCHI_SERVICE_OPTION_T; + +//Callback used by all services / bulk transfers +typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param + VCHI_CALLBACK_REASON_T reason, + void *handle ); //for transmitting msg's only + + + +/* + * Define vector struct for scatter-gather (vector) operations + * Vectors can be nested - if a vector element has negative length, then + * the data pointer is treated as pointing to another vector array, with + * '-vec_len' elements. Thus to append a header onto an existing vector, + * you can do this: + * + * void foo(const VCHI_MSG_VECTOR_T *v, int n) + * { + * VCHI_MSG_VECTOR_T nv[2]; + * nv[0].vec_base = my_header; + * nv[0].vec_len = sizeof my_header; + * nv[1].vec_base = v; + * nv[1].vec_len = -n; + * ... + * + */ +typedef struct vchi_msg_vector { + const void *vec_base; + int32_t vec_len; +} VCHI_MSG_VECTOR_T; + +// Opaque type for a connection API +typedef struct opaque_vchi_connection_api_t VCHI_CONNECTION_API_T; + +// Opaque type for a message driver +typedef struct opaque_vchi_message_driver_t VCHI_MESSAGE_DRIVER_T; + + +// Iterator structure for reading ahead through received message queue. Allocated by client, +// initialised by vchi_msg_look_ahead. Fields are for internal VCHI use only. +// Iterates over messages in queue at the instant of the call to vchi_msg_lookahead - +// will not proceed to messages received since. Behaviour is undefined if an iterator +// is used again after messages for that service are removed/dequeued by any +// means other than vchi_msg_iter_... calls on the iterator itself. +typedef struct { + struct opaque_vchi_service_t *service; + void *last; + void *next; + void *remove; +} VCHI_MSG_ITER_T; + + +#endif // VCHI_COMMON_H_ diff --git a/interface/vchi/vchi_mh.h b/interface/vchi/vchi_mh.h new file mode 100755 index 0000000..a8d78c8 --- /dev/null +++ b/interface/vchi/vchi_mh.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCHI_MH_H_ +#define VCHI_MH_H_ + +#include + +typedef int32_t VCHI_MEM_HANDLE_T; +#define VCHI_MEM_HANDLE_INVALID 0 + +#endif diff --git a/interface/vchiq_arm/CMakeLists.txt b/interface/vchiq_arm/CMakeLists.txt new file mode 100755 index 0000000..7af383d --- /dev/null +++ b/interface/vchiq_arm/CMakeLists.txt @@ -0,0 +1,20 @@ + +add_library(vchiq_arm SHARED + vchiq_lib.c vchiq_util.c) + +# pull in VCHI cond variable emulation +target_link_libraries(vchiq_arm vcos) + +install(TARGETS vchiq_arm DESTINATION lib) +#install(FILES etc/10-vchiq.rules DESTINATION /etc/udev/rules.d) + +include_directories(../..) + +add_executable(vchiq_test + vchiq_test.c) + +target_link_libraries(vchiq_test + vchiq_arm + vcos) + +install(TARGETS vchiq_test RUNTIME DESTINATION bin) diff --git a/interface/vchiq_arm/vchiq.h b/interface/vchiq_arm/vchiq.h new file mode 100755 index 0000000..5995e82 --- /dev/null +++ b/interface/vchiq_arm/vchiq.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCHIQ_VCHIQ_H +#define VCHIQ_VCHIQ_H + +#include "vchiq_if.h" +#include "vchiq_util.h" +#include "interface/vcos/vcos.h" + +#endif + diff --git a/interface/vchiq_arm/vchiq_cfg.h b/interface/vchiq_arm/vchiq_cfg.h new file mode 100755 index 0000000..faa3e89 --- /dev/null +++ b/interface/vchiq_arm/vchiq_cfg.h @@ -0,0 +1,63 @@ +/* +Copyright (c) 2014, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_CFG_H +#define VCHIQ_CFG_H + +#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') +/* The version of VCHIQ - change with any non-trivial change */ +#define VCHIQ_VERSION 8 +/* The minimum compatible version - update to match VCHIQ_VERSION with any +** incompatible change */ +#define VCHIQ_VERSION_MIN 3 + +/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ +#define VCHIQ_VERSION_LIB_VERSION 7 + +/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ +#define VCHIQ_VERSION_CLOSE_DELIVERED 7 + +/* The version that made it safe to use SYNCHRONOUS mode */ +#define VCHIQ_VERSION_SYNCHRONOUS_MODE 8 + +#define VCHIQ_MAX_STATES 2 +#define VCHIQ_MAX_SERVICES 4096 +#define VCHIQ_MAX_SLOTS 128 +#define VCHIQ_MAX_SLOTS_PER_SIDE 64 + +#define VCHIQ_NUM_CURRENT_BULKS 32 +#define VCHIQ_NUM_SERVICE_BULKS 4 + +#ifndef VCHIQ_ENABLE_DEBUG +#define VCHIQ_ENABLE_DEBUG 1 +#endif + +#ifndef VCHIQ_ENABLE_STATS +#define VCHIQ_ENABLE_STATS 1 +#endif + +#endif /* VCHIQ_CFG_H */ diff --git a/interface/vchiq_arm/vchiq_if.h b/interface/vchiq_arm/vchiq_if.h new file mode 100755 index 0000000..8b0630d --- /dev/null +++ b/interface/vchiq_arm/vchiq_if.h @@ -0,0 +1,200 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_IF_H +#define VCHIQ_IF_H + +#include "interface/vchi/vchi_mh.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VCHIQ_SERVICE_HANDLE_INVALID 0 + +#define VCHIQ_SLOT_SIZE 4096 +#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T)) +#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */ + +#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) +#define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) +#define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) + +typedef enum { + VCHIQ_SERVICE_OPENED, // service, -, - + VCHIQ_SERVICE_CLOSED, // service, -, - + VCHIQ_MESSAGE_AVAILABLE, // service, header, - + VCHIQ_BULK_TRANSMIT_DONE, // service, -, bulk_userdata + VCHIQ_BULK_RECEIVE_DONE, // service, -, bulk_userdata + VCHIQ_BULK_TRANSMIT_ABORTED, // service, -, bulk_userdata + VCHIQ_BULK_RECEIVE_ABORTED // service, -, bulk_userdata +} VCHIQ_REASON_T; + +typedef enum +{ + VCHIQ_ERROR = -1, + VCHIQ_SUCCESS = 0, + VCHIQ_RETRY = 1 +} VCHIQ_STATUS_T; + +typedef enum +{ + VCHIQ_BULK_MODE_CALLBACK, + VCHIQ_BULK_MODE_BLOCKING, + VCHIQ_BULK_MODE_NOCALLBACK +} VCHIQ_BULK_MODE_T; + +typedef enum +{ + VCHIQ_SERVICE_OPTION_AUTOCLOSE, + VCHIQ_SERVICE_OPTION_SLOT_QUOTA, + VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, + VCHIQ_SERVICE_OPTION_SYNCHRONOUS, + VCHIQ_SERVICE_OPTION_TRACE +} VCHIQ_SERVICE_OPTION_T; + +#ifdef __HIGHC__ +/* Allow zero-sized arrays without warnings */ +#pragma warning (push) +#pragma warning (disable : 4200) +#endif + +typedef struct vchiq_header_struct { + /* The message identifier - opaque to applications. */ + int msgid; + + /* Size of message data. */ + unsigned int size; + + char data[0]; /* message */ +} VCHIQ_HEADER_T; + +#ifdef __HIGHC__ +#pragma warning (pop) +#endif + +typedef struct { + const void *data; + int size; +} VCHIQ_ELEMENT_T; + +typedef unsigned int VCHIQ_SERVICE_HANDLE_T; + +typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, + VCHIQ_SERVICE_HANDLE_T, void *); + +typedef struct vchiq_service_base_struct { + int fourcc; + VCHIQ_CALLBACK_T callback; + void *userdata; +} VCHIQ_SERVICE_BASE_T; + +typedef struct vchiq_service_params_struct { + int fourcc; + VCHIQ_CALLBACK_T callback; + void *userdata; + short version; /* Increment for non-trivial changes */ + short version_min; /* Update for incompatible changes */ +} VCHIQ_SERVICE_PARAMS_T; + +typedef struct vchiq_config_struct { + int max_msg_size; + int bulk_threshold; /* The message size aboce which it is better to use + a bulk transfer (<= max_msg_size) */ + int max_outstanding_bulks; + int max_services; + short version; /* The version of VCHIQ */ + short version_min; /* The minimum compatible version of VCHIQ */ +} VCHIQ_CONFIG_T; + +typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; +typedef void (*VCHIQ_REMOTE_USE_CALLBACK_T)(void* cb_arg); +struct pagelist_struct; + +extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); +extern VCHIQ_STATUS_T vchiq_initialise_fd(VCHIQ_INSTANCE_T *pinstance, int dev_vchiq_fd); +extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); +extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); +extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *pservice); +extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *pservice); +extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service); +extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service); +extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service); +extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service); + +extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, + const VCHIQ_ELEMENT_T *elements, int count); +extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, + VCHIQ_HEADER_T *header); +extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, + const void *data, int size, void *userdata); +extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, + void *data, int size, void *userdata); +extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle( + VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, + const void *offset, int size, void *userdata); +extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle( + VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, + void *offset, int size, void *userdata); +extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, + const void *data, int size, void *userdata, VCHIQ_BULK_MODE_T mode); +extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, + void *data, int size, void *userdata, VCHIQ_BULK_MODE_T mode); +extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, + VCHI_MEM_HANDLE_T handle, const void *offset, int size, void *userdata, + VCHIQ_BULK_MODE_T mode); +extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, + VCHI_MEM_HANDLE_T handle, void *offset, int size, void *userdata, + VCHIQ_BULK_MODE_T mode, + int copy_pagelist(char *vcptr, const struct pagelist_struct *pagelist)); +extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service); +extern void *vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T service); +extern int vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T service); +extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, + int config_size, VCHIQ_CONFIG_T *pconfig); +extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, + VCHIQ_SERVICE_OPTION_T option, int value); + +extern VCHIQ_STATUS_T vchiq_remote_use(VCHIQ_INSTANCE_T instance, + VCHIQ_REMOTE_USE_CALLBACK_T callback, void *cb_arg); +extern VCHIQ_STATUS_T vchiq_remote_release(VCHIQ_INSTANCE_T instance); + +extern VCHIQ_STATUS_T vchiq_dump_phys_mem(VCHIQ_SERVICE_HANDLE_T service, + void *ptr, size_t num_bytes); + +extern VCHIQ_STATUS_T vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, + short *peer_version); + +#ifdef __cplusplus +} +#endif + +#endif /* VCHIQ_IF_H */ diff --git a/interface/vchiq_arm/vchiq_ioctl.h b/interface/vchiq_arm/vchiq_ioctl.h new file mode 100755 index 0000000..dab4a8b --- /dev/null +++ b/interface/vchiq_arm/vchiq_ioctl.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_IOCTLS_H +#define VCHIQ_IOCTLS_H + +#include +#include "vchiq_if.h" + +#define VCHIQ_IOC_MAGIC 0xc4 +#define VCHIQ_INVALID_HANDLE (~0) + +typedef struct { + VCHIQ_SERVICE_PARAMS_T params; + int is_open; + int is_vchi; + unsigned int handle; /* OUT */ +} VCHIQ_CREATE_SERVICE_T; + +typedef struct { + unsigned int handle; + unsigned int count; + const VCHIQ_ELEMENT_T *elements; +} VCHIQ_QUEUE_MESSAGE_T; + +typedef struct { + unsigned int handle; + void *data; + unsigned int size; + void *userdata; + VCHIQ_BULK_MODE_T mode; +} VCHIQ_QUEUE_BULK_TRANSFER_T; + +typedef struct { + VCHIQ_REASON_T reason; + VCHIQ_HEADER_T *header; + void *service_userdata; + void *bulk_userdata; +} VCHIQ_COMPLETION_DATA_T; + +typedef struct { + unsigned int count; + VCHIQ_COMPLETION_DATA_T *buf; + unsigned int msgbufsize; + unsigned int msgbufcount; /* IN/OUT */ + void **msgbufs; +} VCHIQ_AWAIT_COMPLETION_T; + +typedef struct { + unsigned int handle; + int blocking; + unsigned int bufsize; + void *buf; +} VCHIQ_DEQUEUE_MESSAGE_T; + +typedef struct { + unsigned int config_size; + VCHIQ_CONFIG_T *pconfig; +} VCHIQ_GET_CONFIG_T; + +typedef struct { + unsigned int handle; + VCHIQ_SERVICE_OPTION_T option; + int value; +} VCHIQ_SET_SERVICE_OPTION_T; + +typedef struct { + void *virt_addr; + size_t num_bytes; +} VCHIQ_DUMP_MEM_T; + +#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0) +#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1) +#define VCHIQ_IOC_CREATE_SERVICE _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T) +#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3) +#define VCHIQ_IOC_QUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) +#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT _IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) +#define VCHIQ_IOC_QUEUE_BULK_RECEIVE _IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) +#define VCHIQ_IOC_AWAIT_COMPLETION _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) +#define VCHIQ_IOC_DEQUEUE_MESSAGE _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) +#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) +#define VCHIQ_IOC_GET_CONFIG _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) +#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) +#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) +#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) +#define VCHIQ_IOC_SET_SERVICE_OPTION _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) +#define VCHIQ_IOC_DUMP_PHYS_MEM _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) +#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) +#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) +#define VCHIQ_IOC_MAX 17 + +#endif diff --git a/interface/vchiq_arm/vchiq_lib.c b/interface/vchiq_arm/vchiq_lib.c new file mode 100755 index 0000000..133d5ca --- /dev/null +++ b/interface/vchiq_arm/vchiq_lib.c @@ -0,0 +1,1764 @@ +/* +Copyright (c) 2012-2014, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "vchiq.h" +#include "vchiq_cfg.h" +#include "vchiq_ioctl.h" +#include "interface/vchi/vchi.h" +#include "interface/vchi/common/endian.h" +#include "interface/vcos/vcos.h" + +#define IS_POWER_2(x) ((x & (x - 1)) == 0) +#define VCHIQ_MAX_INSTANCE_SERVICES 32 +#define MSGBUF_SIZE (VCHIQ_MAX_MSG_SIZE + sizeof(VCHIQ_HEADER_T)) + +#define RETRY(r,x) do { r = x; } while ((r == -1) && (errno == EINTR)) + +#define VCOS_LOG_CATEGORY (&vchiq_lib_log_category) + +typedef struct vchiq_service_struct +{ + VCHIQ_SERVICE_BASE_T base; + VCHIQ_SERVICE_HANDLE_T handle; + VCHIQ_SERVICE_HANDLE_T lib_handle; + int fd; + VCHI_CALLBACK_T vchi_callback; + void *peek_buf; + int peek_size; + int client_id; + char is_client; +} VCHIQ_SERVICE_T; + +typedef struct vchiq_service_struct VCHI_SERVICE_T; + +struct vchiq_instance_struct +{ + int fd; + int initialised; + int connected; + int use_close_delivered; + VCOS_THREAD_T completion_thread; + VCOS_MUTEX_T mutex; + int used_services; + VCHIQ_SERVICE_T services[VCHIQ_MAX_INSTANCE_SERVICES]; +} vchiq_instance; + +typedef struct vchiq_instance_struct VCHI_STATE_T; + +/* Local data */ +static VCOS_LOG_LEVEL_T vchiq_default_lib_log_level = VCOS_LOG_WARN; +static VCOS_LOG_CAT_T vchiq_lib_log_category; +static VCOS_MUTEX_T vchiq_lib_mutex; +static void *free_msgbufs; +static unsigned int handle_seq; + +vcos_static_assert(IS_POWER_2(VCHIQ_MAX_INSTANCE_SERVICES)); + +/* Local utility functions */ +static VCHIQ_INSTANCE_T +vchiq_lib_init(const int dev_vchiq_fd); + +static void *completion_thread(void *); + +static VCHIQ_STATUS_T +create_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHI_CALLBACK_T vchi_callback, + int is_open, + VCHIQ_SERVICE_HANDLE_T *phandle); + +static int +fill_peek_buf(VCHI_SERVICE_T *service, + VCHI_FLAGS_T flags); + +static void * +alloc_msgbuf(void); + +static void +free_msgbuf(void *buf); + +static __inline int +is_valid_instance(VCHIQ_INSTANCE_T instance) +{ + return (instance == &vchiq_instance) && (instance->initialised > 0); +} + +static inline VCHIQ_SERVICE_T * +handle_to_service(VCHIQ_SERVICE_HANDLE_T handle) +{ + return &vchiq_instance.services[handle & (VCHIQ_MAX_INSTANCE_SERVICES - 1)]; +} + +static VCHIQ_SERVICE_T * +find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service; + + service = handle_to_service(handle); + if (service && (service->lib_handle != handle)) + service = NULL; + + if (!service) + vcos_log_info("Invalid service handle 0x%x", handle); + + return service; +} + +/* + * VCHIQ API + */ + +// If dev_vchiq_fd == -1 then /dev/vchiq will be opened by this fn (as normal) +// +// Otherwise the given fd will be used. N.B. in this case the fd is duped +// so the caller will probably want to close whatever fd was passed once +// this call has returned. This slightly odd behaviour makes shutdown and +// error cases much simpler. +VCHIQ_STATUS_T +vchiq_initialise_fd(VCHIQ_INSTANCE_T *pinstance, int dev_vchiq_fd) +{ + VCHIQ_INSTANCE_T instance; + + instance = vchiq_lib_init(dev_vchiq_fd); + + vcos_log_trace( "%s: returning instance handle %p", __func__, instance ); + + *pinstance = instance; + + return (instance != NULL) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_initialise(VCHIQ_INSTANCE_T *pinstance) +{ + return vchiq_initialise_fd(pinstance, -1); +} + +VCHIQ_STATUS_T +vchiq_shutdown(VCHIQ_INSTANCE_T instance) +{ + vcos_log_trace( "%s called", __func__ ); + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + vcos_mutex_lock(&instance->mutex); + + if (instance->initialised == 1) + { + int i; + + instance->initialised = -1; /* Enter limbo */ + + /* Remove all services */ + + for (i = 0; i < instance->used_services; i++) + { + if (instance->services[i].lib_handle != VCHIQ_SERVICE_HANDLE_INVALID) + { + vchiq_remove_service(instance->services[i].lib_handle); + instance->services[i].lib_handle = VCHIQ_SERVICE_HANDLE_INVALID; + } + } + + if (instance->connected) + { + int ret; + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_SHUTDOWN, 0)); + vcos_assert(ret == 0); + vcos_thread_join(&instance->completion_thread, NULL); + instance->connected = 0; + } + + close(instance->fd); + instance->fd = -1; + } + else if (instance->initialised > 1) + { + instance->initialised--; + } + + vcos_mutex_unlock(&instance->mutex); + + vcos_global_lock(); + + if (instance->initialised == -1) + { + vcos_mutex_delete(&instance->mutex); + instance->initialised = 0; + } + + vcos_global_unlock(); + + vcos_log_trace( "%s returning", __func__ ); + + vcos_log_unregister(&vchiq_lib_log_category); + + return VCHIQ_SUCCESS; +} + +VCHIQ_STATUS_T +vchiq_connect(VCHIQ_INSTANCE_T instance) +{ + VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + VCOS_THREAD_ATTR_T attrs; + int ret; + + vcos_log_trace( "%s called", __func__ ); + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + vcos_mutex_lock(&instance->mutex); + + if (instance->connected) + goto out; + + ret = ioctl(instance->fd, VCHIQ_IOC_CONNECT, 0); + if (ret != 0) + { + status = VCHIQ_ERROR; + goto out; + } + + vcos_thread_attr_init(&attrs); + if (vcos_thread_create(&instance->completion_thread, "VCHIQ completion", + &attrs, completion_thread, instance) != VCOS_SUCCESS) + { + status = VCHIQ_ERROR; + goto out; + } + + instance->connected = 1; + +out: + vcos_mutex_unlock(&instance->mutex); + return status; +} + +VCHIQ_STATUS_T +vchiq_add_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *phandle) +{ + VCHIQ_STATUS_T status; + + vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)", + __func__, + params->fourcc, + (params->fourcc >> 24) & 0xff, + (params->fourcc >> 16) & 0xff, + (params->fourcc >> 8) & 0xff, + (params->fourcc ) & 0xff ); + + if (!params->callback) + return VCHIQ_ERROR; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + status = create_service(instance, + params, + NULL/*vchi_callback*/, + 0/*!open*/, + phandle); + + vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*phandle ); + + return status; +} + +VCHIQ_STATUS_T +vchiq_open_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *phandle) +{ + VCHIQ_STATUS_T status; + + vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)", + __func__, + params->fourcc, + (params->fourcc >> 24) & 0xff, + (params->fourcc >> 16) & 0xff, + (params->fourcc >> 8) & 0xff, + (params->fourcc ) & 0xff ); + + if (!params->callback) + return VCHIQ_ERROR; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + status = create_service(instance, + params, + NULL/*vchi_callback*/, + 1/*open*/, + phandle); + + vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*phandle ); + + return status; +} + +VCHIQ_STATUS_T +vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle)); + + if (service->is_client) + service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID; + + if (ret != 0) + return VCHIQ_ERROR; + + return VCHIQ_SUCCESS; +} + +VCHIQ_STATUS_T +vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID; + + if (ret != 0) + return VCHIQ_ERROR; + + return VCHIQ_SUCCESS; +} + +VCHIQ_STATUS_T +vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, + const VCHIQ_ELEMENT_T *elements, + int count) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_MESSAGE_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.elements = elements; + args.count = count; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +void +vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, + VCHIQ_HEADER_T *header) +{ + vcos_log_trace( "%s handle=%08x, header=%x", __func__, (uint32_t)handle, (uint32_t)header ); + + free_msgbuf(header); +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, + const void *data, + int size, + void *userdata) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.data = (void *)data; + args.size = size; + args.userdata = userdata; + args.mode = VCHIQ_BULK_MODE_CALLBACK; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, + void *data, + int size, + void *userdata) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.data = data; + args.size = size; + args.userdata = userdata; + args.mode = VCHIQ_BULK_MODE_CALLBACK; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + const void *offset, + int size, + void *userdata) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + return vchiq_queue_bulk_transmit(handle, offset, size, userdata); +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + void *offset, + int size, + void *userdata) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + return vchiq_queue_bulk_receive(handle, offset, size, userdata); +} + +VCHIQ_STATUS_T +vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, + const void *data, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.data = (void *)data; + args.size = size; + args.userdata = userdata; + args.mode = mode; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, + void *data, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + return vchiq_bulk_receive_handle(handle, VCHI_MEM_HANDLE_INVALID, data, size, userdata, mode, NULL); +} + +VCHIQ_STATUS_T +vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + const void *offset, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + return vchiq_bulk_transmit(handle, offset, size, userdata, mode); +} + +VCHIQ_STATUS_T +vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + void *offset, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode, + int (*copy_pagelist)()) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.data = offset; + args.size = size; + args.userdata = userdata; + args.mode = mode; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +int +vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + + if (!service) + return VCHIQ_ERROR; + + return ioctl(service->fd, VCHIQ_IOC_GET_CLIENT_ID, service->handle); +} + +void * +vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + + return service ? service->base.userdata : NULL; +} + +int +vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + + return service ? service->base.fourcc : 0; +} + +VCHIQ_STATUS_T +vchiq_get_config(VCHIQ_INSTANCE_T instance, + int config_size, + VCHIQ_CONFIG_T *pconfig) +{ + VCHIQ_GET_CONFIG_T args; + int ret; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + args.config_size = config_size; + args.pconfig = pconfig; + + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +int32_t +vchiq_use_service( const VCHIQ_SERVICE_HANDLE_T handle ) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); + return ret; +} + +int32_t +vchiq_release_service( const VCHIQ_SERVICE_HANDLE_T handle ) +{ + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); + return ret; +} + +VCHIQ_STATUS_T +vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, + VCHIQ_SERVICE_OPTION_T option, int value) +{ + VCHIQ_SET_SERVICE_OPTION_T args; + VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.option = option; + args.value = value; + + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +/* + * VCHI API + */ + +/* ---------------------------------------------------------------------- + * return pointer to the mphi message driver function table + * -------------------------------------------------------------------- */ +const VCHI_MESSAGE_DRIVER_T * +vchi_mphi_message_driver_func_table( void ) +{ + return NULL; +} + +/* ---------------------------------------------------------------------- + * return a pointer to the 'single' connection driver fops + * -------------------------------------------------------------------- */ +const VCHI_CONNECTION_API_T * +single_get_func_table( void ) +{ + return NULL; +} + +VCHI_CONNECTION_T * +vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, + const VCHI_MESSAGE_DRIVER_T * low_level ) +{ + vcos_unused(function_table); + vcos_unused(low_level); + + return NULL; +} + +/*********************************************************** + * Name: vchi_msg_peek + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle, + * void **data, + * uint32_t *msg_size, + * VCHI_FLAGS_T flags + * + * Description: Routine to return a pointer to the current message (to allow in place processing) + * The message can be removed using vchi_msg_remove when you're finished + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + ret = fill_peek_buf(service, flags); + + if (ret == 0) + { + *data = service->peek_buf; + *msg_size = service->peek_size; + } + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_remove + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle, + * + * Description: Routine to remove a message (after it has been read with vchi_msg_peek) + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + + if (!service) + return VCHIQ_ERROR; + + /* Why would you call vchi_msg_remove without calling vchi_msg_peek first? */ + vcos_assert(service->peek_size >= 0); + + /* Invalidate the content but reuse the buffer */ + service->peek_size = -1; + + return 0; +} + +/*********************************************************** + * Name: vchi_msg_queue + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * const void *data, + * uint32_t data_size, + * VCHI_FLAGS_T flags, + * void *msg_handle, + * + * Description: Thin wrapper to queue a message onto a connection + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, + const void * data, + uint32_t data_size, + VCHI_FLAGS_T flags, + void * msg_handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_MESSAGE_T args; + VCHIQ_ELEMENT_T element = {data, data_size}; + int ret; + + vcos_unused(msg_handle); + vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.elements = &element; + args.count = 1; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_bulk_queue_receive + * + * Arguments: VCHI_BULK_HANDLE_T handle, + * void *data_dst, + * const uint32_t data_size, + * VCHI_FLAGS_T flags + * void *bulk_handle + * + * Description: Routine to setup a rcv buffer + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, + void * data_dst, + uint32_t data_size, + VCHI_FLAGS_T flags, + void * bulk_handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + if (!service) + return VCHIQ_ERROR; + + switch ((int)flags) { + case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + args.mode = VCHIQ_BULK_MODE_CALLBACK; + break; + case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: + args.mode = VCHIQ_BULK_MODE_BLOCKING; + break; + case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + case VCHI_FLAGS_NONE: + args.mode = VCHIQ_BULK_MODE_NOCALLBACK; + break; + default: + vcos_assert(0); + break; + } + + args.handle = service->handle; + args.data = data_dst; + args.size = data_size; + args.userdata = bulk_handle; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_bulk_queue_transmit + * + * Arguments: VCHI_BULK_HANDLE_T handle, + * const void *data_src, + * uint32_t data_size, + * VCHI_FLAGS_T flags, + * void *bulk_handle + * + * Description: Routine to transmit some data + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, + const void * data_src, + uint32_t data_size, + VCHI_FLAGS_T flags, + void * bulk_handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + if (!service) + return VCHIQ_ERROR; + + switch ((int)flags) { + case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + args.mode = VCHIQ_BULK_MODE_CALLBACK; + break; + case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: + case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: + args.mode = VCHIQ_BULK_MODE_BLOCKING; + break; + case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + case VCHI_FLAGS_NONE: + args.mode = VCHIQ_BULK_MODE_NOCALLBACK; + break; + default: + vcos_assert(0); + break; + } + + args.handle = service->handle; + args.data = (void *)data_src; + args.size = data_size; + args.userdata = bulk_handle; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_dequeue + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * void *data, + * uint32_t max_data_size_to_read, + * uint32_t *actual_msg_size + * VCHI_FLAGS_T flags + * + * Description: Routine to dequeue a message into the supplied buffer + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, + void *data, + uint32_t max_data_size_to_read, + uint32_t *actual_msg_size, + VCHI_FLAGS_T flags ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_DEQUEUE_MESSAGE_T args; + int ret; + + vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + + if (!service) + return VCHIQ_ERROR; + + if (service->peek_size >= 0) + { + vcos_log_error("vchi_msg_dequeue -> using peek buffer\n"); + if ((uint32_t)service->peek_size <= max_data_size_to_read) + { + memcpy(data, service->peek_buf, service->peek_size); + *actual_msg_size = service->peek_size; + /* Invalidate the peek data, but retain the buffer */ + service->peek_size = -1; + ret = 0; + } + else + { + ret = -1; + } + } + else + { + args.handle = service->handle; + args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + args.bufsize = max_data_size_to_read; + args.buf = data; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args)); + if (ret >= 0) + { + *actual_msg_size = ret; + ret = 0; + } + } + + if ((ret < 0) && (errno != EWOULDBLOCK)) + fprintf(stderr, "vchi_msg_dequeue -> %d(%d)\n", ret, errno); + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_queuev + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * const void *data, + * uint32_t data_size, + * VCHI_FLAGS_T flags, + * void *msg_handle + * + * Description: Thin wrapper to queue a message onto a connection + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ + +vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); +vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data)); +vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size)); + +int32_t +vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, + VCHI_MSG_VECTOR_T * vector, + uint32_t count, + VCHI_FLAGS_T flags, + void *msg_handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + VCHIQ_QUEUE_MESSAGE_T args; + int ret; + + vcos_unused(msg_handle); + + vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.elements = (const VCHIQ_ELEMENT_T *)vector; + args.count = count; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_held_msg_release + * + * Arguments: VCHI_HELD_MSG_T *message + * + * Description: Routine to release a held message (after it has been read with vchi_msg_hold) + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_held_msg_release( VCHI_HELD_MSG_T *message ) +{ + int ret = -1; + + if (message && message->message && !message->service) + { + free_msgbuf(message->message); + ret = 0; + } + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_hold + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * void **data, + * uint32_t *msg_size, + * VCHI_FLAGS_T flags, + * VCHI_HELD_MSG_T *message_handle + * + * Description: Routine to return a pointer to the current message (to allow in place processing) + * The message is dequeued - don't forget to release the message using + * vchi_held_msg_release when you're finished + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags, + VCHI_HELD_MSG_T *message_handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + ret = fill_peek_buf(service, flags); + + if (ret == 0) + { + *data = service->peek_buf; + *msg_size = service->peek_size; + + message_handle->message = service->peek_buf; + message_handle->service = NULL; + + service->peek_size = -1; + service->peek_buf = NULL; + } + + return 0; +} + +/*********************************************************** + * Name: vchi_initialise + * + * Arguments: VCHI_INSTANCE_T *instance_handle + * VCHI_CONNECTION_T **connections + * const uint32_t num_connections + * + * Description: Initialises the hardware but does not transmit anything + * When run as a Host App this will be called twice hence the need + * to malloc the state information + * + * Returns: 0 if successful, failure otherwise + * + ***********************************************************/ +int32_t +vchi_initialise( VCHI_INSTANCE_T *instance_handle ) +{ + VCHIQ_INSTANCE_T instance; + + instance = vchiq_lib_init(-1); + + vcos_log_trace( "%s: returning instance handle %p", __func__, instance ); + + *instance_handle = (VCHI_INSTANCE_T)instance; + + return (instance != NULL) ? 0 : -1; +} + +/*********************************************************** + * Name: vchi_connect + * + * Arguments: VCHI_CONNECTION_T **connections + * const uint32_t num_connections + * VCHI_INSTANCE_T instance_handle ) + * + * Description: Starts the command service on each connection, + * causing INIT messages to be pinged back and forth + * + * Returns: 0 if successful, failure otherwise + * + ***********************************************************/ +int32_t +vchi_connect( VCHI_CONNECTION_T **connections, + const uint32_t num_connections, + VCHI_INSTANCE_T instance_handle ) +{ + VCHIQ_STATUS_T status; + + vcos_unused(connections); + vcos_unused(num_connections); + + status = vchiq_connect((VCHIQ_INSTANCE_T)instance_handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + + +/*********************************************************** + * Name: vchi_disconnect + * + * Arguments: VCHI_INSTANCE_T instance_handle + * + * Description: Stops the command service on each connection, + * causing DE-INIT messages to be pinged back and forth + * + * Returns: 0 if successful, failure otherwise + * + ***********************************************************/ +int32_t +vchi_disconnect( VCHI_INSTANCE_T instance_handle ) +{ + VCHIQ_STATUS_T status; + + status = vchiq_shutdown((VCHIQ_INSTANCE_T)instance_handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + + +/*********************************************************** + * Name: vchi_service_open + * Name: vchi_service_create + * + * Arguments: VCHI_INSTANCE_T *instance_handle + * SERVICE_CREATION_T *setup, + * VCHI_SERVICE_HANDLE_T *handle + * + * Description: Routine to open a service + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_service_open( VCHI_INSTANCE_T instance_handle, + SERVICE_CREATION_T *setup, + VCHI_SERVICE_HANDLE_T *handle ) +{ + VCHIQ_SERVICE_PARAMS_T params; + VCHIQ_STATUS_T status; + + memset(¶ms, 0, sizeof(params)); + params.fourcc = setup->service_id; + params.userdata = setup->callback_param; + params.version = (short)setup->version.version; + params.version_min = (short)setup->version.version_min; + + status = create_service((VCHIQ_INSTANCE_T)instance_handle, + ¶ms, + setup->callback, + 1/*open*/, + (VCHIQ_SERVICE_HANDLE_T *)handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + +int32_t +vchi_service_create( VCHI_INSTANCE_T instance_handle, + SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle ) +{ + VCHIQ_SERVICE_PARAMS_T params; + VCHIQ_STATUS_T status; + + memset(¶ms, 0, sizeof(params)); + params.fourcc = setup->service_id; + params.userdata = setup->callback_param; + params.version = (short)setup->version.version; + params.version_min = (short)setup->version.version_min; + + status = create_service((VCHIQ_INSTANCE_T)instance_handle, + ¶ms, + setup->callback, + 0/*!open*/, + (VCHIQ_SERVICE_HANDLE_T *)handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + +int32_t +vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle)); + + if (service->is_client) + service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID; + + return ret; +} + +int32_t +vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID; + + return ret; +} + +/* ---------------------------------------------------------------------- + * read a uint32_t from buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +uint32_t +vchi_readbuf_uint32( const void *_ptr ) +{ + const unsigned char *ptr = _ptr; + return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); +} + +/* ---------------------------------------------------------------------- + * write a uint32_t to buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +void +vchi_writebuf_uint32( void *_ptr, uint32_t value ) +{ + unsigned char *ptr = _ptr; + ptr[0] = (unsigned char)((value >> 0) & 0xFF); + ptr[1] = (unsigned char)((value >> 8) & 0xFF); + ptr[2] = (unsigned char)((value >> 16) & 0xFF); + ptr[3] = (unsigned char)((value >> 24) & 0xFF); +} + +/* ---------------------------------------------------------------------- + * read a uint16_t from buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +uint16_t +vchi_readbuf_uint16( const void *_ptr ) +{ + const unsigned char *ptr = _ptr; + return ptr[0] | (ptr[1] << 8); +} + +/* ---------------------------------------------------------------------- + * write a uint16_t into the buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +void +vchi_writebuf_uint16( void *_ptr, uint16_t value ) +{ + unsigned char *ptr = _ptr; + ptr[0] = (value >> 0) & 0xFF; + ptr[1] = (value >> 8) & 0xFF; +} + +/*********************************************************** + * Name: vchi_service_use + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle + * + * Description: Routine to increment refcount on a service + * + * Returns: void + * + ***********************************************************/ +int32_t +vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); + return ret; +} + +/*********************************************************** + * Name: vchi_service_release + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle + * + * Description: Routine to decrement refcount on a service + * + * Returns: void + * + ***********************************************************/ +int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + if (!service) + return VCHIQ_ERROR; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); + return ret; +} + +/*********************************************************** + * Name: vchi_service_set_option + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle + * VCHI_SERVICE_OPTION_T option + * int value + * + * Description: Routine to set a service control option + * + * Returns: 0 on success, otherwise a non-zero error code + * + ***********************************************************/ +int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, + VCHI_SERVICE_OPTION_T option, int value) +{ + VCHIQ_SET_SERVICE_OPTION_T args; + VCHI_SERVICE_T *service = find_service_by_handle(handle); + int ret; + + switch (option) + { + case VCHI_SERVICE_OPTION_TRACE: + args.option = VCHIQ_SERVICE_OPTION_TRACE; + break; + default: + service = NULL; + break; + } + + if (!service) + return VCHIQ_ERROR; + + args.handle = service->handle; + args.value = value; + + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchiq_dump_phys_mem + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle + * void *buffer + * size_t num_bytes + * + * Description: Dumps the physical memory associated with + * a buffer. + * + * Returns: void + * + ***********************************************************/ +VCHIQ_STATUS_T vchiq_dump_phys_mem( VCHIQ_SERVICE_HANDLE_T handle, + void *ptr, + size_t num_bytes ) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_DUMP_MEM_T dump_mem; + int ret; + + if (!service) + return VCHIQ_ERROR; + + dump_mem.virt_addr = ptr; + dump_mem.num_bytes = num_bytes; + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_DUMP_PHYS_MEM, &dump_mem)); + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + + + +/* + * Support functions + */ + +static VCHIQ_INSTANCE_T +vchiq_lib_init(const int dev_vchiq_fd) +{ + static int mutex_initialised = 0; + static VCOS_MUTEX_T vchiq_lib_mutex; + VCHIQ_INSTANCE_T instance = &vchiq_instance; + + vcos_global_lock(); + if (!mutex_initialised) + { + vcos_mutex_create(&vchiq_lib_mutex, "vchiq-init"); + + vcos_log_set_level( &vchiq_lib_log_category, vchiq_default_lib_log_level ); + vcos_log_register( "vchiq_lib", &vchiq_lib_log_category ); + + mutex_initialised = 1; + } + vcos_global_unlock(); + + vcos_mutex_lock(&vchiq_lib_mutex); + + if (instance->initialised == 0) + { + instance->fd = dev_vchiq_fd == -1 ? + open("/dev/vchiq", O_RDWR) : + dup(dev_vchiq_fd); + if (instance->fd >= 0) + { + VCHIQ_GET_CONFIG_T args; + VCHIQ_CONFIG_T config; + int ret; + args.config_size = sizeof(config); + args.pconfig = &config; + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args)); + if ((ret == 0) && (config.version >= VCHIQ_VERSION_MIN) && (config.version_min <= VCHIQ_VERSION)) + { + if (config.version >= VCHIQ_VERSION_LIB_VERSION) + { + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_LIB_VERSION, VCHIQ_VERSION)); + } + if (ret == 0) + { + instance->used_services = 0; + instance->use_close_delivered = (config.version >= VCHIQ_VERSION_CLOSE_DELIVERED); + vcos_mutex_create(&instance->mutex, "VCHIQ instance"); + instance->initialised = 1; + } + } + else + { + if (ret == 0) + { + vcos_log_error("Incompatible VCHIQ library - driver version %d (min %d), library version %d (min %d)", + config.version, config.version_min, VCHIQ_VERSION, VCHIQ_VERSION_MIN); + } + else + { + vcos_log_error("Very incompatible VCHIQ library - cannot retrieve driver version"); + } + close(instance->fd); + instance = NULL; + } + } + else + { + instance = NULL; + } + } + else if (instance->initialised > 0) + { + instance->initialised++; + } + + vcos_mutex_unlock(&vchiq_lib_mutex); + + return instance; +} + +static void * +completion_thread(void *arg) +{ + VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)arg; + VCHIQ_AWAIT_COMPLETION_T args; + VCHIQ_COMPLETION_DATA_T completions[8]; + void *msgbufs[8]; + + static const VCHI_CALLBACK_REASON_T vchiq_reason_to_vchi[] = + { + VCHI_CALLBACK_SERVICE_OPENED, // VCHIQ_SERVICE_OPENED + VCHI_CALLBACK_SERVICE_CLOSED, // VCHIQ_SERVICE_CLOSED + VCHI_CALLBACK_MSG_AVAILABLE, // VCHIQ_MESSAGE_AVAILABLE + VCHI_CALLBACK_BULK_SENT, // VCHIQ_BULK_TRANSMIT_DONE + VCHI_CALLBACK_BULK_RECEIVED, // VCHIQ_BULK_RECEIVE_DONE + VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, // VCHIQ_BULK_TRANSMIT_ABORTED + VCHI_CALLBACK_BULK_RECEIVE_ABORTED, // VCHIQ_BULK_RECEIVE_ABORTED + }; + + args.count = vcos_countof(completions); + args.buf = completions; + args.msgbufsize = MSGBUF_SIZE; + args.msgbufcount = 0; + args.msgbufs = msgbufs; + + while (1) + { + int count, i; + + while ((unsigned int)args.msgbufcount < vcos_countof(msgbufs)) + { + void *msgbuf = alloc_msgbuf(); + if (msgbuf) + { + msgbufs[args.msgbufcount++] = msgbuf; + } + else + { + vcos_log_error("vchiq_lib: failed to allocate a message buffer\n"); + vcos_demand(args.msgbufcount != 0); + } + } + + RETRY(count, ioctl(instance->fd, VCHIQ_IOC_AWAIT_COMPLETION, &args)); + + if (count <= 0) + break; + + for (i = 0; i < count; i++) + { + VCHIQ_COMPLETION_DATA_T *completion = &completions[i]; + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)completion->service_userdata; + if (service->base.callback) + { + vcos_log_trace( "callback(%x, %x, %x(%x,%x), %x)", + completion->reason, (uint32_t)completion->header, + (uint32_t)&service->base, (uint32_t)service->lib_handle, (uint32_t)service->base.userdata, (uint32_t)completion->bulk_userdata ); + service->base.callback(completion->reason, completion->header, + service->lib_handle, completion->bulk_userdata); + } + else if (service->vchi_callback) + { + VCHI_CALLBACK_REASON_T vchi_reason = + vchiq_reason_to_vchi[completion->reason]; + service->vchi_callback(service->base.userdata, vchi_reason, completion->bulk_userdata); + } + + if ((completion->reason == VCHIQ_SERVICE_CLOSED) && + instance->use_close_delivered) + { + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_DELIVERED, service->handle)); + } + } + } + + while (args.msgbufcount) + { + void *msgbuf = msgbufs[--args.msgbufcount]; + free_msgbuf(msgbuf); + } + + return NULL; +} + +static VCHIQ_STATUS_T +create_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHI_CALLBACK_T vchi_callback, + int is_open, + VCHIQ_SERVICE_HANDLE_T *phandle) +{ + VCHIQ_SERVICE_T *service = NULL; + VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + int i; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + vcos_mutex_lock(&instance->mutex); + + /* Find a free service */ + if (is_open) + { + /* Find a free service */ + for (i = 0; i < instance->used_services; i++) + { + if (instance->services[i].lib_handle == VCHIQ_SERVICE_HANDLE_INVALID) + { + service = &instance->services[i]; + break; + } + } + } + else + { + for (i = (instance->used_services - 1); i >= 0; i--) + { + VCHIQ_SERVICE_T *srv = &instance->services[i]; + if (srv->lib_handle == VCHIQ_SERVICE_HANDLE_INVALID) + { + service = srv; + } + else if ( + (srv->base.fourcc == params->fourcc) && + ((srv->base.callback != params->callback) || + (srv->vchi_callback != vchi_callback))) + { + /* There is another server using this fourcc which doesn't match */ + vcos_log_info("service %x already using fourcc 0x%x", + srv->lib_handle, params->fourcc); + service = NULL; + status = VCHIQ_ERROR; + break; + } + } + } + + if (!service && (status == VCHIQ_SUCCESS)) + { + if (instance->used_services < VCHIQ_MAX_INSTANCE_SERVICES) + service = &instance->services[instance->used_services++]; + else + status = VCHIQ_ERROR; + } + + if (service) + { + if (!handle_seq) + handle_seq = VCHIQ_MAX_INSTANCE_SERVICES; + service->lib_handle = handle_seq | (service - instance->services); + handle_seq += VCHIQ_MAX_INSTANCE_SERVICES; + } + + vcos_mutex_unlock(&instance->mutex); + + if (service) + { + VCHIQ_CREATE_SERVICE_T args; + int ret; + + service->base.fourcc = params->fourcc; + service->base.callback = params->callback; + service->vchi_callback = vchi_callback; + service->base.userdata = params->userdata; + service->fd = instance->fd; + service->peek_size = -1; + service->peek_buf = NULL; + service->is_client = is_open; + + args.params = *params; + args.params.userdata = service; + args.is_open = is_open; + args.is_vchi = (params->callback == NULL); + args.handle = VCHIQ_SERVICE_HANDLE_INVALID; /* OUT parameter */ + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_CREATE_SERVICE, &args)); + if (ret == 0) + service->handle = args.handle; + else + status = VCHIQ_ERROR; + } + + if (status == VCHIQ_SUCCESS) + { + *phandle = service->lib_handle; + vcos_log_info("service handle %x lib_handle %x using fourcc 0x%x", + service->handle, service->lib_handle, params->fourcc); + } + else + { + vcos_mutex_lock(&instance->mutex); + + if (service) + service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID; + + vcos_mutex_unlock(&instance->mutex); + + *phandle = VCHIQ_SERVICE_HANDLE_INVALID; + } + + return status; +} + +static int +fill_peek_buf(VCHI_SERVICE_T *service, + VCHI_FLAGS_T flags) +{ + VCHIQ_DEQUEUE_MESSAGE_T args; + int ret = 0; + + vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + + if (service->peek_size < 0) + { + if (!service->peek_buf) + service->peek_buf = alloc_msgbuf(); + + if (service->peek_buf) + { + args.handle = service->handle; + args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + args.bufsize = MSGBUF_SIZE; + args.buf = service->peek_buf; + + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args)); + + if (ret >= 0) + { + service->peek_size = ret; + ret = 0; + } + else + { + ret = -1; + } + } + else + { + ret = -1; + } + } + + return ret; +} + + +static void * +alloc_msgbuf(void) +{ + void *msgbuf; + vcos_mutex_lock(&vchiq_lib_mutex); + msgbuf = free_msgbufs; + if (msgbuf) + free_msgbufs = *(void **)msgbuf; + vcos_mutex_unlock(&vchiq_lib_mutex); + if (!msgbuf) + msgbuf = vcos_malloc(MSGBUF_SIZE, "alloc_msgbuf"); + return msgbuf; +} + +static void +free_msgbuf(void *buf) +{ + vcos_mutex_lock(&vchiq_lib_mutex); + *(void **)buf = free_msgbufs; + free_msgbufs = buf; + vcos_mutex_unlock(&vchiq_lib_mutex); +} diff --git a/interface/vchiq_arm/vchiq_test.c b/interface/vchiq_arm/vchiq_test.c new file mode 100755 index 0000000..127c683 --- /dev/null +++ b/interface/vchiq_arm/vchiq_test.c @@ -0,0 +1,1766 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "vchiq_test.h" +#ifndef USE_VCHIQ_ARM +#define USE_VCHIQ_ARM +#endif +#include "interface/vchi/vchi.h" + +#define NUM_BULK_BUFS 2 +#define BULK_SIZE (1024*256) +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define INIT_PARAMS(sp_, fourcc_, cb_, userdata_, ver_) \ + do { \ + memset((sp_), 0, sizeof(*(sp_))); \ + (sp_)->fourcc = fourcc_; \ + (sp_)->callback = cb_; \ + (sp_)->userdata = userdata_; \ + (sp_)->version = ver_; \ + (sp_)->version_min = ver_; \ + } while (0) + + +static struct test_params g_params = { MSG_CONFIG, 64, 100, 1, 1, 1, 0, 0, 0, 0 }; +static const char *g_servname = "echo"; + +static VCOS_EVENT_T g_server_reply; +static VCOS_EVENT_T g_shutdown; +static VCOS_MUTEX_T g_mutex; + +static const char *g_server_error = NULL; + +static volatile int g_sync_mode = 0; + +static VCOS_EVENT_T func_test_sync; +static int want_echo = 1; +static int func_error = 0; +static int fun2_error = 0; +static int func_data_test_start = -1; +static int func_data_test_end = 0x7fffffff; +static int func_data_test_iter; + +char *bulk_bufs[NUM_BULK_BUFS * 2]; +char *bulk_tx_data[NUM_BULK_BUFS]; +char *bulk_rx_data[NUM_BULK_BUFS]; + +static int ctrl_received = 0; +static int bulk_tx_sent = 0; +static int bulk_rx_sent = 0; +static int bulk_tx_received = 0; +static int bulk_rx_received = 0; + +static char clnt_service1_data[SERVICE1_DATA_SIZE]; +static char clnt_service2_data[SERVICE2_DATA_SIZE]; + +static VCOS_LOG_CAT_T vchiq_test_log_category; + +static int vchiq_test(int argc, char **argv); +static VCHIQ_STATUS_T vchiq_bulk_test(void); +static VCHIQ_STATUS_T vchiq_ctrl_test(void); +static VCHIQ_STATUS_T vchiq_functional_test(void); +static VCHIQ_STATUS_T vchiq_ping_test(void); +static VCHIQ_STATUS_T vchiq_signal_test(void); + +static VCHIQ_STATUS_T do_functional_test(void); +static void do_ping_test(VCHIQ_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters); +static void do_vchi_ping_test(VCHI_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters); + +static VCHIQ_STATUS_T func_data_test(VCHIQ_SERVICE_HANDLE_T service, int size, int align, int server_align); + +#ifdef VCHIQ_LOCAL +static void *vchiq_test_server(void *); +#endif + +static VCHIQ_STATUS_T +clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata); +static void +vchi_clnt_callback(void *callback_param, VCHI_CALLBACK_REASON_T reason, + void *handle); +static VCHIQ_STATUS_T func_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata); +static VCHIQ_STATUS_T fun2_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata); +static int mem_check(const void *expected, const void *actual, int size); +static void usage(void); +static void check_timer(void); +static char *buf_align(char *buf, int align_size, int align); + +#ifdef ANDROID + +static int g_timeout_ms = 0; +static pid_t main_process_pid; +static void kill_timeout_handler(int cause, siginfo_t *how, void *ucontext); +static int setup_auto_kill(int timeout_ms); + +#endif + +#ifdef __linux__ + +#include +#include +#include "interface/vmcs_host/vc_cma.h" + +static void reserve_test(int reserve, int delay) +{ + int fd = open("/dev/vc-cma", O_RDWR); + int rc = -1; + if (fd >= 0) + { + rc = ioctl(fd, VC_CMA_IOC_RESERVE, reserve); + if (rc == 0) + { + printf("Sleeping for %d seconds...\n", delay); + sleep(delay); + } + else + printf("* failed to ioctl /dev/vc-cma - rc %d\n", rc); + close(fd); + } + else + printf("* failed to open /dev/vc-cma - rc %d\n", fd); +} + +#endif + +static int vchiq_test(int argc, char **argv) +{ + VCHIQ_STATUS_T status; + int run_bulk_test = 0; + int run_ctrl_test = 0; + int run_functional_test = 0; + int run_ping_test = 0; + int run_signal_test = 0; + int verbose = 0; + int argn; + + argn = 1; + while ((argn < argc) && (argv[argn][0] == '-')) + { + const char *arg = argv[argn++]; + if (strcmp(arg, "-s") == 0) + { + g_servname = argv[argn++]; + if (!g_servname || (strlen(g_servname) != 4)) + { + usage(); + } + } + else if (strcasecmp(arg, "-a") == 0) + { + g_params.align_size = (strcmp(arg, "-A") == 0) ? 4096 : 32; + g_params.client_align = atoi(argv[argn++]); + g_params.server_align = atoi(argv[argn++]); + } + else if (strcmp(arg, "-b") == 0) + { + run_bulk_test = 1; + g_params.blocksize = atoi(argv[argn++]); + } + else if (strcmp(arg, "-c") == 0) + { + run_ctrl_test = 1; + g_params.blocksize = atoi(argv[argn++]); + } + else if (strcmp(arg, "-e") == 0) + { + want_echo = 0; + } + else if (strcmp(arg, "-f") == 0) + { + run_functional_test = 1; + } + else if (strcmp(arg, "-h") == 0) + { + usage(); + } + else if (strcmp(arg, "-i") == 0) + { + run_signal_test = 1; + } + else if (strcmp(arg, "-m") == 0) + { + g_params.client_message_quota = atoi(argv[argn++]); + } + else if (strcmp(arg, "-M") == 0) + { + g_params.server_message_quota = atoi(argv[argn++]); + } + else if (strcmp(arg, "-p") == 0) + { + run_ping_test = 1; + g_params.iters = 1000; + } + else if (strcmp(arg, "-q") == 0) + { + /* coverity[missing_lock : FALSE] - g_server_reply is not used for mutual exclusion */ + g_params.verify = 0; + } +#ifdef __linux__ + else if (strcmp(arg, "-r") == 0) + { + int reserve, delay; + if (argn+1 < argc) + { + reserve = atoi(argv[argn++]); + delay = atoi(argv[argn++]); + reserve_test(reserve, delay); + exit(0); + } + else + { + printf("not enough arguments (-r reserve delay)\n"); + exit(-1); + } + } +#endif +#ifdef ANDROID + else if (strcmp(arg, "-K") == 0) + { + if (argn < argc) + g_timeout_ms = atoi(argv[argn++]); + else + { + printf("not enough arguments (-K timeout)\n"); + exit(-1); + } + } +#endif + else if (strcmp(arg, "-t") == 0) + { + check_timer(); + exit(0); + } + else if (strcmp(arg, "-v") == 0) + { + verbose = 1; + } + else if (strcmp(arg, "-S") == 0) + { + func_data_test_start = atoi(argv[argn++]); + } + else if (strcmp(arg, "-E") == 0) + { + func_data_test_end = atoi(argv[argn++]); + } + else + { + printf("* unknown option '%s'\n", arg); + usage(); + } + } + + if ((run_ctrl_test + run_bulk_test + run_functional_test + run_ping_test + run_signal_test) != 1) + usage(); + + if (argn < argc) + { + g_params.iters = atoi(argv[argn++]); + if (argn != argc) + { + usage(); + } + } + + vcos_log_set_level(VCOS_LOG_CATEGORY, verbose ? VCOS_LOG_TRACE : VCOS_LOG_INFO); + vcos_log_register("vchiq_test", VCOS_LOG_CATEGORY); + +#ifdef VCHIQ_LOCAL + { + static VCOS_THREAD_T server_task; + void *pointer = NULL; + int stack_size = 4096; + +#if VCOS_CAN_SET_STACK_ADDR + pointer = malloc(stack_size); + vcos_demand(pointer); +#endif + vcos_thread_create_classic(&server_task, "vchiq_test server", vchiq_test_server, (void *)0, pointer, stack_size, + 10 | VCOS_AFFINITY_CPU1, 20, VCOS_START); + } +#endif + + vcos_event_create(&g_server_reply, "g_server_reply"); + vcos_event_create(&g_shutdown, "g_shutdown"); + vcos_mutex_create(&g_mutex, "g_mutex"); + + + status = VCHIQ_ERROR; + + if (run_bulk_test) + status = vchiq_bulk_test(); + else if (run_ctrl_test) + status = vchiq_ctrl_test(); + else if (run_functional_test) + status = vchiq_functional_test(); + else if (run_ping_test) + status = vchiq_ping_test(); + else if (run_signal_test) + status = vchiq_signal_test(); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + +static VCHIQ_STATUS_T +vchiq_bulk_test(void) +{ + VCHIQ_INSTANCE_T vchiq_instance; + VCHIQ_SERVICE_HANDLE_T vchiq_service; + VCHIQ_SERVICE_PARAMS_T service_params; + VCHIQ_ELEMENT_T elements[4]; + VCHIQ_ELEMENT_T *element; + int num_bulk_bufs = NUM_BULK_BUFS; + uint32_t start, end; + int i; + + g_params.blocksize *= 1024; + + for (i = 0; i < (NUM_BULK_BUFS * 2); i++) + { + bulk_bufs[i] = malloc(g_params.blocksize + BULK_ALIGN_SIZE - 1); + if (!bulk_bufs[i]) + { + printf("* out of memory\n"); + while (i > 0) + { + free(bulk_bufs[--i]); + } + return VCHIQ_ERROR; + } + } + + for (i = 0; i < NUM_BULK_BUFS; i++) + { + int j; + bulk_tx_data[i] = buf_align(bulk_bufs[i*2 + 0], g_params.align_size, g_params.client_align); + bulk_rx_data[i] = buf_align(bulk_bufs[i*2 + 1], g_params.align_size, g_params.client_align); + for (j = 0; j < g_params.blocksize; j+=4) + { + *(unsigned int *)(bulk_tx_data[i] + j) = ((0x80 | i) << 24) + j; + } + memset(bulk_rx_data[i], 0xff, g_params.blocksize); + } + +#ifdef ANDROID + if (g_timeout_ms) + { + setup_auto_kill(g_timeout_ms); + } +#endif + + if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) + { + printf("* failed to open vchiq instance\n"); + return VCHIQ_ERROR; + } + + vchiq_connect(vchiq_instance); + + memset(&service_params, 0, sizeof(service_params)); + + service_params.version = service_params.version_min = VCHIQ_TEST_VER; + service_params.fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); + service_params.callback = clnt_callback; + service_params.userdata = "clnt userdata"; + + if (vchiq_open_service(vchiq_instance, &service_params, &vchiq_service) != VCHIQ_SUCCESS) + { + printf("* failed to open service - already in use?\n"); + return VCHIQ_ERROR; + } + + printf("Bulk test - service:%s, block size:%d, iters:%d\n", g_servname, g_params.blocksize, g_params.iters); + + /* coverity[missing_lock : FALSE] - g_server_reply is not used for mutual exclusion */ + g_params.echo = want_echo; + element = elements; + element->data = &g_params; + element->size = sizeof(g_params); + element++; + + vchiq_queue_message(vchiq_service, elements, element - elements); + + vcos_event_wait(&g_server_reply); + + if (g_server_error) + { + printf("* server error: %s\n", g_server_error); + return VCHIQ_ERROR; + } + + if ( num_bulk_bufs > g_params.iters ) + num_bulk_bufs = g_params.iters; + + start = vcos_getmicrosecs(); + + vcos_mutex_lock(&g_mutex); + + for (i = 0; i < num_bulk_bufs; i++) + { + vchiq_queue_bulk_transmit(vchiq_service, bulk_tx_data[i], g_params.blocksize, (void *)i); + + vcos_log_trace("vchiq_test: queued bulk tx %d", i); + bulk_tx_sent++; + + if (g_params.echo) + { + vchiq_queue_bulk_receive(vchiq_service, bulk_rx_data[i], g_params.blocksize, (void *)i); + + vcos_log_trace("vchiq_test: queued bulk rx %d", i); + bulk_rx_sent++; + } + } + + vcos_mutex_unlock(&g_mutex); + + vcos_log_trace("Sent all messages"); + + vcos_log_trace("vchiq_test: waiting for shutdown"); + + vcos_event_wait(&g_shutdown); + + end = vcos_getmicrosecs(); + + for (i = 0; i < (NUM_BULK_BUFS * 2); i++) + { + free(bulk_bufs[i]); + } + + vchiq_remove_service(vchiq_service); + + vcos_log_trace("vchiq_test: shutting down"); + + vchiq_shutdown(vchiq_instance); + + printf("Elapsed time: %dus per iteration\n", (end - start) / g_params.iters); + + return VCHIQ_SUCCESS; +} + +static VCHIQ_STATUS_T +vchiq_ctrl_test(void) +{ + VCHIQ_INSTANCE_T vchiq_instance; + VCHIQ_SERVICE_HANDLE_T vchiq_service; + VCHIQ_SERVICE_PARAMS_T service_params; + uint32_t start, end; + int i; + + ctrl_received = 0; + if (g_params.blocksize < 4) + g_params.blocksize = 4; + + for (i = 0; i < NUM_BULK_BUFS; i++) + { + int j; + bulk_tx_data[i] = malloc(g_params.blocksize); + if (!bulk_tx_data[i]) + { + printf("* out of memory\n"); + return VCHIQ_ERROR; + } + *(int *)bulk_tx_data[i] = MSG_ECHO; + for (j = 4; j < g_params.blocksize; j+=4) + { + *(unsigned int *)(bulk_tx_data[i] + j) = ((0x80 | i) << 24) + j; + } + } + +#ifdef ANDROID + if (g_timeout_ms) + { + setup_auto_kill(g_timeout_ms); + } +#endif + + if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) + { + printf("* failed to open vchiq instance\n"); + return VCHIQ_ERROR; + } + + vchiq_connect(vchiq_instance); + + memset(&service_params, 0, sizeof(service_params)); + + service_params.fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); + service_params.callback = clnt_callback; + service_params.userdata = "clnt userdata"; + service_params.version = VCHIQ_TEST_VER; + service_params.version_min = VCHIQ_TEST_VER; + + if (vchiq_open_service(vchiq_instance, &service_params, &vchiq_service) != VCHIQ_SUCCESS) + { + printf("* failed to open service - already in use?\n"); + return VCHIQ_ERROR; + } + + printf("Ctrl test - service:%s, block size:%d, iters:%d\n", g_servname, g_params.blocksize, g_params.iters); + + start = vcos_getmicrosecs(); + + for (i = 0; i < g_params.iters; i++) + { + VCHIQ_ELEMENT_T element; + element.data = bulk_tx_data[i % NUM_BULK_BUFS]; + element.size = g_params.blocksize; + + if (vchiq_queue_message(vchiq_service, &element, 1) != VCHIQ_SUCCESS) + { + printf("* failed to send a message\n"); + goto error_exit; + } + if (g_server_error) + { + printf("* error - %s\n", g_server_error); + goto error_exit; + } + } + + vcos_log_trace("Sent all messages"); + + if (g_params.echo) + { + vcos_log_trace("vchiq_test: waiting for shutdown"); + + vcos_event_wait(&g_shutdown); + } + + if (g_server_error) + { + printf("* error - %s\n", g_server_error); + goto error_exit; + } + + end = vcos_getmicrosecs(); + + vchiq_remove_service(vchiq_service); + + vcos_log_trace("vchiq_test: shutting down"); + + vchiq_shutdown(vchiq_instance); + + printf("Elapsed time: %dus per iteration\n", (end - start) / g_params.iters); + + return VCHIQ_SUCCESS; + +error_exit: + vchiq_remove_service(vchiq_service); + vchiq_shutdown(vchiq_instance); + return VCHIQ_ERROR; +} + +static VCHIQ_STATUS_T +vchiq_functional_test(void) +{ + int i; + printf("Functional test - iters:%d\n", g_params.iters); + for (i = 0; i < g_params.iters; i++) + { + printf("======== iteration %d ========\n", i+1); + + if (do_functional_test() != VCHIQ_SUCCESS) + return VCHIQ_ERROR; + } + return VCHIQ_SUCCESS; +} + +static VCHIQ_STATUS_T +vchiq_ping_test(void) +{ + /* Measure message round trip time for various sizes*/ + VCHIQ_INSTANCE_T vchiq_instance; + VCHIQ_SERVICE_HANDLE_T vchiq_service; + VCHI_SERVICE_HANDLE_T vchi_service; + SERVICE_CREATION_T service_params; + VCHIQ_SERVICE_PARAMS_T vchiq_service_params; + int fourcc; + + static int sizes[] = { 0, 1024, 2048, VCHIQ_MAX_MSG_SIZE }; + unsigned int i; + + fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); + + printf("Ping test - service:%s, iters:%d, version %d\n", g_servname, g_params.iters, VCHIQ_TEST_VER); + +#ifdef ANDROID + if (g_timeout_ms) + { + setup_auto_kill(g_timeout_ms); + } +#endif + + if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) + { + printf("* failed to open vchiq instance\n"); + return VCHIQ_ERROR; + } + + vchiq_connect(vchiq_instance); + + memset(&service_params, 0, sizeof(service_params)); + service_params.version.version = service_params.version.version_min = VCHIQ_TEST_VER; + service_params.service_id = fourcc; + service_params.callback = vchi_clnt_callback; + service_params.callback_param = &vchi_service; + + if (vchi_service_open((VCHI_INSTANCE_T)vchiq_instance, &service_params, &vchi_service) != VCHIQ_SUCCESS) + { + printf("* failed to open service - already in use?\n"); + return VCHIQ_ERROR; + } + + for (i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) + { + const int iter_count = g_params.iters; + do_vchi_ping_test(vchi_service, sizes[i], 0, 0, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 0, 0, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 1, 0, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 2, 0, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 10, 0, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 0, 1, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 0, 2, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 0, 10, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 10, 10, iter_count); + do_vchi_ping_test(vchi_service, sizes[i], 100, 0, iter_count/10); + do_vchi_ping_test(vchi_service, sizes[i], 0, 100, iter_count/10); + do_vchi_ping_test(vchi_service, sizes[i], 100, 100, iter_count/10); + do_vchi_ping_test(vchi_service, sizes[i], 200, 0, iter_count/10); + do_vchi_ping_test(vchi_service, sizes[i], 0, 200, iter_count/10); + do_vchi_ping_test(vchi_service, sizes[i], 200, 200, iter_count/10); + do_vchi_ping_test(vchi_service, sizes[i], 400, 0, iter_count/20); + do_vchi_ping_test(vchi_service, sizes[i], 0, 400, iter_count/20); + do_vchi_ping_test(vchi_service, sizes[i], 400, 400, iter_count/20); + do_vchi_ping_test(vchi_service, sizes[i], 1000, 0, iter_count/50); + do_vchi_ping_test(vchi_service, sizes[i], 0, 1000, iter_count/50); + do_vchi_ping_test(vchi_service, sizes[i], 1000, 1000, iter_count/50); + } + + vchi_service_close(vchi_service); + + INIT_PARAMS(&vchiq_service_params, fourcc, clnt_callback, "clnt userdata", VCHIQ_TEST_VER); + if (vchiq_open_service(vchiq_instance, &vchiq_service_params, &vchiq_service) != VCHIQ_SUCCESS) + { + printf("* failed to open service - already in use?\n"); + return VCHIQ_ERROR; + } + + for (i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) + { + const int iter_count = g_params.iters; + do_ping_test(vchiq_service, sizes[i], 0, 0, iter_count); + do_ping_test(vchiq_service, sizes[i], 0, 0, iter_count); + do_ping_test(vchiq_service, sizes[i], 1, 0, iter_count); + do_ping_test(vchiq_service, sizes[i], 2, 0, iter_count); + do_ping_test(vchiq_service, sizes[i], 10, 0, iter_count); + do_ping_test(vchiq_service, sizes[i], 0, 1, iter_count); + do_ping_test(vchiq_service, sizes[i], 0, 2, iter_count); + do_ping_test(vchiq_service, sizes[i], 0, 10, iter_count); + do_ping_test(vchiq_service, sizes[i], 10, 10, iter_count); + do_ping_test(vchiq_service, sizes[i], 100, 0, iter_count/10); + do_ping_test(vchiq_service, sizes[i], 0, 100, iter_count/10); + do_ping_test(vchiq_service, sizes[i], 100, 100, iter_count/10); + do_ping_test(vchiq_service, sizes[i], 200, 0, iter_count/10); + do_ping_test(vchiq_service, sizes[i], 0, 200, iter_count/10); + do_ping_test(vchiq_service, sizes[i], 200, 200, iter_count/10); + do_ping_test(vchiq_service, sizes[i], 400, 0, iter_count/20); + do_ping_test(vchiq_service, sizes[i], 0, 400, iter_count/20); + do_ping_test(vchiq_service, sizes[i], 400, 400, iter_count/20); + do_ping_test(vchiq_service, sizes[i], 1000, 0, iter_count/50); + do_ping_test(vchiq_service, sizes[i], 0, 1000, iter_count/50); + do_ping_test(vchiq_service, sizes[i], 1000, 1000, iter_count/50); + } + + vchiq_close_service(vchiq_service); + + return VCHIQ_SUCCESS; +} + +static VCHIQ_STATUS_T +vchiq_signal_test(void) +{ + /* Measure message round trip time for various sizes*/ + VCHIQ_INSTANCE_T vchiq_instance; + VCHIQ_SERVICE_HANDLE_T vchiq_service; + VCHIQ_SERVICE_PARAMS_T vchiq_service_params; + int fourcc; + + static int sizes[] = { 0, 1024, 2048, VCHIQ_MAX_MSG_SIZE }; + + fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); + + printf("signal test - service:%s, iters:%d, version %d\n", g_servname, g_params.iters, VCHIQ_TEST_VER); + +#ifdef ANDROID + if (g_timeout_ms) + { + setup_auto_kill(g_timeout_ms); + } +#endif + + if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) + { + printf("* failed to open vchiq instance\n"); + return VCHIQ_ERROR; + } + + vchiq_connect(vchiq_instance); + + INIT_PARAMS(&vchiq_service_params, fourcc, clnt_callback, "clnt userdata", VCHIQ_TEST_VER); + if (vchiq_open_service(vchiq_instance, &vchiq_service_params, &vchiq_service) != VCHIQ_SUCCESS) + { + printf("* failed to open service - already in use?\n"); + return VCHIQ_ERROR; + } + + vchiq_bulk_transmit(vchiq_service, &sizes, 16, 0, VCHIQ_BULK_MODE_BLOCKING); + + vchiq_close_service(vchiq_service); + + return VCHIQ_SUCCESS; +} + +static VCHIQ_STATUS_T +do_functional_test(void) +{ + VCHIQ_ELEMENT_T elements[4]; + VCHIQ_INSTANCE_T instance; + VCHIQ_SERVICE_HANDLE_T service, service2, service3; + VCHIQ_SERVICE_PARAMS_T service_params; + VCHIQ_CONFIG_T config; + unsigned int size, i; + + vcos_event_create(&func_test_sync, "test_sync"); + +#ifdef ANDROID + if (g_timeout_ms) + { + setup_auto_kill(g_timeout_ms); + } +#endif + + if (func_data_test_start != -1) + goto bulk_tests_only; + + EXPECT(vchiq_initialise(&instance), VCHIQ_SUCCESS); + EXPECT(vchiq_get_config(instance, sizeof(config) - 1, &config), VCHIQ_SUCCESS); // too small, but allowed for backwards compatibility + EXPECT(vchiq_get_config(instance, sizeof(config) + 1, &config), VCHIQ_ERROR); // too large + EXPECT(vchiq_get_config(instance, sizeof(config), &config), VCHIQ_SUCCESS); // just right + EXPECT(config.max_msg_size, VCHIQ_MAX_MSG_SIZE); + + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void *)1, VCHIQ_TEST_VER); + EXPECT(vchiq_add_service(instance, &service_params, &service), VCHIQ_SUCCESS); + + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void *)2, VCHIQ_TEST_VER); + EXPECT(vchiq_add_service(instance, &service_params, &service2), VCHIQ_SUCCESS); + + INIT_PARAMS(&service_params, FUNC_FOURCC, clnt_callback, (void *)3, VCHIQ_TEST_VER); + EXPECT(vchiq_add_service(instance, &service_params, &service3), VCHIQ_ERROR); // callback doesn't match + + EXPECT(vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 0), VCHIQ_SUCCESS); + EXPECT(vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 1), VCHIQ_SUCCESS); + EXPECT(vchiq_set_service_option(service, 42, 1), VCHIQ_ERROR); // invalid option + EXPECT(vchiq_remove_service(service), VCHIQ_SUCCESS); + EXPECT(vchiq_remove_service(service), VCHIQ_ERROR); // service already removed + EXPECT(vchiq_remove_service(service2), VCHIQ_SUCCESS); + EXPECT(vchiq_queue_message(service, NULL, 0), VCHIQ_ERROR); // service not valid + EXPECT(vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 0), VCHIQ_ERROR); // service not valid + + INIT_PARAMS(&service_params, FUNC_FOURCC, clnt_callback, (void *)3, VCHIQ_TEST_VER); + EXPECT(vchiq_add_service(instance, &service_params, &service3), VCHIQ_SUCCESS); + + EXPECT(vchiq_queue_message(service, NULL, 0), VCHIQ_ERROR); // service not open + EXPECT(vchiq_queue_bulk_transmit(service, clnt_service1_data, sizeof(clnt_service1_data), (void *)1), VCHIQ_ERROR); // service not open + EXPECT(vchiq_queue_bulk_receive(service2, clnt_service2_data, sizeof(clnt_service2_data), (void *)2), VCHIQ_ERROR); // service not open + EXPECT(vchiq_queue_bulk_receive(service, 0, sizeof(clnt_service1_data), (void *)1), VCHIQ_ERROR); // invalid buffer + EXPECT(vchiq_shutdown(instance), VCHIQ_SUCCESS); + EXPECT(vchiq_initialise(&instance), VCHIQ_SUCCESS); + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)1, 0); + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // not connected + EXPECT(vchiq_connect(instance), VCHIQ_SUCCESS); + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // wrong version number + memset(&service_params, 0, sizeof(service_params)); + service_params.fourcc = FUNC_FOURCC; + service_params.callback = func_clnt_callback; + service_params.userdata = (void*)1; + service_params.version = 1; + service_params.version_min = 1; + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // Still the wrong version number + service_params.version = VCHIQ_TEST_VER + 1; + service_params.version_min = VCHIQ_TEST_VER + 1; + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // Still the wrong version number + service_params.version = VCHIQ_TEST_VER; + service_params.version_min = VCHIQ_TEST_VER; + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_SUCCESS); // That's better + + INIT_PARAMS(&service_params, VCHIQ_MAKE_FOURCC('n','o','n','e'), func_clnt_callback, (void*)2, VCHIQ_TEST_VER); + EXPECT(vchiq_open_service(instance, &service_params, &service2), VCHIQ_ERROR); // no listener + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)2, VCHIQ_TEST_VER); + EXPECT(vchiq_open_service(instance, &service_params, &service2), VCHIQ_SUCCESS); + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)3, VCHIQ_TEST_VER); + EXPECT(vchiq_open_service(instance, &service_params, &service3), VCHIQ_ERROR); // no more listeners + EXPECT(vchiq_remove_service(service2), VCHIQ_SUCCESS); + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)2, VCHIQ_TEST_VER); + EXPECT(vchiq_open_service(instance, &service_params, &service2), VCHIQ_SUCCESS); + + elements[0].data = "a"; + elements[0].size = 1; + elements[1].data = "bcdef"; + elements[1].size = 5; + elements[2].data = "ghijklmnopq"; + elements[2].size = 11; + elements[3].data = "rstuvwxyz"; + elements[3].size = 9; + EXPECT(vchiq_queue_message(service, elements, 4), VCHIQ_SUCCESS); + + EXPECT(vchiq_queue_bulk_transmit(service2, clnt_service2_data, sizeof(clnt_service2_data), (void *)0x2001), VCHIQ_SUCCESS); + for (i = 0; i < sizeof(clnt_service1_data); i++) + { + clnt_service1_data[i] = (char)i; + } + EXPECT(vchiq_queue_bulk_transmit(service, clnt_service1_data, sizeof(clnt_service1_data), (void*)0x1001), VCHIQ_SUCCESS); + + vcos_event_wait(&func_test_sync); + EXPECT(func_error, 0); + EXPECT(vchiq_remove_service(service), VCHIQ_SUCCESS); + vcos_event_wait(&func_test_sync); + + EXPECT(vchiq_shutdown(instance), VCHIQ_SUCCESS); + + vcos_event_wait(&func_test_sync); + EXPECT(func_error, 0); + + INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, NULL, VCHIQ_TEST_VER); + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); /* Instance not initialised */ + EXPECT(vchiq_add_service(instance, &service_params, &service), VCHIQ_ERROR); /* Instance not initialised */ + EXPECT(vchiq_connect(instance), VCHIQ_ERROR); /* Instance not initialised */ + +bulk_tests_only: + /* Now test the bulk data transfers */ + EXPECT(vchiq_initialise(&instance), VCHIQ_SUCCESS); + EXPECT(vchiq_connect(instance), VCHIQ_SUCCESS); + + func_data_test_iter = 0; + + INIT_PARAMS(&service_params, FUN2_FOURCC, fun2_clnt_callback, NULL, VCHIQ_TEST_VER); + EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_SUCCESS); + + if (func_data_test_end < func_data_test_start) + goto skip_bulk_tests; + + printf("Testing bulk transfer for alignment.\n"); + for (size = 1; size < 64; size++) + { + int align, srvr_align; + for (srvr_align = 32; srvr_align; srvr_align >>= 1) + { + for (align = 32; align; align >>= 1) + { + EXPECT(func_data_test(service, size, align & 31, srvr_align & 31), VCHIQ_SUCCESS); + } + } + } + + printf("Testing bulk transfer at PAGE_SIZE.\n"); + for (size = 1; size < 64; size++) + { + int align, srvr_align; + for (srvr_align = 32; srvr_align; srvr_align >>= 1) + { + for (align = 32; align; align >>= 1) + { + EXPECT(func_data_test(service, size, PAGE_SIZE - align, srvr_align & 31), VCHIQ_SUCCESS); + } + } + } + + for (size = 64; size < FUN2_MAX_DATA_SIZE; size<<=1) + { + static const int aligns[] = { 0, 1, 31 }; + + for (i = 0; i < vcos_countof(aligns); i++) + { + int srvr_align = aligns[i]; + unsigned int j; + for (j = 0; j < vcos_countof(aligns); j++) + { + int k; + int align = aligns[j]; + for (k = 0; k <= 8; k++) + { + EXPECT(func_data_test(service, size, align, srvr_align + k), VCHIQ_SUCCESS); + } + } + } + } + +skip_bulk_tests: + + EXPECT(vchiq_shutdown(instance), VCHIQ_SUCCESS); + + vcos_event_delete(&func_test_sync); + + return VCHIQ_SUCCESS; + +error_exit: + return VCHIQ_ERROR; +} + +static void +do_ping_test(VCHIQ_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters) +{ + uint32_t start, end; + char *ping_buf = malloc(size + sizeof(struct test_params)); + struct test_params *params = (struct test_params *)ping_buf; + VCHIQ_ELEMENT_T element; + int i; + + element.data = ping_buf; + + /* Set up the quotas for messages */ + *params = g_params; + params->magic = MSG_CONFIG; + params->blocksize = 0; + element.size = sizeof(*params); + vchiq_queue_message(service, &element, 1); + vcos_event_wait(&g_server_reply); + vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, params->client_message_quota); + + /* Allow enough room for the type header */ + element.size = (size < 4) ? 4 : size; + + bulk_tx_received = -1; + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + int j; + for (j = 0; j < vcos_max(async, oneway); j++) + { + if (j < async) + { + params->magic = MSG_ASYNC; + vchiq_queue_message(service, &element, 1); + } + if (j < oneway) + { + params->magic = MSG_ONEWAY; + vchiq_queue_message(service, &element, 1); + } + } + params->magic = MSG_SYNC; + vchiq_queue_message(service, &element, 1); + vcos_event_wait(&g_server_reply); + } + end = vcos_getmicrosecs(); + + printf("ping (size %d, %d async, %d oneway) -> %fus\n", size, async, oneway, ((float)(end - start))/iters); + + vcos_sleep(20); + + if ((async == 0) && (oneway == 0)) + { + *params = g_params; + params->magic = MSG_CONFIG; + params->blocksize = size ? size : 8; + params->iters = iters; + params->verify = 0; + params->echo = 0; + + element.size = sizeof(*params); + vchiq_queue_message(service, &element, 1); + vcos_event_wait(&g_server_reply); + + vcos_sleep(30); + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + vchiq_queue_bulk_transmit(service, ping_buf, params->blocksize, 0); + vcos_event_wait(&g_server_reply); + } + end = vcos_getmicrosecs(); + + printf("bulk (size %d, async) -> %fus\n", size, ((float)(end - start))/iters); + + vcos_sleep(40); + } + + if (oneway == 0) + { + *params = g_params; + params->magic = MSG_CONFIG; + params->blocksize = size ? size : 8; + params->iters = iters * (async + 1); + params->verify = 0; + params->echo = 0; + + element.size = sizeof(*params); + vchiq_queue_message(service, &element, 1); + vcos_event_wait(&g_server_reply); + + vcos_sleep(50); + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + int j; + for (j = 0; j < async; j++) + vchiq_bulk_transmit(service, ping_buf, params->blocksize, 0, VCHIQ_BULK_MODE_NOCALLBACK); + vchiq_bulk_transmit(service, ping_buf, params->blocksize, 0, VCHIQ_BULK_MODE_BLOCKING); + } + end = vcos_getmicrosecs(); + + printf("bulk (size %d, %d async) -> %fus\n", size, async, ((float)(end - start))/iters); + + vcos_sleep(60); + } + + free(ping_buf); + + bulk_tx_received = 0; +} + +static void +do_vchi_ping_test(VCHI_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters) +{ + uint32_t start, end; + uint32_t actual; + char *ping_buf = malloc(size + sizeof(struct test_params)); + char pong_buf[100]; + struct test_params *params = (struct test_params *)ping_buf; + int msg_size; + int i; + + /* Set up the quotas for messages */ + *params = g_params; + params->magic = MSG_CONFIG; + params->blocksize = 0; + vchi_msg_queue(service, params, sizeof(*params), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + vcos_event_wait(&g_server_reply); + vchiq_set_service_option((VCHIQ_SERVICE_HANDLE_T)service, VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, params->client_message_quota); + + /* Allow enough room for the type header */ + msg_size = (size < 4) ? 4 : size; + + bulk_tx_received = -1; + + if ((oneway == 0) && (async == 0)) + { + params->magic = MSG_SYNC; + + g_sync_mode = 1; + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + vchi_msg_dequeue(service, pong_buf, sizeof(pong_buf), &actual, VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + } + end = vcos_getmicrosecs(); + + printf("vchi ping (size %d) -> %fus\n", size, ((float)(end - start))/iters); + + vcos_sleep(10); + + g_sync_mode = 0; + } + + while (vchi_msg_dequeue(service, pong_buf, sizeof(pong_buf), &actual, VCHI_FLAGS_NONE) != -1) + { + printf("* Unexpected message found in queue - size %d\n", actual); + } + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + int j; + for (j = 0; j < vcos_max(async, oneway); j++) + { + if (j < async) + { + params->magic = MSG_ASYNC; + vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + } + if (j < oneway) + { + params->magic = MSG_ONEWAY; + vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + } + } + params->magic = MSG_SYNC; + vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + vcos_event_wait(&g_server_reply); + } + end = vcos_getmicrosecs(); + + printf("vchi ping (size %d, %d async, %d oneway) -> %fus\n", size, async, oneway, ((float)(end - start))/iters); + + vcos_sleep(20); + + if ((async == 0) && (oneway == 0)) + { + *params = g_params; + params->magic = MSG_CONFIG; + params->blocksize = size ? size : 8; + params->iters = iters; + params->verify = 0; + params->echo = 0; + + vchi_msg_queue(service, params, sizeof(*params), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + vcos_event_wait(&g_server_reply); + + vcos_sleep(30); + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + vchi_bulk_queue_transmit(service, ping_buf, params->blocksize, + VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + vcos_event_wait(&g_server_reply); + } + end = vcos_getmicrosecs(); + + printf("vchi bulk (size %d, %d async, %d oneway) -> %fus\n", size, async, oneway, ((float)(end - start))/iters); + + vcos_sleep(40); + } + + if (oneway == 0) + { + *params = g_params; + params->magic = MSG_CONFIG; + params->blocksize = size ? size : 8; + params->iters = iters * (async + 1); + params->verify = 0; + params->echo = 0; + + vchi_msg_queue(service, params, sizeof(*params), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); + vcos_event_wait(&g_server_reply); + + vcos_sleep(50); + + start = vcos_getmicrosecs(); + for (i = 0; i < iters; i++) + { + int j; + for (j = 0; j < async; j++) + vchi_bulk_queue_transmit(service, ping_buf, params->blocksize, VCHI_FLAGS_NONE, 0); + vchi_bulk_queue_transmit(service, ping_buf, params->blocksize, VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, 0); + } + end = vcos_getmicrosecs(); + + printf("vchi bulk (size %d, %d oneway) -> %fus\n", size, oneway, ((float)(end - start))/iters); + + vcos_sleep(60); + } + + free(ping_buf); + + bulk_tx_received = 0; +} + +static VCHIQ_STATUS_T +func_data_test(VCHIQ_SERVICE_HANDLE_T service, int datalen, int align, int server_align) +{ + enum { PROLOGUE_SIZE = 32, EPILOGUE_SIZE = 32 }; + static uint8_t databuf[PAGE_SIZE + PROLOGUE_SIZE + FUN2_MAX_DATA_SIZE + EPILOGUE_SIZE]; + static uint8_t databuf2[PAGE_SIZE + PROLOGUE_SIZE + FUN2_MAX_DATA_SIZE + EPILOGUE_SIZE]; + uint8_t *data, *data2, *prologue, *epilogue; + VCHIQ_ELEMENT_T element; + int params[4] = { datalen, server_align, align, func_data_test_iter }; + int success = 1, i; + + if (!vcos_verify(datalen < FUN2_MAX_DATA_SIZE)) + return VCHIQ_ERROR; + + if ((func_data_test_iter < func_data_test_start) || (func_data_test_iter > func_data_test_end)) + goto skip_iter; + + element.size = sizeof(params); + element.data = ¶ms; + EXPECT(vchiq_queue_message(service, &element, 1), VCHIQ_SUCCESS); + + memset(databuf, 0xff, sizeof(databuf)); + data = (uint8_t *)((uintptr_t)databuf & ~(PAGE_SIZE - 1)) + align; + data = (uint8_t *)((((intptr_t)databuf + PROLOGUE_SIZE) & ~(FUN2_MAX_ALIGN - 1)) + align - PROLOGUE_SIZE); + if (data < databuf) + data += PAGE_SIZE; + data += PROLOGUE_SIZE; + + EXPECT(vchiq_queue_bulk_receive(service, data, datalen, NULL), VCHIQ_SUCCESS); + + data2 = (uint8_t *)(((uintptr_t)databuf2 + PROLOGUE_SIZE) & ~(PAGE_SIZE - 1)) + align - PROLOGUE_SIZE; + if (data2 < databuf2) + data2 += PAGE_SIZE; + prologue = data2; + data2 += PROLOGUE_SIZE; + epilogue = data2 + datalen; + + memset(prologue, 0xff, PROLOGUE_SIZE); + memset(epilogue, 0xff, EPILOGUE_SIZE); + + for (i = 0; i < (datalen - 1); i++) + { + data2[i] = (uint8_t)(((i & 0x1f) == 0) ? (i >> 5) : i); + } + data2[i] = '\0'; + + /* Attempt to pull the boundaries into the cache */ + prologue = data - PROLOGUE_SIZE; + epilogue = data + datalen; + prologue[PROLOGUE_SIZE - 1] = 0xfe; + epilogue[0] = 0xfe; + + EXPECT(vchiq_queue_bulk_transmit(service, data2, datalen, NULL), VCHIQ_SUCCESS); + vcos_event_wait(&func_test_sync); /* Wait for the receive to complete */ + + for (i = 0; i < PROLOGUE_SIZE; i++) + { + if (prologue[i] != (uint8_t)((i == PROLOGUE_SIZE - 1) ? '\xfe' : '\xff')) + { + vcos_log_error("%d: Prologue corrupted at %x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i, datalen, align, server_align, prologue[i]); + VCOS_BKPT; + success = 0; + break; + } + } + for (i = 0; i < EPILOGUE_SIZE; i++) + { + if (epilogue[i] != (uint8_t)((i == 0) ? '\xfe' : '\xff')) + { + vcos_log_error("%d: Epilogue corrupted at %x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i, datalen, align, server_align, epilogue[i]); + VCOS_BKPT; + success = 0; + break; + } + } + + if (success) + { + int diffs = 0; + for (i = 0; i < datalen; i++) + { + int diff = (i == datalen - 1) ? + (data[i] != 0) : + ((i & 0x1f) == 0) ? + (data[i] != (uint8_t)(i >> 5)) : + (data[i] != (uint8_t)i); + + if (diff) + diffs++; + else if (diffs) + { + vcos_log_error("%d: Data corrupted at %x-%x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i - diffs, i - 1, datalen, align, server_align, data[i-1]); + VCOS_BKPT; + success = 0; + diffs = 0; + } + } + if (diffs) + { + vcos_log_error("%d: Data corrupted at %x-%x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i - diffs, i - 1, datalen, align, server_align, data[i-1]); + VCOS_BKPT; + success = 0; + } + } + +skip_iter: + if (success) + { + func_data_test_iter++; + return VCHIQ_SUCCESS; + } + +error_exit: + return VCHIQ_ERROR; +} + + +#ifdef VCHIQ_LOCAL + +static void *vchiq_test_server(void *param) +{ + VCHIQ_INSTANCE_T instance; + + vcos_demand(vchiq_initialise(&instance) == VCHIQ_SUCCESS); + vchiq_test_start_services(instance); + vchiq_connect(instance); + printf("test server started\n"); + return 0; +} + +#endif + +static VCHIQ_STATUS_T +clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata) +{ + int data; + vcos_mutex_lock(&g_mutex); + if (reason == VCHIQ_MESSAGE_AVAILABLE) + { + /* + * Store the header size as it is going to be released + * and the size may be overwritten by the release. + */ + size_t header_size = header->size; + + if (header_size <= 1) + vchiq_release_message(service, header); + else + /* Responses of length 0 are not sync points */ + if ((header_size >= 4) && (memcpy(&data, header->data, sizeof(data)), data == MSG_ECHO)) + { + /* This is a complete echoed packet */ + if (g_params.verify && (mem_check(header->data, bulk_tx_data[ctrl_received % NUM_BULK_BUFS], g_params.blocksize) != 0)) + g_server_error = "corrupt data"; + else + ctrl_received++; + if (g_server_error || (ctrl_received == g_params.iters)) + vcos_event_signal(&g_shutdown); + vchiq_release_message(service, header); + } + else if (header_size != 0) + g_server_error = header->data; + + if ((header_size != 0) || g_server_error) + vcos_event_signal(&g_server_reply); + } + else if (reason == VCHIQ_BULK_TRANSMIT_DONE) + { + int i = (int)bulk_userdata; + vcos_log_trace(" BULK_TRANSMIT_DONE(%d)", i); + if (bulk_tx_received < 0) + vcos_event_signal(&g_server_reply); + else + { + vcos_assert(i == bulk_tx_received); + bulk_tx_received++; + if (bulk_tx_sent < g_params.iters) + { + vchiq_queue_bulk_transmit(service, bulk_tx_data[i % NUM_BULK_BUFS], g_params.blocksize, (void *)bulk_tx_sent); + bulk_tx_sent++; + } + } + } + else if (reason == VCHIQ_BULK_RECEIVE_DONE) + { + int i = (int)bulk_userdata; + vcos_log_trace(" BULK_RECEIVE_DONE(%d): data '%s'", i, bulk_rx_data[i % NUM_BULK_BUFS]); + vcos_assert(i == bulk_rx_received); + if (g_params.verify && (mem_check(bulk_tx_data[i % NUM_BULK_BUFS], bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize) != 0)) + { + vcos_log_error("* Data corruption - %d: %x, %x, %x", i, (unsigned int)bulk_tx_data[i % NUM_BULK_BUFS], (unsigned int)bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize); + VCOS_BKPT; + } + bulk_rx_received++; + if (bulk_rx_sent < g_params.iters) + { + if (g_params.verify) + memset(bulk_rx_data[i % NUM_BULK_BUFS], 0xff, g_params.blocksize); + vchiq_queue_bulk_receive(service, bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize, (void *)bulk_rx_sent); + bulk_rx_sent++; + } + } + else if (reason == VCHIQ_BULK_TRANSMIT_ABORTED) + { + int i = (int)bulk_userdata; + vcos_log_info(" BULK_TRANSMIT_ABORTED(%d)", i); + } + else if (reason == VCHIQ_BULK_RECEIVE_ABORTED) + { + int i = (int)bulk_userdata; + vcos_log_info(" BULK_RECEIVE_ABORTED(%d)", i); + } + if ((bulk_tx_received == g_params.iters) && + ((g_params.echo == 0) || (bulk_rx_received == g_params.iters))) + vcos_event_signal(&g_shutdown); + vcos_mutex_unlock(&g_mutex); + return VCHIQ_SUCCESS; +} + +static void +vchi_clnt_callback(void *callback_param, + VCHI_CALLBACK_REASON_T reason, + void *handle) +{ + VCHI_SERVICE_HANDLE_T service = *(VCHI_SERVICE_HANDLE_T *)callback_param; + vcos_mutex_lock(&g_mutex); + if (reason == VCHI_CALLBACK_MSG_AVAILABLE) + { + if (!g_sync_mode) + { + static char pong_buf[100]; + uint32_t actual; + while (vchi_msg_dequeue(service, pong_buf, sizeof(pong_buf), &actual, VCHI_FLAGS_NONE) == 0) + { + if (actual > 1) + g_server_error = pong_buf; + if (actual != 0) + { + /* Responses of length 0 are not sync points */ + vcos_event_signal(&g_server_reply); + break; + } + } + } + } + else if (reason == VCHI_CALLBACK_BULK_SENT) + { + int i = (int)handle; + vcos_log_trace(" BULK_TRANSMIT_DONE(%d)", i); + if (bulk_tx_received < 0) + vcos_event_signal(&g_server_reply); + else + { + vcos_assert(i == bulk_tx_received); + bulk_tx_received++; + if (bulk_tx_sent < g_params.iters) + { + vchi_bulk_queue_transmit(service, bulk_tx_data[i % NUM_BULK_BUFS], + g_params.blocksize, + VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED, + (void *)bulk_tx_sent); + bulk_tx_sent++; + } + } + } + else if (reason == VCHI_CALLBACK_BULK_RECEIVED) + { + int i = (int)handle; + vcos_log_trace(" BULK_RECEIVE_DONE(%d): data '%s'", i, bulk_rx_data[i % NUM_BULK_BUFS]); + vcos_assert(i == bulk_rx_received); + if (g_params.verify && (mem_check(bulk_tx_data[i % NUM_BULK_BUFS], bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize) != 0)) + { + vcos_log_error("* Data corruption - %x, %x, %x", (unsigned int)bulk_tx_data[i % NUM_BULK_BUFS], (unsigned int)bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize); + VCOS_BKPT; + } + bulk_rx_received++; + if (bulk_rx_sent < g_params.iters) + { + if (g_params.verify) + memset(bulk_rx_data[i % NUM_BULK_BUFS], 0xff, g_params.blocksize); + vchi_bulk_queue_receive(service, bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize, + VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED, + (void *)bulk_rx_sent); + bulk_rx_sent++; + } + } + else if (reason == VCHI_CALLBACK_BULK_TRANSMIT_ABORTED) + { + int i = (int)handle; + vcos_log_info(" BULK_TRANSMIT_ABORTED(%d)", i); + } + else if (reason == VCHI_CALLBACK_BULK_RECEIVE_ABORTED) + { + int i = (int)handle; + vcos_log_info(" BULK_RECEIVE_ABORTED(%d)", i); + } + if ((bulk_tx_received == g_params.iters) && (bulk_rx_received == g_params.iters)) + vcos_event_signal(&g_shutdown); + vcos_mutex_unlock(&g_mutex); +} + +static VCHIQ_STATUS_T +func_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata) +{ + static int callback_count = 0, bulk_count = 0; + int callback_index = 0, bulk_index = 0; + + if (reason < VCHIQ_BULK_TRANSMIT_DONE) + { + callback_count++; + + START_CALLBACK(VCHIQ_SERVICE_CLOSED, 2) + END_CALLBACK(VCHIQ_SUCCESS) + + START_CALLBACK(VCHIQ_MESSAGE_AVAILABLE, 1) + EXPECT(bulk_userdata, NULL); + EXPECT(header->size, 26); + EXPECT(mem_check(header->data, "abcdefghijklmnopqrstuvwxyz", 26), 0); + vchiq_release_message(service, header); + END_CALLBACK(VCHIQ_SUCCESS) + + START_CALLBACK(VCHIQ_MESSAGE_AVAILABLE, 1) + EXPECT(bulk_userdata, NULL); + EXPECT(header->size, 0); + vchiq_release_message(service, header); + EXPECT(vchiq_queue_bulk_receive(service, clnt_service2_data, sizeof(clnt_service2_data), (void*)0x1004), VCHIQ_SUCCESS); + vcos_event_signal(&func_test_sync); + END_CALLBACK(VCHIQ_SUCCESS) + + START_CALLBACK(VCHIQ_SERVICE_CLOSED, 1) + vcos_event_signal(&func_test_sync); + END_CALLBACK(VCHIQ_SUCCESS) + + START_CALLBACK(VCHIQ_SERVICE_CLOSED, 2) + vcos_event_signal(&func_test_sync); + callback_count = 0; + bulk_count = 0; + END_CALLBACK(VCHIQ_SUCCESS) + } + else + { + bulk_count++; + + START_BULK_CALLBACK(VCHIQ_BULK_TRANSMIT_DONE, 1, 0x1001) + memset(clnt_service2_data, 0xff, sizeof(clnt_service2_data)); + EXPECT(vchiq_queue_bulk_receive(service, clnt_service2_data, sizeof(clnt_service2_data), (void*)0x1002), VCHIQ_SUCCESS); + END_CALLBACK(VCHIQ_SUCCESS) + + START_BULK_CALLBACK(VCHIQ_BULK_RECEIVE_ABORTED, 1, 0x1002) + EXPECT(vchiq_queue_bulk_receive(service, clnt_service2_data, sizeof(clnt_service2_data), (void*)0x1003), VCHIQ_SUCCESS); + END_CALLBACK(VCHIQ_SUCCESS) + + START_BULK_CALLBACK(VCHIQ_BULK_RECEIVE_DONE, 1, 0x1003) + (void)(mem_check(clnt_service1_data, clnt_service2_data, sizeof(clnt_service1_data)), 0); + (void)(mem_check(clnt_service1_data, clnt_service2_data + sizeof(clnt_service1_data), sizeof(clnt_service1_data)), 0); + END_CALLBACK(VCHIQ_SUCCESS) + + START_BULK_CALLBACK(VCHIQ_BULK_RECEIVE_ABORTED, 1, 0x1004) + END_CALLBACK(VCHIQ_SUCCESS) + + START_BULK_CALLBACK(VCHIQ_BULK_TRANSMIT_ABORTED, 2, 0x2001) + END_CALLBACK(VCHIQ_SUCCESS) + } + +error_exit: + callback_count = 0; + bulk_count = 0; + + func_error = 1; + vcos_event_signal(&func_test_sync); + + return VCHIQ_ERROR; +} + +static VCHIQ_STATUS_T +fun2_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata) +{ + vcos_unused(header); + vcos_unused(service); + vcos_unused(bulk_userdata); + + switch (reason) + { + case VCHIQ_SERVICE_OPENED: + case VCHIQ_SERVICE_CLOSED: + case VCHIQ_BULK_TRANSMIT_DONE: + break; + case VCHIQ_BULK_RECEIVE_DONE: + vcos_event_signal(&func_test_sync); + break; + default: + fun2_error = 1; + vcos_event_signal(&func_test_sync); + break; + } + + return VCHIQ_SUCCESS; +} + +static int mem_check(const void *expected, const void *actual, int size) +{ + if (memcmp(expected, actual, size) != 0) + { + int i; + for (i = 0; i < size; i++) + { + int ce = ((const char *)expected)[i]; + int ca = ((const char *)actual)[i]; + if (ca != ce) + printf("%08x,%x: %02x <-> %02x\n", i + (unsigned int)actual, i, ce, ca); + } + printf("mem_check failed - buffer %x, size %d\n", (unsigned int)actual, size); + return 1; + } + return 0; +} + +static void usage(void) +{ + printf("Usage: vchiq_test [] \n"); + printf(" where is any of:\n"); + printf(" -a set the client and server bulk alignment (modulo 32)\n"); + printf(" -A set the client and server bulk alignment (modulo 4096)\n"); + printf(" -e disable echoing in the main bulk transfer mode\n"); + printf(" -k skip the first func data tests\n"); + printf(" -m set the client message quota to \n"); + printf(" -M set the server message quota to \n"); + printf(" -q disable data verification\n"); + printf(" -s ???? service (any 4 characters)\n"); + printf(" -v enable more verbose output\n"); + printf(" -r reserve bytes for seconds\n"); + printf(" -K send a SIGKILL after ms\n"); + printf(" and is one of:\n"); + printf(" -c control test (size in bytes)\n"); + printf(" -b bulk test (size in kilobytes)\n"); + printf(" -f functional test\n"); + printf(" -p ping test\n"); + printf(" -t check the timer\n"); + printf(" and is the number of test iterations\n"); + exit(1); +} + +static void check_timer(void) +{ + uint32_t start = vcos_getmicrosecs(); + uint32_t sleep_time = 1000; + + printf("0\n"); + + while (1) + { + uint32_t now; + vcos_sleep(sleep_time); + now = vcos_getmicrosecs(); + printf("%d - sleep %d\n", now - start, sleep_time); + } +} + +static char *buf_align(char *buf, int align_size, int align) +{ + char *aligned = buf - ((intptr_t)buf & (align_size - 1)) + align; + if (aligned < buf) + aligned += align_size; + return aligned; +} + +#ifdef ANDROID + +static void kill_timeout_handler(int cause, siginfo_t *how, void *ucontext) +{ + printf("Sending signal SIGKILL\n"); + kill(main_process_pid, SIGKILL); +} + +static int setup_auto_kill(int timeout_ms) +{ + long timeout; + struct timeval interval; + + if (timeout_ms <= 0) + { + return -1; + } + timeout = 1000 * timeout_ms; + + /* install a signal handler for the alarm */ + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction = kill_timeout_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGALRM, &sa, 0)) + { + perror("sigaction"); + exit(1); + } + + /* when to expire */ + interval.tv_sec = timeout / 1000000; + interval.tv_usec = timeout % 1000000; + + struct itimerval alarm_spec = { + .it_interval = {0,0}, + .it_value = interval + }; + + int rc = setitimer(ITIMER_REAL, &alarm_spec, NULL); + if (rc < 0) + { + perror("setitimer failed"); + exit(1); + } + + return 0; +} + + + +#endif + +#ifdef VCOS_APPLICATION_INITIALIZE + +static VCOS_THREAD_T Task_0; + +void *main_task(void *param) +{ + vchiq_test(rtos_argc, rtos_argv); + + VCOS_BKPT; + + return NULL; +} + +#include "vcfw/logging/logging.h" + +void VCOS_APPLICATION_INITIALIZE(void *first_available_memory) +{ + const int stack_size = 64*1024; + void *pointer = NULL; + VCOS_STATUS_T status; + + logging_init(); + logging_level(LOGGING_VCOS); + vcos_init(); + + /* Create task 0. */ +#if VCOS_CAN_SET_STACK_ADDR + pointer = malloc(stack_size); + vcos_demand(pointer); +#endif + status = vcos_thread_create_classic( &Task_0, "TASK 0", main_task, (void *)0, pointer, stack_size, + 10 | VCOS_AFFINITY_DEFAULT, 20, VCOS_START ); + vcos_demand(status == VCOS_SUCCESS); +} + +#else + +int main(int argc, char **argv) +{ +#ifdef ANDROID + main_process_pid = getpid(); +#endif + + vcos_init(); + vcos_use_android_log = 0; + return vchiq_test(argc, argv); +} + +#endif diff --git a/interface/vchiq_arm/vchiq_test.h b/interface/vchiq_arm/vchiq_test.h new file mode 100755 index 0000000..4a93345 --- /dev/null +++ b/interface/vchiq_arm/vchiq_test.h @@ -0,0 +1,135 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_TEST_H +#define VCHIQ_TEST_H + +#include "vchiq_test_if.h" + +#define VCOS_LOG_CATEGORY (&vchiq_test_log_category) + +#define VERBOSE_TRACE 1 + +#define FUNC_FOURCC VCHIQ_MAKE_FOURCC('f','u','n','c') +#define FUN2_FOURCC VCHIQ_MAKE_FOURCC('f','u','n','2') + +#define SERVICE1_DATA_SIZE 1024 +#define SERVICE2_DATA_SIZE 2048 +#define FUN2_MAX_DATA_SIZE 16384 +#define FUN2_MAX_ALIGN 4096 +#define BULK_ALIGN_SIZE 4096 + +#define VCHIQ_TEST_VER 3 + +enum { + MSG_ERROR, + MSG_ONEWAY, + MSG_ASYNC, + MSG_SYNC, + MSG_CONFIG, + MSG_ECHO +}; + +struct test_params +{ + int magic; /* = MSG_CONFIG */ + int blocksize; + int iters; + int verify; + int echo; + int align_size; + int client_align; + int server_align; + int client_message_quota; + int server_message_quota; +}; + +#if VERBOSE_TRACE + +#define EXPECT(_e, _v) if (_e != _v) { vcos_log_error("%d: " #_e " != " #_v, __LINE__); VCOS_BKPT; goto error_exit; } else { vcos_log_trace("%d: " #_e " == " #_v, __LINE__); } + +#define START_CALLBACK(_r, _u) \ + if (++callback_index == callback_count) { \ + if (reason != _r) { \ + vcos_log_error("%d: expected callback reason " #_r ", got %d", __LINE__, reason); VCOS_BKPT; goto error_exit; \ + } \ + else if ((int)VCHIQ_GET_SERVICE_USERDATA(service) != _u) { \ + vcos_log_error("%d: expected userdata %d, got %d", __LINE__, _u, (int)VCHIQ_GET_SERVICE_USERDATA(service)); VCOS_BKPT; goto error_exit; \ + } \ + else \ + { \ + vcos_log_trace("%d: " #_r ", " #_u, __LINE__); \ + } + +#define START_BULK_CALLBACK(_r, _u, _bu) \ + if (++bulk_index == bulk_count) { \ + if (reason != _r) { \ + vcos_log_error("%d: expected callback reason " #_r ", got %d", __LINE__, reason); VCOS_BKPT; goto error_exit; \ + } \ + else if ((int)VCHIQ_GET_SERVICE_USERDATA(service) != _u) { \ + vcos_log_error("%d: expected userdata %d, got %d", __LINE__, _u, (int)VCHIQ_GET_SERVICE_USERDATA(service)); VCOS_BKPT; goto error_exit; \ + } \ + else if ((int)bulk_userdata != _bu) { \ + vcos_log_error("%d: expected bulk_userdata %d, got %d", __LINE__, _bu, (int)bulk_userdata); VCOS_BKPT; goto error_exit; \ + } \ + else \ + { \ + vcos_log_trace("%d: " #_r ", " #_u ", " #_bu, __LINE__); \ + } + +#else + +#define EXPECT(_e, _v) if (_e != _v) { vcos_log_trace("%d: " #_e " != " #_v, __LINE__); VCOS_BKPT; goto error_exit; } + +#define START_CALLBACK(_r, _u) \ + if (++callback_index == callback_count) { \ + if (reason != _r) { \ + vcos_log_error("%d: expected callback reason " #_r ", got %d", __LINE__, reason); VCOS_BKPT; goto error_exit; \ + } \ + else if ((int)VCHIQ_GET_SERVICE_USERDATA(service) != _u) { \ + vcos_log_error("%d: expected userdata %d, got %d", __LINE__, _u, (int)VCHIQ_GET_SERVICE_USERDATA(service)); VCOS_BKPT; goto error_exit; \ + } + +#define START_BULK_CALLBACK(_r, _u, _bu) \ + if (++bulk_index == bulk_count) { \ + if (reason != _r) { \ + vcos_log_error("%d: expected callback reason " #_r ", got %d", __LINE__, reason); VCOS_BKPT; goto error_exit; \ + } \ + else if ((int)VCHIQ_GET_SERVICE_USERDATA(service) != _u) { \ + vcos_log_error("%d: expected userdata %d, got %d", __LINE__, _u, (int)VCHIQ_GET_SERVICE_USERDATA(service)); VCOS_BKPT; goto error_exit; \ + } \ + else if ((int)bulk_userdata != _bu) { \ + vcos_log_error("%d: expected bulkuserdata %d, got %d", __LINE__, _bu, (int)bulk_userdata); VCOS_BKPT; goto error_exit; \ + } + +#endif + +#define END_CALLBACK(_s) \ + return _s; \ + } + +#endif diff --git a/interface/vchiq_arm/vchiq_test_if.h b/interface/vchiq_arm/vchiq_test_if.h new file mode 100755 index 0000000..665c1a6 --- /dev/null +++ b/interface/vchiq_arm/vchiq_test_if.h @@ -0,0 +1,35 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCHIQ_TEST_IF_H +#define VCHIQ_TEST_IF_H + +#include "vchiq.h" + +extern void vchiq_test_start_services(VCHIQ_INSTANCE_T instance); + +#endif diff --git a/interface/vchiq_arm/vchiq_util.c b/interface/vchiq_arm/vchiq_util.c new file mode 100755 index 0000000..f6e361f --- /dev/null +++ b/interface/vchiq_arm/vchiq_util.c @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "vchiq_util.h" + +#if !defined(__KERNEL__) +#include +#endif + +static __inline int is_pow2(int i) +{ + return i && !(i & (i - 1)); +} + +int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) +{ + vcos_assert(is_pow2(size)); + + queue->size = size; + queue->read = 0; + queue->write = 0; + + vcos_event_create(&queue->pop, "vchiu"); + vcos_event_create(&queue->push, "vchiu"); + + queue->storage = vcos_malloc(size * sizeof(VCHIQ_HEADER_T *), VCOS_FUNCTION); + if (queue->storage == NULL) + { + vchiu_queue_delete(queue); + return 0; + } + return 1; +} + +void vchiu_queue_delete(VCHIU_QUEUE_T *queue) +{ + vcos_event_delete(&queue->pop); + vcos_event_delete(&queue->push); + if (queue->storage != NULL) + vcos_free(queue->storage); +} + +int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) +{ + return queue->read == queue->write; +} + +int vchiu_queue_is_full(VCHIU_QUEUE_T *queue) +{ + return queue->write == queue->read + queue->size; +} + +void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) +{ + while (queue->write == queue->read + queue->size) + vcos_event_wait(&queue->pop); + + queue->storage[queue->write & (queue->size - 1)] = header; + + queue->write++; + + vcos_event_signal(&queue->push); +} + +VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) +{ + while (queue->write == queue->read) + vcos_event_wait(&queue->push); + + return queue->storage[queue->read & (queue->size - 1)]; +} + +VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) +{ + VCHIQ_HEADER_T *header; + + while (queue->write == queue->read) + vcos_event_wait(&queue->push); + + header = queue->storage[queue->read & (queue->size - 1)]; + + queue->read++; + + vcos_event_signal(&queue->pop); + + return header; +} diff --git a/interface/vchiq_arm/vchiq_util.h b/interface/vchiq_arm/vchiq_util.h new file mode 100755 index 0000000..3628da7 --- /dev/null +++ b/interface/vchiq_arm/vchiq_util.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_UTIL_H +#define VCHIQ_UTIL_H + +#include "vchiq_if.h" +#include "interface/vcos/vcos.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int size; + int read; + int write; + + VCOS_EVENT_T pop; + VCOS_EVENT_T push; + + VCHIQ_HEADER_T **storage; +} VCHIU_QUEUE_T; + +extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); +extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); + +extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); +extern int vchiu_queue_is_full(VCHIU_QUEUE_T *queue); + +extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); + +extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); +extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/interface/vcos/CMakeLists.txt b/interface/vcos/CMakeLists.txt new file mode 100755 index 0000000..23a8d72 --- /dev/null +++ b/interface/vcos/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required (VERSION 2.8) + +get_filename_component (VIDEOCORE_ROOT "../.." ABSOLUTE) +include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) + +set (HEADERS + vcos_assert.h + vcos_atomic_flags.h + vcos_blockpool.h + vcos_cmd.h + vcos_dlfcn.h + vcos_event_flags.h + vcos_event.h + vcos.h + vcos_init.h + vcos_inttypes.h + vcos_isr.h + vcos_legacy_isr.h + vcos_logging.h + vcos_logging_control.h + vcos_lowlevel_thread.h + vcos_mem.h + vcos_mempool.h + vcos_msgqueue.h + vcos_mutex.h + vcos_named_semaphore.h + vcos_once.h + vcos_queue.h + vcos_quickslow_mutex.h + vcos_reentrant_mutex.h + vcos_semaphore.h + vcos_stdint.h + vcos_string.h + vcos_thread_attr.h + vcos_thread.h + vcos_timer.h + vcos_tls.h + vcos_types.h +) + +foreach (header ${HEADERS}) + configure_file ("${header}" "${VCOS_HEADERS_BUILD_DIR}/${header}" COPYONLY) +endforeach () + +if (CMAKE_COMPILER_IS_GNUCC) + add_definitions (-ggdb -Werror -Wall) +endif () + +if (CMAKE_COMPILER_2005) + add_definitions (/WX /W4 /wd4127 /D_CRT_SECURE_NO_DEPRECATE) +endif () + +include_directories (${VIDEOCORE_ROOT} ${VCOS_HEADERS_BUILD_DIR}) + +add_subdirectory (${RTOS}) + +set(VCOS_EXCLUDE_TESTS TRUE) +if (NOT DEFINED VCOS_EXCLUDE_TESTS) +add_testapp_subdirectory (test) +endif (NOT DEFINED VCOS_EXCLUDE_TESTS) + +if (WIN32) + build_command (RELEASE_BUILD_CMD CONFIGURATION Release) + build_command (DEBUG_BUILD_CMD CONFIGURATION Debug) + configure_file (build_all.bat.in build_all.bat @ONLY) +endif () + +#install (FILES ${HEADERS} DESTINATION include/interface/vcos) diff --git a/interface/vcos/generic/CMakeLists.txt b/interface/vcos/generic/CMakeLists.txt new file mode 100755 index 0000000..c09f376 --- /dev/null +++ b/interface/vcos/generic/CMakeLists.txt @@ -0,0 +1,21 @@ + +set (HEADERS + vcos_common.h + vcos_generic_blockpool.h + vcos_generic_event_flags.h + vcos_generic_named_sem.h + vcos_generic_quickslow_mutex.h + vcos_generic_reentrant_mtx.h + vcos_generic_tls.h + vcos_joinable_thread_from_plain.h + vcos_latch_from_sem.h + vcos_mem_from_malloc.h + vcos_mutexes_are_reentrant.h + vcos_thread_reaper.h +) + +foreach (header ${HEADERS}) + configure_file ("${header}" "${VCOS_HEADERS_BUILD_DIR}/generic/${header}" COPYONLY) +endforeach () + +install (FILES ${HEADERS} DESTINATION include/interface/vcos/generic) diff --git a/interface/vcos/generic/vcos_abort.c b/interface/vcos/generic/vcos_abort.c new file mode 100755 index 0000000..7da16ac --- /dev/null +++ b/interface/vcos/generic/vcos_abort.c @@ -0,0 +1,85 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vcos/vcos.h" +#ifdef __VIDEOCORE__ +#include "host_support/include/vc_debug_sym.h" +#include "vcfw/vclib/vclib.h" +#endif +#include + + +int vcos_verify_bkpts = 0; +#ifdef __VIDEOCORE__ +VC_DEBUG_VAR(vcos_verify_bkpts); +#endif + +int vcos_verify_bkpts_enabled(void) +{ + return vcos_verify_bkpts; +} + +int vcos_verify_bkpts_enable(int enable) +{ + int old = vcos_verify_bkpts; + vcos_verify_bkpts = enable; + return old; +} + +/** + * Call the fatal error handler. + */ +void vcos_abort(void) +{ + VCOS_ALERT("vcos_abort: Halting"); + +#ifdef __VIDEOCORE__ + _bkpt(); +#endif + +#if defined(VCOS_HAVE_BACKTRACE) && !defined(NDEBUG) + vcos_backtrace_self(); +#endif + +#ifdef __VIDEOCORE__ + /* Flush the cache to help with postmortem RAM-dump debugging */ + vclib_cache_flush(); +#endif + +#ifdef PLATFORM_RASPBERRYPI + extern void pattern(int); + while(1) + pattern(8); +#endif + + /* Insert chosen fatal error handler here */ +#if defined __VIDEOCORE__ && !defined(NDEBUG) + while(1); /* allow us to attach a debugger after the fact and see where we came from. */ +#else + abort(); /* on vc this ends up in _exit_halt which doesn't give us any stack backtrace */ +#endif +} diff --git a/interface/vcos/generic/vcos_cmd.c b/interface/vcos/generic/vcos_cmd.c new file mode 100755 index 0000000..3dfbdb3 --- /dev/null +++ b/interface/vcos/generic/vcos_cmd.c @@ -0,0 +1,722 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/***************************************************************************** +* +* This file provides a generic command line interface which allows +* vcos internals to be manipulated and/or displayed. +* +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ + +#include "interface/vcos/vcos.h" + +#ifdef HAVE_VCOS_VERSION +#include "interface/vcos/vcos_build_info.h" +#endif + +#ifdef _VIDEOCORE +#include "vcfw/logging/logging.h" +#endif + +/* ---- Public Variables ------------------------------------------------- */ + +/* ---- Private Constants and Types -------------------------------------- */ + +#define VCOS_LOG_CATEGORY (&vcos_cmd_log_category) +VCOS_LOG_CAT_T vcos_cmd_log_category; + +/* ---- Private Variables ------------------------------------------------ */ + +static struct VCOS_CMD_GLOBALS_T +{ + VCOS_MUTEX_T lock; + VCOS_ONCE_T initialized; + + unsigned num_cmd_entries; + unsigned num_cmd_alloc; + VCOS_CMD_T *cmd_entry; + + VCOS_LOG_CAT_T *log_category; +} cmd_globals; + +#ifdef HAVE_VCOS_VERSION +/* Keep the static strings in the image from being dropped by + * the linker. + */ +extern const char *vcos_get_build_strings(unsigned id); +const char *(*vcos_keep_static_strings)(unsigned); +#endif + +/* ---- Private Function Prototypes -------------------------------------- */ + +static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param ); + +/* ---- Functions ------------------------------------------------------- */ + +/***************************************************************************** +* +* Walks through the commands looking for a particular command +* +*****************************************************************************/ + +static VCOS_CMD_T *find_cmd( VCOS_CMD_T *cmd_entry, const char *name ) +{ + VCOS_CMD_T *scan_entry = cmd_entry; + + while ( scan_entry->name != NULL ) + { + if ( vcos_strcmp( scan_entry->name, name ) == 0 ) + { + return scan_entry; + } + scan_entry++; + } + + return NULL; +} + +/***************************************************************************** +* +* Saves away +* each line individually. +* +*****************************************************************************/ + +void vcos_cmd_always_log_output( VCOS_LOG_CAT_T *log_category ) +{ + cmd_globals.log_category = log_category; +} + +/***************************************************************************** +* +* Walks through a buffer containing newline separated lines, and logs +* each line individually. +* +*****************************************************************************/ + +static void cmd_log_results( VCOS_CMD_PARAM_T *param ) +{ + char *start; + char *end; + + start = end = param->result_buf; + + while ( *start != '\0' ) + { + while (( *end != '\0' ) && ( *end != '\n' )) + end++; + + if ( *end == '\n' ) + { + *end++ = '\0'; + } + + if ( cmd_globals.log_category != NULL ) + { + if ( vcos_is_log_enabled( cmd_globals.log_category, VCOS_LOG_INFO )) + { + vcos_log_impl( cmd_globals.log_category, VCOS_LOG_INFO, "%s", start ); + } + } + else + { + vcos_log_info( "%s", start ); + } + + start = end; + } + + /* Since we logged the buffer, reset the pointer back to the beginning. */ + + param->result_ptr = param->result_buf; + param->result_buf[0] = '\0'; +} + +/***************************************************************************** +* +* Since we may have limited output space, we create a generic routine +* which tries to use the result space, but will switch over to using +* logging if the output is too large. +* +*****************************************************************************/ + +void vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args ) +{ + int bytes_written; + int bytes_remaining; + + bytes_remaining = (int)(param->result_size - ( param->result_ptr - param->result_buf )); + + bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args ); + + if ( cmd_globals.log_category != NULL ) + { + /* We're going to log each line as we encounter it. If the buffer + * doesn't end in a newline, then we'll wait for one first. + */ + + if ( (( bytes_written + 1 ) >= bytes_remaining ) + || ( param->result_ptr[ bytes_written - 1 ] == '\n' )) + { + cmd_log_results( param ); + } + else + { + param->result_ptr += bytes_written; + } + } + else + { + if (( bytes_written + 1 ) >= bytes_remaining ) + { + /* Output doesn't fit - switch over to logging */ + + param->use_log = 1; + + *param->result_ptr = '\0'; /* Zap the partial line that didn't fit above. */ + + cmd_log_results( param ); /* resets result_ptr */ + + bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args ); + } + param->result_ptr += bytes_written; + } +} + +/***************************************************************************** +* +* Prints the output. +* +*****************************************************************************/ + +void vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + vcos_cmd_vprintf( param, fmt, args ); + va_end( args ); +} + +/***************************************************************************** +* +* Prints the arguments which were on the command line prior to ours. +* +*****************************************************************************/ + +static void print_argument_prefix( VCOS_CMD_PARAM_T *param ) +{ + int arg_idx; + + for ( arg_idx = 0; ¶m->argv_orig[arg_idx] != param->argv; arg_idx++ ) + { + vcos_cmd_printf( param, "%s ", param->argv_orig[arg_idx] ); + } +} + +/***************************************************************************** +* +* Prints an error message, prefixed by the command chain required to get +* to where we're at. +* +*****************************************************************************/ + +void vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) +{ + va_list args; + + print_argument_prefix( param ); + + va_start( args, fmt ); + vcos_cmd_vprintf( param, fmt, args ); + va_end( args ); + vcos_cmd_printf( param, "\n" ); +} + +/**************************************************************************** +* +* usage - prints command usage for an array of commands. +* +***************************************************************************/ + +static void usage( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry ) +{ + int cmd_idx; + int nameWidth = 0; + int argsWidth = 0; + VCOS_CMD_T *scan_entry; + + vcos_cmd_printf( param, "Usage: " ); + print_argument_prefix( param ); + vcos_cmd_printf( param, "command [args ...]\n" ); + vcos_cmd_printf( param, "\n" ); + vcos_cmd_printf( param, "Where command is one of the following:\n" ); + + for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ ) + { + int aw; + int nw; + + scan_entry = &cmd_entry[cmd_idx]; + + nw = vcos_strlen( scan_entry->name ); + aw = vcos_strlen( scan_entry->args ); + + if ( nw > nameWidth ) + { + nameWidth = nw; + } + if ( aw > argsWidth ) + { + argsWidth = aw; + } + } + + for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ ) + { + scan_entry = &cmd_entry[cmd_idx]; + + vcos_cmd_printf( param, " %-*s %-*s - %s\n", + nameWidth, scan_entry->name, + argsWidth, scan_entry->args, + scan_entry->descr ); + } +} + +/**************************************************************************** +* +* Prints the usage for the current command. +* +***************************************************************************/ + +void vcos_cmd_usage( VCOS_CMD_PARAM_T *param ) +{ + VCOS_CMD_T *cmd_entry; + + cmd_entry = param->cmd_entry; + + if ( cmd_entry->sub_cmd_entry != NULL ) + { + /* This command is command with sub-commands */ + + usage( param, param->cmd_entry->sub_cmd_entry ); + } + else + { + vcos_cmd_printf( param, "Usage: " ); + print_argument_prefix( param ); + vcos_cmd_printf( param, "%s %s - %s\n", + param->argv[0], + param->cmd_entry->args, + param->cmd_entry->descr ); + } +} + +/***************************************************************************** +* +* Command to print out the help +* +* This help command is only called from the main menu. +* +*****************************************************************************/ + +static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param ) +{ + VCOS_CMD_T *found_entry; + +#if 0 + { + int arg_idx; + + vcos_log_trace( "%s: argc = %d", __func__, param->argc ); + for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) + { + vcos_log_trace( "%s: argv[%d] = '%s'", __func__, arg_idx, param->argv[arg_idx] ); + } + } +#endif + + /* If there is an argument after the word help, then we want to print + * help for that command. + */ + + if ( param->argc == 1 ) + { + if ( param->cmd_parent_entry == cmd_globals.cmd_entry ) + { + /* Bare help - print the command usage for the root */ + + usage( param, cmd_globals.cmd_entry ); + return VCOS_SUCCESS; + } + + /* For all other cases help requires an argument */ + + vcos_cmd_error( param, "%s requires an argument", param->argv[0] ); + return VCOS_EINVAL; + } + + /* We were given an argument. */ + + if (( found_entry = find_cmd( param->cmd_parent_entry, param->argv[1] )) != NULL ) + { + /* Make it look like the command that was specified is the one that's + * currently running + */ + + param->cmd_entry = found_entry; + param->argv[0] = param->argv[1]; + param->argv++; + param->argc--; + + vcos_cmd_usage( param ); + return VCOS_SUCCESS; + } + + vcos_cmd_error( param, "- unrecognized command: '%s'", param->argv[1] ); + return VCOS_ENOENT; +} + +/***************************************************************************** +* +* Command to print out the version/build information. +* +*****************************************************************************/ + +#ifdef HAVE_VCOS_VERSION + +static VCOS_STATUS_T version_cmd( VCOS_CMD_PARAM_T *param ) +{ + static const char* copyright = "Copyright (c) 2011 Broadcom"; + + vcos_cmd_printf( param, "%s %s\n%s\nversion %s\nhost %s", + vcos_get_build_date(), + vcos_get_build_time(), + copyright, + vcos_get_build_version(), + vcos_get_build_hostname() ); + + return VCOS_SUCCESS; +} + +#endif + +/***************************************************************************** +* +* Internal commands +* +*****************************************************************************/ + +static VCOS_CMD_T cmd_help = { "help", "[command]", help_cmd, NULL, "Prints command help information" }; + +#ifdef HAVE_VCOS_VERSION +static VCOS_CMD_T cmd_version = { "version", "", version_cmd, NULL, "Prints build/version information" }; +#endif + +/***************************************************************************** +* +* Walks the command table and executes the commands +* +*****************************************************************************/ + +static VCOS_STATUS_T execute_cmd( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry ) +{ + const char *cmdStr; + VCOS_CMD_T *found_entry; + +#if 0 + { + int arg_idx; + + vcos_cmd_printf( param, "%s: argc = %d", __func__, param->argc ); + for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) + { + vcos_cmd_printf( param, " argv[%d] = '%s'", arg_idx, param->argv[arg_idx] ); + } + vcos_cmd_printf( param, "\n" ); + } +#endif + + if ( param->argc <= 1 ) + { + /* No command specified */ + + vcos_cmd_error( param, "%s - no command specified", param->argv[0] ); + return VCOS_EINVAL; + } + + /* argv[0] is the command/program that caused us to get invoked, so we strip + * it off. + */ + + param->argc--; + param->argv++; + param->cmd_parent_entry = cmd_entry; + + /* Not the help command, scan for the command and execute it. */ + + cmdStr = param->argv[0]; + + if (( found_entry = find_cmd( cmd_entry, cmdStr )) != NULL ) + { + if ( found_entry->sub_cmd_entry != NULL ) + { + return execute_cmd( param, found_entry->sub_cmd_entry ); + } + + param->cmd_entry = found_entry; + return found_entry->cmd_fn( param ); + } + + /* Unrecognized command - check to see if it was the help command */ + + if ( vcos_strcmp( cmdStr, cmd_help.name ) == 0 ) + { + return help_cmd( param ); + } + + vcos_cmd_error( param, "- unrecognized command: '%s'", cmdStr ); + return VCOS_ENOENT; +} + +/***************************************************************************** +* +* Initializes the command line parser. +* +*****************************************************************************/ + +static void vcos_cmd_init( void ) +{ + vcos_mutex_create( &cmd_globals.lock, "vcos_cmd" ); + + cmd_globals.num_cmd_entries = 0; + cmd_globals.num_cmd_alloc = 0; + cmd_globals.cmd_entry = NULL; + +#ifdef HAVE_VCOS_VERSION + vcos_keep_static_strings = vcos_get_build_strings; +#endif +} + +/***************************************************************************** +* +* Shuts down the command line parser. +* +*****************************************************************************/ + +void vcos_cmd_shutdown( void ) +{ + vcos_mutex_delete( &cmd_globals.lock ); + + vcos_free( cmd_globals.cmd_entry ); + cmd_globals.cmd_entry = NULL; +} + +/***************************************************************************** +* +* Command line processor. +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf ) +{ + VCOS_STATUS_T rc = VCOS_EINVAL; + VCOS_CMD_PARAM_T param; + + vcos_once( &cmd_globals.initialized, vcos_cmd_init ); + + param.argc = argc; + param.argv = param.argv_orig = argv; + + param.use_log = 0; + param.result_size = result_size; + param.result_ptr = result_buf; + param.result_buf = result_buf; + + result_buf[0] = '\0'; + + vcos_mutex_lock( &cmd_globals.lock ); + + rc = execute_cmd( ¶m, cmd_globals.cmd_entry ); + + if ( param.use_log ) + { + cmd_log_results( ¶m ); + vcos_snprintf( result_buf, result_size, "results logged" ); + } + else + if ( cmd_globals.log_category != NULL ) + { + if ( result_buf[0] != '\0' ) + { + /* There is a partial line still buffered. */ + + vcos_cmd_printf( ¶m, "\n" ); + } + } + + vcos_mutex_unlock( &cmd_globals.lock ); + + return rc; +} + +/***************************************************************************** +* +* Registers a command entry with the command line processor +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_cmd_register( VCOS_CMD_T *cmd_entry ) +{ + VCOS_STATUS_T rc; + VCOS_UNSIGNED new_num_cmd_alloc; + VCOS_CMD_T *new_cmd_entry; + VCOS_CMD_T *old_cmd_entry; + VCOS_CMD_T *scan_entry; + + vcos_once( &cmd_globals.initialized, vcos_cmd_init ); + + vcos_assert( cmd_entry != NULL ); + vcos_assert( cmd_entry->name != NULL ); + + vcos_log_trace( "%s: cmd '%s'", __FUNCTION__, cmd_entry->name ); + + vcos_assert( cmd_entry->args != NULL ); + vcos_assert(( cmd_entry->cmd_fn != NULL ) || ( cmd_entry->sub_cmd_entry != NULL )); + vcos_assert( cmd_entry->descr != NULL ); + + /* We expect vcos_cmd_init to be called before vcos_logging_init, so we + * need to defer registering our logging category until someplace + * like right here. + */ + + if ( vcos_cmd_log_category.name == NULL ) + { + /* + * If you're using the command interface, you pretty much always want + * log messages from this file to show up. So we change the default + * from ERROR to be the more reasonable INFO level. + */ + + vcos_log_set_level(&vcos_cmd_log_category, VCOS_LOG_INFO); + vcos_log_register("vcos_cmd", &vcos_cmd_log_category); + + /* We register a help command so that it shows up in the usage. */ + + vcos_cmd_register( &cmd_help ); +#ifdef HAVE_VCOS_VERSION + vcos_cmd_register( &cmd_version ); +#endif + } + + vcos_mutex_lock( &cmd_globals.lock ); + + if ( cmd_globals.num_cmd_entries >= cmd_globals.num_cmd_alloc ) + { + if ( cmd_globals.num_cmd_alloc == 0 ) + { + /* We haven't allocated a table yet */ + } + + /* The number 8 is rather arbitrary. */ + + new_num_cmd_alloc = cmd_globals.num_cmd_alloc + 8; + + /* The + 1 is to ensure that we always have a NULL entry at the end. */ + + new_cmd_entry = (VCOS_CMD_T *)vcos_calloc( new_num_cmd_alloc + 1, sizeof( *cmd_entry ), "vcos_cmd_entries" ); + if ( new_cmd_entry == NULL ) + { + rc = VCOS_ENOMEM; + goto out; + } + memcpy( new_cmd_entry, cmd_globals.cmd_entry, cmd_globals.num_cmd_entries * sizeof( *cmd_entry )); + cmd_globals.num_cmd_alloc = new_num_cmd_alloc; + old_cmd_entry = cmd_globals.cmd_entry; + cmd_globals.cmd_entry = new_cmd_entry; + vcos_free( old_cmd_entry ); + } + + if ( cmd_globals.num_cmd_entries == 0 ) + { + /* This is the first command being registered */ + + cmd_globals.cmd_entry[0] = *cmd_entry; + } + else + { + /* Keep the list in alphabetical order. We start at the end and work backwards + * shuffling entries up one until we find an insertion point. + */ + + for ( scan_entry = &cmd_globals.cmd_entry[cmd_globals.num_cmd_entries - 1]; + scan_entry >= cmd_globals.cmd_entry; scan_entry-- ) + { + if ( vcos_strcmp( cmd_entry->name, scan_entry->name ) > 0 ) + { + /* We found an insertion point. */ + + break; + } + + scan_entry[1] = scan_entry[0]; + } + scan_entry[1] = *cmd_entry; + } + cmd_globals.num_cmd_entries++; + + rc = VCOS_SUCCESS; + +out: + + vcos_mutex_unlock( &cmd_globals.lock ); + return rc; +} + +/***************************************************************************** +* +* Registers multiple commands. +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry ) +{ + VCOS_STATUS_T status; + + while ( cmd_entry->name != NULL ) + { + if (( status = vcos_cmd_register( cmd_entry )) != VCOS_SUCCESS ) + { + return status; + } + cmd_entry++; + } + return VCOS_SUCCESS; +} + diff --git a/interface/vcos/generic/vcos_common.h b/interface/vcos/generic/vcos_common.h new file mode 100755 index 0000000..93949e0 --- /dev/null +++ b/interface/vcos/generic/vcos_common.h @@ -0,0 +1,96 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - common postamble code +=============================================================================*/ + +/** \file + * + * Postamble code included by the platform-specific header files + */ + +#define VCOS_THREAD_PRI_DEFAULT VCOS_THREAD_PRI_NORMAL + +#if !defined(VCOS_THREAD_PRI_INCREASE) +#error Which way to thread priorities go? +#endif + +#if VCOS_THREAD_PRI_INCREASE < 0 +/* smaller numbers are higher priority */ +#define VCOS_THREAD_PRI_LESS(x) ((x)VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN) +#else +/* bigger numbers are lower priority */ +#define VCOS_THREAD_PRI_MORE(x) ((x)VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN) +#endif + +/* Convenience for Brits: */ +#define VCOS_APPLICATION_INITIALISE VCOS_APPLICATION_INITIALIZE + +/* + * Check for constant definitions + */ +#ifndef VCOS_TICKS_PER_SECOND +#error VCOS_TICKS_PER_SECOND not defined +#endif + +#if !defined(VCOS_THREAD_PRI_MIN) || !defined(VCOS_THREAD_PRI_MAX) +#error Priority range not defined +#endif + +#if !defined(VCOS_THREAD_PRI_HIGHEST) || !defined(VCOS_THREAD_PRI_LOWEST) || !defined(VCOS_THREAD_PRI_NORMAL) +#error Priority ordering not defined +#endif + +#if !defined(VCOS_CAN_SET_STACK_ADDR) +#error Can stack addresses be set on this platform? Please set this macro to either 0 or 1. +#endif + +#if (_VCOS_AFFINITY_CPU0|_VCOS_AFFINITY_CPU1) & (~_VCOS_AFFINITY_MASK) +#error _VCOS_AFFINITY_CPUxxx values are not consistent with _VCOS_AFFINITY_MASK +#endif + +/** Append to the end of a singly-linked queue, O(1). Works with + * any structure where list has members 'head' and 'tail' and + * item has a 'next' pointer. + */ +#define VCOS_QUEUE_APPEND_TAIL(list, item) {\ + (item)->next = NULL;\ + if (!(list)->head) {\ + (list)->head = (list)->tail = (item); \ + } else {\ + (list)->tail->next = (item); \ + (list)->tail = (item); \ + } \ +} + +#ifndef VCOS_HAVE_TIMER +VCOSPRE_ void VCOSPOST_ vcos_timer_init(void); +#endif + diff --git a/interface/vcos/generic/vcos_deprecated.h b/interface/vcos/generic/vcos_deprecated.h new file mode 100755 index 0000000..0efa5c4 --- /dev/null +++ b/interface/vcos/generic/vcos_deprecated.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +The symbol vcos_deprecated_code may be defined at most once, by the inclusion of "vcos_deprecated.h" in vcos_init.c. +Any other inclusions of this header will cause the linker to warn about multiple definitions of vcos_deprecated_code, for example: + [ldvc] (Warning) "vcos_deprecated_code" is multiply defined in libs/vcos_threadx/vcos_init.c.o and libs/xxxxx/xxxxx.c.o +If you see a build message like this then the configuration you are building is using deprecated code. +Contact the person named in the accompanying comment for advice - do not remove the inclusion. +*/ + +int vcos_deprecated_code; diff --git a/interface/vcos/generic/vcos_generic_blockpool.c b/interface/vcos/generic/vcos_generic_blockpool.c new file mode 100755 index 0000000..5b228a8 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_blockpool.c @@ -0,0 +1,568 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define VCOS_LOG_CATEGORY (&vcos_blockpool_log) + +#include +#include +#include "interface/vcos/vcos.h" +#include "interface/vcos/generic/vcos_generic_blockpool.h" + +#define VCOS_BLOCKPOOL_FOURCC(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24)) +#define VCOS_BLOCKPOOL_MAGIC VCOS_BLOCKPOOL_FOURCC('v', 'b', 'p', 'l') +#define VCOS_BLOCKPOOL_SUBPOOL_MAGIC VCOS_BLOCKPOOL_FOURCC('v', 's', 'p', 'l') + +#define VCOS_BLOCKPOOL_SUBPOOL_FLAG_NONE (0) +#define VCOS_BLOCKPOOL_SUBPOOL_FLAG_OWNS_MEM (1 << 0) +#define VCOS_BLOCKPOOL_SUBPOOL_FLAG_EXTENSION (1 << 1) + +/* Uncomment to enable really verbose debug messages */ +/* #define VCOS_BLOCKPOOL_DEBUGGING */ +/* Whether to overwrite freed blocks with 0xBD */ +#ifdef VCOS_BLOCKPOOL_DEBUGGING +#define VCOS_BLOCKPOOL_OVERWRITE_ON_FREE 1 +#define VCOS_BLOCKPOOL_DEBUG_MEMSET_MAX_SIZE (UINT32_MAX) +#else +#define VCOS_BLOCKPOOL_OVERWRITE_ON_FREE 0 +#define VCOS_BLOCKPOOL_DEBUG_MEMSET_MAX_SIZE (2 * 1024 * 1024) +#endif + +#ifdef VCOS_BLOCKPOOL_DEBUGGING +#define VCOS_BLOCKPOOL_ASSERT vcos_demand +#define VCOS_BLOCKPOOL_TRACE_LEVEL VCOS_LOG_TRACE +#define VCOS_BLOCKPOOL_DEBUG_LOG(s, ...) vcos_log_trace("%s: " s, VCOS_FUNCTION, __VA_ARGS__) +#undef VCOS_BLOCKPOOL_OVERWRITE_ON_FREE +#define VCOS_BLOCKPOOL_OVERWRITE_ON_FREE 1 +#else +#define VCOS_BLOCKPOOL_ASSERT vcos_demand +#define VCOS_BLOCKPOOL_TRACE_LEVEL VCOS_LOG_ERROR +#define VCOS_BLOCKPOOL_DEBUG_LOG(s, ...) +#endif + +#define ASSERT_POOL(p) \ + VCOS_BLOCKPOOL_ASSERT((p) && (p)->magic == VCOS_BLOCKPOOL_MAGIC); + +#define ASSERT_SUBPOOL(p) \ + VCOS_BLOCKPOOL_ASSERT((p) && (p)->magic == VCOS_BLOCKPOOL_SUBPOOL_MAGIC && \ + p->start >= p->mem); + +#if defined(VCOS_LOGGING_ENABLED) +static VCOS_LOG_CAT_T vcos_blockpool_log = +VCOS_LOG_INIT("vcos_blockpool", VCOS_BLOCKPOOL_TRACE_LEVEL); +#endif + +static void vcos_generic_blockpool_subpool_init( + VCOS_BLOCKPOOL_T *pool, VCOS_BLOCKPOOL_SUBPOOL_T *subpool, + void *mem, size_t pool_size, VCOS_UNSIGNED num_blocks, int align, + uint32_t flags) +{ + VCOS_BLOCKPOOL_HEADER_T *block; + VCOS_BLOCKPOOL_HEADER_T *end; + + vcos_unused(flags); + + vcos_log_trace( + "%s: pool %p subpool %p mem %p pool_size %d " \ + "num_blocks %d align %d flags %x", + VCOS_FUNCTION, + pool, subpool, mem, (uint32_t) pool_size, + num_blocks, align, flags); + + subpool->magic = VCOS_BLOCKPOOL_SUBPOOL_MAGIC; + subpool->mem = mem; + + /* The block data pointers must be aligned according to align and the + * block header pre-preceeds the first block data. + * For large alignments there may be wasted space between subpool->mem + * and the first block header. + */ + subpool->start = (char *) subpool->mem + sizeof(VCOS_BLOCKPOOL_HEADER_T); + subpool->start = (void*) + VCOS_BLOCKPOOL_ROUND_UP((unsigned long) subpool->start, align); + subpool->start = (char *) subpool->start - sizeof(VCOS_BLOCKPOOL_HEADER_T); + + vcos_assert(subpool->start >= subpool->mem); + + vcos_log_trace("%s: mem %p subpool->start %p" \ + " pool->block_size %d pool->block_data_size %d", + VCOS_FUNCTION, mem, subpool->start, + (int) pool->block_size, (int) pool->block_data_size); + + subpool->num_blocks = num_blocks; + subpool->available_blocks = num_blocks; + subpool->free_list = NULL; + subpool->owner = pool; + + /* Initialise to a predictable bit pattern unless the pool is so big + * that the delay would be noticeable. */ + if (pool_size < VCOS_BLOCKPOOL_DEBUG_MEMSET_MAX_SIZE) + memset(subpool->mem, 0xBC, pool_size); /* For debugging */ + + block = (VCOS_BLOCKPOOL_HEADER_T*) subpool->start; + end = (VCOS_BLOCKPOOL_HEADER_T*) + ((char *) subpool->start + (pool->block_size * num_blocks)); + subpool->end = end; + + /* Initialise the free list for this subpool */ + while (block < end) + { + block->owner.next = subpool->free_list; + subpool->free_list = block; + block = (VCOS_BLOCKPOOL_HEADER_T*)((char*) block + pool->block_size); + } + +} + +VCOS_STATUS_T vcos_generic_blockpool_init(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, + void *start, VCOS_UNSIGNED pool_size, VCOS_UNSIGNED align, + VCOS_UNSIGNED flags, const char *name) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + + vcos_unused(name); + vcos_unused(flags); + + vcos_log_trace( + "%s: pool %p num_blocks %d block_size %d start %p pool_size %d name %p", + VCOS_FUNCTION, pool, num_blocks, block_size, start, pool_size, name); + + vcos_demand(pool); + vcos_demand(start); + vcos_assert((block_size > 0)); + vcos_assert(num_blocks > 0); + + if (! align) + align = VCOS_BLOCKPOOL_ALIGN_DEFAULT; + + if (align & 0x3) + { + vcos_log_error("%s: invalid alignment %d", VCOS_FUNCTION, align); + return VCOS_EINVAL; + } + + if (VCOS_BLOCKPOOL_SIZE(num_blocks, block_size, align) > pool_size) + { + vcos_log_error("%s: Pool is too small" \ + " num_blocks %d block_size %d align %d" + " pool_size %d required size %d", VCOS_FUNCTION, + num_blocks, block_size, align, + pool_size, (int) VCOS_BLOCKPOOL_SIZE(num_blocks, block_size, align)); + return VCOS_ENOMEM; + } + + status = vcos_mutex_create(&pool->mutex, "vcos blockpool mutex"); + if (status != VCOS_SUCCESS) + return status; + + pool->block_data_size = block_size; + + /* TODO - create flag that if set forces the header to be in its own cache + * line */ + pool->block_size = VCOS_BLOCKPOOL_ROUND_UP(pool->block_data_size + + (align >= 4096 ? 32 : 0) + + sizeof(VCOS_BLOCKPOOL_HEADER_T), align); + + pool->magic = VCOS_BLOCKPOOL_MAGIC; + pool->num_subpools = 1; + pool->num_extension_blocks = 0; + pool->align = align; + memset(pool->subpools, 0, sizeof(pool->subpools)); + + vcos_generic_blockpool_subpool_init(pool, &pool->subpools[0], start, + pool_size, num_blocks, align, VCOS_BLOCKPOOL_SUBPOOL_FLAG_NONE); + + return status; +} + +VCOS_STATUS_T vcos_generic_blockpool_create_on_heap(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, VCOS_UNSIGNED align, + VCOS_UNSIGNED flags, const char *name) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + size_t size = VCOS_BLOCKPOOL_SIZE(num_blocks, block_size, align); + void* mem = vcos_malloc(size, name); + + vcos_log_trace("%s: num_blocks %d block_size %d name %s", + VCOS_FUNCTION, num_blocks, block_size, name); + + if (! mem) + return VCOS_ENOMEM; + + status = vcos_generic_blockpool_init(pool, num_blocks, + block_size, mem, size, align, flags, name); + + if (status != VCOS_SUCCESS) + goto fail; + + pool->subpools[0].flags |= VCOS_BLOCKPOOL_SUBPOOL_FLAG_OWNS_MEM; + return status; + +fail: + vcos_free(mem); + return status; +} + +VCOS_STATUS_T vcos_generic_blockpool_extend(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks) +{ + VCOS_UNSIGNED i; + ASSERT_POOL(pool); + + vcos_log_trace("%s: pool %p num_extensions %d num_blocks %d", + VCOS_FUNCTION, pool, num_extensions, num_blocks); + + /* Extend may only be called once */ + if (pool->num_subpools > 1) + return VCOS_EACCESS; + + if (num_extensions < 1 || + num_extensions > VCOS_BLOCKPOOL_MAX_SUBPOOLS - 1) + return VCOS_EINVAL; + + if (num_blocks < 1) + return VCOS_EINVAL; + + pool->num_subpools += num_extensions; + pool->num_extension_blocks = num_blocks; + + /* Mark these subpools as valid but unallocated */ + for (i = 1; i < pool->num_subpools; ++i) + { + pool->subpools[i].magic = VCOS_BLOCKPOOL_SUBPOOL_MAGIC; + pool->subpools[i].start = NULL; + pool->subpools[i].mem = NULL; + } + + return VCOS_SUCCESS; +} + +void *vcos_generic_blockpool_alloc(VCOS_BLOCKPOOL_T *pool) +{ + VCOS_UNSIGNED i; + void* ret = NULL; + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = NULL; + + ASSERT_POOL(pool); + vcos_mutex_lock(&pool->mutex); + + /* Starting with the main pool try and find a free block */ + for (i = 0; i < pool->num_subpools; ++i) + { + if (pool->subpools[i].start && pool->subpools[i].available_blocks > 0) + { + subpool = &pool->subpools[i]; + break; /* Found a subpool with free blocks */ + } + } + + if (! subpool) + { + /* All current subpools are full, try to allocate a new one */ + for (i = 1; i < pool->num_subpools; ++i) + { + if (! pool->subpools[i].start) + { + VCOS_BLOCKPOOL_SUBPOOL_T *s = &pool->subpools[i]; + size_t size = VCOS_BLOCKPOOL_SIZE(pool->num_extension_blocks, + pool->block_data_size, pool->align); + void *mem = vcos_malloc(size, pool->name); + if (mem) + { + vcos_log_trace("%s: Allocated subpool %d", VCOS_FUNCTION, i); + vcos_generic_blockpool_subpool_init(pool, s, mem, size, + pool->num_extension_blocks, + pool->align, + VCOS_BLOCKPOOL_SUBPOOL_FLAG_OWNS_MEM | + VCOS_BLOCKPOOL_SUBPOOL_FLAG_EXTENSION); + subpool = s; + break; /* Created a subpool */ + } + else + { + vcos_log_warn("%s: Failed to allocate subpool", VCOS_FUNCTION); + } + } + } + } + + if (subpool) + { + /* Remove from free list */ + VCOS_BLOCKPOOL_HEADER_T* nb = subpool->free_list; + + vcos_assert(subpool->free_list); + subpool->free_list = nb->owner.next; + + /* Owner is pool so free can be called without passing pool + * as a parameter */ + nb->owner.subpool = subpool; + + ret = nb + 1; /* Return pointer to block data */ + --(subpool->available_blocks); + } + vcos_mutex_unlock(&pool->mutex); + VCOS_BLOCKPOOL_DEBUG_LOG("pool %p subpool %p ret %p", pool, subpool, ret); + + if (ret) + { + vcos_assert(ret > subpool->start); + vcos_assert(ret < subpool->end); + } + return ret; +} + +void *vcos_generic_blockpool_calloc(VCOS_BLOCKPOOL_T *pool) +{ + void* ret = vcos_generic_blockpool_alloc(pool); + if (ret) + memset(ret, 0, pool->block_data_size); + return ret; +} + +void vcos_generic_blockpool_free(void *block) +{ + VCOS_BLOCKPOOL_DEBUG_LOG("block %p", block); + if (block) + { + VCOS_BLOCKPOOL_HEADER_T* hdr = (VCOS_BLOCKPOOL_HEADER_T*) block - 1; + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = hdr->owner.subpool; + VCOS_BLOCKPOOL_T *pool = NULL; + + ASSERT_SUBPOOL(subpool); + pool = subpool->owner; + ASSERT_POOL(pool); + + vcos_mutex_lock(&pool->mutex); + vcos_assert((unsigned) subpool->available_blocks < subpool->num_blocks); + + /* Change ownership of block to be the free list */ + hdr->owner.next = subpool->free_list; + subpool->free_list = hdr; + ++(subpool->available_blocks); + + if (VCOS_BLOCKPOOL_OVERWRITE_ON_FREE) + memset(block, 0xBD, pool->block_data_size); /* For debugging */ + + if ( (subpool->flags & VCOS_BLOCKPOOL_SUBPOOL_FLAG_EXTENSION) && + subpool->available_blocks == subpool->num_blocks) + { + VCOS_BLOCKPOOL_DEBUG_LOG("%s: freeing subpool %p mem %p", VCOS_FUNCTION, + subpool, subpool->mem); + /* Free the sub-pool if it was dynamically allocated */ + vcos_free(subpool->mem); + subpool->mem = NULL; + subpool->start = NULL; + } + vcos_mutex_unlock(&pool->mutex); + } +} + +VCOS_UNSIGNED vcos_generic_blockpool_available_count(VCOS_BLOCKPOOL_T *pool) +{ + VCOS_UNSIGNED ret = 0; + VCOS_UNSIGNED i; + + ASSERT_POOL(pool); + vcos_mutex_lock(&pool->mutex); + for (i = 0; i < pool->num_subpools; ++i) + { + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = &pool->subpools[i]; + ASSERT_SUBPOOL(subpool); + + /* Assume the malloc of sub pool would succeed */ + if (subpool->start) + ret += subpool->available_blocks; + else + ret += pool->num_extension_blocks; + } + vcos_mutex_unlock(&pool->mutex); + return ret; +} + +VCOS_UNSIGNED vcos_generic_blockpool_used_count(VCOS_BLOCKPOOL_T *pool) +{ + VCOS_UNSIGNED ret = 0; + VCOS_UNSIGNED i; + + ASSERT_POOL(pool); + vcos_mutex_lock(&pool->mutex); + + for (i = 0; i < pool->num_subpools; ++i) + { + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = &pool->subpools[i]; + ASSERT_SUBPOOL(subpool); + if (subpool->start) + ret += (subpool->num_blocks - subpool->available_blocks); + } + vcos_mutex_unlock(&pool->mutex); + return ret; +} + +void vcos_generic_blockpool_delete(VCOS_BLOCKPOOL_T *pool) +{ + vcos_log_trace("%s: pool %p", VCOS_FUNCTION, pool); + + if (pool) + { + VCOS_UNSIGNED i; + + ASSERT_POOL(pool); + for (i = 0; i < pool->num_subpools; ++i) + { + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = &pool->subpools[i]; + ASSERT_SUBPOOL(subpool); + if (subpool->mem) + { + /* For debugging */ + memset(subpool->mem, + 0xBE, + VCOS_BLOCKPOOL_SIZE(subpool->num_blocks, + pool->block_data_size, pool->align)); + + if (subpool->flags & VCOS_BLOCKPOOL_SUBPOOL_FLAG_OWNS_MEM) + vcos_free(subpool->mem); + subpool->mem = NULL; + subpool->start = NULL; + } + } + vcos_mutex_delete(&pool->mutex); + memset(pool, 0xBE, sizeof(VCOS_BLOCKPOOL_T)); /* For debugging */ + } +} + +uint32_t vcos_generic_blockpool_elem_to_handle(void *block) +{ + uint32_t ret = -1; + uint32_t index = -1; + VCOS_BLOCKPOOL_HEADER_T *hdr = NULL; + VCOS_BLOCKPOOL_T *pool = NULL; + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = NULL; + uint32_t subpool_id; + + vcos_assert(block); + hdr = (VCOS_BLOCKPOOL_HEADER_T*) block - 1; + subpool = hdr->owner.subpool; + ASSERT_SUBPOOL(subpool); + + pool = subpool->owner; + ASSERT_POOL(pool); + vcos_mutex_lock(&pool->mutex); + + /* The handle is the index into the array of blocks combined + * with the subpool id. + */ + index = ((size_t) hdr - (size_t) subpool->start) / pool->block_size; + vcos_assert(index < subpool->num_blocks); + + subpool_id = ((char*) subpool - (char*) &pool->subpools[0]) / + sizeof(VCOS_BLOCKPOOL_SUBPOOL_T); + + vcos_assert(subpool_id < VCOS_BLOCKPOOL_MAX_SUBPOOLS); + vcos_assert(subpool_id < pool->num_subpools); + ret = VCOS_BLOCKPOOL_HANDLE_CREATE(index, subpool_id); + + vcos_log_trace("%s: index %d subpool_id %d handle 0x%08x", + VCOS_FUNCTION, index, subpool_id, ret); + + vcos_mutex_unlock(&pool->mutex); + return ret; +} + +void *vcos_generic_blockpool_elem_from_handle( + VCOS_BLOCKPOOL_T *pool, uint32_t handle) +{ + VCOS_BLOCKPOOL_SUBPOOL_T *subpool; + uint32_t subpool_id; + uint32_t index; + void *ret = NULL; + + + ASSERT_POOL(pool); + vcos_mutex_lock(&pool->mutex); + subpool_id = VCOS_BLOCKPOOL_HANDLE_GET_SUBPOOL(handle); + + if (subpool_id < pool->num_subpools) + { + index = VCOS_BLOCKPOOL_HANDLE_GET_INDEX(handle); + subpool = &pool->subpools[subpool_id]; + if (pool->subpools[subpool_id].magic == VCOS_BLOCKPOOL_SUBPOOL_MAGIC && + pool->subpools[subpool_id].mem && index < subpool->num_blocks) + { + VCOS_BLOCKPOOL_HEADER_T *hdr = (VCOS_BLOCKPOOL_HEADER_T*) + ((size_t) subpool->start + (index * pool->block_size)); + + if (hdr->owner.subpool == subpool) /* Check block is allocated */ + ret = hdr + 1; + } + } + vcos_mutex_unlock(&pool->mutex); + + vcos_log_trace("%s: pool %p handle 0x%08x elem %p", VCOS_FUNCTION, pool, + handle, ret); + return ret; +} + +uint32_t vcos_generic_blockpool_is_valid_elem( + VCOS_BLOCKPOOL_T *pool, const void *block) +{ + uint32_t ret = 0; + const char *pool_end; + VCOS_UNSIGNED i = 0; + + ASSERT_POOL(pool); + if (((size_t) block) & 0x3) + return 0; + + vcos_mutex_lock(&pool->mutex); + + for (i = 0; i < pool->num_subpools; ++i) + { + VCOS_BLOCKPOOL_SUBPOOL_T *subpool = &pool->subpools[i]; + ASSERT_SUBPOOL(subpool); + + if (subpool->mem && subpool->start) + { + pool_end = (const char*)subpool->start + + (subpool->num_blocks * pool->block_size); + + if ((const char*)block > (const char*)subpool->start && + (const char*)block < pool_end) + { + const VCOS_BLOCKPOOL_HEADER_T *hdr = ( + const VCOS_BLOCKPOOL_HEADER_T*) block - 1; + + /* If the block has a header where the owner points to the pool then + * it's a valid block. */ + ret = (hdr->owner.subpool == subpool && subpool->owner == pool); + break; + } + } + } + vcos_mutex_unlock(&pool->mutex); + return ret; +} diff --git a/interface/vcos/generic/vcos_generic_blockpool.h b/interface/vcos/generic/vcos_generic_blockpool.h new file mode 100755 index 0000000..1301f90 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_blockpool.h @@ -0,0 +1,294 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - event flags implemented via a semaphore +=============================================================================*/ + +#ifndef VCOS_GENERIC_BLOCKPOOL_H +#define VCOS_GENERIC_BLOCKPOOL_H + +/** + * \file + * + * This provides a generic, thread safe implementation of a VCOS block pool + * fixed size memory allocator. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +/** Bits 0 to (VCOS_BLOCKPOOL_SUBPOOL_BITS - 1) are used to store the + * subpool id. */ +#define VCOS_BLOCKPOOL_SUBPOOL_BITS 3 +#define VCOS_BLOCKPOOL_MAX_SUBPOOLS (1 << VCOS_BLOCKPOOL_SUBPOOL_BITS) + +/* Make zero an invalid handle at the cost of decreasing the maximum + * number of blocks (2^28) by 1. Alternatively, a spare bit could be + * used to indicated valid blocks but there are likely to be better + * uses for spare bits. e.g. allowing more subpools + */ +#define INDEX_OFFSET 1 + +#define VCOS_BLOCKPOOL_HANDLE_GET_INDEX(h) \ + (((h) >> VCOS_BLOCKPOOL_SUBPOOL_BITS) - INDEX_OFFSET) + +#define VCOS_BLOCKPOOL_HANDLE_GET_SUBPOOL(h) \ + ((h) & ((1 << VCOS_BLOCKPOOL_SUBPOOL_BITS) - 1)) + +#define VCOS_BLOCKPOOL_HANDLE_CREATE(i,s) \ + ((((i) + INDEX_OFFSET) << VCOS_BLOCKPOOL_SUBPOOL_BITS) | (s)) + +#define VCOS_BLOCKPOOL_INVALID_HANDLE 0 +#define VCOS_BLOCKPOOL_ALIGN_DEFAULT sizeof(unsigned long) +#define VCOS_BLOCKPOOL_FLAG_NONE 0 + +typedef struct VCOS_BLOCKPOOL_HEADER_TAG +{ + /* Blocks either refer to to the pool if they are allocated + * or the free list if they are available. + */ + union { + struct VCOS_BLOCKPOOL_HEADER_TAG *next; + struct VCOS_BLOCKPOOL_SUBPOOL_TAG* subpool; + } owner; +} VCOS_BLOCKPOOL_HEADER_T; + +typedef struct VCOS_BLOCKPOOL_SUBPOOL_TAG +{ + /** VCOS_BLOCKPOOL_SUBPOOL_MAGIC */ + uint32_t magic; + VCOS_BLOCKPOOL_HEADER_T* free_list; + /* The start of the pool memory */ + void *mem; + /* Address of the first block header */ + void *start; + /* The end of the subpool */ + void *end; + /** The number of blocks in this sub-pool */ + VCOS_UNSIGNED num_blocks; + /** Current number of available blocks in this sub-pool */ + VCOS_UNSIGNED available_blocks; + /** Pointers to the pool that owns this sub-pool */ + struct VCOS_BLOCKPOOL_TAG* owner; + /** Define properties such as memory ownership */ + uint32_t flags; +} VCOS_BLOCKPOOL_SUBPOOL_T; + +typedef struct VCOS_BLOCKPOOL_TAG +{ + /** VCOS_BLOCKPOOL_MAGIC */ + uint32_t magic; + /** Thread safety for Alloc, Free, Delete, Stats */ + VCOS_MUTEX_T mutex; + /** Alignment of block data pointers */ + VCOS_UNSIGNED align; + /** Flags for future use e.g. cache options */ + VCOS_UNSIGNED flags; + /** The size of the block data */ + size_t block_data_size; + /** Block size inc overheads */ + size_t block_size; + /** Name for debugging */ + const char *name; + /* The number of subpools that may be used */ + VCOS_UNSIGNED num_subpools; + /** Number of blocks in each dynamically allocated subpool */ + VCOS_UNSIGNED num_extension_blocks; + /** Array of subpools. Subpool zero is is not deleted until the pool is + * destroed. If the index of the pool is < num_subpools and + * subpool[index.mem] is null then the subpool entry is valid but + * "not currently allocated" */ + VCOS_BLOCKPOOL_SUBPOOL_T subpools[VCOS_BLOCKPOOL_MAX_SUBPOOLS]; +} VCOS_BLOCKPOOL_T; + +#define VCOS_BLOCKPOOL_ROUND_UP(x,s) (((x) + ((s) - 1)) & ~((s) - 1)) +/** + * Calculates the size in bytes required for a block pool containing + * num_blocks of size block_size plus any overheads. + * + * The block pool header (VCOS_BLOCKPOOL_T) is allocated separately + * + * Overheads: + * block_size + header must be rounded up to meet the required alignment + * The start of the first block may need to be up to align bytes + * into the given buffer because statically allocated buffers within structures + * are not guaranteed to be aligned as required. + */ +#define VCOS_BLOCKPOOL_SIZE(num_blocks, block_size, align) \ + ((VCOS_BLOCKPOOL_ROUND_UP((block_size) + (align >= 4096 ? 32 : 0) + sizeof(VCOS_BLOCKPOOL_HEADER_T), \ + (align)) * (num_blocks)) + (align)) + +/** + * Sanity check to verify whether a handle is potentially a blockpool handle + * when the pool pointer is not available. + * + * If the pool pointer is available use vcos_blockpool_elem_to_handle instead. + * + * @param handle the handle to verify + * @param max_blocks the expected maximum number of block in the pool + * that the handle belongs to. + */ +#define VCOS_BLOCKPOOL_IS_VALID_HANDLE_FORMAT(handle, max_blocks) \ + ((handle) != VCOS_BLOCKPOOL_INVALID_HANDLE \ + && VCOS_BLOCKPOOL_HANDLE_GET_INDEX((handle)) < (max_blocks)) + +VCOSPRE_ + VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_init(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, + void *start, VCOS_UNSIGNED pool_size, + VCOS_UNSIGNED align, VCOS_UNSIGNED flags, + const char *name); + +VCOSPRE_ + VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_create_on_heap( + VCOS_BLOCKPOOL_T *pool, VCOS_UNSIGNED num_blocks, + VCOS_UNSIGNED block_size, + VCOS_UNSIGNED align, VCOS_UNSIGNED flags, + const char *name); + +VCOSPRE_ + VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_extend(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks); + +VCOSPRE_ void VCOSPOST_ *vcos_generic_blockpool_alloc(VCOS_BLOCKPOOL_T *pool); + +VCOSPRE_ void VCOSPOST_ *vcos_generic_blockpool_calloc(VCOS_BLOCKPOOL_T *pool); + +VCOSPRE_ void VCOSPOST_ vcos_generic_blockpool_free(void *block); + +VCOSPRE_ + VCOS_UNSIGNED VCOSPOST_ vcos_generic_blockpool_available_count( + VCOS_BLOCKPOOL_T *pool); + +VCOSPRE_ + VCOS_UNSIGNED VCOSPOST_ vcos_generic_blockpool_used_count( + VCOS_BLOCKPOOL_T *pool); + +VCOSPRE_ void VCOSPOST_ vcos_generic_blockpool_delete(VCOS_BLOCKPOOL_T *pool); + +VCOSPRE_ uint32_t VCOSPOST_ vcos_generic_blockpool_elem_to_handle(void *block); + +VCOSPRE_ void VCOSPOST_ + *vcos_generic_blockpool_elem_from_handle( + VCOS_BLOCKPOOL_T *pool, uint32_t handle); + +VCOSPRE_ uint32_t VCOSPOST_ + vcos_generic_blockpool_is_valid_elem( + VCOS_BLOCKPOOL_T *pool, const void *block); +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_blockpool_init(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, + void *start, VCOS_UNSIGNED pool_size, + VCOS_UNSIGNED align, VCOS_UNSIGNED flags, const char *name) +{ + return vcos_generic_blockpool_init(pool, num_blocks, block_size, + start, pool_size, align, flags, name); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_blockpool_create_on_heap(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, + VCOS_UNSIGNED align, VCOS_UNSIGNED flags, const char *name) +{ + return vcos_generic_blockpool_create_on_heap( + pool, num_blocks, block_size, align, flags, name); +} + +VCOS_INLINE_IMPL + VCOS_STATUS_T VCOSPOST_ vcos_blockpool_extend(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks) +{ + return vcos_generic_blockpool_extend(pool, num_extensions, num_blocks); +} + +VCOS_INLINE_IMPL +void *vcos_blockpool_alloc(VCOS_BLOCKPOOL_T *pool) +{ + return vcos_generic_blockpool_alloc(pool); +} + +VCOS_INLINE_IMPL +void *vcos_blockpool_calloc(VCOS_BLOCKPOOL_T *pool) +{ + return vcos_generic_blockpool_calloc(pool); +} + +VCOS_INLINE_IMPL +void vcos_blockpool_free(void *block) +{ + vcos_generic_blockpool_free(block); +} + +VCOS_INLINE_IMPL +VCOS_UNSIGNED vcos_blockpool_available_count(VCOS_BLOCKPOOL_T *pool) +{ + return vcos_generic_blockpool_available_count(pool); +} + +VCOS_INLINE_IMPL +VCOS_UNSIGNED vcos_blockpool_used_count(VCOS_BLOCKPOOL_T *pool) +{ + return vcos_generic_blockpool_used_count(pool); +} + +VCOS_INLINE_IMPL +void vcos_blockpool_delete(VCOS_BLOCKPOOL_T *pool) +{ + vcos_generic_blockpool_delete(pool); +} + +VCOS_INLINE_IMPL +uint32_t vcos_blockpool_elem_to_handle(void *block) +{ + return vcos_generic_blockpool_elem_to_handle(block); +} + +VCOS_INLINE_IMPL +void *vcos_blockpool_elem_from_handle(VCOS_BLOCKPOOL_T *pool, uint32_t handle) +{ + return vcos_generic_blockpool_elem_from_handle(pool, handle); +} + +VCOS_INLINE_IMPL +uint32_t vcos_blockpool_is_valid_elem(VCOS_BLOCKPOOL_T *pool, const void *block) +{ + return vcos_generic_blockpool_is_valid_elem(pool, block); +} +#endif /* VCOS_INLINE_BODIES */ + + +#ifdef __cplusplus +} +#endif +#endif /* VCOS_GENERIC_BLOCKPOOL_H */ + diff --git a/interface/vcos/generic/vcos_generic_event_flags.c b/interface/vcos/generic/vcos_generic_event_flags.c new file mode 100755 index 0000000..9a7c1bb --- /dev/null +++ b/interface/vcos/generic/vcos_generic_event_flags.c @@ -0,0 +1,320 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - event flags implemented via mutexes +=============================================================================*/ + +#include "interface/vcos/vcos.h" +#include "interface/vcos/generic/vcos_generic_event_flags.h" + +#include + +/** A structure created by a thread that waits on the event flags + * for a particular combination of flags to arrive. + */ +typedef struct VCOS_EVENT_WAITER_T +{ + VCOS_UNSIGNED requested_events; /**< The events wanted */ + VCOS_UNSIGNED actual_events; /**< Actual events found */ + VCOS_UNSIGNED op; /**< The event operation to be used */ + VCOS_STATUS_T return_status; /**< The return status the waiter should pass back */ + VCOS_EVENT_FLAGS_T *flags; /**< Pointer to the original 'flags' structure */ + VCOS_THREAD_T *thread; /**< Thread waiting */ + struct VCOS_EVENT_WAITER_T *next; +} VCOS_EVENT_WAITER_T; + +#ifndef NDEBUG +static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags); +#endif +static void event_flags_timer_expired(void *cxt); + +VCOS_STATUS_T vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name) +{ + VCOS_STATUS_T rc; + if ((rc=vcos_mutex_create(&flags->lock, name)) != VCOS_SUCCESS) + { + return rc; + } + + flags->events = 0; + flags->waiters.head = flags->waiters.tail = 0; + return rc; +} + +void vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED bitmask, + VCOS_OPTION op) +{ + vcos_assert(flags); + vcos_mutex_lock(&flags->lock); + if (op == VCOS_OR) + { + flags->events |= bitmask; + } + else if (op == VCOS_AND) + { + flags->events &= bitmask; + } + else + { + vcos_assert(0); + } + + /* Now wake up any threads that have now become signalled. */ + if (flags->waiters.head != NULL) + { + VCOS_UNSIGNED consumed_events = 0; + VCOS_EVENT_WAITER_T **pcurrent_waiter = &flags->waiters.head; + VCOS_EVENT_WAITER_T *prev_waiter = NULL; + + /* Walk the chain of tasks suspend on this event flag group to determine + * if any of their requests can be satisfied. + */ + while ((*pcurrent_waiter) != NULL) + { + VCOS_EVENT_WAITER_T *curr_waiter = *pcurrent_waiter; + + /* Determine if this request has been satisfied */ + + /* First, find the event flags in common. */ + VCOS_UNSIGNED waiter_satisfied = flags->events & curr_waiter->requested_events; + + /* Second, determine if all the event flags must match */ + if (curr_waiter->op & VCOS_AND) + { + /* All requested events must be present */ + waiter_satisfied = (waiter_satisfied == curr_waiter->requested_events); + } + + /* Wake this one up? */ + if (waiter_satisfied) + { + + if (curr_waiter->op & VCOS_CONSUME) + { + consumed_events |= curr_waiter->requested_events; + } + + /* remove this block from the list, taking care at the end */ + *pcurrent_waiter = curr_waiter->next; + if (curr_waiter->next == NULL) + flags->waiters.tail = prev_waiter; + + vcos_assert(waiter_list_valid(flags)); + + curr_waiter->return_status = VCOS_SUCCESS; + curr_waiter->actual_events = flags->events; + + _vcos_thread_sem_post(curr_waiter->thread); + } + else + { + /* move to next element in the list */ + prev_waiter = *pcurrent_waiter; + pcurrent_waiter = &(curr_waiter->next); + } + } + + flags->events &= ~consumed_events; + + } + + vcos_mutex_unlock(&flags->lock); +} + +void vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *flags) +{ + vcos_mutex_delete(&flags->lock); +} + +extern VCOS_STATUS_T vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED bitmask, + VCOS_OPTION op, + VCOS_UNSIGNED suspend, + VCOS_UNSIGNED *retrieved_bits) +{ + VCOS_EVENT_WAITER_T waitreq; + VCOS_STATUS_T rc = VCOS_EAGAIN; + int satisfied = 0; + + vcos_assert(flags); + + /* default retrieved bits to 0 */ + *retrieved_bits = 0; + + vcos_mutex_lock(&flags->lock); + switch (op & VCOS_EVENT_FLAG_OP_MASK) + { + case VCOS_AND: + if ((flags->events & bitmask) == bitmask) + { + *retrieved_bits = flags->events; + rc = VCOS_SUCCESS; + satisfied = 1; + if (op & VCOS_CONSUME) + flags->events &= ~bitmask; + } + break; + + case VCOS_OR: + if (flags->events & bitmask) + { + *retrieved_bits = flags->events; + rc = VCOS_SUCCESS; + satisfied = 1; + if (op & VCOS_CONSUME) + flags->events &= ~bitmask; + } + break; + + default: + vcos_assert(0); + rc = VCOS_EINVAL; + break; + } + + if (!satisfied && suspend) + { + /* Have to go to sleep. + * + * Append to tail so we get FIFO ordering. + */ + waitreq.requested_events = bitmask; + waitreq.op = op; + waitreq.return_status = VCOS_EAGAIN; + waitreq.flags = flags; + waitreq.actual_events = 0; + waitreq.thread = vcos_thread_current(); + waitreq.next = 0; + vcos_assert(waitreq.thread != (VCOS_THREAD_T*)-1); + VCOS_QUEUE_APPEND_TAIL(&flags->waiters, &waitreq); + + if (suspend != (VCOS_UNSIGNED)-1) + _vcos_task_timer_set(event_flags_timer_expired, &waitreq, suspend); + + vcos_mutex_unlock(&flags->lock); + /* go to sleep and wait to be signalled or timeout */ + + _vcos_thread_sem_wait(); + + *retrieved_bits = waitreq.actual_events; + rc = waitreq.return_status; + + /* cancel the timer - do not do this while holding the mutex as it + * might be waiting for the timeout function to complete, which will + * try to take the mutex. + */ + if (suspend != (VCOS_UNSIGNED)-1) + _vcos_task_timer_cancel(); + } + else + { + vcos_mutex_unlock(&flags->lock); + } + + return rc; +} + + +/** Called when a get call times out. Remove this thread's + * entry from the waiting queue, then resume the thread. + */ +static void event_flags_timer_expired(void *cxt) +{ + VCOS_EVENT_WAITER_T *waitreq = (VCOS_EVENT_WAITER_T *)cxt; + VCOS_EVENT_FLAGS_T *flags = waitreq->flags; + VCOS_EVENT_WAITER_T **plist; + VCOS_EVENT_WAITER_T *prev = NULL; + VCOS_THREAD_T *thread = 0; + + vcos_assert(flags); + + vcos_mutex_lock(&flags->lock); + + /* walk the list of waiting threads on this event group, and remove + * the one that has expired. + * + * FIXME: could use doubly-linked list if lots of threads are found + * to be waiting on a single event flag instance. + */ + plist = &flags->waiters.head; + while (*plist != NULL) + { + if (*plist == waitreq) + { + int at_end; + /* found it */ + thread = (*plist)->thread; + at_end = ((*plist)->next == NULL); + + /* link past */ + *plist = (*plist)->next; + if (at_end) + flags->waiters.tail = prev; + + break; + } + prev = *plist; + plist = &(*plist)->next; + } + vcos_assert(waiter_list_valid(flags)); + + vcos_mutex_unlock(&flags->lock); + + if (thread) + { + _vcos_thread_sem_post(thread); + } +} + +#ifndef NDEBUG + +static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags) +{ + int valid; + /* Either both head and tail are NULL, or neither are NULL */ + if (flags->waiters.head == NULL) + { + valid = (flags->waiters.tail == NULL); + } + else + { + valid = (flags->waiters.tail != NULL); + } + + /* If head and tail point at the same non-NULL element, then there + * is only one element in the list. + */ + if (flags->waiters.head && (flags->waiters.head == flags->waiters.tail)) + { + valid = (flags->waiters.head->next == NULL); + } + return valid; +} + +#endif diff --git a/interface/vcos/generic/vcos_generic_event_flags.h b/interface/vcos/generic/vcos_generic_event_flags.h new file mode 100755 index 0000000..a21abd4 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_event_flags.h @@ -0,0 +1,127 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - event flags implemented via a semaphore +=============================================================================*/ + +#ifndef VCOS_GENERIC_EVENT_FLAGS_H +#define VCOS_GENERIC_EVENT_FLAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +/** + * \file + * + * This provides event flags (as per Nucleus Event Groups) based on a + * mutex, a semaphore (per waiting thread) and a timer (per waiting + * thread). + * + * The data structure is a 32 bit unsigned int (the current set of + * flags) and a linked list of clients waiting to be 'satisfied'. + * + * The mutex merely locks access to the data structure. If a client + * calls vcos_event_flags_get() and the requested bits are not already + * present, it then sleeps on its per-thread semaphore after adding + * this semaphore to the queue waiting. It also sets up a timer. + * + * The per-thread semaphore and timer are actually stored in the + * thread context (joinable thread). In future it may become necessary + * to support non-VCOS threads by using thread local storage to + * create these objects and associate them with the thread. + */ + +struct VCOS_EVENT_WAITER_T; + +typedef struct VCOS_EVENT_FLAGS_T +{ + VCOS_UNSIGNED events; /**< Events currently set */ + VCOS_MUTEX_T lock; /**< Serialize access */ + struct + { + struct VCOS_EVENT_WAITER_T *head; /**< List of threads waiting */ + struct VCOS_EVENT_WAITER_T *tail; /**< List of threads waiting */ + } waiters; +} VCOS_EVENT_FLAGS_T; + +#define VCOS_OR 1 +#define VCOS_AND 2 +#define VCOS_CONSUME 4 +#define VCOS_OR_CONSUME (VCOS_OR | VCOS_CONSUME) +#define VCOS_AND_CONSUME (VCOS_AND | VCOS_CONSUME) +#define VCOS_EVENT_FLAG_OP_MASK (VCOS_OR|VCOS_AND) + +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name); +VCOSPRE_ void VCOSPOST_ vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED events, + VCOS_OPTION op); +VCOSPRE_ void VCOSPOST_ vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED requested_events, + VCOS_OPTION op, + VCOS_UNSIGNED suspend, + VCOS_UNSIGNED *retrieved_events); + +#ifdef VCOS_INLINE_BODIES + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name) { + return vcos_generic_event_flags_create(flags, name); +} + +VCOS_INLINE_IMPL +void vcos_event_flags_set(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED events, + VCOS_OPTION op) { + vcos_generic_event_flags_set(flags, events, op); +} + +VCOS_INLINE_IMPL +void vcos_event_flags_delete(VCOS_EVENT_FLAGS_T *f) { + vcos_generic_event_flags_delete(f); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_event_flags_get(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED requested_events, + VCOS_OPTION op, + VCOS_UNSIGNED suspend, + VCOS_UNSIGNED *retrieved_events) { + return vcos_generic_event_flags_get(flags, requested_events, op, suspend, retrieved_events); +} + +#endif /* VCOS_INLINE_BODIES */ + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/generic/vcos_generic_named_sem.c b/interface/vcos/generic/vcos_generic_named_sem.c new file mode 100755 index 0000000..cf47962 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_named_sem.c @@ -0,0 +1,268 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define VCOS_LOG_CATEGORY (&vcos_named_sem_log_cat) + +#include "interface/vcos/vcos.h" +#include "interface/vcos/generic/vcos_generic_named_sem.h" +#include "interface/vcos/vcos_blockpool.h" + +#if defined(VCOS_LOGGING_ENABLED) +static VCOS_LOG_CAT_T vcos_named_sem_log_cat = +VCOS_LOG_INIT("vcos_named_sem", VCOS_LOG_ERROR); +#endif + +/** + * \file + * + * Named semaphores, primarily for VCFW. + * + * Does not actually work across processes; merely emulate the API. + * + * The client initialises a VCOS_NAMED_SEMAPHORE_T, but this merely + * points at the real underlying VCOS_NAMED_SEMAPHORE_IMPL_T. + * + * semaphore_t ---\ + * ----- semaphore_impl_t + * semaphore_t ---/ + * / + * semaphore_t -/ + * + */ + +/* Maintain a block pool of semaphore implementations */ +#define NUM_SEMS 16 + +/* Allow the pool to expand to MAX_SEMS in size */ +#define MAX_SEMS 512 + +/** Each actual real semaphore is stored in one of these. Clients just + * get a structure with a pointer to this in it. + * + * It also contains a doubly linked list tracking the semaphores in-use. + */ +typedef struct VCOS_NAMED_SEMAPHORE_IMPL_T +{ + VCOS_SEMAPHORE_T sem; /**< Actual underlying semaphore */ + char name[VCOS_NAMED_SEMAPHORE_NAMELEN]; /**< Name of semaphore, copied */ + unsigned refs; /**< Reference count */ + struct VCOS_NAMED_SEMAPHORE_IMPL_T *next; /**< Next in the in-use list */ + struct VCOS_NAMED_SEMAPHORE_IMPL_T *prev; /**< Previous in the in-use list */ +} VCOS_NAMED_SEMAPHORE_IMPL_T; + +static VCOS_MUTEX_T lock; +static VCOS_NAMED_SEMAPHORE_IMPL_T* sems_in_use = NULL; +static int sems_in_use_count = 0; +static int sems_total_ref_count = 0; + +static VCOS_BLOCKPOOL_T sems_pool; +static char pool_mem[VCOS_BLOCKPOOL_SIZE( + NUM_SEMS, sizeof(VCOS_NAMED_SEMAPHORE_IMPL_T), VCOS_BLOCKPOOL_ALIGN_DEFAULT)]; + +VCOS_STATUS_T _vcos_named_semaphore_init() +{ + VCOS_STATUS_T status; + + status = vcos_blockpool_init(&sems_pool, + NUM_SEMS, sizeof(VCOS_NAMED_SEMAPHORE_IMPL_T), + pool_mem, sizeof(pool_mem), + VCOS_BLOCKPOOL_ALIGN_DEFAULT, 0, "vcos named semaphores"); + + if (status != VCOS_SUCCESS) + goto fail_blockpool; + + status = vcos_blockpool_extend(&sems_pool, VCOS_BLOCKPOOL_MAX_SUBPOOLS - 1, + (MAX_SEMS - NUM_SEMS) / (VCOS_BLOCKPOOL_MAX_SUBPOOLS - 1)); + if (status != VCOS_SUCCESS) + goto fail_extend; + + status = vcos_mutex_create(&lock, "vcosnmsem"); + if (status != VCOS_SUCCESS) + goto fail_mutex; + + return status; + +fail_mutex: +fail_extend: + vcos_blockpool_delete(&sems_pool); +fail_blockpool: + return status; +} + +void _vcos_named_semaphore_deinit(void) +{ + vcos_blockpool_delete(&sems_pool); + vcos_mutex_delete(&lock); + sems_in_use = NULL; +} + +VCOS_STATUS_T +vcos_generic_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, + const char *name, VCOS_UNSIGNED count) +{ + VCOS_STATUS_T status = VCOS_ENOSPC; + int name_len, cmp = -1; + VCOS_NAMED_SEMAPHORE_IMPL_T *impl; + VCOS_NAMED_SEMAPHORE_IMPL_T *new_impl; + + vcos_log_trace("%s: sem %p name %s count %d", __FUNCTION__, + sem, (name ? name : "null"), count); + + vcos_assert(name); + + vcos_mutex_lock(&lock); + name_len = vcos_strlen(name); + if (name_len >= VCOS_NAMED_SEMAPHORE_NAMELEN) + { + vcos_assert(0); + status = VCOS_EINVAL; + goto end; + } + + /* do we already have this semaphore? */ + impl = sems_in_use; + while (impl && (cmp = vcos_strcmp(name, impl->name)) < 0) + impl = impl->next; + + if (impl && cmp == 0) + { + /* Semaphore is already in use so just increase the ref count */ + impl->refs++; + sems_total_ref_count++; + sem->actual = impl; + sem->sem = &impl->sem; + status = VCOS_SUCCESS; + vcos_log_trace( + "%s: ref count %d name %s total refs %d num sems %d", + __FUNCTION__, impl->refs, impl->name, + sems_total_ref_count, sems_in_use_count); + goto end; + } + + /* search for unused semaphore */ + new_impl = vcos_blockpool_calloc(&sems_pool); + if (new_impl) + { + status = vcos_semaphore_create(&new_impl->sem, name, count); + if (status == VCOS_SUCCESS) + { + new_impl->refs = 1; + sems_total_ref_count++; + sems_in_use_count++; + memcpy(new_impl->name, name, name_len + 1); /* already checked length! */ + sem->actual = new_impl; + sem->sem = &new_impl->sem; + + /* Insert into the sorted list + * impl is either NULL or the first element where + * name > impl->name. + */ + if (impl) + { + new_impl->prev = impl->prev; + impl->prev = new_impl; + new_impl->next = impl; + + if (new_impl->prev) + new_impl->prev->next = new_impl; + } + else + { + /* Appending to the tail of the list / empty list */ + VCOS_NAMED_SEMAPHORE_IMPL_T *tail = sems_in_use; + while(tail && tail->next) + tail = tail->next; + + if (tail) + { + tail->next = new_impl; + new_impl->prev = tail; + } + } + + if (sems_in_use == impl) + { + /* Inserted at head or list was empty */ + sems_in_use = new_impl; + } + + vcos_log_trace( + "%s: new ref actual %p prev %p next %p count %d name %s " \ + "total refs %d num sems %d", + __FUNCTION__, + new_impl, new_impl->prev, new_impl->next, + new_impl->refs, new_impl->name, + sems_total_ref_count, sems_in_use_count); + } + } + +end: + vcos_mutex_unlock(&lock); + if (status != VCOS_SUCCESS) + { + vcos_log_error("%s: failed to create named semaphore name %s status %d " \ + "total refs %d num sems %d", + __FUNCTION__, (name ? name : "NULL"), status, + sems_total_ref_count, sems_in_use_count); + } + return status; +} + +void vcos_named_semaphore_delete(VCOS_NAMED_SEMAPHORE_T *sem) +{ + VCOS_NAMED_SEMAPHORE_IMPL_T *actual = sem->actual; + vcos_mutex_lock(&lock); + + /* if this fires, the semaphore has already been deleted */ + vcos_assert(actual->refs); + + vcos_log_trace( + "%s: actual %p ref count %d name %s prev %p next %p total refs %d num sems %d", + __FUNCTION__, actual, actual->refs, actual->name, + actual->prev, actual->next, + sems_total_ref_count, sems_in_use_count); + + sems_total_ref_count--; + if (--actual->refs == 0) + { + sems_in_use_count--; + if (actual->prev) + actual->prev->next = actual->next; + + if (actual->next) + actual->next->prev = actual->prev; + + if (sems_in_use == actual) + sems_in_use = actual->next; + + vcos_semaphore_delete(&actual->sem); + sem->actual = NULL; + sem->sem = NULL; + vcos_blockpool_free(actual); + } + vcos_mutex_unlock(&lock); +} diff --git a/interface/vcos/generic/vcos_generic_named_sem.h b/interface/vcos/generic/vcos_generic_named_sem.h new file mode 100755 index 0000000..81a0716 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_named_sem.h @@ -0,0 +1,101 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - named semaphores +=============================================================================*/ + +#ifndef VCOS_GENERIC_NAMED_SEM_H +#define VCOS_GENERIC_NAMED_SEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +/** + * \file + * + * Generic support for named semaphores, using regular ones. This is only + * suitable for emulating them on an embedded MMUless system, since there is + * no support for opening semaphores across process boundaries. + * + */ + +#define VCOS_NAMED_SEMAPHORE_NAMELEN 64 + +/* In theory we could use the name facility provided within Nucleus. However, this + * is hard to do as semaphores are constantly being created and destroyed; we + * would need to stop everything while allocating the memory for the semaphore + * list and then walking it. So keep our own list. + */ +typedef struct VCOS_NAMED_SEMAPHORE_T +{ + struct VCOS_NAMED_SEMAPHORE_IMPL_T *actual; /**< There are 'n' named semaphores per 1 actual semaphore */ + VCOS_SEMAPHORE_T *sem; /**< Pointer to actual underlying semaphore */ +} VCOS_NAMED_SEMAPHORE_T; + +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ +vcos_generic_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count); + +VCOSPRE_ void VCOSPOST_ vcos_named_semaphore_delete(VCOS_NAMED_SEMAPHORE_T *sem); + +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ _vcos_named_semaphore_init(void); +VCOSPRE_ void VCOSPOST_ _vcos_named_semaphore_deinit(void); + +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count) { + return vcos_generic_named_semaphore_create(sem, name, count); +} + +VCOS_INLINE_IMPL +void vcos_named_semaphore_wait(VCOS_NAMED_SEMAPHORE_T *sem) { + vcos_semaphore_wait(sem->sem); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_named_semaphore_trywait(VCOS_NAMED_SEMAPHORE_T *sem) { + return vcos_semaphore_trywait(sem->sem); +} + +VCOS_INLINE_IMPL +void vcos_named_semaphore_post(VCOS_NAMED_SEMAPHORE_T *sem) { + vcos_semaphore_post(sem->sem); +} + + +#endif + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/interface/vcos/generic/vcos_generic_quickslow_mutex.h b/interface/vcos/generic/vcos_generic_quickslow_mutex.h new file mode 100755 index 0000000..ddc7b97 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_quickslow_mutex.h @@ -0,0 +1,95 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones. +=============================================================================*/ + +#ifndef VCOS_GENERIC_QUICKSLOW_MUTEX_H +#define VCOS_GENERIC_QUICKSLOW_MUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +/** + * \file + * + * Quickslow Mutexes implemented as regular ones (i.e. quick and slow modes are the same). + * + */ + +typedef VCOS_MUTEX_T VCOS_QUICKSLOW_MUTEX_T; + +#if defined(VCOS_INLINE_BODIES) +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_quickslow_mutex_create(VCOS_QUICKSLOW_MUTEX_T *m, const char *name) +{ + return vcos_mutex_create(m, name); +} + +VCOS_INLINE_IMPL +void vcos_quickslow_mutex_delete(VCOS_QUICKSLOW_MUTEX_T *m) +{ + vcos_mutex_delete(m); +} + +VCOS_INLINE_IMPL +void vcos_quickslow_mutex_lock(VCOS_QUICKSLOW_MUTEX_T *m) +{ + while (vcos_mutex_lock(m) == VCOS_EAGAIN); +} + +VCOS_INLINE_IMPL +void vcos_quickslow_mutex_unlock(VCOS_QUICKSLOW_MUTEX_T *m) +{ + vcos_mutex_unlock(m); +} + +VCOS_INLINE_IMPL +void vcos_quickslow_mutex_lock_quick(VCOS_QUICKSLOW_MUTEX_T *m) +{ + while (vcos_mutex_lock(m) == VCOS_EAGAIN); +} + +VCOS_INLINE_IMPL +void vcos_quickslow_mutex_unlock_quick(VCOS_QUICKSLOW_MUTEX_T *m) +{ + vcos_mutex_unlock(m); +} + +#endif + + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/interface/vcos/generic/vcos_generic_reentrant_mtx.c b/interface/vcos/generic/vcos_generic_reentrant_mtx.c new file mode 100755 index 0000000..7258cbf --- /dev/null +++ b/interface/vcos/generic/vcos_generic_reentrant_mtx.c @@ -0,0 +1,76 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_reentrant_mutex.h" + +VCOS_STATUS_T vcos_generic_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) +{ + m->count = 0; + m->owner = 0; + return vcos_mutex_create(&m->mutex, name); +} + +void vcos_generic_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) +{ + vcos_assert(m->count == 0); + vcos_mutex_delete(&m->mutex); +} + +void vcos_generic_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) +{ + VCOS_THREAD_T *thread = vcos_thread_current(); + vcos_assert(m); + + vcos_assert(thread != 0); + + if (m->owner != thread) + { + vcos_mutex_lock(&m->mutex); + m->owner = thread; + vcos_assert(m->count == 0); + } + m->count++; +} + +void vcos_generic_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) +{ + vcos_assert(m->count != 0); + vcos_assert(m->owner == vcos_thread_current()); + m->count--; + if (m->count == 0) + { + m->owner = 0; + vcos_mutex_unlock(&m->mutex); + } +} + + + + + + diff --git a/interface/vcos/generic/vcos_generic_reentrant_mtx.h b/interface/vcos/generic/vcos_generic_reentrant_mtx.h new file mode 100755 index 0000000..21ae1c9 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_reentrant_mtx.h @@ -0,0 +1,95 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones. +=============================================================================*/ + +#ifndef VCOS_GENERIC_REENTRANT_MUTEX_H +#define VCOS_GENERIC_REENTRANT_MUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +/** + * \file + * + * Reentrant Mutexes from regular ones. + * + */ + +typedef struct VCOS_REENTRANT_MUTEX_T +{ + VCOS_MUTEX_T mutex; + VCOS_THREAD_T *owner; + unsigned count; +} VCOS_REENTRANT_MUTEX_T; + +/* Extern definitions of functions that do the actual work */ + +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name); + +VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m); + +VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m); + +VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m); + +/* Inline forwarding functions */ + +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) { + return vcos_generic_reentrant_mutex_create(m,name); +} + +VCOS_INLINE_IMPL +void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) { + vcos_generic_reentrant_mutex_delete(m); +} + +VCOS_INLINE_IMPL +void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) { + vcos_generic_reentrant_mutex_lock(m); +} + +VCOS_INLINE_IMPL +void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) { + vcos_generic_reentrant_mutex_unlock(m); +} +#endif + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/interface/vcos/generic/vcos_generic_safe_string.c b/interface/vcos/generic/vcos_generic_safe_string.c new file mode 100755 index 0000000..6b88694 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_safe_string.c @@ -0,0 +1,92 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vcos/vcos.h" + +#if defined( __KERNEL__ ) +#include +#include +#else +#include +#endif + +#include + +/** Like vsnprintf, except it places the output at the specified offset. + * Output is truncated to fit in buflen bytes, and is guaranteed to be NUL-terminated. + * Returns the string length before/without truncation. + */ +size_t vcos_safe_vsprintf(char *buf, size_t buflen, size_t offset, const char *fmt, va_list ap) +{ + size_t space = (offset < buflen) ? (buflen - offset) : 0; + + offset += vcos_vsnprintf(buf ? (buf + offset) : NULL, space, fmt, ap); + + return offset; +} + +/** Like snprintf, except it places the output at the specified offset. + * Output is truncated to fit in buflen bytes, and is guaranteed to be NUL-terminated. + * Returns the string length before/without truncation. + */ +size_t vcos_safe_sprintf(char *buf, size_t buflen, size_t offset, const char *fmt, ...) +{ + size_t space = (offset < buflen) ? (buflen - offset) : 0; + va_list ap; + + va_start(ap, fmt); + + offset += vcos_vsnprintf(buf ? (buf + offset) : NULL, space, fmt, ap); + + va_end(ap); + + return offset; +} + +/** Copies string src to dst at the specified offset. + * Output is truncated to fit in dstlen bytes, i.e. the string is at most + * (buflen - 1) characters long. Unlike strncpy, exactly one NUL is written + * to dst, which is always NUL-terminated. + * Returns the string length before/without truncation. + */ +size_t vcos_safe_strcpy(char *dst, const char *src, size_t dstlen, size_t offset) +{ + if (offset < dstlen) + { + const char *p = src; + char *endp = dst + dstlen -1; + + dst += offset; + + for (; *p!='\0' && dst != endp; dst++, p++) + *dst = *p; + *dst = '\0'; + } + offset += strlen(src); + + return offset; +} diff --git a/interface/vcos/generic/vcos_generic_tls.h b/interface/vcos/generic/vcos_generic_tls.h new file mode 100755 index 0000000..6460ae7 --- /dev/null +++ b/interface/vcos/generic/vcos_generic_tls.h @@ -0,0 +1,164 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - generic thread local storage +=============================================================================*/ + +#ifndef VCOS_GENERIC_TLS_H +#define VCOS_GENERIC_TLS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +/** + * \file + * + * Do an emulation of Thread Local Storage. The platform needs to + * provide a way to set and get a per-thread pointer which is + * where the TLS data itself is stored. + * + * + * Each thread that wants to join in this scheme needs to call + * vcos_tls_thread_register(). + * + * The platform needs to support the macros/functions + * _vcos_tls_thread_ptr_set() and _vcos_tls_thread_ptr_get(). + */ + +#ifndef VCOS_WANT_TLS_EMULATION +#error Should not be included unless TLS emulation is defined +#endif + +/** Number of slots to reserve per thread. This results in an overhead + * of this many words per thread. + */ +#define VCOS_TLS_MAX_SLOTS 4 + +/** TLS key. Allocating one of these reserves the client one of the + * available slots. + */ +typedef VCOS_UNSIGNED VCOS_TLS_KEY_T; + +/** TLS per-thread structure. Each thread gets one of these + * if TLS emulation (rather than native TLS support) is + * being used. + */ +typedef struct VCOS_TLS_THREAD_T +{ + void *slots[VCOS_TLS_MAX_SLOTS]; +} VCOS_TLS_THREAD_T; + +/* + * Internal APIs + */ + +/** Register this thread's TLS storage area. */ +VCOSPRE_ void VCOSPOST_ vcos_tls_thread_register(VCOS_TLS_THREAD_T *); + +/** Create a new TLS key */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_tls_create(VCOS_TLS_KEY_T *key); + +/** Delete a TLS key */ +VCOSPRE_ void VCOSPOST_ vcos_generic_tls_delete(VCOS_TLS_KEY_T tls); + +/** Initialise the TLS library */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_tls_init(void); + +/** Deinitialise the TLS library */ +VCOSPRE_ void VCOSPOST_ vcos_tls_deinit(void); + +#if defined(VCOS_INLINE_BODIES) + +#undef VCOS_ASSERT_LOGGING_DISABLE +#define VCOS_ASSERT_LOGGING_DISABLE 1 + +/* + * Implementations of public API functions + */ + +/** Set the given value. Since everything is per-thread, there is no need + * for any locking. + */ +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_tls_set(VCOS_TLS_KEY_T tls, void *v) { + VCOS_TLS_THREAD_T *tlsdata = _vcos_tls_thread_ptr_get(); + vcos_assert(tlsdata); /* Fires if this thread has not been registered */ + if (tlsslots[tls] = v; + return VCOS_SUCCESS; + } + else + { + vcos_assert(0); + return VCOS_EINVAL; + } +} + +/** Get the given value. No locking required. + */ +VCOS_INLINE_IMPL +void *vcos_tls_get(VCOS_TLS_KEY_T tls) { + VCOS_TLS_THREAD_T *tlsdata = _vcos_tls_thread_ptr_get(); + vcos_assert(tlsdata); /* Fires if this thread has not been registered */ + if (tlsslots[tls]; + } + else + { + vcos_assert(0); + return NULL; + } +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_tls_create(VCOS_TLS_KEY_T *key) { + return vcos_generic_tls_create(key); +} + +VCOS_INLINE_IMPL +void vcos_tls_delete(VCOS_TLS_KEY_T tls) { + vcos_generic_tls_delete(tls); +} + +#undef VCOS_ASSERT_LOGGING_DISABLE +#define VCOS_ASSERT_LOGGING_DISABLE 0 + +#endif /* VCOS_INLINE_BODIES */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/interface/vcos/generic/vcos_init.c b/interface/vcos/generic/vcos_init.c new file mode 100755 index 0000000..6f4c1bb --- /dev/null +++ b/interface/vcos/generic/vcos_init.c @@ -0,0 +1,84 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +The following header is included in order to provide one instance of the symbol vcos_deprecated_code. +Any other inclusions of this header (or "vcos_deprecated_code.inc" for assembly language) will cause the linker to warn +about multiple definitions of vcos_deprecated_code. +The idea is to include this header file for the source files which are deprecated. +Therefore the above warnning in a build indicates that the build is using deprecated code! +Contact the person named in the accompanying comment for advice - do not remove the inclusion. +*/ +#include "vcos_deprecated.h" + +#include "interface/vcos/vcos.h" + +static int init_refcount; + +VCOS_STATUS_T vcos_init(void) +{ + VCOS_STATUS_T st = VCOS_SUCCESS; + + vcos_global_lock(); + + if (init_refcount++ == 0) + st = vcos_platform_init(); + + vcos_global_unlock(); + + return st; +} + +void vcos_deinit(void) +{ + vcos_global_lock(); + + vcos_assert(init_refcount > 0); + + if (init_refcount > 0 && --init_refcount == 0) + vcos_platform_deinit(); + + vcos_global_unlock(); +} + +#if defined(__GNUC__) && (__GNUC__ > 2) + +void vcos_ctor(void) __attribute__((constructor, used)); + +void vcos_ctor(void) +{ + vcos_init(); +} + +void vcos_dtor(void) __attribute__((destructor, used)); + +void vcos_dtor(void) +{ + vcos_deinit(); +} + +#endif diff --git a/interface/vcos/generic/vcos_joinable_thread_from_plain.h b/interface/vcos/generic/vcos_joinable_thread_from_plain.h new file mode 100755 index 0000000..4471168 --- /dev/null +++ b/interface/vcos/generic/vcos_joinable_thread_from_plain.h @@ -0,0 +1,229 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - implementation: joinable thread from plain +=============================================================================*/ + +/** \file + * + * Header file for platforms creating the joinable thread from a lowlevel + * thread. + * + * In addition to the actual thread, the following are also created: + * + * - a semaphore to wait on when joining the thread + * - a semaphore to support counted suspend/resume (used by event group) + * - a per-thread timer (used by event group, but could be removed) + */ + +#ifndef VCOS_JOINABLE_THREAD_FROM_PLAIN_H +#define VCOS_JOINABLE_THREAD_FROM_PLAIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef VCOS_SEMAPHORE_H +#include "interface/vcos/vcos_semaphore.h" +#endif +#ifndef VCOS_LOWLEVEL_THREAD_H +#include "interface/vcos/vcos_lowlevel_thread.h" +#endif +#ifndef VCOS_TIMER_H +#include "interface/vcos/vcos_timer.h" +#endif + +#ifdef VCOS_WANT_TLS_EMULATION +#include "interface/vcos/generic/vcos_generic_tls.h" +#endif + +#define VCOS_THREAD_MAGIC 0x56436a74 + +#define VCOS_THREAD_VALID(t) (t->magic == VCOS_THREAD_MAGIC) +#define VCOS_HAVE_THREAD_AT_EXIT 1 + +/** Thread attribute structure. Clients should not manipulate this directly, but + * should instead use the provided functions. + */ +typedef struct VCOS_THREAD_ATTR_T +{ + void *ta_stackaddr; + VCOS_UNSIGNED ta_stacksz; + VCOS_UNSIGNED ta_priority; + VCOS_UNSIGNED ta_affinity; + VCOS_UNSIGNED ta_timeslice; + VCOS_UNSIGNED legacy; + VCOS_UNSIGNED ta_autostart; +} VCOS_THREAD_ATTR_T; + +/** Each thread gets a timer, which is for internal VCOS use. + */ +typedef struct _VCOS_THREAD_TIMER_T +{ + VCOS_TIMER_T timer; + void (*pfn)(void *); + void *cxt; +} _VCOS_THREAD_TIMER_T; + +typedef void (*VCOS_THREAD_EXIT_HANDLER_T)(void *); +/** Called at thread exit. + */ +typedef struct VCOS_THREAD_EXIT_T +{ + VCOS_THREAD_EXIT_HANDLER_T pfn; + void *cxt; +} VCOS_THREAD_EXIT_T; +#define VCOS_MAX_EXIT_HANDLERS 8 + +/* The name field isn't used for anything, so we can just copy the + * the pointer. Nucleus makes its own copy. + */ +typedef const char * VCOS_LLTHREAD_T_NAME; +#define _VCOS_LLTHREAD_NAME(dst,src) (dst)=(src) + +/* + * Simulated TLS support + */ + + +/** Thread structure. + * + * \warning Do not access the members of this structure directly! + */ +typedef struct VCOS_THREAD_T +{ + VCOS_LLTHREAD_T thread; /**< The underlying thread */ + char name[16]; /**< The name */ + unsigned int magic; /**< For debug */ + void *exit_data; /**< Exit data passed out in vcos_joinable_thread_exit() */ + void *stack; /**< Stack, if not supplied by caller */ + VCOS_SEMAPHORE_T wait; /**< Semaphore to wait on at join */ + VCOS_SEMAPHORE_T suspend; /**< Semaphore to wait on for counted suspend */ + int16_t joined; /**< Joined yet? For debug. */ + VCOS_UNSIGNED legacy; /**< Use (argc,argv) for entry point arguments */ + void *(*entry)(void*); /**< Entry point */ + void *arg; /**< Argument passed to entry point */ + void *(*term)(void*); /**< Termination function, used by reaper */ + void *term_arg; /**< Argument passed to termination function */ + _VCOS_THREAD_TIMER_T _timer; /**< Internal timer, mainly for event groups */ +#ifdef VCOS_WANT_TLS_EMULATION + VCOS_TLS_THREAD_T _tls; /**< TLS data when native TLS not available, or NULL */ +#endif + /** Array of functions to call at thread exit */ + VCOS_THREAD_EXIT_T at_exit[VCOS_MAX_EXIT_HANDLERS]; + + struct VCOS_THREAD_T *next; /**< For linked lists of threads */ +} VCOS_THREAD_T; + +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attrs, void *addr, VCOS_UNSIGNED stacksz) { + attrs->ta_stackaddr = addr; + attrs->ta_stacksz = stacksz; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED stacksz) { + attrs->ta_stacksz = stacksz; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED pri) { + attrs->ta_priority = pri; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED affinity) { + attrs->ta_affinity = affinity; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts) { + attrs->ta_timeslice = ts; +} + +VCOS_INLINE_IMPL +void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy) { + attrs->legacy = legacy; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart) { + attrs->ta_autostart = autostart; +} + +VCOS_INLINE_IMPL +VCOS_THREAD_T *vcos_thread_current(void) { + VCOS_THREAD_T *ret = (VCOS_THREAD_T*)vcos_llthread_current(); + /*If we're called from a non-vcos thread, this assert will fail. + *XXX FIXME why is this commented out? + *vcos_assert(ret->magic == VCOS_THREAD_MAGIC); + */ + return ret; +} + +VCOS_INLINE_IMPL +int vcos_thread_running(VCOS_THREAD_T *thread) { + return vcos_llthread_running(&thread->thread); +} + +VCOS_INLINE_IMPL +void vcos_thread_resume(VCOS_THREAD_T *thread) { + vcos_llthread_resume(&thread->thread); +} + +#endif /* VCOS_INLINE_BODIES */ + +/** + * \brief Create a VCOS_THREAD_T for the current thread. This is so we can have + * VCOS_THREAD_Ts even for threads not originally created by VCOS (eg the + * thread that calls vcos_init) + */ +extern VCOS_STATUS_T _vcos_thread_create_attach(VCOS_THREAD_T *thread, + const char *name); + +/** + * \brief Deletes the VCOS_THREAD_T, but does not wait for the underlying + * thread to exit. This will cleanup everything created by + * _vcos_thread_create_attach + */ +extern void _vcos_thread_delete(VCOS_THREAD_T *thread); + +/** Register a function to be called when the current thread exits. + */ +extern VCOS_STATUS_T vcos_thread_at_exit(void (*pfn)(void*), void *cxt); + +/** Deregister a previously registered at-exit function. + */ +extern void vcos_thread_deregister_at_exit(void (*pfn)(void*), void *cxt); + +#ifdef __cplusplus +} +#endif +#endif /* VCOS_JOINABLE_THREAD_FROM_PLAIN_H */ diff --git a/interface/vcos/generic/vcos_latch_from_sem.h b/interface/vcos/generic/vcos_latch_from_sem.h new file mode 100755 index 0000000..94f4b13 --- /dev/null +++ b/interface/vcos/generic/vcos_latch_from_sem.h @@ -0,0 +1,68 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - Construct a latch from a semaphore +=============================================================================*/ + +/** FIXME: rename to vcos_mutex_from_sem.c + */ + +typedef struct VCOS_MUTEX_T { + VCOS_SEMAPHORE_T sem; + struct VCOS_THREAD_T *owner; +} VCOS_MUTEX_T; + +extern VCOS_STATUS_T vcos_generic_mutex_create(VCOS_MUTEX_T *latch, const char *name); +extern void vcos_generic_mutex_delete(VCOS_MUTEX_T *latch); +extern VCOS_STATUS_T vcos_generic_mutex_lock(VCOS_MUTEX_T *latch); +extern void vcos_generic_mutex_unlock(VCOS_MUTEX_T *latch); + +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *latch, const char *name) { + return vcos_generic_mutex_create(latch,name); +} + +VCOS_INLINE_IMPL +void vcos_mutex_delete(VCOS_MUTEX_T *latch) { + vcos_generic_mutex_delete(latch); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *latch) { + return vcos_generic_mutex_lock(latch); +} + +VCOS_INLINE_IMPL +void vcos_mutex_unlock(VCOS_MUTEX_T *latch) { + vcos_generic_mutex_unlock(latch); +} + +#endif /* VCOS_INLINE_BODIES */ + diff --git a/interface/vcos/generic/vcos_logcat.c b/interface/vcos/generic/vcos_logcat.c new file mode 100755 index 0000000..b9b9c88 --- /dev/null +++ b/interface/vcos/generic/vcos_logcat.c @@ -0,0 +1,571 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +Categorized logging for VCOS - a generic implementation. +=============================================================================*/ + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_ctype.h" +#include "interface/vcos/vcos_string.h" +#include "interface/vcos/vcos_inttypes.h" + +static VCOS_MUTEX_T lock; +static int warned_loglevel; /* only warn about invalid log level once */ +static VCOS_VLOG_IMPL_FUNC_T vcos_vlog_impl_func = vcos_vlog_default_impl; + +#define VCOS_LOG_CATEGORY (&dflt_log_category) +static VCOS_LOG_CAT_T dflt_log_category; +VCOS_LOG_CAT_T *vcos_logging_categories = NULL; +static int inited; + +#if VCOS_HAVE_CMD + +/* + * For kernel or videocore purposes, we generally want the log command. For + * user-space apps, they might want to provide their own log command, so we + * don't include the built in on. + * + * So pthreads/vcos_platform.h defines VCOS_WANT_LOG_CMD to be 0. It is + * undefined elsewhere. + */ + +# if !defined( VCOS_WANT_LOG_CMD ) +# define VCOS_WANT_LOG_CMD 1 +# endif +#else +# define VCOS_WANT_LOG_CMD 0 +#endif + +#if VCOS_WANT_LOG_CMD + +/***************************************************************************** +* +* Does a vcos_assert(0), which is useful to test logging. +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param ) +{ + (void)param; + +#if defined( NDEBUG ) && !defined( VCOS_RELEASE_ASSERTS ) + vcos_log_error( "vcos_asserts have been compiled out" ); + vcos_cmd_printf( param, "vcos_asserts have been compiled out - did a vcos_log_error instead\n" ); +#else + vcos_assert(0); + vcos_cmd_printf( param, "Executed vcos_assert(0)\n" ); +#endif + + return VCOS_SUCCESS; +} + +/***************************************************************************** +* +* Sets a vcos logging level +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_log_set_cmd( VCOS_CMD_PARAM_T *param ) +{ + VCOS_LOG_CAT_T *cat; + char *name; + char *levelStr; + VCOS_LOG_LEVEL_T level; + VCOS_STATUS_T status; + + if ( param->argc != 3 ) + { + vcos_cmd_usage( param ); + return VCOS_EINVAL; + } + + name = param->argv[1]; + levelStr = param->argv[2]; + + if ( vcos_string_to_log_level( levelStr, &level ) != VCOS_SUCCESS ) + { + vcos_cmd_printf( param, "Unrecognized logging level: '%s'\n", levelStr ); + return VCOS_EINVAL; + } + + vcos_mutex_lock(&lock); + + status = VCOS_SUCCESS; + for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) + { + if ( vcos_strcmp( name, cat->name ) == 0 ) + { + cat->level = level; + vcos_cmd_printf( param, "Category %s level set to %s\n", name, levelStr ); + break; + } + } + if ( cat == NULL ) + { + vcos_cmd_printf( param, "Unrecognized category: '%s'\n", name ); + status = VCOS_ENOENT; + } + + vcos_mutex_unlock(&lock); + + return status; +} + +/***************************************************************************** +* +* Prints out the current settings for a given category (or all cvategories) +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_log_status_cmd( VCOS_CMD_PARAM_T *param ) +{ + VCOS_LOG_CAT_T *cat; + VCOS_STATUS_T status; + + vcos_mutex_lock(&lock); + + if ( param->argc == 1) + { + int nw; + int nameWidth = 0; + + /* Print information about all of the categories. */ + + for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) + { + nw = (int)strlen( cat->name ); + + if ( nw > nameWidth ) + { + nameWidth = nw; + } + } + + for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) + { + vcos_cmd_printf( param, "%-*s - %s\n", nameWidth, cat->name, vcos_log_level_to_string( cat->level )); + } + } + else + { + /* Print information about a particular category */ + + for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) + { + if ( vcos_strcmp( cat->name, param->argv[1] ) == 0 ) + { + vcos_cmd_printf( param, "%s - %s\n", cat->name, vcos_log_level_to_string( cat->level )); + break; + } + } + if ( cat == NULL ) + { + vcos_cmd_printf( param, "Unrecognized logging category: '%s'\n", param->argv[1] ); + status = VCOS_ENOENT; + goto out; + } + } + + status = VCOS_SUCCESS; +out: + vcos_mutex_unlock(&lock); + + return status; +} + +/***************************************************************************** +* +* Prints out the current settings for a given category (or all cvategories) +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_log_test_cmd( VCOS_CMD_PARAM_T *param ) +{ + if ( param->argc == 1 ) + { + static int seq_num = 100; + + /* No additional arguments - generate a message with an incrementing number */ + + vcos_log_error( "Test message %d", seq_num ); + + seq_num++; + vcos_cmd_printf( param, "Logged 'Test message %d'\n", seq_num ); + } + else + { + int arg_idx; + + /* Arguments supplied - log these */ + + for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) + { + vcos_log_error( "argv[%d] = '%s'", arg_idx, param->argv[arg_idx] ); + } + vcos_cmd_printf( param, "Logged %d line(s) of test data\n", param->argc ); + } + return VCOS_SUCCESS; +} + +/***************************************************************************** +* +* Internal commands +* +*****************************************************************************/ + +static VCOS_CMD_T log_cmd_entry[] = +{ + { "assert", "", vcos_log_assert_cmd, NULL, "Does a vcos_assert(0) to test logging" }, + { "set", "category level", vcos_log_set_cmd, NULL, "Sets the vcos logging level for a category" }, + { "status", "[category]", vcos_log_status_cmd, NULL, "Prints the vcos log status for a (or all) categories" }, + { "test", "[arbitrary text]", vcos_log_test_cmd, NULL, "Does a vcos_log to test logging" }, + + { NULL, NULL, NULL, NULL, NULL } +}; + +static VCOS_CMD_T cmd_log = + { "log", "command [args]", NULL, log_cmd_entry, "Commands related to vcos logging" }; + +#endif + +void vcos_logging_init(void) +{ + if (inited) + { + /* FIXME: should print a warning or something here */ + return; + } + vcos_mutex_create(&lock, "vcos_log"); + + vcos_log_platform_init(); + + vcos_log_register("default", &dflt_log_category); + +#if VCOS_WANT_LOG_CMD + vcos_cmd_register( &cmd_log ); +#endif + + vcos_assert(!inited); + inited = 1; +} + +/** Read an alphanumeric token, returning True if we succeeded. + */ + +static int read_tok(char *tok, size_t toklen, const char **pstr, char sep) +{ + const char *str = *pstr; + size_t n = 0; + char ch; + + /* skip past any whitespace */ + while (str[0] && isspace((int)(str[0]))) + str++; + + while ((ch = *str) != '\0' && + ch != sep && + (isalnum((int)ch) || (ch == '_')) && + n != toklen-1) + { + tok[n++] = ch; + str++; + } + + /* did it work out? */ + if (ch == '\0' || ch == sep) + { + if (ch) str++; /* move to next token if not at end */ + /* yes */ + tok[n] = '\0'; + *pstr = str; + return 1; + } + else + { + /* no */ + return 0; + } +} + +const char *vcos_log_level_to_string( VCOS_LOG_LEVEL_T level ) +{ + switch (level) + { + case VCOS_LOG_UNINITIALIZED: return "uninit"; + case VCOS_LOG_NEVER: return "never"; + case VCOS_LOG_ERROR: return "error"; + case VCOS_LOG_WARN: return "warn"; + case VCOS_LOG_INFO: return "info"; + case VCOS_LOG_TRACE: return "trace"; + } + return "???"; +} + +VCOS_STATUS_T vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level ) +{ + if (strcmp(str,"error") == 0) + *level = VCOS_LOG_ERROR; + else if (strcmp(str,"never") == 0) + *level = VCOS_LOG_NEVER; + else if (strcmp(str,"warn") == 0) + *level = VCOS_LOG_WARN; + else if (strcmp(str,"warning") == 0) + *level = VCOS_LOG_WARN; + else if (strcmp(str,"info") == 0) + *level = VCOS_LOG_INFO; + else if (strcmp(str,"trace") == 0) + *level = VCOS_LOG_TRACE; + else + return VCOS_EINVAL; + + return VCOS_SUCCESS; +} + +static int read_level(VCOS_LOG_LEVEL_T *level, const char **pstr, char sep) +{ + char buf[16]; + int ret = 1; + if (read_tok(buf,sizeof(buf),pstr,sep)) + { + if (vcos_string_to_log_level(buf,level) != VCOS_SUCCESS) + { + vcos_log("Invalid trace level '%s'\n", buf); + ret = 0; + } + } + else + { + ret = 0; + } + return ret; +} + +void vcos_log_register(const char *name, VCOS_LOG_CAT_T *category) +{ + const char *env; + VCOS_LOG_CAT_T *i; + + category->name = name; + if ( category->level == VCOS_LOG_UNINITIALIZED ) + { + category->level = VCOS_LOG_ERROR; + } + category->flags.want_prefix = (category != &dflt_log_category ); + + vcos_mutex_lock(&lock); + + /* is it already registered? */ + for (i = vcos_logging_categories; i ; i = i->next ) + { + if (i == category) + { + i->refcount++; + break; + } + } + + if (!i) + { + /* not yet registered */ + category->next = vcos_logging_categories; + vcos_logging_categories = category; + category->refcount++; + + vcos_log_platform_register(category); + } + + vcos_mutex_unlock(&lock); + + /* Check to see if this log level has been enabled. Look for + * (,)* + * + * VC_LOGLEVEL=ilcs:info,vchiq:warn + */ + + env = _VCOS_LOG_LEVEL(); + if (env) + { + do + { + char env_name[64]; + VCOS_LOG_LEVEL_T level; + if (read_tok(env_name, sizeof(env_name), &env, ':') && + read_level(&level, &env, ',')) + { + if (strcmp(env_name, name) == 0) + { + category->level = level; + break; + } + } + else + { + if (!warned_loglevel) + { + vcos_log("VC_LOGLEVEL format invalid at %s\n", env); + warned_loglevel = 1; + } + return; + } + } while (env[0] != '\0'); + } + + vcos_log_info( "Registered log category '%s' with level %s", + category->name, + vcos_log_level_to_string( category->level )); +} + +void vcos_log_unregister(VCOS_LOG_CAT_T *category) +{ + VCOS_LOG_CAT_T **pcat; + + vcos_mutex_lock(&lock); + category->refcount--; + if (category->refcount == 0) + { + pcat = &vcos_logging_categories; + while (*pcat != category) + { + if (!*pcat) + break; /* possibly deregistered twice? */ + if ((*pcat)->next == NULL) + { + vcos_assert(0); /* already removed! */ + vcos_mutex_unlock(&lock); + return; + } + pcat = &(*pcat)->next; + } + if (*pcat) + *pcat = category->next; + + vcos_log_platform_unregister(category); + } + vcos_mutex_unlock(&lock); +} + +VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void) +{ + return &dflt_log_category; +} + +void vcos_set_log_options(const char *opt) +{ + (void)opt; +} + +void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat, + const char *label, + uint32_t addr, + const void *voidMem, + size_t numBytes ) +{ + const uint8_t *mem = (const uint8_t *)voidMem; + size_t offset; + char lineBuf[ 100 ]; + char *s; + + while ( numBytes > 0 ) + { + s = lineBuf; + + for ( offset = 0; offset < 16; offset++ ) + { + if ( offset < numBytes ) + { + s += vcos_snprintf( s, 4, "%02x ", mem[ offset ]); + } + else + { + s += vcos_snprintf( s, 4, " " ); + } + } + + for ( offset = 0; offset < 16; offset++ ) + { + if ( offset < numBytes ) + { + uint8_t ch = mem[ offset ]; + + if (( ch < ' ' ) || ( ch > '~' )) + { + ch = '.'; + } + *s++ = (char)ch; + } + } + *s++ = '\0'; + + if (( label != NULL ) && ( *label != '\0' )) + { + vcos_log_impl( cat, VCOS_LOG_INFO, "%s: %08" PRIx32 ": %s", label, addr, lineBuf ); + } + else + { + vcos_log_impl( cat, VCOS_LOG_INFO, "%08" PRIx32 ": %s", addr, lineBuf ); + } + + addr += 16; + mem += 16; + if ( numBytes > 16 ) + { + numBytes -= 16; + } + else + { + numBytes = 0; + } + } + +} + +void vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...) +{ + va_list ap; + va_start(ap,fmt); + vcos_vlog_impl( cat, _level, fmt, ap ); + va_end(ap); +} + +void vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) +{ + vcos_vlog_impl_func( cat, _level, fmt, args ); +} + +void vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func ) +{ + if ( vlog_impl_func == NULL ) + { + vcos_vlog_impl_func = vcos_vlog_default_impl; + } + else + { + vcos_vlog_impl_func = vlog_impl_func; + } +} + diff --git a/interface/vcos/generic/vcos_mem_from_malloc.c b/interface/vcos/generic/vcos_mem_from_malloc.c new file mode 100755 index 0000000..02f9f28 --- /dev/null +++ b/interface/vcos/generic/vcos_mem_from_malloc.c @@ -0,0 +1,98 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - memory alloc implementation +=============================================================================*/ + +#include "interface/vcos/vcos.h" + +#ifndef _vcos_platform_malloc +#include +#define _vcos_platform_malloc malloc +#define _vcos_platform_free free +#endif + +typedef struct malloc_header_s { + uint32_t guardword; + uint32_t size; + const char *description; + void *ptr; +} MALLOC_HEADER_T; + + +#define MIN_ALIGN sizeof(MALLOC_HEADER_T) + +#define GUARDWORDHEAP 0xa55a5aa5 + +void *vcos_generic_mem_alloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *desc) +{ + int local_align = align == 0 ? 1 : align; + int required_size = size + local_align + sizeof(MALLOC_HEADER_T); + void *ptr = _vcos_platform_malloc(required_size); + void *ret = NULL; + MALLOC_HEADER_T *h; + + if (ptr) + { + ret = (void *)VCOS_ALIGN_UP(((char *)ptr)+sizeof(MALLOC_HEADER_T), local_align); + h = ((MALLOC_HEADER_T *)ret)-1; + h->size = size; + h->description = desc; + h->guardword = GUARDWORDHEAP; + h->ptr = ptr; + } + + return ret; +} + +void *vcos_generic_mem_alloc(VCOS_UNSIGNED size, const char *desc) +{ + return vcos_generic_mem_alloc_aligned(size,MIN_ALIGN,desc); +} + +void *vcos_generic_mem_calloc(VCOS_UNSIGNED count, VCOS_UNSIGNED sz, const char *desc) +{ + uint32_t size = count*sz; + void *ptr = vcos_generic_mem_alloc_aligned(size,MIN_ALIGN,desc); + if (ptr) + { + memset(ptr, 0, size); + } + return ptr; +} + +void vcos_generic_mem_free(void *ptr) +{ + MALLOC_HEADER_T *h; + if (! ptr) return; + + h = ((MALLOC_HEADER_T *)ptr)-1; + vcos_assert(h->guardword == GUARDWORDHEAP); + _vcos_platform_free(h->ptr); +} + diff --git a/interface/vcos/generic/vcos_mem_from_malloc.h b/interface/vcos/generic/vcos_mem_from_malloc.h new file mode 100755 index 0000000..e0bd99d --- /dev/null +++ b/interface/vcos/generic/vcos_mem_from_malloc.h @@ -0,0 +1,80 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +Create the vcos_malloc API from the regular system malloc/free +=============================================================================*/ + +/** + * \file + * + * Create the vcos malloc API from a regular system malloc/free library. + * + * The API lets callers specify an alignment. + * + * Under VideoCore this is not needed, as we can simply use the rtos_malloc routines. + * But on host platforms that won't be the case. + * + */ + +VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_alloc(VCOS_UNSIGNED sz, const char *desc); +VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_calloc(VCOS_UNSIGNED count, VCOS_UNSIGNED sz, const char *descr); +VCOSPRE_ void VCOSPOST_ vcos_generic_mem_free(void *ptr); +VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_alloc_aligned(VCOS_UNSIGNED sz, VCOS_UNSIGNED align, const char *desc); + +#ifdef VCOS_INLINE_BODIES + +VCOS_INLINE_IMPL +void *vcos_malloc(VCOS_UNSIGNED size, const char *description) { + return vcos_generic_mem_alloc(size, description); +} + +VCOS_INLINE_IMPL +void *vcos_calloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description) { + return vcos_generic_mem_calloc(num, size, description); +} + +VCOS_INLINE_IMPL +void vcos_free(void *ptr) { + vcos_generic_mem_free(ptr); +} + +VCOS_INLINE_IMPL +void * vcos_malloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description) { + return vcos_generic_mem_alloc_aligned(size, align, description); +} + +/* Returns invalid result, do not use */ + +VCOS_INLINE_IMPL +unsigned long VCOS_DEPRECATED("returns invalid result") vcos_get_free_mem(void) { + return 0; +} + +#endif /* VCOS_INLINE_BODIES */ + + diff --git a/interface/vcos/generic/vcos_msgqueue.c b/interface/vcos/generic/vcos_msgqueue.c new file mode 100755 index 0000000..b8e558b --- /dev/null +++ b/interface/vcos/generic/vcos_msgqueue.c @@ -0,0 +1,389 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "vcos.h" +#include "vcos_msgqueue.h" +#include +#include +#include + +#define MAGIC VCOS_MSGQ_MAGIC + +/* Probably a good idea for MSG_T to be multiple of 8 so that doubles + * are naturally aligned without problem. + */ +vcos_static_assert((sizeof(VCOS_MSG_T) & 7) == 0); + +static void vcos_msgq_pool_on_reply(VCOS_MSG_WAITER_T *waiter, + VCOS_MSG_T *msg); +static void vcos_msgq_queue_waiter_on_reply(VCOS_MSG_WAITER_T *waiter, + VCOS_MSG_T *msg); + +/** Simple reply protocol. The client creates a semaphore and waits + * for it. No queuing of multiple replies is possible but nothing needs + * to be setup in advance. Because creating semaphores is very fast on + * VideoCore there's no need to do anything elaborate to optimize create + * time - this might need revisiting on other platforms. + */ + +typedef struct +{ + VCOS_MSG_WAITER_T waiter; + VCOS_SEMAPHORE_T waitsem; +} VCOS_MSG_SIMPLE_WAITER_T; + +static void vcos_msgq_simple_waiter_on_reply(VCOS_MSG_WAITER_T *waiter, + VCOS_MSG_T *msg) +{ + VCOS_MSG_SIMPLE_WAITER_T *self; + (void)msg; + self = (VCOS_MSG_SIMPLE_WAITER_T*)waiter; + vcos_semaphore_post(&self->waitsem); +} + +static VCOS_STATUS_T vcos_msgq_simple_waiter_init(VCOS_MSG_SIMPLE_WAITER_T *waiter) +{ + VCOS_STATUS_T status; + status = vcos_semaphore_create(&waiter->waitsem, "waiter", 0); + waiter->waiter.on_reply = vcos_msgq_simple_waiter_on_reply; + return status; +} + +static void vcos_msgq_simple_waiter_deinit(VCOS_MSG_SIMPLE_WAITER_T *waiter) +{ + vcos_semaphore_delete(&waiter->waitsem); +} + +/* + * Message queues + */ + +static VCOS_STATUS_T vcos_msgq_create_internal(VCOS_MSGQUEUE_T *q, const char *name) +{ + VCOS_STATUS_T st; + + memset(q, 0, sizeof(*q)); + + q->waiter.on_reply = vcos_msgq_queue_waiter_on_reply; + st = vcos_semaphore_create(&q->sem, name, 0); + if (st != VCOS_SUCCESS) + goto fail_sem; + + st = vcos_mutex_create(&q->lock, name); + if (st != VCOS_SUCCESS) + goto fail_mtx; + + return st; + +fail_mtx: + vcos_semaphore_delete(&q->sem); +fail_sem: + return st; +} + +static void vcos_msgq_delete_internal(VCOS_MSGQUEUE_T *q) +{ + vcos_semaphore_delete(&q->sem); + vcos_mutex_delete(&q->lock); +} + +VCOS_STATUS_T vcos_msgq_create(VCOS_MSGQUEUE_T *q, const char *name) +{ + VCOS_STATUS_T st; + + st = vcos_msgq_create_internal(q, name); + + return st; +} + +void vcos_msgq_delete(VCOS_MSGQUEUE_T *q) +{ + vcos_msgq_delete_internal(q); +} + +/* append a message to a message queue */ +static _VCOS_INLINE void msgq_append(VCOS_MSGQUEUE_T *q, VCOS_MSG_T *msg) +{ + vcos_mutex_lock(&q->lock); + if (q->head == NULL) + { + q->head = q->tail = msg; + } + else + { + q->tail->next = msg; + q->tail = msg; + } + vcos_mutex_unlock(&q->lock); +} + +/* + * A waiter for a message queue. Just appends the message to the + * queue, waking up the waiting thread. + */ +static void vcos_msgq_queue_waiter_on_reply(VCOS_MSG_WAITER_T *waiter, + VCOS_MSG_T *msg) +{ + VCOS_MSGQUEUE_T *queue = (VCOS_MSGQUEUE_T*)waiter; + msgq_append(queue, msg); + vcos_semaphore_post(&queue->sem); +} + +/* initialise this library */ + +VCOS_STATUS_T vcos_msgq_init(void) +{ + return VCOS_SUCCESS; +} + +void vcos_msgq_deinit(void) +{ +} + +static _VCOS_INLINE +void vcos_msg_send_helper(VCOS_MSG_WAITER_T *waiter, + VCOS_MSGQUEUE_T *dest, + uint32_t code, + VCOS_MSG_T *msg) +{ + vcos_assert(msg); + vcos_assert(dest); + + msg->code = code; + if (waiter) + msg->waiter = waiter; + msg->next = NULL; + msg->src_thread = vcos_thread_current(); + + msgq_append(dest, msg); + vcos_semaphore_post(&dest->sem); +} + +/* wait on a queue for a message */ +VCOS_MSG_T *vcos_msg_wait(VCOS_MSGQUEUE_T *queue) +{ + VCOS_MSG_T *msg; + vcos_semaphore_wait(&queue->sem); + vcos_mutex_lock(&queue->lock); + + msg = queue->head; + vcos_assert(msg); /* should always be a message here! */ + + queue->head = msg->next; + if (queue->head == NULL) + queue->tail = NULL; + + vcos_mutex_unlock(&queue->lock); + return msg; +} + +/* peek on a queue for a message */ +VCOS_MSG_T *vcos_msg_peek(VCOS_MSGQUEUE_T *queue) +{ + VCOS_MSG_T *msg; + vcos_mutex_lock(&queue->lock); + + msg = queue->head; + + /* if there's a message, remove it from the queue */ + if (msg) + { + queue->head = msg->next; + if (queue->head == NULL) + queue->tail = NULL; + + /* keep the semaphore count consistent */ + + /* coverity[lock_order] + * the semaphore must have a non-zero count so cannot block here. + */ + vcos_semaphore_wait(&queue->sem); + } + + vcos_mutex_unlock(&queue->lock); + return msg; +} + +void vcos_msg_send(VCOS_MSGQUEUE_T *dest, uint32_t code, VCOS_MSG_T *msg) +{ + vcos_assert(msg->magic == MAGIC); + vcos_msg_send_helper(NULL, dest, code, msg); +} + +/** Send on to the target queue, then wait on a simple waiter for the reply + */ +VCOS_STATUS_T vcos_msg_sendwait(VCOS_MSGQUEUE_T *dest, uint32_t code, VCOS_MSG_T *msg) +{ + VCOS_STATUS_T st; + VCOS_MSG_SIMPLE_WAITER_T waiter; + + vcos_assert(msg->magic == MAGIC); + + /* if this fires, you've set a waiter up but are now about to obliterate it + * with the 'wait for a reply' waiter. + */ + vcos_assert(msg->waiter == NULL); + + if ((st=vcos_msgq_simple_waiter_init(&waiter)) != VCOS_SUCCESS) + return st; + + vcos_msg_send_helper(&waiter.waiter, dest, code, msg); + vcos_semaphore_wait(&waiter.waitsem); + vcos_msgq_simple_waiter_deinit(&waiter); + + return VCOS_SUCCESS; +} + +/** Send a reply to a message + */ +void vcos_msg_reply(VCOS_MSG_T *msg) +{ + vcos_assert(msg->magic == MAGIC); + msg->code |= MSG_REPLY_BIT; + if (msg->waiter) + { + msg->waiter->on_reply(msg->waiter, msg); + } + else + { + VCOS_ALERT("%s: reply to non-reply message id %d", + VCOS_FUNCTION, + msg->code); + vcos_assert(0); + } +} + +void vcos_msg_set_source(VCOS_MSG_T *msg, VCOS_MSGQUEUE_T *queue) +{ + vcos_assert(msg); + vcos_assert(msg->magic == MAGIC); + vcos_assert(queue); + msg->waiter = &queue->waiter; +} + +/* + * Message pools + */ + +VCOS_STATUS_T vcos_msgq_pool_create(VCOS_MSGQ_POOL_T *pool, + size_t count, + size_t payload_size, + const char *name) +{ + VCOS_STATUS_T status; + int bp_size = payload_size + sizeof(VCOS_MSG_T); + status = vcos_blockpool_create_on_heap(&pool->blockpool, + count, bp_size, + VCOS_BLOCKPOOL_ALIGN_DEFAULT, + 0, + name); + if (status != VCOS_SUCCESS) + goto fail_pool; + + status = vcos_semaphore_create(&pool->sem, name, count); + if (status != VCOS_SUCCESS) + goto fail_sem; + + pool->waiter.on_reply = vcos_msgq_pool_on_reply; + pool->magic = MAGIC; + return status; + +fail_sem: + vcos_blockpool_delete(&pool->blockpool); +fail_pool: + return status; +} + +void vcos_msgq_pool_delete(VCOS_MSGQ_POOL_T *pool) +{ + vcos_blockpool_delete(&pool->blockpool); + vcos_semaphore_delete(&pool->sem); +} + +/** Called when a message from a pool is replied-to. Just returns + * the message back to the blockpool. + */ +static void vcos_msgq_pool_on_reply(VCOS_MSG_WAITER_T *waiter, + VCOS_MSG_T *msg) +{ + vcos_unused(waiter); + vcos_assert(msg->magic == MAGIC); + vcos_msgq_pool_free(msg); +} + +VCOS_MSG_T *vcos_msgq_pool_alloc(VCOS_MSGQ_POOL_T *pool) +{ + VCOS_MSG_T *msg; + if (vcos_semaphore_trywait(&pool->sem) == VCOS_SUCCESS) + { + msg = vcos_blockpool_calloc(&pool->blockpool); + vcos_assert(msg); + msg->magic = MAGIC; + msg->waiter = &pool->waiter; + msg->pool = pool; + } + else + { + msg = NULL; + } + return msg; +} + +void vcos_msgq_pool_free(VCOS_MSG_T *msg) +{ + if (msg) + { + VCOS_MSGQ_POOL_T *pool; + vcos_assert(msg->pool); + + pool = msg->pool; + vcos_assert(msg->pool->magic == MAGIC); + + vcos_blockpool_free(msg); + vcos_semaphore_post(&pool->sem); + } +} + +VCOS_MSG_T *vcos_msgq_pool_wait(VCOS_MSGQ_POOL_T *pool) +{ + VCOS_MSG_T *msg; + vcos_semaphore_wait(&pool->sem); + msg = vcos_blockpool_calloc(&pool->blockpool); + vcos_assert(msg); + msg->magic = MAGIC; + msg->waiter = &pool->waiter; + msg->pool = pool; + return msg; +} + +void vcos_msg_init(VCOS_MSG_T *msg) +{ + msg->magic = MAGIC; + msg->next = NULL; + msg->waiter = NULL; + msg->pool = NULL; +} diff --git a/interface/vcos/generic/vcos_mutexes_are_reentrant.h b/interface/vcos/generic/vcos_mutexes_are_reentrant.h new file mode 100755 index 0000000..976b61a --- /dev/null +++ b/interface/vcos/generic/vcos_mutexes_are_reentrant.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - reentrant mutexes mapped directly to regular ones +=============================================================================*/ + +#ifndef VCOS_GENERIC_REENTRANT_MUTEX_H +#define VCOS_GENERIC_REENTRANT_MUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "interface/vcos/vcos_mutex.h" + +/** + * \file + * + * Reentrant Mutexes directly using the native re-entrant mutex. + * + */ + +typedef VCOS_MUTEX_T VCOS_REENTRANT_MUTEX_T; + +/* Inline forwarding functions */ + +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) { + return vcos_mutex_create(m,name); +} + +VCOS_INLINE_IMPL +void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) { + vcos_mutex_delete(m); +} + +VCOS_INLINE_IMPL +void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) { + vcos_mutex_lock(m); +} + +VCOS_INLINE_IMPL +void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) { + vcos_mutex_unlock(m); +} + +VCOS_INLINE_IMPL +int vcos_reentrant_mutex_is_locked(VCOS_REENTRANT_MUTEX_T *m) { + return vcos_mutex_is_locked(m); +} + +#endif + +#ifdef __cplusplus +} +#endif +#endif + + + diff --git a/interface/vcos/generic/vcos_thread_reaper.h b/interface/vcos/generic/vcos_thread_reaper.h new file mode 100755 index 0000000..4190dba --- /dev/null +++ b/interface/vcos/generic/vcos_thread_reaper.h @@ -0,0 +1,55 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - thread reaping +=============================================================================*/ + +#ifndef VCOS_THREAD_REAPER_H +#define VCOS_THREAD_REAPER_H + +#define VCOS_HAVE_THREAD_REAPER + +/** Initialise the thread reaper. + */ +VCOS_STATUS_T vcos_thread_reaper_init(void); + +/** Reap a thread. Arranges for the thread to be automatically + * joined. + * + * @sa vcos_thread_join(). + * + * @param thread the thread to terminate + * @param on_terminated called after the thread has exited + * @param cxt pass back to the callback + * + */ +void vcos_thread_reap(VCOS_THREAD_T *thread, void (*on_terminated)(void*), void *cxt); + +#endif + + diff --git a/interface/vcos/glibc/vcos_backtrace.c b/interface/vcos/glibc/vcos_backtrace.c new file mode 100755 index 0000000..3bb8aa3 --- /dev/null +++ b/interface/vcos/glibc/vcos_backtrace.c @@ -0,0 +1,56 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#ifdef HAVE_CMAKE_CONFIG +#include "cmake_config.h" +#endif +#ifdef HAVE_EXECINFO_H +#include +#endif +#include +#include +#include + +void vcos_backtrace_self(void) +{ +#ifdef HAVE_EXECINFO_H + void *stack[64]; + int depth = backtrace(stack, sizeof(stack)/sizeof(stack[0])); + char **names = backtrace_symbols(stack, depth); + int i; + if (names) + { + for (i=0; i + +void *vcos_dlopen(const char *name, int mode) +{ + return dlopen(name, mode); +} + +void (*vcos_dlsym(void *handle, const char *name))(void) +{ + return dlsym(handle, name); +} + +int vcos_dlclose (void *handle) +{ + return dlclose(handle); +} + +int vcos_dlerror(int *err, char *buf, size_t buflen) +{ + /* not really threadsafe! */ + const char *errmsg = dlerror(); + + vcos_assert(buflen > 0); + + if (errmsg) + { + *err = -1; + strncpy(buf, errmsg, buflen); + buf[buflen-1] = '\0'; + } + else + { + *err = 0; + buf[0] = '\0'; + } + return 0; +} + + + + diff --git a/interface/vcos/pthreads/vcos_futex_mutex.h b/interface/vcos/pthreads/vcos_futex_mutex.h new file mode 100755 index 0000000..3c44720 --- /dev/null +++ b/interface/vcos/pthreads/vcos_futex_mutex.h @@ -0,0 +1,102 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +FIXME: This code should be moved to 'linux', it is linux-specific and not generic +on 'pthreads'. +============================================================================*/ + +#ifndef VCOS_MUTEX_FROM_FUTEX_H +#define VCOS_MUTEX_FROM_FUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos_platform.h" + +typedef struct VCOS_FUTEX_T +{ + volatile int value; +} VCOS_FUTEX_T; + +typedef VCOS_FUTEX_T VCOS_MUTEX_T; + +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_futex_init(VCOS_FUTEX_T *futex); +VCOSPRE_ void VCOSPOST_ vcos_futex_delete(VCOS_FUTEX_T *futex); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_futex_lock(VCOS_FUTEX_T *futex); +VCOSPRE_ void VCOSPOST_ vcos_futex_unlock(VCOS_FUTEX_T *futex); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_futex_trylock(VCOS_FUTEX_T *futex); + +#if defined(VCOS_INLINE_BODIES) + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *latch, const char *name) { + vcos_unused(name); + return vcos_futex_init(latch); +} + +VCOS_INLINE_IMPL +void vcos_mutex_delete(VCOS_MUTEX_T *latch) { + vcos_futex_delete(latch); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *latch) { + return vcos_futex_lock(latch); +} + +VCOS_INLINE_IMPL +void vcos_mutex_unlock(VCOS_MUTEX_T *latch) { + vcos_futex_unlock(latch); +} + +VCOS_INLINE_IMPL +int vcos_mutex_is_locked(VCOS_MUTEX_T *latch) { + int rc = latch->value; + if (!rc) { + /* it wasn't locked */ + return 0; + } + else { + return 1; /* it was locked */ + } +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m) { + return vcos_futex_trylock(m); +} + +#endif /* VCOS_INLINE_BODIES */ + +#ifdef __cplusplus +} +#endif +#endif /* VCOS_MUTEX_FROM_FUTEX_H */ + diff --git a/interface/vcos/pthreads/vcos_platform.h b/interface/vcos/pthreads/vcos_platform.h new file mode 100755 index 0000000..ba64e16 --- /dev/null +++ b/interface/vcos/pthreads/vcos_platform.h @@ -0,0 +1,828 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - pthreads types +=============================================================================*/ + +/* DO NOT include this file directly - instead include it via vcos.h */ + +/** @file + * + * Pthreads implementation of VCOS. + * + */ + +#ifndef VCOS_PLATFORM_H +#define VCOS_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define VCOS_HAVE_RTOS 1 +#define VCOS_HAVE_SEMAPHORE 1 +#define VCOS_HAVE_EVENT 1 +#define VCOS_HAVE_QUEUE 0 +#define VCOS_HAVE_LEGACY_ISR 0 +#define VCOS_HAVE_TIMER 1 +#define VCOS_HAVE_CANCELLATION_SAFE_TIMER 1 +#define VCOS_HAVE_MEMPOOL 0 +#define VCOS_HAVE_ISR 0 +#define VCOS_HAVE_ATOMIC_FLAGS 1 +#define VCOS_HAVE_THREAD_AT_EXIT 1 +#define VCOS_HAVE_ONCE 1 +#define VCOS_HAVE_BLOCK_POOL 1 +#define VCOS_HAVE_FILE 0 +#define VCOS_HAVE_PROC 0 +#define VCOS_HAVE_CFG 0 +#define VCOS_HAVE_ALIEN_THREADS 1 +#define VCOS_HAVE_CMD 1 +#define VCOS_HAVE_EVENT_FLAGS 1 +#define VCOS_WANT_LOG_CMD 0 /* User apps should do their own thing */ + +#define VCOS_ALWAYS_WANT_LOGGING + +#ifdef __linux__ +#define VCOS_HAVE_BACKTRACE 1 +#endif + +#define VCOS_SO_EXT ".so" + +/* Linux/pthreads seems to have different timer characteristics */ +#define VCOS_TIMER_MARGIN_EARLY 0 +#define VCOS_TIMER_MARGIN_LATE 15 + +typedef sem_t VCOS_SEMAPHORE_T; +typedef uint32_t VCOS_UNSIGNED; +typedef uint32_t VCOS_OPTION; +typedef pthread_key_t VCOS_TLS_KEY_T; +typedef pthread_once_t VCOS_ONCE_T; + +typedef struct VCOS_LLTHREAD_T +{ + pthread_t thread; // Must be first field. +} VCOS_LLTHREAD_T; + +/* VCOS_CASSERT(offsetof(VCOS_LLTHREAD_T, thread) == 0); */ + +#ifndef VCOS_USE_VCOS_FUTEX +typedef pthread_mutex_t VCOS_MUTEX_T; +#else +#include "vcos_futex_mutex.h" +#endif /* VCOS_USE_VCOS_FUTEX */ + +typedef struct +{ + VCOS_MUTEX_T mutex; + sem_t sem; +} VCOS_EVENT_T; + +#define VCOS_ONCE_INIT PTHREAD_ONCE_INIT + +typedef struct VCOS_TIMER_T +{ + pthread_t thread; /**< id of the timer thread */ + + pthread_mutex_t lock; /**< lock protecting all other members of the struct */ + pthread_cond_t settings_changed; /**< cond. var. for informing the timer thread about changes*/ + int quit; /**< non-zero if the timer thread is requested to quit*/ + + struct timespec expires; /**< absolute time of next expiration, or 0 if disarmed*/ + + void (*orig_expiration_routine)(void*);/**< the expiration routine provided by the user of the timer*/ + void *orig_context; /**< the context for exp. routine provided by the user*/ + +} VCOS_TIMER_T; + +/** Thread attribute structure. Don't use pthread_attr directly, as + * the calls can fail, and inits must match deletes. + */ +typedef struct VCOS_THREAD_ATTR_T +{ + void *ta_stackaddr; + VCOS_UNSIGNED ta_stacksz; + VCOS_UNSIGNED ta_priority; + VCOS_UNSIGNED ta_affinity; + VCOS_UNSIGNED ta_timeslice; + VCOS_UNSIGNED legacy; +} VCOS_THREAD_ATTR_T; + +/** Called at thread exit. + */ +typedef struct VCOS_THREAD_EXIT_T +{ + void (*pfn)(void *); + void *cxt; +} VCOS_THREAD_EXIT_T; +#define VCOS_MAX_EXIT_HANDLERS 4 + +typedef struct VCOS_THREAD_T +{ + pthread_t thread; /**< The thread itself */ + VCOS_THREAD_ENTRY_FN_T entry; /**< The thread entry point */ + void *arg; /**< The argument to be passed to entry */ + VCOS_SEMAPHORE_T suspend; /**< For support event groups and similar - a per thread semaphore */ + + VCOS_TIMER_T task_timer; + int task_timer_created; /**< non-zero if the task timer has already been created*/ + void (*orig_task_timer_expiration_routine)(void*); + void *orig_task_timer_context; + + VCOS_UNSIGNED legacy; + char name[16]; /**< Record the name of this thread, for diagnostics */ + VCOS_UNSIGNED dummy; /**< Dummy thread created for non-vcos created threads */ + + /** Callback invoked at thread exit time */ + VCOS_THREAD_EXIT_T at_exit[VCOS_MAX_EXIT_HANDLERS]; +} VCOS_THREAD_T; + +#ifdef VCOS_PTHREADS_WANT_HISR_EMULATION + +typedef struct +{ + VCOS_THREAD_T thread; + char stack[1024]; + VCOS_SEMAPHORE_T waitsem; +} VCOS_HISR_T; + +#endif + +#define VCOS_SUSPEND -1 +#define VCOS_NO_SUSPEND 0 + +#define VCOS_START 1 +#define VCOS_NO_START 0 + +#define VCOS_THREAD_PRI_MIN (sched_get_priority_min(SCHED_OTHER)) +#define VCOS_THREAD_PRI_MAX (sched_get_priority_max(SCHED_OTHER)) + +#define VCOS_THREAD_PRI_INCREASE (1) +#define VCOS_THREAD_PRI_HIGHEST VCOS_THREAD_PRI_MAX +#define VCOS_THREAD_PRI_LOWEST VCOS_THREAD_PRI_MIN +#define VCOS_THREAD_PRI_NORMAL ((VCOS_THREAD_PRI_MAX+VCOS_THREAD_PRI_MIN)/2) +#define VCOS_THREAD_PRI_BELOW_NORMAL (VCOS_THREAD_PRI_NORMAL-VCOS_THREAD_PRI_INCREASE) +#define VCOS_THREAD_PRI_ABOVE_NORMAL (VCOS_THREAD_PRI_NORMAL+VCOS_THREAD_PRI_INCREASE) +#define VCOS_THREAD_PRI_REALTIME VCOS_THREAD_PRI_MAX + +#define _VCOS_AFFINITY_DEFAULT 0 +#define _VCOS_AFFINITY_CPU0 0x100 +#define _VCOS_AFFINITY_CPU1 0x200 +#define _VCOS_AFFINITY_MASK 0x300 +#define VCOS_CAN_SET_STACK_ADDR 0 + +#define VCOS_TICKS_PER_SECOND _vcos_get_ticks_per_second() + +#include "interface/vcos/generic/vcos_generic_event_flags.h" +#include "interface/vcos/generic/vcos_generic_blockpool.h" +#include "interface/vcos/generic/vcos_mem_from_malloc.h" + +/** Convert errno values into the values recognized by vcos */ +VCOSPRE_ VCOS_STATUS_T vcos_pthreads_map_error(int error); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_pthreads_map_errno(void); + +/** Register a function to be called when the current thread exits. + */ +extern VCOS_STATUS_T vcos_thread_at_exit(void (*pfn)(void*), void *cxt); + +extern uint32_t _vcos_get_ticks_per_second(void); + +/** + * Set to 1 by default when ANDROID is defined. Allows runtime + * switching for console apps. + */ +extern int vcos_use_android_log; + +typedef struct { + VCOS_MUTEX_T mutex; + uint32_t flags; +} VCOS_ATOMIC_FLAGS_T; + +#if defined(VCOS_INLINE_BODIES) + +#undef VCOS_ASSERT_LOGGING_DISABLE +#define VCOS_ASSERT_LOGGING_DISABLE 1 + + +/* + * Counted Semaphores + */ +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_semaphore_wait(VCOS_SEMAPHORE_T *sem) { + int ret; + /* gdb causes sem_wait() to EINTR when a breakpoint is hit, retry here */ + while ((ret = sem_wait(sem)) == -1 && errno == EINTR) + continue; + vcos_assert(ret==0); + return VCOS_SUCCESS; +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_semaphore_trywait(VCOS_SEMAPHORE_T *sem) { + int ret; + while ((ret = sem_trywait(sem)) == -1 && errno == EINTR) + continue; + if (ret == 0) + return VCOS_SUCCESS; + else if (errno == EAGAIN) + return VCOS_EAGAIN; + else { + vcos_assert(0); + return VCOS_EINVAL; + } +} + +/** + * \brief Wait on a semaphore with a timeout. + * + * Note that this function may not be implemented on all + * platforms, and may not be efficient on all platforms + * (see comment in vcos_semaphore_wait) + * + * Try to obtain the semaphore. If it is already taken, return + * VCOS_EAGAIN. + * @param sem Semaphore to wait on + * @param timeout Number of milliseconds to wait before + * returning if the semaphore can't be acquired. + * @return VCOS_SUCCESS - semaphore was taken. + * VCOS_EAGAIN - could not take semaphore (i.e. timeout + * expired) + * VCOS_EINVAL - Some other error (most likely bad + * parameters). + */ +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_semaphore_wait_timeout(VCOS_SEMAPHORE_T *sem, VCOS_UNSIGNED timeout) { + struct timespec ts; + int ret; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) + return VCOS_EINVAL; + ts.tv_sec += timeout/1000; + ts.tv_nsec += (timeout%1000)*1000*1000; + if (ts.tv_nsec > 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + + while (1) { + ret = sem_timedwait( sem, &ts ); + if (ret == 0) { + return VCOS_SUCCESS; + } else { + if (errno == EINTR) { + continue; + } else if (errno == ETIMEDOUT) { + return VCOS_EAGAIN; + } else { + vcos_assert(0); + return VCOS_EINVAL; + } + } + } +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_semaphore_create(VCOS_SEMAPHORE_T *sem, + const char *name, + VCOS_UNSIGNED initial_count) { + int rc = sem_init(sem, 0, initial_count); + (void)name; + if (rc != -1) return VCOS_SUCCESS; + else return vcos_pthreads_map_errno(); +} + +VCOS_INLINE_IMPL +void vcos_semaphore_delete(VCOS_SEMAPHORE_T *sem) { + int rc = sem_destroy(sem); + vcos_assert(rc != -1); + (void)rc; +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_semaphore_post(VCOS_SEMAPHORE_T *sem) { + int rc = sem_post(sem); + vcos_assert(rc == 0); + (void)rc; + return VCOS_SUCCESS; +} + +/*********************************************************** + * + * Threads + * + ***********************************************************/ + + +extern VCOS_THREAD_T *vcos_dummy_thread_create(void); +extern pthread_key_t _vcos_thread_current_key; +extern uint64_t vcos_getmicrosecs64_internal(void); + +VCOS_INLINE_IMPL +uint32_t vcos_getmicrosecs(void) { return (uint32_t)vcos_getmicrosecs64_internal(); } + +VCOS_INLINE_IMPL +uint64_t vcos_getmicrosecs64(void) { return vcos_getmicrosecs64_internal(); } + +VCOS_INLINE_IMPL +VCOS_THREAD_T *vcos_thread_current(void) { + void *ret = pthread_getspecific(_vcos_thread_current_key); + if (ret == NULL) + { + ret = vcos_dummy_thread_create(); + } + +#ifdef __cplusplus + return static_cast(ret); +#else + return (VCOS_THREAD_T *)ret; +#endif +} + +VCOS_INLINE_IMPL +void vcos_sleep(uint32_t ms) { + struct timespec ts; + ts.tv_sec = ms/1000; + ts.tv_nsec = ms % 1000 * (1000000); + nanosleep(&ts, NULL); +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attr, void *addr, VCOS_UNSIGNED sz) { + attr->ta_stackaddr = addr; + attr->ta_stacksz = sz; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attr, VCOS_UNSIGNED sz) { + attr->ta_stacksz = sz; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attr, VCOS_UNSIGNED pri) { + (void)attr; + (void)pri; +} + +VCOS_INLINE_IMPL +void vcos_thread_set_priority(VCOS_THREAD_T *thread, VCOS_UNSIGNED p) { + /* not implemented */ + (void)thread; + (void)p; +} + +VCOS_INLINE_IMPL +VCOS_UNSIGNED vcos_thread_get_priority(VCOS_THREAD_T *thread) { + /* not implemented */ + (void)thread; + return 0; +} + +VCOS_INLINE_IMPL +void vcos_thread_set_affinity(VCOS_THREAD_T *thread, VCOS_UNSIGNED affinity) { + /* not implemented */ + vcos_unused(thread); + vcos_unused(affinity); +} + + +VCOS_INLINE_IMPL +void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED affinity) { + attrs->ta_affinity = affinity; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts) { + attrs->ta_timeslice = ts; +} + +VCOS_INLINE_IMPL +void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy) { + attrs->legacy = legacy; +} + +VCOS_INLINE_IMPL +void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart) { + (void)attrs; + (void)autostart; +} + +VCOS_INLINE_IMPL +VCOS_LLTHREAD_T *vcos_llthread_current(void) { + return (VCOS_LLTHREAD_T *)pthread_self(); +} + +VCOS_INLINE_IMPL +VCOS_UNSIGNED vcos_thread_get_affinity(VCOS_THREAD_T *thread) { + vcos_unused(thread); + return _VCOS_AFFINITY_CPU0; +} + +VCOS_INLINE_IMPL +int vcos_thread_running(VCOS_THREAD_T *thread) { + vcos_unused(thread); + /* Not applicable to pthreads */ + return 0; +} + +VCOS_INLINE_IMPL +VCOS_UNSIGNED vcos_change_preemption(VCOS_UNSIGNED pe) { + vcos_unused(pe); + /* Nothing to do */ + return 0; +} + +VCOS_INLINE_IMPL +void vcos_thread_relinquish(void) { + /* Nothing to do */ +} + +VCOS_INLINE_IMPL +void vcos_thread_resume(VCOS_THREAD_T *thread) { + vcos_unused(thread); + /* Nothing to do */ +} + + +/* + * Mutexes + */ + +#ifndef VCOS_USE_VCOS_FUTEX + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *latch, const char *name) { + int rc = pthread_mutex_init(latch, NULL); + (void)name; + if (rc == 0) return VCOS_SUCCESS; + else return vcos_pthreads_map_errno(); +} + +VCOS_INLINE_IMPL +void vcos_mutex_delete(VCOS_MUTEX_T *latch) { + int rc = pthread_mutex_destroy(latch); + (void)rc; + vcos_assert(rc==0); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *latch) { + int rc = pthread_mutex_lock(latch); + vcos_assert(rc==0); + (void)rc; + return VCOS_SUCCESS; +} + +VCOS_INLINE_IMPL +void vcos_mutex_unlock(VCOS_MUTEX_T *latch) { + int rc = pthread_mutex_unlock(latch); + (void)rc; + vcos_assert(rc==0); +} + +VCOS_INLINE_IMPL +int vcos_mutex_is_locked(VCOS_MUTEX_T *m) { + int rc = pthread_mutex_trylock(m); + if (rc == 0) { + pthread_mutex_unlock(m); + /* it wasn't locked */ + return 0; + } + else { + return 1; /* it was locked */ + } +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m) { + int rc = pthread_mutex_trylock(m); + (void)rc; + return (rc == 0) ? VCOS_SUCCESS : VCOS_EAGAIN; +} + +#endif /* VCOS_USE_VCOS_FUTEX */ + +/* + * Events + */ + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_event_create(VCOS_EVENT_T *event, const char *debug_name) +{ + VCOS_STATUS_T status; + + int rc = sem_init(&event->sem, 0, 0); + if (rc != 0) return vcos_pthreads_map_errno(); + + status = vcos_mutex_create(&event->mutex, debug_name); + if (status != VCOS_SUCCESS) { + sem_destroy(&event->sem); + return status; + } + + return VCOS_SUCCESS; +} + +VCOS_INLINE_IMPL +void vcos_event_signal(VCOS_EVENT_T *event) +{ + int ok = 0; + int value; + + if (vcos_mutex_lock(&event->mutex) != VCOS_SUCCESS) + goto fail_mtx; + + if (sem_getvalue(&event->sem, &value) != 0) + goto fail_sem; + + if (value == 0) + if (sem_post(&event->sem) != 0) + goto fail_sem; + + ok = 1; +fail_sem: + vcos_mutex_unlock(&event->mutex); +fail_mtx: + if (!ok) + vcos_assert(ok); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_event_wait(VCOS_EVENT_T *event) +{ + int ret; + /* gdb causes sem_wait() to EINTR when a breakpoint is hit, retry here */ + while ((ret = sem_wait(&event->sem)) == -1 && errno == EINTR) + continue; + vcos_assert(ret==0); + return ret == 0 ? VCOS_SUCCESS : (VCOS_STATUS_T)errno; +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_event_try(VCOS_EVENT_T *event) +{ + int ret; + while ((ret = sem_trywait(&event->sem)) == -1 && errno == EINTR) + continue; + + if (ret == -1 && errno == EAGAIN) + return VCOS_EAGAIN; + else + return VCOS_SUCCESS; +} + +VCOS_INLINE_IMPL +void vcos_event_delete(VCOS_EVENT_T *event) +{ + int rc = sem_destroy(&event->sem); + vcos_assert(rc != -1); + (void)rc; + + vcos_mutex_delete(&event->mutex); +} + +VCOS_INLINE_IMPL +VCOS_UNSIGNED vcos_process_id_current(void) { + return (VCOS_UNSIGNED) getpid(); +} + +VCOS_INLINE_IMPL +int vcos_strcasecmp(const char *s1, const char *s2) { + return strcasecmp(s1,s2); +} + +VCOS_INLINE_IMPL +int vcos_strncasecmp(const char *s1, const char *s2, size_t n) { + return strncasecmp(s1,s2,n); +} + +VCOS_INLINE_IMPL +int vcos_in_interrupt(void) { + return 0; +} + +/* For support event groups - per thread semaphore */ +VCOS_INLINE_IMPL +void _vcos_thread_sem_wait(void) { + VCOS_THREAD_T *t = vcos_thread_current(); + vcos_semaphore_wait(&t->suspend); +} + +VCOS_INLINE_IMPL +void _vcos_thread_sem_post(VCOS_THREAD_T *target) { + vcos_semaphore_post(&target->suspend); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_tls_create(VCOS_TLS_KEY_T *key) { + int st = pthread_key_create(key, NULL); + return st == 0 ? VCOS_SUCCESS: VCOS_ENOMEM; +} + +VCOS_INLINE_IMPL +void vcos_tls_delete(VCOS_TLS_KEY_T tls) { + pthread_key_delete(tls); +} + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_tls_set(VCOS_TLS_KEY_T tls, void *v) { + pthread_setspecific(tls, v); + return VCOS_SUCCESS; +} + +VCOS_INLINE_IMPL +void *vcos_tls_get(VCOS_TLS_KEY_T tls) { + return pthread_getspecific(tls); +} + +/*********************************************************** + * + * Timers + * + ***********************************************************/ + +//Other platforms can call compatible OS implementations directly +//from inline functions with minimal overhead. +//Pthreads needs a little bit more, so call functions +//in vcos_pthreads.c from the inline functions. +VCOS_STATUS_T vcos_pthreads_timer_create(VCOS_TIMER_T *timer, + const char *name, + void (*expiration_routine)(void *context), + void *context); +void vcos_pthreads_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms); +void vcos_pthreads_timer_cancel(VCOS_TIMER_T *timer); +void vcos_pthreads_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms); +void vcos_pthreads_timer_delete(VCOS_TIMER_T *timer); + +/** Create a timer. + * + * Note that we just cast the expiry function - this assumes that UNSIGNED + * and VOID* are the same size. + */ + + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_timer_create(VCOS_TIMER_T *timer, + const char *name, + void (*expiration_routine)(void *context), + void *context) { + return vcos_pthreads_timer_create(timer, name, expiration_routine, context); +} + +VCOS_INLINE_IMPL +void vcos_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms) { + return vcos_pthreads_timer_set(timer, delay_ms); +} + +VCOS_INLINE_IMPL +void vcos_timer_cancel(VCOS_TIMER_T *timer) { + return vcos_pthreads_timer_cancel(timer); +} + +VCOS_INLINE_IMPL +void vcos_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay) { + vcos_timer_set(timer, delay); +} + +VCOS_INLINE_IMPL +void vcos_timer_delete(VCOS_TIMER_T *timer) { + vcos_pthreads_timer_delete(timer); +} + +#if VCOS_HAVE_ATOMIC_FLAGS + +/* + * Atomic flags + */ + +/* TODO implement properly... */ + +VCOS_INLINE_IMPL +VCOS_STATUS_T vcos_atomic_flags_create(VCOS_ATOMIC_FLAGS_T *atomic_flags) +{ + atomic_flags->flags = 0; + return vcos_mutex_create(&atomic_flags->mutex, "VCOS_ATOMIC_FLAGS_T"); +} + +VCOS_INLINE_IMPL +void vcos_atomic_flags_or(VCOS_ATOMIC_FLAGS_T *atomic_flags, uint32_t flags) +{ + vcos_mutex_lock(&atomic_flags->mutex); + atomic_flags->flags |= flags; + vcos_mutex_unlock(&atomic_flags->mutex); +} + +VCOS_INLINE_IMPL +uint32_t vcos_atomic_flags_get_and_clear(VCOS_ATOMIC_FLAGS_T *atomic_flags) +{ + uint32_t flags; + vcos_mutex_lock(&atomic_flags->mutex); + flags = atomic_flags->flags; + atomic_flags->flags = 0; + vcos_mutex_unlock(&atomic_flags->mutex); + return flags; +} + +VCOS_INLINE_IMPL +void vcos_atomic_flags_delete(VCOS_ATOMIC_FLAGS_T *atomic_flags) +{ + vcos_mutex_delete(&atomic_flags->mutex); +} + +#endif + +#ifdef VCOS_PTHREADS_WANT_HISR_EMULATION +VCOS_STATUS_T vcos_legacy_hisr_create(VCOS_HISR_T *hisr, const char *name, + void (*entry)(void), + VCOS_UNSIGNED pri, + void *stack, VCOS_UNSIGNED stack_size); + +void vcos_legacy_hisr_activate(VCOS_HISR_T *hisr); + +void vcos_legacy_hisr_delete(VCOS_HISR_T *hisr); + +#endif + +#undef VCOS_ASSERT_LOGGING_DISABLE +#define VCOS_ASSERT_LOGGING_DISABLE 0 + +#endif /* VCOS_INLINE_BODIES */ + +#define vcos_log_platform_init() _vcos_log_platform_init() +VCOSPRE_ void VCOSPOST_ _vcos_log_platform_init(void); + +VCOS_INLINE_DECL void _vcos_thread_sem_wait(void); +VCOS_INLINE_DECL void _vcos_thread_sem_post(VCOS_THREAD_T *); + +#define VCOS_APPLICATION_ARGC vcos_get_argc() +#define VCOS_APPLICATION_ARGV vcos_get_argv() + +#include "interface/vcos/generic/vcos_generic_reentrant_mtx.h" +#include "interface/vcos/generic/vcos_generic_named_sem.h" +#include "interface/vcos/generic/vcos_generic_quickslow_mutex.h" +#include "interface/vcos/generic/vcos_common.h" + +#define _VCOS_LOG_LEVEL() getenv("VC_LOGLEVEL") + +VCOS_STATIC_INLINE +char *vcos_strdup(const char *str) +{ + size_t len = strlen(str) + 1; + void *p = malloc(len); + + if (p == NULL) + return NULL; + + return (char *)memcpy(p, str, len); +} + +typedef void (*VCOS_ISR_HANDLER_T)(VCOS_UNSIGNED vecnum); + +#define VCOS_DL_LAZY RTLD_LAZY +#define VCOS_DL_NOW RTLD_NOW +#define VCOS_DL_LOCAL RTLD_LOCAL +#define VCOS_DL_GLOBAL RTLD_GLOBAL + +#ifdef __cplusplus +} +#endif +#endif /* VCOS_PLATFORM_H */ + diff --git a/interface/vcos/pthreads/vcos_platform_types.h b/interface/vcos/pthreads/vcos_platform_types.h new file mode 100755 index 0000000..17b422d --- /dev/null +++ b/interface/vcos/pthreads/vcos_platform_types.h @@ -0,0 +1,71 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - platform-specific types and defines +=============================================================================*/ + +#ifndef VCOS_PLATFORM_TYPES_H +#define VCOS_PLATFORM_TYPES_H + +#include "interface/vcos/vcos_inttypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VCOSPRE_ extern +#define VCOSPOST_ + +#if defined(__GNUC__) && (( __GNUC__ > 2 ) || (( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 3 ))) +#define VCOS_FORMAT_ATTR_(ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK) __attribute__ ((format (ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK))) +#else +#define VCOS_FORMAT_ATTR_(ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK) +#endif + +#if defined(__linux__) && !defined(NDEBUG) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + #define VCOS_BKPT ({ __asm volatile ("int3":::"memory"); }) +#endif +/*#define VCOS_BKPT vcos_abort() */ + +#define VCOS_ASSERT_LOGGING 1 +#define VCOS_ASSERT_LOGGING_DISABLE 0 + +extern void +vcos_pthreads_logging_assert(const char *file, const char *func, unsigned int line, const char *fmt, ...); + +#define VCOS_ASSERT_MSG(...) ((VCOS_ASSERT_LOGGING && !VCOS_ASSERT_LOGGING_DISABLE) ? vcos_pthreads_logging_assert(__FILE__, __func__, __LINE__, __VA_ARGS__) : (void)0) + +#define VCOS_INLINE_BODIES +#define VCOS_INLINE_DECL extern __inline__ +#define VCOS_INLINE_IMPL static __inline__ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/interface/vcos/pthreads/vcos_pthreads.c b/interface/vcos/pthreads/vcos_pthreads.c new file mode 100755 index 0000000..77ce58c --- /dev/null +++ b/interface/vcos/pthreads/vcos_pthreads.c @@ -0,0 +1,897 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*#define VCOS_INLINE_BODIES */ +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_msgqueue.h" +#include +#include +#include +#include +#include +#include + +/* Cygwin doesn't always have prctl.h and it doesn't have PR_SET_NAME */ +#if defined( __linux__ ) +# if !defined(HAVE_PRCTL) +# define HAVE_PRCTL +# endif +#include +#endif + +#ifdef HAVE_CMAKE_CONFIG +#include "cmake_config.h" +#endif + +#ifdef HAVE_MTRACE +#include +#endif + +#if defined(ANDROID) +#include +#endif + +#ifndef VCOS_DEFAULT_STACK_SIZE +#define VCOS_DEFAULT_STACK_SIZE 4096 +#endif + +static int vcos_argc; +static const char **vcos_argv; + +typedef void (*LEGACY_ENTRY_FN_T)(int, void *); + +static VCOS_THREAD_ATTR_T default_attrs = { + .ta_stacksz = VCOS_DEFAULT_STACK_SIZE, +}; + +/** Singleton global lock used for vcos_global_lock/unlock(). */ +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +#ifdef ANDROID +static VCOS_MUTEX_T printf_lock; +#endif + +/* Create a per-thread key for faking up vcos access + * on non-vcos threads. + */ +pthread_key_t _vcos_thread_current_key; + +static VCOS_UNSIGNED _vcos_thread_current_key_created = 0; +static VCOS_ONCE_T current_thread_key_once; /* init just once */ + +static void vcos_thread_cleanup(VCOS_THREAD_T *thread) +{ + vcos_semaphore_delete(&thread->suspend); + if (thread->task_timer_created) + { + vcos_timer_delete(&thread->task_timer); + } +} + +static void vcos_dummy_thread_cleanup(void *cxt) +{ + VCOS_THREAD_T *thread = cxt; + if (thread->dummy) + { + int i; + /* call termination functions */ + for (i=0; thread->at_exit[i].pfn != NULL; i++) + { + thread->at_exit[i].pfn(thread->at_exit[i].cxt); + } + vcos_thread_cleanup(thread); + vcos_free(thread); + } +} + +static void current_thread_key_init(void) +{ + vcos_assert(!_vcos_thread_current_key_created); + pthread_key_create (&_vcos_thread_current_key, vcos_dummy_thread_cleanup); + _vcos_thread_current_key_created = 1; +} + + +/* A VCOS wrapper for the thread which called vcos_init. */ +static VCOS_THREAD_T vcos_thread_main; + +static void *vcos_thread_entry(void *arg) +{ + int i; + void *ret; + VCOS_THREAD_T *thread = (VCOS_THREAD_T *)arg; + + vcos_assert(thread != NULL); + thread->dummy = 0; + + pthread_setspecific(_vcos_thread_current_key, thread); +#if defined( HAVE_PRCTL ) && defined( PR_SET_NAME ) + /* cygwin doesn't have PR_SET_NAME */ + prctl( PR_SET_NAME, (unsigned long)thread->name, 0, 0, 0 ); +#endif + if (thread->legacy) + { + LEGACY_ENTRY_FN_T fn = (LEGACY_ENTRY_FN_T)thread->entry; + (*fn)(0, thread->arg); + ret = 0; + } + else + { + ret = (*thread->entry)(thread->arg); + } + + /* call termination functions */ + for (i=0; thread->at_exit[i].pfn != NULL; i++) + { + thread->at_exit[i].pfn(thread->at_exit[i].cxt); + } + + return ret; +} + +static void _task_timer_expiration_routine(void *cxt) +{ + VCOS_THREAD_T *thread = (VCOS_THREAD_T *)cxt; + + vcos_assert(thread->orig_task_timer_expiration_routine); + thread->orig_task_timer_expiration_routine(thread->orig_task_timer_context); + thread->orig_task_timer_expiration_routine = NULL; +} + +VCOS_STATUS_T vcos_thread_create(VCOS_THREAD_T *thread, + const char *name, + VCOS_THREAD_ATTR_T *attrs, + VCOS_THREAD_ENTRY_FN_T entry, + void *arg) +{ + VCOS_STATUS_T st; + pthread_attr_t pt_attrs; + VCOS_THREAD_ATTR_T *local_attrs = attrs ? attrs : &default_attrs; + int rc; + + vcos_assert(thread); + memset(thread, 0, sizeof(VCOS_THREAD_T)); + + rc = pthread_attr_init(&pt_attrs); + if (rc < 0) + return VCOS_ENOMEM; + + st = vcos_semaphore_create(&thread->suspend, NULL, 0); + if (st != VCOS_SUCCESS) + { + pthread_attr_destroy(&pt_attrs); + return st; + } + + pthread_attr_setstacksize(&pt_attrs, local_attrs->ta_stacksz); +#if VCOS_CAN_SET_STACK_ADDR + if (local_attrs->ta_stackaddr) + { + pthread_attr_setstackaddr(&pt_attrs, local_attrs->ta_stackaddr); + } +#else + vcos_demand(local_attrs->ta_stackaddr == 0); +#endif + + /* pthread_attr_setpriority(&pt_attrs, local_attrs->ta_priority); */ + + vcos_assert(local_attrs->ta_stackaddr == 0); /* Not possible */ + + thread->entry = entry; + thread->arg = arg; + thread->legacy = local_attrs->legacy; + + strncpy(thread->name, name, sizeof(thread->name)); + thread->name[sizeof(thread->name)-1] = '\0'; + memset(thread->at_exit, 0, sizeof(thread->at_exit)); + + rc = pthread_create(&thread->thread, &pt_attrs, vcos_thread_entry, thread); + + pthread_attr_destroy(&pt_attrs); + + if (rc < 0) + { + vcos_semaphore_delete(&thread->suspend); + return VCOS_ENOMEM; + } + else + { + return VCOS_SUCCESS; + } +} + +void vcos_thread_join(VCOS_THREAD_T *thread, + void **pData) +{ + pthread_join(thread->thread, pData); + vcos_thread_cleanup(thread); +} + +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create_classic(VCOS_THREAD_T *thread, + const char *name, + void *(*entry)(void *arg), + void *arg, + void *stack, + VCOS_UNSIGNED stacksz, + VCOS_UNSIGNED priaff, + VCOS_UNSIGNED timeslice, + VCOS_UNSIGNED autostart) +{ + VCOS_THREAD_ATTR_T attrs; + vcos_thread_attr_init(&attrs); + vcos_thread_attr_setstacksize(&attrs, stacksz); + vcos_thread_attr_setpriority(&attrs, priaff & ~_VCOS_AFFINITY_MASK); + vcos_thread_attr_setaffinity(&attrs, priaff & _VCOS_AFFINITY_MASK); + (void)timeslice; + (void)autostart; + + if (VCOS_CAN_SET_STACK_ADDR) + { + vcos_thread_attr_setstack(&attrs, stack, stacksz); + } + + return vcos_thread_create(thread, name, &attrs, entry, arg); +} + +uint64_t vcos_getmicrosecs64_internal(void) +{ + struct timeval tv; + uint64_t tm = 0; + + if (!gettimeofday(&tv, NULL)) + { + tm = (tv.tv_sec * 1000000LL) + tv.tv_usec; + } + + return tm; +} + +#ifdef ANDROID + +static int log_prio[] = +{ + ANDROID_LOG_INFO, /* VCOS_LOG_UNINITIALIZED */ + ANDROID_LOG_INFO, /* VCOS_LOG_NEVER */ + ANDROID_LOG_ERROR, /* VCOS_LOG_ERROR */ + ANDROID_LOG_WARN, /* VCOS_LOG_WARN */ + ANDROID_LOG_INFO, /* VCOS_LOG_INFO */ + ANDROID_LOG_DEBUG /* VCOS_LOG_TRACE */ +}; + +int vcos_use_android_log = 1; +int vcos_log_to_file = 0; +#else +int vcos_use_android_log = 0; +int vcos_log_to_file = 0; +#endif + +static FILE * log_fhandle = NULL; + +void vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) +{ + (void)_level; + +#ifdef ANDROID + if ( vcos_use_android_log ) + { + __android_log_vprint(log_prio[_level], cat->name, fmt, args); + } + else + { + vcos_mutex_lock(&printf_lock); +#endif + if(NULL != log_fhandle) + { + if (cat->flags.want_prefix) + fprintf( log_fhandle, "%s: ", cat->name ); + vfprintf(log_fhandle, fmt, args); + fputs("\n", log_fhandle); + fflush(log_fhandle); + } +#ifdef ANDROID + vcos_mutex_unlock(&printf_lock); + } +#endif +} + +void _vcos_log_platform_init(void) +{ + if(vcos_log_to_file) + { + char log_fname[100]; +#ifdef ANDROID + snprintf(log_fname, 100, "/data/log/vcos_log%u.txt", vcos_process_id_current()); +#else + snprintf(log_fname, 100, "/var/log/vcos_log%u.txt", vcos_process_id_current()); +#endif + log_fhandle = fopen(log_fname, "w"); + } + else + log_fhandle = stderr; +} + +/* Flags for init/deinit components */ +enum +{ + VCOS_INIT_NAMED_SEM = (1 << 0), + VCOS_INIT_PRINTF_LOCK = (1 << 1), + VCOS_INIT_MAIN_SEM = (1 << 2), + VCOS_INIT_MSGQ = (1 << 3), + VCOS_INIT_ALL = 0xffffffff +}; + +static void vcos_term(uint32_t flags) +{ + if (flags & VCOS_INIT_MSGQ) + vcos_msgq_deinit(); + + if (flags & VCOS_INIT_MAIN_SEM) + vcos_semaphore_delete(&vcos_thread_main.suspend); + +#ifdef ANDROID + if (flags & VCOS_INIT_PRINTF_LOCK) + vcos_mutex_delete(&printf_lock); +#endif + + if (flags & VCOS_INIT_NAMED_SEM) + _vcos_named_semaphore_deinit(); +} + +VCOS_STATUS_T vcos_platform_init(void) +{ + VCOS_STATUS_T st; + uint32_t flags = 0; + int pst; + + st = _vcos_named_semaphore_init(); + if (!vcos_verify(st == VCOS_SUCCESS)) + goto end; + + flags |= VCOS_INIT_NAMED_SEM; + +#ifdef HAVE_MTRACE + /* enable glibc memory debugging, if the environment + * variable MALLOC_TRACE names a valid file. + */ + mtrace(); +#endif + +#ifdef ANDROID + st = vcos_mutex_create(&printf_lock, "printf"); + if (!vcos_verify(st == VCOS_SUCCESS)) + goto end; + + flags |= VCOS_INIT_PRINTF_LOCK; +#endif + + st = vcos_once(¤t_thread_key_once, current_thread_key_init); + if (!vcos_verify(st == VCOS_SUCCESS)) + goto end; + + /* Initialise a VCOS wrapper for the thread which called vcos_init. */ + st = vcos_semaphore_create(&vcos_thread_main.suspend, NULL, 0); + if (!vcos_verify(st == VCOS_SUCCESS)) + goto end; + + flags |= VCOS_INIT_MAIN_SEM; + + vcos_thread_main.thread = pthread_self(); + + pst = pthread_setspecific(_vcos_thread_current_key, &vcos_thread_main); + if (!vcos_verify(pst == 0)) + { + st = VCOS_EINVAL; + goto end; + } + + st = vcos_msgq_init(); + if (!vcos_verify(st == VCOS_SUCCESS)) + goto end; + + flags |= VCOS_INIT_MSGQ; + + vcos_logging_init(); + +end: + if (st != VCOS_SUCCESS) + vcos_term(flags); + + return st; +} + +void vcos_platform_deinit(void) +{ + vcos_term(VCOS_INIT_ALL); +} + +void vcos_global_lock(void) +{ + pthread_mutex_lock(&lock); +} + +void vcos_global_unlock(void) +{ + pthread_mutex_unlock(&lock); +} + +void vcos_thread_exit(void *arg) +{ + VCOS_THREAD_T *thread = vcos_thread_current(); + + if ( thread && thread->dummy ) + { + vcos_free ( (void*) thread ); + thread = NULL; + } + + pthread_exit(arg); +} + + +void vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs) +{ + *attrs = default_attrs; +} + +VCOS_STATUS_T vcos_pthreads_map_error(int error) +{ + switch (error) + { + case ENOMEM: + return VCOS_ENOMEM; + case ENXIO: + return VCOS_ENXIO; + case EAGAIN: + return VCOS_EAGAIN; + case ENOSPC: + return VCOS_ENOSPC; + default: + return VCOS_EINVAL; + } +} + +VCOS_STATUS_T vcos_pthreads_map_errno(void) +{ + return vcos_pthreads_map_error(errno); +} + +void _vcos_task_timer_set(void (*pfn)(void*), void *cxt, VCOS_UNSIGNED ms) +{ + VCOS_THREAD_T *thread = vcos_thread_current(); + + if (thread == NULL) + return; + + vcos_assert(thread->orig_task_timer_expiration_routine == NULL); + + if (!thread->task_timer_created) + { + VCOS_STATUS_T st = vcos_timer_create(&thread->task_timer, NULL, + _task_timer_expiration_routine, thread); + (void)st; + vcos_assert(st == VCOS_SUCCESS); + thread->task_timer_created = 1; + } + + thread->orig_task_timer_expiration_routine = pfn; + thread->orig_task_timer_context = cxt; + + vcos_timer_set(&thread->task_timer, ms); +} + +void _vcos_task_timer_cancel(void) +{ + VCOS_THREAD_T *thread = vcos_thread_current(); + + if (thread == NULL || !thread->task_timer_created) + return; + + vcos_timer_cancel(&thread->task_timer); + thread->orig_task_timer_expiration_routine = NULL; +} + +int vcos_vsnprintf( char *buf, size_t buflen, const char *fmt, va_list ap ) +{ + return vsnprintf( buf, buflen, fmt, ap ); +} + +int vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap,fmt); + ret = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + return ret; +} + +int vcos_have_rtos(void) +{ + return 1; +} + +const char * vcos_thread_get_name(const VCOS_THREAD_T *thread) +{ + return thread->name; +} + +#ifdef VCOS_HAVE_BACKTRACK +void __attribute__((weak)) vcos_backtrace_self(void); +#endif + +void vcos_pthreads_logging_assert(const char *file, const char *func, unsigned int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "assertion failure:%s:%d:%s():", + file, line, func); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + +#ifdef VCOS_HAVE_BACKTRACK + if (vcos_backtrace_self) + vcos_backtrace_self(); +#endif + abort(); +} + +extern VCOS_STATUS_T vcos_thread_at_exit(void (*pfn)(void*), void *cxt) +{ + int i; + VCOS_THREAD_T *self = vcos_thread_current(); + if (!self) + { + vcos_assert(0); + return VCOS_EINVAL; + } + for (i=0; iat_exit[i].pfn == NULL) + { + self->at_exit[i].pfn = pfn; + self->at_exit[i].cxt = cxt; + return VCOS_SUCCESS; + } + } + return VCOS_ENOSPC; +} + +void vcos_set_args(int argc, const char **argv) +{ + vcos_argc = argc; + vcos_argv = argv; +} + +int vcos_get_argc(void) +{ + return vcos_argc; +} + +const char ** vcos_get_argv(void) +{ + return vcos_argv; +} + +/* we can't inline this, because HZ comes from sys/param.h which + * dumps all sorts of junk into the global namespace, notable MIN and + * MAX. + */ +uint32_t _vcos_get_ticks_per_second(void) +{ + return HZ; +} + +VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control, + void (*init_routine)(void)) +{ + int rc = pthread_once(once_control, init_routine); + if (rc != 0) + { + switch (errno) + { + case EINVAL: + return VCOS_EINVAL; + default: + vcos_assert(0); + return VCOS_EACCESS; + } + } + else + { + return VCOS_SUCCESS; + } +} + + +VCOS_THREAD_T *vcos_dummy_thread_create(void) +{ + VCOS_STATUS_T st; + VCOS_THREAD_T *thread_hndl = NULL; + int rc; + + thread_hndl = (VCOS_THREAD_T *)vcos_malloc(sizeof(VCOS_THREAD_T), NULL); + vcos_assert(thread_hndl != NULL); + + memset(thread_hndl, 0, sizeof(VCOS_THREAD_T)); + + thread_hndl->dummy = 1; + thread_hndl->thread = pthread_self(); + + st = vcos_semaphore_create(&thread_hndl->suspend, NULL, 0); + if (st != VCOS_SUCCESS) + { + vcos_free(thread_hndl); + return( thread_hndl ); + } + + vcos_once(¤t_thread_key_once, current_thread_key_init); + + rc = pthread_setspecific(_vcos_thread_current_key, + thread_hndl); + (void)rc; + + return( thread_hndl ); +} + + +/*********************************************************** + * + * Timers + * + ***********************************************************/ + +/* On Linux we could use POSIX timers with a bit of synchronization. + * Unfortunately POSIX timers on Bionic are NOT POSIX compliant + * what makes that option not viable. + * That's why we ended up with our own implementation of timers. + * NOTE: That condition variables on Bionic are also buggy and + * they work incorrectly with CLOCK_MONOTONIC, so we have to + * use CLOCK_REALTIME (and hope that no one will change the time + * significantly after the timer has been set up + */ +#define NSEC_IN_SEC (1000*1000*1000) +#define MSEC_IN_SEC (1000) +#define NSEC_IN_MSEC (1000*1000) + +static int _timespec_is_zero(struct timespec *ts) +{ + return ((ts->tv_sec == 0) && (ts->tv_nsec == 0)); +} + +static void _timespec_set_zero(struct timespec *ts) +{ + ts->tv_sec = ts->tv_nsec = 0; +} + +/* Adds left to right and stores the result in left */ +static void _timespec_add(struct timespec *left, struct timespec *right) +{ + left->tv_sec += right->tv_sec; + left->tv_nsec += right->tv_nsec; + if (left->tv_nsec >= (NSEC_IN_SEC)) + { + left->tv_nsec -= NSEC_IN_SEC; + left->tv_sec++; + } +} + +static int _timespec_is_larger(struct timespec *left, struct timespec *right) +{ + if (left->tv_sec != right->tv_sec) + return left->tv_sec > right->tv_sec; + else + return left->tv_nsec > right->tv_nsec; +} + +static void* _timer_thread(void *arg) +{ + VCOS_TIMER_T *timer = (VCOS_TIMER_T*)arg; + + pthread_mutex_lock(&timer->lock); + while (!timer->quit) + { + struct timespec now; + + /* Wait until next expiry time, or until timer's settings are changed */ + if (_timespec_is_zero(&timer->expires)) + pthread_cond_wait(&timer->settings_changed, &timer->lock); + else + pthread_cond_timedwait(&timer->settings_changed, &timer->lock, &timer->expires); + + /* See if the timer has expired - reloop if it didn't */ + clock_gettime(CLOCK_REALTIME, &now); + if (_timespec_is_zero(&timer->expires) || _timespec_is_larger(&timer->expires, &now)) + continue; + + /* The timer has expired. Clear the expiry time and call the + * expiration routine + */ + _timespec_set_zero(&timer->expires); + timer->orig_expiration_routine(timer->orig_context); + } + pthread_mutex_unlock(&timer->lock); + + return NULL; +} + +VCOS_STATUS_T vcos_timer_init(void) +{ + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vcos_pthreads_timer_create(VCOS_TIMER_T *timer, + const char *name, + void (*expiration_routine)(void *context), + void *context) +{ + pthread_mutexattr_t lock_attr; + VCOS_STATUS_T result = VCOS_SUCCESS; + int settings_changed_initialized = 0; + int lock_attr_initialized = 0; + int lock_initialized = 0; + + (void)name; + + vcos_assert(timer); + vcos_assert(expiration_routine); + + memset(timer, 0, sizeof(VCOS_TIMER_T)); + + timer->orig_expiration_routine = expiration_routine; + timer->orig_context = context; + + /* Create conditional variable for notifying the timer's thread + * when settings change. + */ + if (result == VCOS_SUCCESS) + { + int rc = pthread_cond_init(&timer->settings_changed, NULL); + if (rc == 0) + settings_changed_initialized = 1; + else + result = vcos_pthreads_map_error(rc); + } + + /* Create attributes for the lock (we want it to be recursive) */ + if (result == VCOS_SUCCESS) + { + int rc = pthread_mutexattr_init(&lock_attr); + if (rc == 0) + { + pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE); + lock_attr_initialized = 1; + } + else + { + result = vcos_pthreads_map_error(rc); + } + } + + /* Create lock for the timer structure */ + if (result == VCOS_SUCCESS) + { + int rc = pthread_mutex_init(&timer->lock, &lock_attr); + if (rc == 0) + lock_initialized = 1; + else + result = vcos_pthreads_map_error(rc); + } + + /* Lock attributes are no longer needed */ + if (lock_attr_initialized) + pthread_mutexattr_destroy(&lock_attr); + + /* Create the underlying thread */ + if (result == VCOS_SUCCESS) + { + int rc = pthread_create(&timer->thread, NULL, _timer_thread, timer); + if (rc != 0) + result = vcos_pthreads_map_error(rc); + } + + /* Clean up if anything went wrong */ + if (result != VCOS_SUCCESS) + { + if (lock_initialized) + pthread_mutex_destroy(&timer->lock); + + if (settings_changed_initialized) + pthread_cond_destroy(&timer->settings_changed); + } + + return result; +} + +void vcos_pthreads_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms) +{ + struct timespec now; + + vcos_assert(timer); + + /* Other implementations of this function do undefined things + * when delay_ms is 0. This implementation will simply assert and return + */ + vcos_assert(delay_ms != 0); + if (delay_ms == 0) + return; + + pthread_mutex_lock(&timer->lock); + + /* Calculate the new absolute expiry time */ + clock_gettime(CLOCK_REALTIME, &now); + timer->expires.tv_sec = delay_ms / MSEC_IN_SEC; + timer->expires.tv_nsec = (delay_ms % MSEC_IN_SEC) * NSEC_IN_MSEC; + _timespec_add(&timer->expires, &now); + + /* Notify the timer's thread about the change */ + pthread_cond_signal(&timer->settings_changed); + + pthread_mutex_unlock(&timer->lock); +} + +void vcos_pthreads_timer_cancel(VCOS_TIMER_T *timer) +{ + vcos_assert(timer); + + pthread_mutex_lock(&timer->lock); + + _timespec_set_zero(&timer->expires); + pthread_cond_signal(&timer->settings_changed); + + pthread_mutex_unlock(&timer->lock); +} + +void vcos_pthreads_timer_delete(VCOS_TIMER_T *timer) +{ + vcos_assert(timer); + + pthread_mutex_lock(&timer->lock); + + /* Other implementation of this function (e.g. ThreadX) + * disallow it being called from the expiration routine + */ + vcos_assert(pthread_self() != timer->thread); + + /* Stop the timer and set flag telling the timer thread to quit */ + _timespec_set_zero(&timer->expires); + timer->quit = 1; + + /* Notify the timer's thread about the change */ + pthread_cond_signal(&timer->settings_changed); + + /* Release the lock, so that the timer's thread can quit */ + pthread_mutex_unlock(&timer->lock); + + /* Wait for the timer thread to finish */ + pthread_join(timer->thread, NULL); + + /* Free resources used by the timer */ + pthread_mutex_destroy(&timer->lock); + pthread_cond_destroy(&timer->settings_changed); +} + diff --git a/interface/vcos/user_nodefs.h b/interface/vcos/user_nodefs.h new file mode 100755 index 0000000..ded2fb4 --- /dev/null +++ b/interface/vcos/user_nodefs.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef USER_NODEFS_H +#define USER_NODEFS_H + +/* + * This tells coverity not to expand the assert macro, so it still sees the + * asserts in the code, even in release builds (we currently run coverity on + * our release builds). Unfortunately MetaWare won't compile it, even though + * __COVERITY__ isn't defined, so we put this in its own header. + * + * FIXME: This belongs in the Coverity config (in a file called + * config/user_nodefs.h) + */ +#nodef assert + +/* + * So we need to declare the function now that it isn't a macro any more. It's + * already built into coverity that assert is a "killpath". + */ +extern void assert(int cond); + +#endif /* USER_NODEFS_H */ diff --git a/interface/vcos/vcos.h b/interface/vcos/vcos.h new file mode 100755 index 0000000..2b83226 --- /dev/null +++ b/interface/vcos/vcos.h @@ -0,0 +1,226 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +/** + * \mainpage OS Abstraction Layer + * + * \section intro Introduction + * + * This abstraction layer is here to allow the underlying OS to be easily changed (e.g. from + * Nucleus to ThreadX) and to aid in porting host applications to new targets. + * + * \subsection error Error handling + * + * Wherever possible, VCOS functions assert internally and return void. The only exceptions + * are creation functions (which might fail due to lack of resources) and functions that + * might timeout or fail due to lack of space. Errors that might be reported by the underlying + * OS API (e.g. invalid mutex) are treated as a programming error, and are merely asserted on. + * + * \section thread_synch Threads and synchronisation + * + * \subsection thread Threads + * + * The thread API is somewhat different to that found in Nucleus. In particular, threads + * cannot just be destroyed at arbitrary times and nor can they merely exit. This is so + * that the same API can be implemented across all interesting platforms without too much + * difficulty. See vcos_thread.h for details. Thread attributes are configured via + * the VCOS_THREAD_ATTR_T structure, found in vcos_thread_attr.h. + * + * \subsection sema Semaphores + * + * Counted semaphores (c.f. Nucleus NU_SEMAPHORE) are created with VCOS_SEMAPHORE_T. + * Under ThreadX on VideoCore, semaphores are implemented using VideoCore spinlocks, and + * so are quite a lot faster than ordinary ThreadX semaphores. See vcos_semaphore.h. + * + * \subsection mtx Mutexes + * + * Mutexes are used for locking. Attempts to take a mutex twice, or to unlock it + * in a different thread to the one in which it was locked should be expected to fail. + * Mutexes are not re-entrant (see vcos_reentrant_mutex.h for a slightly slower + * re-entrant mutex). + * + * \subsection evflags Event flags + * + * Event flags (the ThreadX name - also known as event groups under Nucleus) provide + * 32 flags which can be waited on by multiple clients, and signalled by multiple clients. + * A timeout can be specified. See vcos_event_flags.h. An alternative to this is the + * VCOS_EVENT_T (see vcos_event.h) which is akin to the Win32 auto-reset event, or a + * saturating counted semaphore. + * + * \subsection event Events + * + * A VCOS_EVENT_T is a bit like a saturating semaphore. No matter how many times it + * is signalled, the waiter will only wake up once. See vcos_event.h. You might think this + * is useful if you suspect that the cost of reading the semaphore count (perhaps via a + * system call) is expensive on your platform. + * + * \subsection tls Thread local storage + * + * Thread local storage is supported using vcos_tls.h. This is emulated on Nucleus + * and ThreadX. + * + * \section int Interrupts + * + * The legacy LISR/HISR scheme found in Nucleus is supported via the legacy ISR API, + * which is also supported on ThreadX. New code should avoid this, and old code should + * be migrated away from it, since it is slow. See vcos_legacy_isr.h. + * + * Registering an interrupt handler, and disabling/restoring interrupts, is handled + * using the functions in vcos_isr.h. + * + */ + +/** + * \file vcos.h + * + * This is the top level header file. Clients include this. It pulls in the platform-specific + * header file (vcos_platform.h) together with header files defining the expected APIs, such + * as vcos_mutex.h, vcos_semaphore.h, etc. It is also possible to include these header files + * directly. + * + */ + +#ifndef VCOS_H +#define VCOS_H + +#include "interface/vcos/vcos_assert.h" +#include "vcos_types.h" + +#if defined(__unix__) && !defined(__ANDROID__) +#include "interface/vcos/pthreads/vcos_platform.h" +#else +#include "vcos_platform.h" +#endif + +#ifndef VCOS_INIT_H +#include "interface/vcos/vcos_init.h" +#endif + +#ifndef VCOS_SEMAPHORE_H +#include "interface/vcos/vcos_semaphore.h" +#endif + +#ifndef VCOS_THREAD_H +#include "interface/vcos/vcos_thread.h" +#endif + +#ifndef VCOS_MUTEX_H +#include "interface/vcos/vcos_mutex.h" +#endif + +#ifndef VCOS_MEM_H +#include "interface/vcos/vcos_mem.h" +#endif + +#ifndef VCOS_LOGGING_H +#include "interface/vcos/vcos_logging.h" +#endif + +#ifndef VCOS_STRING_H +#include "interface/vcos/vcos_string.h" +#endif + +#ifndef VCOS_EVENT_H +#include "interface/vcos/vcos_event.h" +#endif + +#ifndef VCOS_THREAD_ATTR_H +#include "interface/vcos/vcos_thread_attr.h" +#endif + +#ifndef VCOS_TLS_H +#include "interface/vcos/vcos_tls.h" +#endif + +#ifndef VCOS_REENTRANT_MUTEX_H +#include "interface/vcos/vcos_reentrant_mutex.h" +#endif + +#ifndef VCOS_NAMED_SEMAPHORE_H +#include "interface/vcos/vcos_named_semaphore.h" +#endif + +#ifndef VCOS_QUICKSLOW_MUTEX_H +#include "interface/vcos/vcos_quickslow_mutex.h" +#endif + +/* Headers with predicates */ + +#if VCOS_HAVE_EVENT_FLAGS +#include "interface/vcos/vcos_event_flags.h" +#endif + +#if VCOS_HAVE_QUEUE +#include "interface/vcos/vcos_queue.h" +#endif + +#if VCOS_HAVE_LEGACY_ISR +#include "interface/vcos/vcos_legacy_isr.h" +#endif + +#if VCOS_HAVE_TIMER +#include "interface/vcos/vcos_timer.h" +#endif + +#if VCOS_HAVE_MEMPOOL +#include "interface/vcos/vcos_mempool.h" +#endif + +#if VCOS_HAVE_ISR +#include "interface/vcos/vcos_isr.h" +#endif + +#if VCOS_HAVE_ATOMIC_FLAGS +#include "interface/vcos/vcos_atomic_flags.h" +#endif + +#if VCOS_HAVE_ONCE +#include "interface/vcos/vcos_once.h" +#endif + +#if VCOS_HAVE_BLOCK_POOL +#include "interface/vcos/vcos_blockpool.h" +#endif + +#if VCOS_HAVE_FILE +#include "interface/vcos/vcos_file.h" +#endif + +#if VCOS_HAVE_CFG +#include "interface/vcos/vcos_cfg.h" +#endif + +#if VCOS_HAVE_CMD +#include "interface/vcos/vcos_cmd.h" +#endif + +#endif /* VCOS_H */ + diff --git a/interface/vcos/vcos_assert.h b/interface/vcos/vcos_assert.h new file mode 100755 index 0000000..3645494 --- /dev/null +++ b/interface/vcos/vcos_assert.h @@ -0,0 +1,324 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - Assertion and error-handling macros. +=============================================================================*/ + + +#ifndef VCOS_ASSERT_H +#define VCOS_ASSERT_H + +/* + * Macro: + * vcos_assert(cond) + * vcos_assert_msg(cond, fmt, ...) + * Use: + * Detecting programming errors by ensuring that assumptions are correct. + * On failure: + * Performs a platform-dependent "breakpoint", usually with an assert-style + * message. The '_msg' variant expects a printf-style format string and + * parameters. + * If a failure is detected, the code should be fixed and rebuilt. + * In release builds: + * Generates no code, i.e. does not evaluate 'cond'. + * Returns: + * Nothing. + * + * Macro: + * vcos_demand(cond) + * vcos_demand_msg(cond, fmt, ...) + * Use: + * Detecting fatal system errors that require a reboot. + * On failure: + * Performs a platform-dependent "breakpoint", usually with an assert-style + * message, then calls vcos_abort (see below). + * In release builds: + * Calls vcos_abort() if 'cond' is false. + * Returns: + * Nothing (never, on failure). + * + * Macro: + * vcos_verify(cond) + * vcos_verify_msg(cond, fmt, ...) + * Use: + * Detecting run-time errors and interesting conditions, normally within an + * 'if' statement to catch the failures, i.e. + * if (!vcos_verify(cond)) handle_error(); + * On failure: + * Generates a message and optionally stops at a platform-dependent + * "breakpoint" (usually disabled). See vcos_verify_bkpts_enable below. + * In release builds: + * Just evaluates and returns 'cond'. + * Returns: + * Non-zero if 'cond' is true, otherwise zero. + * + * Macro: + * vcos_static_assert(cond) + * Use: + * Detecting compile-time errors. + * On failure: + * Generates a compiler error. + * In release builds: + * Generates a compiler error. + * + * Function: + * void vcos_abort(void) + * Use: + * Invokes the fatal error handling mechanism, alerting the host where + * applicable. + * Returns: + * Never. + * + * Macro: + * VCOS_VERIFY_BKPTS + * Use: + * Define in a module (before including vcos.h) to specify an alternative + * flag to control breakpoints on vcos_verify() failures. + * Returns: + * Non-zero values enable breakpoints. + * + * Function: + * int vcos_verify_bkpts_enable(int enable); + * Use: + * Sets the global flag controlling breakpoints on vcos_verify failures, + * enabling the breakpoints iff 'enable' is non-zero. + * Returns: + * The previous state of the flag. + * + * Function: + * int vcos_verify_bkpts_enabled(void); + * Use: + * Queries the state of the global flag enabling breakpoints on vcos_verify + * failures. + * Returns: + * The current state of the flag. + * + * Examples: + * + * int my_breakpoint_enable_flag = 1; + * + * #define VCOS_VERIFY_BKPTS my_breakpoint_enable_flag + * + * #include "interface/vcos/vcos.h" + * + * vcos_static_assert((sizeof(object) % 32) == 0); + * + * // ... + * + * vcos_assert_msg(postcondition_is_true, "Coding error"); + * + * if (!vcos_verify_msg(buf, "Buffer allocation failed (%d bytes)", size)) + * { + * // Tidy up + * // ... + * return OUT_OF_MEMORY; + * } + * + * vcos_demand(*p++==GUARDWORDHEAP); + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" + +#ifdef __COVERITY__ +#include "interface/vcos/user_nodefs.h" + +extern void __coverity_panic__(void); +#undef VCOS_ASSERT_BKPT +#define VCOS_ASSERT_BKPT __coverity_panic__() +#endif + +/* + * ANDROID should NOT be defined for files built for Videocore, but currently it + * is. FIXME When that's fixed, remove the __VIDEOCORE__ band-aid. + */ +#if (defined(ANDROID) && !defined(__VIDEOCORE__)) +# include "assert.h" +# define vcos_assert assert +#endif + +#ifndef VCOS_VERIFY_BKPTS +#define VCOS_VERIFY_BKPTS vcos_verify_bkpts_enabled() +#endif + +#ifndef VCOS_BKPT +#if defined(__VIDEOCORE__) && !defined(VCOS_ASSERT_NO_BKPTS) +#define VCOS_BKPT _bkpt() +#else +#define VCOS_BKPT (void )0 +#endif +#endif + +#ifndef VCOS_ASSERT_BKPT +#define VCOS_ASSERT_BKPT VCOS_BKPT +#endif + +#ifndef VCOS_VERIFY_BKPT +#define VCOS_VERIFY_BKPT (VCOS_VERIFY_BKPTS ? VCOS_BKPT : (void)0) +#endif + +VCOSPRE_ int VCOSPOST_ vcos_verify_bkpts_enabled(void); +VCOSPRE_ int VCOSPOST_ vcos_verify_bkpts_enable(int enable); +VCOSPRE_ void VCOSPOST_ vcos_abort(void); + +#ifndef VCOS_ASSERT_MSG +#ifdef LOGGING +extern void logging_assert(const char *file, const char *func, int line, const char *format, ...); +extern void logging_assert_dump(void); +#define VCOS_ASSERT_MSG(...) ((VCOS_ASSERT_LOGGING && !VCOS_ASSERT_LOGGING_DISABLE) ? logging_assert_dump(), logging_assert(__FILE__, __func__, __LINE__, __VA_ARGS__) : (void)0) +#else +#define VCOS_ASSERT_MSG(...) ((void)0) +#endif +#endif + +#ifndef VCOS_VERIFY_MSG +#define VCOS_VERIFY_MSG(...) VCOS_ASSERT_MSG(__VA_ARGS__) +#endif + +#ifndef VCOS_ASSERT_LOGGING +#define VCOS_ASSERT_LOGGING 0 +#endif + +#ifndef VCOS_ASSERT_LOGGING_DISABLE +#define VCOS_ASSERT_LOGGING_DISABLE 0 +#endif + +#if !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS) +#define VCOS_ASSERT_ENABLED 1 +#define VCOS_VERIFY_ENABLED 1 +#else +#define VCOS_ASSERT_ENABLED 0 +#define VCOS_VERIFY_ENABLED 0 +#endif + +#define VCOS_DEMAND_ENABLED 1 + +#if VCOS_ASSERT_ENABLED + +#ifndef vcos_assert +#define vcos_assert(cond) \ + ( (cond) ? (void)0 : (VCOS_ASSERT_BKPT, VCOS_ASSERT_MSG("%s", #cond)) ) +#endif + +#ifndef vcos_assert_msg +#define vcos_assert_msg(cond, ...) \ + ( (cond) ? (void)0 : (VCOS_ASSERT_BKPT, VCOS_ASSERT_MSG(__VA_ARGS__)) ) +#endif + +#else /* VCOS_ASSERT_ENABLED */ + +#ifndef vcos_assert +#define vcos_assert(cond) (void)0 +#endif + +#ifndef vcos_assert_msg +#define vcos_assert_msg(cond, ...) (void)0 +#endif + +#endif /* VCOS_ASSERT_ENABLED */ + + +#if VCOS_DEMAND_ENABLED + +#ifndef vcos_demand +#define vcos_demand(cond) \ + ( (cond) ? (void)0 : (VCOS_ASSERT_BKPT, VCOS_ASSERT_MSG("%s", #cond), vcos_abort()) ) +#endif + +#ifndef vcos_demand_msg +#define vcos_demand_msg(cond, ...) \ + ( (cond) ? (void)0 : (VCOS_ASSERT_BKPT, VCOS_ASSERT_MSG(__VA_ARGS__), vcos_abort()) ) +#endif + +#else /* VCOS_DEMAND_ENABLED */ + +#ifndef vcos_demand +#define vcos_demand(cond) \ + ( (cond) ? (void)0 : vcos_abort() ) +#endif + +#ifndef vcos_demand_msg +#define vcos_demand_msg(cond, ...) \ + ( (cond) ? (void)0 : vcos_abort() ) +#endif + +#endif /* VCOS_DEMAND_ENABLED */ + + +#if VCOS_VERIFY_ENABLED + +#ifndef vcos_verify +#define vcos_verify(cond) \ + ( (cond) ? 1 : (VCOS_VERIFY_MSG("%s", #cond), VCOS_VERIFY_BKPT, 0) ) +#endif + +#ifndef vcos_verify_msg +#define vcos_verify_msg(cond, ...) \ + ( (cond) ? 1 : (VCOS_VERIFY_MSG(__VA_ARGS__), VCOS_VERIFY_BKPT, 0) ) +#endif + +#else /* VCOS_VERIFY_ENABLED */ + +#ifndef vcos_verify +#define vcos_verify(cond) (cond) +#endif + +#ifndef vcos_verify_msg +#define vcos_verify_msg(cond, ...) (cond) +#endif + +#endif /* VCOS_VERIFY_ENABLED */ + + +#ifndef vcos_static_assert +#if defined(__GNUC__) +#define vcos_static_assert(cond) __attribute__((unused)) extern int vcos_static_assert[(cond)?1:-1] +#else +#define vcos_static_assert(cond) extern int vcos_static_assert[(cond)?1:-1] +#endif +#endif + +#ifndef vc_assert +#define vc_assert(cond) vcos_assert(cond) +#endif + +#define vcos_unreachable() vcos_assert(0) +#define vcos_not_impl() vcos_assert(0) + +/** Print out a backtrace, on supported platforms. + */ +extern void vcos_backtrace_self(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VCOS_ASSERT_H */ diff --git a/interface/vcos/vcos_atomic_flags.h b/interface/vcos/vcos_atomic_flags.h new file mode 100755 index 0000000..d0ddd92 --- /dev/null +++ b/interface/vcos/vcos_atomic_flags.h @@ -0,0 +1,92 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +#ifndef VCOS_ATOMIC_FLAGS_H +#define VCOS_ATOMIC_FLAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file vcos_atomic_flags.h + * + * Defines atomic flags API. + * + * 32 flags. Atomic "or" and "get and clear" operations + */ + +/** + * Create an atomic flags instance. + * + * @param atomic_flags Pointer to atomic flags instance, filled in on return + * + * @return VCOS_SUCCESS if succeeded. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_atomic_flags_create(VCOS_ATOMIC_FLAGS_T *atomic_flags); + +/** + * Atomically set the specified flags. + * + * @param atomic_flags Instance to set flags on + * @param flags Mask of flags to set + */ +VCOS_INLINE_DECL +void vcos_atomic_flags_or(VCOS_ATOMIC_FLAGS_T *atomic_flags, uint32_t flags); + +/** + * Retrieve the current flags and then clear them. The entire operation is + * atomic. + * + * @param atomic_flags Instance to get/clear flags from/on + * + * @return Mask of flags which were set (and we cleared) + */ +VCOS_INLINE_DECL +uint32_t vcos_atomic_flags_get_and_clear(VCOS_ATOMIC_FLAGS_T *atomic_flags); + +/** + * Delete an atomic flags instance. + * + * @param atomic_flags Instance to delete + */ +VCOS_INLINE_DECL +void vcos_atomic_flags_delete(VCOS_ATOMIC_FLAGS_T *atomic_flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/interface/vcos/vcos_attr.h b/interface/vcos/vcos_attr.h new file mode 100755 index 0000000..33f7eee --- /dev/null +++ b/interface/vcos/vcos_attr.h @@ -0,0 +1,153 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - compiler-specific attributes +=============================================================================*/ + +#ifndef VCOS_ATTR_H +#define VCOS_ATTR_H + +/** + * Type attribute indicating the enum should be stored in as few bytes as + * possible. MetaWare does this by default, so the attribute is useful when + * structs need to be portable to GCC too. + * + * MSVC doesn't support VCOS_ENUM_PACKED, so code that needs to be portable + * across all platforms but wants the type-safety and debug-info benefits + * of enum types when possible, should do: + * + * typedef enum VCOS_ENUM_PACKED { a = 0, b = 0xffff } EXAMPLE_T; + * struct foo { + * int bar; + * #if VCOS_HAS_ENUM_PACKED + * EXAMPLE_T baz; + * #else + * uint16_t baz; + * #endif + * }; + */ + +#if defined(__VECTORC__) +# define VCOS_ENUM_PACKED +# define VCOS_HAS_ENUM_PACKED 0 +#elif defined(__GNUC__) +# define VCOS_ENUM_PACKED __attribute__ ((packed)) +# define VCOS_HAS_ENUM_PACKED 1 +#elif defined(__HIGHC__) +# define VCOS_ENUM_PACKED /* packed enums are default on Metaware */ +# define VCOS_HAS_ENUM_PACKED 1 +#else +# define VCOS_ENUM_PACKED +# define VCOS_HAS_ENUM_PACKED 0 +#endif + +/** Variable attribute indicating the variable must be emitted even if it appears unused. */ +#if defined(__GNUC__) || defined(__HIGHC__) +# define VCOS_ATTR_USED __attribute__ ((used)) +#else +# define VCOS_ATTR_USED +#endif + +/** Variable attribute indicating the compiler should not warn if the variable is unused. */ +#if defined(__GNUC__) || defined(__HIGHC__) +# define VCOS_ATTR_POSSIBLY_UNUSED __attribute__ ((unused)) +#else +# define VCOS_ATTR_POSSIBLY_UNUSED +#endif + +/** Variable attribute requiring specific alignment. + * + * Use as: + * int VCOS_ATTR_ALIGNED(256) n; + * or: + * VCOS_ATTR_ALIGNED(256) int n; + * or if you don't want to support MSVC: + * int n VCOS_ATTR_ALIGNED(256); + */ +#if defined(__GNUC__) || defined(__HIGHC__) +# define VCOS_ATTR_ALIGNED(n) __attribute__ ((aligned(n))) +#elif defined(_MSC_VER) +# define VCOS_ATTR_ALIGNED(n) __declspec(align(n)) +#else +/* Force a syntax error if this is used when the compiler doesn't support it, + * instead of silently misaligning */ +# define VCOS_ATTR_ALIGNED(n) VCOS_ATTR_ALIGNED_NOT_SUPPORTED_ON_THIS_COMPILER +#endif + +/** Variable attribute requiring specific ELF section. + * + * Use as: + * int n VCOS_ATTR_SECTION(".foo") = 1; + * + * A pointer like &n will have type "VCOS_ATTR_SECTION_QUALIFIER int *". + */ +#if defined(__HIGHC__) || defined(__VECTORC__) +/* hcvc requires 'far' else it'll put small objects in .sdata/.rsdata/.sbss */ +# define VCOS_ATTR_SECTION(s) __attribute__ ((far, section(s))) +# define VCOS_ATTR_SECTION_QUALIFIER _Far +#elif defined(__GNUC__) +# define VCOS_ATTR_SECTION(s) __attribute__ ((section(s))) +# define VCOS_ATTR_SECTION_QUALIFIER +#else +/* Force a syntax error if this is used when the compiler doesn't support it */ +# define VCOS_ATTR_SECTION(s) VCOS_ATTR_SECTION_NOT_SUPPORTED_ON_THIS_COMPILER +# define VCOS_ATTR_SECTION_QUALIFIER +#endif + +/** Define a function as a weak alias to another function. + * @param ret_type Function return type. + * @param alias_name Name of the alias. + * @param param_list Function parameter list, including the parentheses. + * @param target_name Target function (bare function name, not a string). + */ +#if defined(__GNUC__) || defined(__HIGHC__) + /* N.B. gcc allows __attribute__ after parameter list, but hcvc seems to silently ignore it. */ +# define VCOS_WEAK_ALIAS(ret_type, alias_name, param_list, target_name) \ + __attribute__ ((weak, alias(#target_name))) ret_type alias_name param_list +#else +# define VCOS_WEAK_ALIAS(ret_type, alias, params, target) VCOS_CASSERT(0) +#endif + +/** Define a function as a weak alias to another function, specified as a string. + * @param ret_type Function return type. + * @param alias_name Name of the alias. + * @param param_list Function parameter list, including the parentheses. + * @param target_name Target function name as a string. + * @note Prefer the use of VCOS_WEAK_ALIAS - it is likely to be more portable. + * Only use VCOS_WEAK_ALIAS_STR if you need to do pre-processor mangling of the target + * symbol. + */ +#if defined(__GNUC__) || defined(__HIGHC__) + /* N.B. gcc allows __attribute__ after parameter list, but hcvc seems to silently ignore it. */ +# define VCOS_WEAK_ALIAS_STR(ret_type, alias_name, param_list, target_name) \ + __attribute__ ((weak, alias(target_name))) ret_type alias_name param_list +#else +# define VCOS_WEAK_ALIAS_STR(ret_type, alias, params, target) VCOS_CASSERT(0) +#endif + +#endif /* VCOS_ATTR_H */ diff --git a/interface/vcos/vcos_blockpool.h b/interface/vcos/vcos_blockpool.h new file mode 100755 index 0000000..f82013a --- /dev/null +++ b/interface/vcos/vcos_blockpool.h @@ -0,0 +1,171 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - fixed size allocator support +=============================================================================*/ + +#ifndef VCOS_BLOCKPOOL_H +#define VCOS_BLOCKPOOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** \file + * + * Thread safe, fixed size allocator API. + * + */ + +/** Initialises a block pool to use already allocated (e.g. statically) + * allocated memory. + * + * Different implementations will incur different overheads. Use + * VCOS_BLOCKPOOL_SIZE(num_blocks, block_size) to calculate the number + * of bytes required including overheads for the desired pools. + * + * @param pool Pointer to pool object + * @param num_blocks The number of blocks required. + * @param block_size The size of an individual block. + * @param start The address of the start of the pool. + * @param pool_size The size of the pool in bytes. + * @param align Alignment for block data. Use VCOS_BLOCKPOOL_ALIGN_DEFAULT + * for default word alignment. + * @param flags Reserved for future use. + * @param name Name of the pool. Used for diagnostics. + * + * @return VCOS_SUCCESS if the pool was created. + */ + +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_blockpool_init(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, + void *start, VCOS_UNSIGNED pool_size, VCOS_UNSIGNED align, + VCOS_UNSIGNED flags, const char *name); + +/** Creates a pool of blocks of a given size within a buffer allocated on + * the heap. + * + * The heap memory is freed when the block pool is destroyed by + * calling vcos_blockpool_delete. + * + * @param pool Pointer to pool object + * @param num_blocks The number of blocks required. + * @param block_size The size of an individual block. + * @param align Alignment for block data. Use VCOS_BLOCKPOOL_ALIGN_DEFAULT + * for default word alignment. + * @param flags Reserved for future use. + * @param name Name of the pool. Used for diagnostics. + * + * @return VCOS_SUCCESS if the pool was created. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_blockpool_create_on_heap(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, + VCOS_UNSIGNED align, VCOS_UNSIGNED flags, + const char *name); + +/** Allocate a block from the pool + * + * @param pool Pointer to the pool to allocate from. + * @return a pointer to the newly allocated block or NULL if no blocks were + * available. + */ +VCOS_INLINE_DECL +void *vcos_blockpool_alloc(VCOS_BLOCKPOOL_T *pool); + +/** Allocate a block from the pool and zero it. + * + * @param pool Pointer to the pool to allocate from. + * @return a pointer to the newly allocated block or NULL if no blocks were + * available. + */ +VCOS_INLINE_DECL +void *vcos_blockpool_calloc(VCOS_BLOCKPOOL_T *pool); + +/** Returns a block to the pool. + * + * @param block The block to free. + */ +VCOS_INLINE_DECL +void vcos_blockpool_free(void *block); + +/** Queries the number of available blocks in the pool. + * @param pool The pool to query. + */ +VCOS_INLINE_IMPL + VCOS_UNSIGNED vcos_blockpool_available_count(VCOS_BLOCKPOOL_T *pool); + +/** Queries the number of used blocks in the pool. + * @param pool The pool to query. + */ +VCOS_INLINE_IMPL + VCOS_UNSIGNED vcos_blockpool_used_count(VCOS_BLOCKPOOL_T *pool); + +/** Deinitialize a memory pool. + * + * @param pool The pool to de-initialize. + */ +VCOS_INLINE_DECL +void vcos_blockpool_delete(VCOS_BLOCKPOOL_T *pool); + +/** Return an integer handle for a given allocated block. */ +VCOS_INLINE_DECL +uint32_t vcos_blockpool_elem_to_handle(void *block); + +/** Convert an integer handle back into a pointer. + * Returns NULL if invalid. */ +VCOS_INLINE_DECL +void *vcos_blockpool_elem_from_handle(VCOS_BLOCKPOOL_T *pool, uint32_t handle); + +/** Checks whether a pointer is an allocated block within the specified pool. + * Returns true if the block is valid, otherwise, false is returned. */ +VCOS_INLINE_DECL +uint32_t vcos_blockpool_is_valid_elem( + VCOS_BLOCKPOOL_T *pool, const void *block); + +/** May be called once to allow the block pool to be extended by dynamically + * adding subpools. The block size cannot be altered. + * + * @param num_extensions The number of extensions that may be created. + * The maximum is (VCOS_BLOCKPOOL_MAX_SUBPOOLS - 1) + * @param num_blocks The number of blocks to allocate in each in each + * dynamically allocated subpool. + * @return VCOS_SUCCESS if successful. + */ +VCOS_INLINE_DECL + VCOS_STATUS_T vcos_blockpool_extend(VCOS_BLOCKPOOL_T *pool, + VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_build_info.h b/interface/vcos/vcos_build_info.h new file mode 100755 index 0000000..710619e --- /dev/null +++ b/interface/vcos/vcos_build_info.h @@ -0,0 +1,32 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +const char *vcos_get_build_hostname( void ); +const char *vcos_get_build_version( void ); +const char *vcos_get_build_time( void ); +const char *vcos_get_build_date( void ); + diff --git a/interface/vcos/vcos_cfg.h b/interface/vcos/vcos_cfg.h new file mode 100755 index 0000000..132ed3e --- /dev/null +++ b/interface/vcos/vcos_cfg.h @@ -0,0 +1,126 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !defined( VCOS_CFG_H ) +#define VCOS_CFG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +typedef struct opaque_vcos_cfg_buf_t *VCOS_CFG_BUF_T; +typedef struct opaque_vcos_cfg_entry_t *VCOS_CFG_ENTRY_T; + +/** \file vcos_file.h + * + * API for accessing configuration/statistics information. This + * is loosely modelled on the linux proc entries. + */ + +typedef void (*VCOS_CFG_SHOW_FPTR)( VCOS_CFG_BUF_T buf, void *data ); +typedef void (*VCOS_CFG_PARSE_FPTR)( VCOS_CFG_BUF_T buf, void *data ); + +/** Create a configuration directory. + * + * @param entry Place to store the created config entry. + * @param parent Parent entry (for directory like config + * options). + * @param entryName Name of the directory. + */ + +VCOS_STATUS_T vcos_cfg_mkdir( VCOS_CFG_ENTRY_T *entry, + VCOS_CFG_ENTRY_T *parent, + const char *dirName ); + +/** Create a configuration entry. + * + * @param entry Place to store the created config entry. + * @param parent Parent entry (for directory like config + * options). + * @param entryName Name of the configuration entry. + * @param showFunc Function pointer to show configuration + * data. + * @param parseFunc Function pointer to parse new data. + */ + +VCOS_STATUS_T vcos_cfg_create_entry( VCOS_CFG_ENTRY_T *entry, + VCOS_CFG_ENTRY_T *parent, + const char *entryName, + VCOS_CFG_SHOW_FPTR showFunc, + VCOS_CFG_PARSE_FPTR parseFunc, + void *data ); + +/** Determines if a configuration entry has been created or not. + * + * @param entry Configuration entry to query. + */ + +int vcos_cfg_is_entry_created( VCOS_CFG_ENTRY_T entry ); + +/** Returns the name of a configuration entry. + * + * @param entry Configuration entry to query. + */ + +const char *vcos_cfg_get_entry_name( VCOS_CFG_ENTRY_T entry ); + +/** Removes a configuration entry. + * + * @param entry Configuration entry to remove. + */ + +VCOS_STATUS_T vcos_cfg_remove_entry( VCOS_CFG_ENTRY_T *entry ); + + +/** Writes data into a configuration buffer. Only valid inside + * the show function. + * + * @param buf Buffer to write data into. + * @param fmt printf style format string. + */ + +void vcos_cfg_buf_printf( VCOS_CFG_BUF_T buf, const char *fmt, ... ); + +/** Retrieves a null terminated string of the data associated + * with the buffer. Only valid inside the parse function. + * + * @param buf Buffer to get data from. + * @param fmt printf style format string. + */ + +char *vcos_cfg_buf_get_str( VCOS_CFG_BUF_T buf ); + +void *vcos_cfg_get_proc_entry( VCOS_CFG_ENTRY_T entry ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_cmd.h b/interface/vcos/vcos_cmd.h new file mode 100755 index 0000000..9d617fd --- /dev/null +++ b/interface/vcos/vcos_cmd.h @@ -0,0 +1,120 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !defined( VCOS_CMD_H ) +#define VCOS_CMD_H + +/* ---- Include Files ----------------------------------------------------- */ + +#ifndef VCOS_H +#include "interface/vcos/vcos.h" +#endif +#include "interface/vcos/vcos_stdint.h" + + +/* ---- Constants and Types ---------------------------------------------- */ + +struct VCOS_CMD_S; +typedef struct VCOS_CMD_S VCOS_CMD_T; + +typedef struct +{ + int argc; /* Number of arguments (includes the command/sub-command) */ + char **argv; /* Array of arguments */ + char **argv_orig; /* Original array of arguments */ + + VCOS_CMD_T *cmd_entry; + VCOS_CMD_T *cmd_parent_entry; + + int use_log; /* Output being logged? */ + size_t result_size; /* Size of result buffer. */ + char *result_ptr; /* Next place to put output. */ + char *result_buf; /* Start of the buffer. */ + +} VCOS_CMD_PARAM_T; + +typedef VCOS_STATUS_T (*VCOS_CMD_FUNC_T)( VCOS_CMD_PARAM_T *param ); + +struct VCOS_CMD_S +{ + const char *name; + const char *args; + VCOS_CMD_FUNC_T cmd_fn; + VCOS_CMD_T *sub_cmd_entry; + const char *descr; + +}; + +/* ---- Variable Externs ------------------------------------------------- */ + +/* ---- Function Prototypes ---------------------------------------------- */ + +/* + * Common printing routine for generating command output. + */ +VCOSPRE_ void VCOSPOST_ vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) VCOS_FORMAT_ATTR_(printf, 2, 3); +VCOSPRE_ void VCOSPOST_ vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) VCOS_FORMAT_ATTR_(printf, 2, 3); +VCOSPRE_ void VCOSPOST_ vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args ) VCOS_FORMAT_ATTR_(printf, 2, 0); + +/* + * Cause vcos_cmd_error, printf and vprintf to always log to the provided + * category. When this call is made, the results buffer passed into + * vcos_cmd_execute is used as a line buffer and does not need to be + * output by the caller. + */ +struct VCOS_LOG_CAT_T; +VCOSPRE_ void VCOSPOST_ vcos_cmd_always_log_output( struct VCOS_LOG_CAT_T *log_category ); + +/* + * Prints command usage for the current command. + */ +VCOSPRE_ void VCOSPOST_ vcos_cmd_usage( VCOS_CMD_PARAM_T *param ); + +/* + * Register commands to be processed + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_register( VCOS_CMD_T *cmd_entry ); + +/* + * Registers multiple commands to be processed. The array should + * be terminated by an entry with all zeros. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry ); + +/* + * Executes a command based on a command line. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf ); + +/* + * Shut down the command system and free all allocated data. + * Do not call any other command functions after this. + */ +VCOSPRE_ void VCOSPOST_ vcos_cmd_shutdown( void ); + +#endif /* VCOS_CMD_H */ + diff --git a/interface/vcos/vcos_ctype.h b/interface/vcos/vcos_ctype.h new file mode 100755 index 0000000..64bda96 --- /dev/null +++ b/interface/vcos/vcos_ctype.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +#ifndef VCOS_CTYPE_H +#define VCOS_CTYPE_H + +/** + * \file + * + * ctype functions. + * + */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#endif + diff --git a/interface/vcos/vcos_dlfcn.h b/interface/vcos/vcos_dlfcn.h new file mode 100755 index 0000000..6971d74 --- /dev/null +++ b/interface/vcos/vcos_dlfcn.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VCOS - abstraction over dynamic library opening +=============================================================================*/ + +#ifndef VCOS_DLFCN_H +#define VCOS_DLFCN_H + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * + * Loading dynamic libraries. See also dlfcn.h. + */ + +/** Open a dynamic library. + * + * @param name name of the library + * @param mode Load lazily or immediately (VCOS_DL_LAZY, VCOS_DL_NOW, VCOS_DL_LOCAL, VCOS_DL_GLOBAL). + * + * @return A handle for use in subsequent calls. + */ +VCOSPRE_ void * VCOSPOST_ vcos_dlopen(const char *name, int mode); + +/** Look up a symbol. + * + * @param handle Handle to open + * @param name Name of function + * + * @return Function pointer, or NULL. + */ +VCOSPRE_ void VCOSPOST_ (*vcos_dlsym(void *handle, const char *name))(void); + +/** Close a library + * + * @param handle Handle to close + */ +VCOSPRE_ int VCOSPOST_ vcos_dlclose (void *handle); + +/** Return error message from library. + * + * @param err On return, set to non-zero if an error has occurred + * @param buf Buffer to write error to + * @param len Size of buffer (including terminating NUL). + */ +VCOSPRE_ int VCOSPOST_ vcos_dlerror(int *err, char *buf, size_t buflen); + + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/interface/vcos/vcos_event.h b/interface/vcos/vcos_event.h new file mode 100755 index 0000000..f047ded --- /dev/null +++ b/interface/vcos/vcos_event.h @@ -0,0 +1,117 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file for events +=============================================================================*/ + +#ifndef VCOS_EVENT_H +#define VCOS_EVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file + * + * An event is akin to the Win32 auto-reset event. + * + * + * Signalling an event will wake up one waiting thread only. Once one + * thread has been woken the event atomically returns to the unsignalled + * state. + * + * If no threads are waiting on the event when it is signalled it remains + * signalled. + * + * This is almost, but not quite, completely unlike the "event flags" + * object based on Nucleus event groups and ThreadX event flags. + * + * In particular, it should be similar in speed to a semaphore, unlike + * the event flags. + */ + +/** + * Create an event instance. + * + * @param event Filled in with constructed event. + * @param name Name of the event (for debugging) + * + * @return VCOS_SUCCESS on success, or error code. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_event_create(VCOS_EVENT_T *event, const char *name); + +#ifndef vcos_event_signal + +/** + * Signal the event. The event will return to being unsignalled + * after exactly one waiting thread has been woken up. If no + * threads are waiting it remains signalled. + * + * @param event The event to signal + */ +VCOS_INLINE_DECL +void vcos_event_signal(VCOS_EVENT_T *event); + +/** + * Wait for the event. + * + * @param event The event to wait for + * @return VCOS_SUCCESS on success, VCOS_EAGAIN if the wait was interrupted. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_event_wait(VCOS_EVENT_T *event); + +/** + * Try event, but don't block. + * + * @param event The event to try + * @return VCOS_SUCCESS on success, VCOS_EAGAIN if the event is not currently signalled + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_event_try(VCOS_EVENT_T *event); + +#endif + +/* + * Destroy an event. + */ +VCOS_INLINE_DECL +void vcos_event_delete(VCOS_EVENT_T *event); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/interface/vcos/vcos_event_flags.h b/interface/vcos/vcos_event_flags.h new file mode 100755 index 0000000..cd5fbb2 --- /dev/null +++ b/interface/vcos/vcos_event_flags.h @@ -0,0 +1,118 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +#ifndef VCOS_EVENT_FLAGS_H +#define VCOS_EVENT_FLAGS_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +#define VCOS_EVENT_FLAGS_SUSPEND VCOS_SUSPEND +#define VCOS_EVENT_FLAGS_NO_SUSPEND VCOS_NO_SUSPEND +typedef VCOS_OPTION VCOS_EVENTGROUP_OPERATION_T; + +/** + * \file vcos_event_flags.h + * + * Defines event flags API. + * + * Similar to Nucleus event groups. + * + * These have the same semantics as Nucleus event groups and ThreadX event + * flags. As such, they are quite complex internally; if speed is important + * they might not be your best choice. + * + */ + +/** + * Create an event flags instance. + * + * @param flags Pointer to event flags instance, filled in on return. + * @param name Name for the event flags, used for debug. + * + * @return VCOS_SUCCESS if succeeded. + */ + +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name); + +/** + * Set some events. + * + * @param flags Instance to set flags on + * @param events Bitmask of the flags to actually set + * @param op How the flags should be set. VCOS_OR will OR in the flags; VCOS_AND + * will AND them in, possibly clearing existing flags. + */ +VCOS_INLINE_DECL +void vcos_event_flags_set(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED events, + VCOS_OPTION op); + +/** + * Retrieve some events. + * + * Waits until the specified events have been set. + * + * @param flags Instance to wait on + * @param requested_events The bitmask to wait for + * @param op VCOS_OR - get any; VCOS_AND - get all. + * @param ms_suspend How long to wait, in milliseconds + * @param retrieved_events the events actually retrieved. + * + * @return VCOS_SUCCESS if events were retrieved. VCOS_EAGAIN if the + * timeout expired. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_event_flags_get(VCOS_EVENT_FLAGS_T *flags, + VCOS_UNSIGNED requested_events, + VCOS_OPTION op, + VCOS_UNSIGNED ms_suspend, + VCOS_UNSIGNED *retrieved_events); + + +/** + * Delete an event flags instance. + */ +VCOS_INLINE_DECL +void vcos_event_flags_delete(VCOS_EVENT_FLAGS_T *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/interface/vcos/vcos_init.h b/interface/vcos/vcos_init.h new file mode 100755 index 0000000..2d770a5 --- /dev/null +++ b/interface/vcos/vcos_init.h @@ -0,0 +1,110 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - initialization routines +=============================================================================*/ + + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + * + * Some OS support libraries need some initialization. To support this, call + * vcos_init() function at the start of day; vcos_deinit() at the end. + */ + +/** + * vcos initialization. Call this function before using other vcos functions. + * Calls can be nested within the same process; they are reference counted so + * that only a call from uninitialized state has any effect. + * @note On platforms/toolchains that support it, gcc's constructor attribute or + * similar is used to invoke this function before main() or equivalent. + * @return Status of initialisation. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_init(void); + +/** + * vcos deinitialization. Call this function when vcos is no longer required, + * in order to free resources. + * Calls can be nested within the same process; they are reference counted so + * that only a call that decrements the reference count to 0 has any effect. + * @note On platforms/toolchains that support it, gcc's destructor attribute or + * similar is used to invoke this function after exit() or equivalent. + * @return Status of initialisation. + */ +VCOSPRE_ void VCOSPOST_ vcos_deinit(void); + +/** + * Acquire global lock. This must be available independent of vcos_init()/vcos_deinit(). + */ +VCOSPRE_ void VCOSPOST_ vcos_global_lock(void); + +/** + * Release global lock. This must be available independent of vcos_init()/vcos_deinit(). + */ +VCOSPRE_ void VCOSPOST_ vcos_global_unlock(void); + +/** Pass in the argv/argc arguments passed to main() */ +VCOSPRE_ void VCOSPOST_ vcos_set_args(int argc, const char **argv); + +/** Return argc. */ +VCOSPRE_ int VCOSPOST_ vcos_get_argc(void); + +/** Return argv. */ +VCOSPRE_ const char ** VCOSPOST_ vcos_get_argv(void); + +/** + * Platform-specific initialisation. + * VCOS internal function, not part of public API, do not call from outside + * vcos. vcos_init()/vcos_deinit() reference count calls, so this function is + * only called from an uninitialized state, i.e. there will not be two + * consecutive calls to vcos_platform_init() without an intervening call to + * vcos_platform_deinit(). + * This function is called with vcos_global_lock held. + * @return Status of initialisation. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_platform_init(void); + +/** + * Platform-specific de-initialisation. + * VCOS internal function, not part of public API, do not call from outside + * vcos. + * See vcos_platform_init() re reference counting. + * This function is called with vcos_global_lock held. + */ +VCOSPRE_ void VCOSPOST_ vcos_platform_deinit(void); + +#ifdef __cplusplus +} +#endif + diff --git a/interface/vcos/vcos_inttypes.h b/interface/vcos/vcos_inttypes.h new file mode 100755 index 0000000..7818ab8 --- /dev/null +++ b/interface/vcos/vcos_inttypes.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCOS_INTTYPES_H +#define VCOS_INTTYPES_H + +/** \file + * Attempt to provide the support for fixed width integer types as per + * inttypes.h. This simply includes inttypes.h, which should find the + * system/toolchain version if present, otherwise falling back to the version + * in interface/vcos/. The vcos versions initially only provide the + * most common printf() macros. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif /* VCOS_INTTYPES_H */ diff --git a/interface/vcos/vcos_isr.h b/interface/vcos/vcos_isr.h new file mode 100755 index 0000000..79e371b --- /dev/null +++ b/interface/vcos/vcos_isr.h @@ -0,0 +1,90 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - IRQ support +=============================================================================*/ + +#ifndef VCOS_ISR_H +#define VCOS_ISR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file vcos_isr.h + * + * \section isr ISR support + * + * API for dispatching interrupts. + */ + +/** + * + * Register an interrupt handler. The old handler (if any) is returned in + * old_handler. The old handler should be called if the interrupt was not + * for you. + * + * The handler function will be called in a context with interrupts disabled, + * so should be written to be as short as possible. If significant processing + * is needed, the handler should delegate to a thread. + * + * The handler function can call any OS primitive that does not block (e.g. + * post a semaphore or set an event flag). Blocking operations (including memory + * allocation from the system heap) are not permitted. + * + * To deregister an ISR, pass in NULL. + * + * @param vec Vector to register for + * @param handler Handler to be called + * @param old_handler Updated with the old handler, or NULL. + */ + +VCOS_INLINE_DECL +void vcos_register_isr(VCOS_UNSIGNED vec, + VCOS_ISR_HANDLER_T handler, + VCOS_ISR_HANDLER_T *old_handler); + +/** Disable interrupts, returning the old value (enabled/disabled) to the caller. + */ +VCOS_INLINE_DECL +VCOS_UNSIGNED vcos_int_disable(void); + +/** Restore the previous interrupt enable/disable state. + */ +VCOS_INLINE_DECL +void vcos_int_restore(VCOS_UNSIGNED previous); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_legacy_isr.h b/interface/vcos/vcos_legacy_isr.h new file mode 100755 index 0000000..dd8c3bf --- /dev/null +++ b/interface/vcos/vcos_legacy_isr.h @@ -0,0 +1,102 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - legacy (Nucleus) IRQ support +=============================================================================*/ + +#ifndef VCOS_LEGACY_ISR_H +#define VCOS_LEGACY_ISR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** \file vcos_legacy_isr.h + * + * API for dispatching interrupts the Nucleus way, via a LISR and HISR. + * New code should use the single-dispatch scheme - the LISR/HISR + * distinction is not necessary. + * + * Under ThreadX, a HISR is implemented as a high-priority thread which + * waits on a counting semaphore to call the HISR function. Although this + * provides a good approximation to the Nucleus semantics, it is potentially + * slow if all you are trying to do is to wake a thread from LISR context. + */ + +/** Register a LISR. This is identical to the NU_Register_LISR API. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_register_legacy_lisr(VCOS_UNSIGNED vecnum, + void (*lisr)(VCOS_INT), + void (**old_lisr)(VCOS_INT)); + +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_legacy_hisr_create(VCOS_HISR_T *hisr, const char *name, + void (*entry)(void), + VCOS_UNSIGNED pri, + void *stack, VCOS_UNSIGNED stack_size); + +/** Activate a HISR. On an OS which has no distinction between a HISR and LISR, + * this may use some kind of emulation, which could well be less efficient than + * a normal ISR.` + * + * @param hisr HISR to activate. + */ +VCOS_INLINE_DECL +void vcos_legacy_hisr_activate(VCOS_HISR_T *hisr); + +/** Delete a HISR. + * + * @param hisr HISR to delete. + */ +VCOS_INLINE_DECL +void vcos_legacy_hisr_delete(VCOS_HISR_T *hisr); + +/** Are we in a legacy LISR? + * + * @return On Nucleus, non-zero if in a LISR. On other platforms, non-zero if + * in an interrupt. + */ +VCOS_INLINE_DECL +int vcos_in_legacy_lisr(void); + +/** Is the current thread actually a fake HISR thread? Only implemented + * on platforms that fake up HISRs. + */ + +#ifndef VCOS_LISRS_NEED_HISRS +VCOSPRE_ int VCOSPOST_ vcos_current_thread_is_fake_hisr_thread(VCOS_HISR_T *); +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_logging.h b/interface/vcos/vcos_logging.h new file mode 100755 index 0000000..a9a132f --- /dev/null +++ b/interface/vcos/vcos_logging.h @@ -0,0 +1,315 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - logging support +=============================================================================*/ + +#ifndef VCOS_LOGGING_H +#define VCOS_LOGGING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" +#include "vcos_logging_control.h" + +/** + * \file + * + * Logging support + * + * This provides categorised logging. Clients register + * a category, and then get a number of logging levels for + * that category. + * + * The logging level flag is tested using a flag *before* the + * function call, which makes logging very fast when disabled - there + * is no function call overhead just to find out that this log + * message is disabled. + * + * \section VCOS_LOG_CATEGORY + * + * As a convenience, clients define VCOS_LOG_CATEGORY to point to + * their category; the various vcos_log_xxx() macros then expand to + * use this. + * + * e.g. + * + * #define VCOS_LOG_CATEGORY (&my_category) + * + * #include + * + * VCOS_LOG_CAT_T my_category; + * + * .... + * + * vcos_log_trace("Stuff happened: %d", n_stuff); + * + */ + +/** Logging levels */ +typedef enum VCOS_LOG_LEVEL_T +{ + VCOS_LOG_UNINITIALIZED = 0, + VCOS_LOG_NEVER, + VCOS_LOG_ERROR, + VCOS_LOG_WARN, + VCOS_LOG_INFO, + VCOS_LOG_TRACE, +} VCOS_LOG_LEVEL_T; + + +/** Initialize a logging category without going through vcos_log_register(). + * + * This is useful for the case where there is no obvious point to do the + * registration (no initialization function for the module). However, it + * means that your logging category is not registered, so cannot be easily + * changed at run-time. + */ +#define VCOS_LOG_INIT(n,l) { l, n, 0, {0}, 0, 0 } + +/** A registered logging category. + */ +typedef struct VCOS_LOG_CAT_T +{ + VCOS_LOG_LEVEL_T level; /**< Which levels are enabled for this category */ + const char *name; /**< Name for this category. */ + struct VCOS_LOG_CAT_T *next; + struct { + unsigned int want_prefix:1; + } flags; + unsigned int refcount; + void *platform_data; /**< platform specific data */ +} VCOS_LOG_CAT_T; + +typedef void (*VCOS_VLOG_IMPL_FUNC_T)(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args); + +/** Convert a VCOS_LOG_LEVEL_T into a printable string. + * The platform needs to implement this function. + */ +VCOSPRE_ const char * VCOSPOST_ vcos_log_level_to_string( VCOS_LOG_LEVEL_T level ); + +/** Convert a string into a VCOS_LOG_LEVEL_T + * The platform needs to implement this function. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level ); + +/** Log a message. Basic API. Normal code should not use this. + * The platform needs to implement this function. + */ +VCOSPRE_ void VCOSPOST_ vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...) VCOS_FORMAT_ATTR_(printf, 3, 4); + +/** Log a message using a varargs parameter list. Normal code should + * not use this. + */ +VCOSPRE_ void VCOSPOST_ vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) VCOS_FORMAT_ATTR_(printf, 3, 0); + +/** Set the function which does the actual logging output. + * Passing in NULL causes the default logging function to be + * used. + */ +VCOSPRE_ void VCOSPOST_ vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func ); + +/** The default logging function, which is provided by each + * platform. + */ + +VCOSPRE_ void VCOSPOST_ vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) VCOS_FORMAT_ATTR_(printf, 3, 0); + +/* + * Initialise the logging subsystem. This is called from + * vcos_init() so you don't normally need to call it. + */ +VCOSPRE_ void VCOSPOST_ vcos_logging_init(void); + +/** Register a logging category. + * + * @param name the name of this category. + * @param category the category to register. + */ +VCOSPRE_ void VCOSPOST_ vcos_log_register(const char *name, VCOS_LOG_CAT_T *category); + +/** Unregister a logging category. + */ +VCOSPRE_ void VCOSPOST_ vcos_log_unregister(VCOS_LOG_CAT_T *category); + +/** Return a default logging category, for people too lazy to create their own. + * + * Using the default category will be slow (there's an extra function + * call overhead). Don't do this in normal code. + */ +VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void); + +VCOSPRE_ void VCOSPOST_ vcos_set_log_options(const char *opt); + +/** Set the logging level for a category at run time. Without this, the level + * will be that set by vcos_log_register from a platform-specific source. + * + * @param category the category to modify. + * @param level the new logging level for this category. + */ +VCOS_STATIC_INLINE void vcos_log_set_level(VCOS_LOG_CAT_T *category, VCOS_LOG_LEVEL_T level) +{ + category->level = level; +} + +#define vcos_log_dump_mem(cat,label,addr,voidMem,numBytes) do { if (vcos_is_log_enabled(cat,VCOS_LOG_TRACE)) vcos_log_dump_mem_impl(cat,label,addr,voidMem,numBytes); } while (0) + +void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat, + const char *label, + uint32_t addr, + const void *voidMem, + size_t numBytes ); + +/* + * Platform specific hooks (optional). + */ +#ifndef vcos_log_platform_init +#define vcos_log_platform_init() (void)0 +#endif + +#ifndef vcos_log_platform_register +#define vcos_log_platform_register(category) (void)0 +#endif + +#ifndef vcos_log_platform_unregister +#define vcos_log_platform_unregister(category) (void)0 +#endif + +/* VCOS_TRACE() - deprecated macro which just outputs in a debug build and + * is a no-op in a release build. + * + * _VCOS_LOG_X() - internal macro which outputs if the current level for the + * particular category is higher than the supplied message level. + */ + +#define VCOS_LOG_DFLT_CATEGORY vcos_log_get_default_category() + +#define _VCOS_LEVEL(x) (x) + +#define vcos_is_log_enabled(cat,_level) (_VCOS_LEVEL((cat)->level) >= _VCOS_LEVEL(_level)) + +#if defined(_VCOS_METAWARE) || defined(__GNUC__) + +# if !defined(AMPUTATE_ALL_VCOS_LOGGING) && (!defined(NDEBUG) || defined(VCOS_ALWAYS_WANT_LOGGING)) +# define VCOS_LOGGING_ENABLED +# define _VCOS_LOG_X(cat, _level, fmt...) do { if (vcos_is_log_enabled(cat,_level)) vcos_log_impl(cat,_level,fmt); } while (0) +# define _VCOS_VLOG_X(cat, _level, fmt, ap) do { if (vcos_is_log_enabled(cat,_level)) vcos_vlog_impl(cat,_level,fmt,ap); } while (0) +# else +# define _VCOS_LOG_X(cat, _level, fmt...) (void)0 +# define _VCOS_VLOG_X(cat, _level, fmt, ap) (void)0 +# endif + + + +# define vcos_log_error(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__) +# define vcos_log_warn(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, __VA_ARGS__) +# define vcos_log_info(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) +# define vcos_log_trace(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, __VA_ARGS__) + +# define vcos_vlog_error(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, fmt, ap) +# define vcos_vlog_warn(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, fmt, ap) +# define vcos_vlog_info(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, fmt, ap) +# define vcos_vlog_trace(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, fmt, ap) + +# define vcos_logc_error(cat,...) _VCOS_LOG_X(cat, VCOS_LOG_ERROR, __VA_ARGS__) +# define vcos_logc_warn(cat,...) _VCOS_LOG_X(cat, VCOS_LOG_WARN, __VA_ARGS__) +# define vcos_logc_info(cat,...) _VCOS_LOG_X(cat, VCOS_LOG_INFO, __VA_ARGS__) +# define vcos_logc_trace(cat,...) _VCOS_LOG_X(cat, VCOS_LOG_TRACE, __VA_ARGS__) + +# define vcos_vlogc_error(cat,fmt,ap) _VCOS_VLOG_X(cat, VCOS_LOG_ERROR, fmt, ap) +# define vcos_vlogc_warn(cat,fmt,ap) _VCOS_VLOG_X(cat, VCOS_LOG_WARN, fmt, ap) +# define vcos_vlogc_info(cat,fmt,ap) _VCOS_VLOG_X(cat, VCOS_LOG_INFO, fmt, ap) +# define vcos_vlogc_trace(cat,fmt,ap) _VCOS_VLOG_X(cat, VCOS_LOG_TRACE, fmt, ap) + +# define vcos_log(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) +# define vcos_vlog(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt, ap) +# define VCOS_ALERT(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__) +# define VCOS_TRACE(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) + +/* + * MS Visual Studio - pre 2005 does not grok variadic macros + */ +#elif defined(_MSC_VER) + +# if _MSC_VER >= 1400 + +# if !defined(AMPUTATE_ALL_VCOS_LOGGING) && (!defined(NDEBUG) || defined(VCOS_ALWAYS_WANT_LOGGING)) +# define VCOS_LOGGING_ENABLED +# define _VCOS_LOG_X(cat, _level, fmt,...) do { if (vcos_is_log_enabled(cat,_level)) vcos_log_impl(cat, _level, fmt, __VA_ARGS__); } while (0) +# else +# define _VCOS_LOG_X(cat, _level, fmt,...) (void)0 +# endif + +# define vcos_log_error(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, fmt, __VA_ARGS__) +# define vcos_log_warn(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, fmt, __VA_ARGS__) +# define vcos_log_info(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, fmt, __VA_ARGS__) +# define vcos_log_trace(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, fmt, __VA_ARGS__) + +# define vcos_logc_error(cat,fmt,...) _VCOS_LOG_X(cat, VCOS_LOG_ERROR, fmt, __VA_ARGS__) +# define vcos_logc_warn(cat,fmt,...) _VCOS_LOG_X(cat, VCOS_LOG_WARN, fmt, __VA_ARGS__) +# define vcos_logc_info(cat,fmt,...) _VCOS_LOG_X(cat, VCOS_LOG_INFO, fmt, __VA_ARGS__) +# define vcos_logc_trace(cat,fmt,...) _VCOS_LOG_X(cat, VCOS_LOG_TRACE, fmt, __VA_ARGS__) + +# define vcos_log(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt, __VA_ARGS__) +# define VCOS_ALERT(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_ERROR, fmt, __VA_ARGS__) +# define VCOS_TRACE(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt, __VA_ARGS__) + +# else /* _MSC_VER >= 1400 */ + +/* do not define these */ + +# endif /* _MSC_VER >= 1400 */ + +#endif + +#if VCOS_HAVE_CMD + +#include "interface/vcos/vcos_cmd.h" + +/* + * These are the log sub-commands. They're exported here for user-mode apps which + * may want to call these, since the "log" command isn't registered for user-mode + * apps (vcdbg for example, has its own log command). + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param ); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_set_cmd( VCOS_CMD_PARAM_T *param ); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_status_cmd( VCOS_CMD_PARAM_T *param ); +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_test_cmd( VCOS_CMD_PARAM_T *param ); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* VCOS_LOGGING_H */ + + diff --git a/interface/vcos/vcos_logging_control.h b/interface/vcos/vcos_logging_control.h new file mode 100755 index 0000000..b180ad8 --- /dev/null +++ b/interface/vcos/vcos_logging_control.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + diff --git a/interface/vcos/vcos_lowlevel_thread.h b/interface/vcos/vcos_lowlevel_thread.h new file mode 100755 index 0000000..c6e3ced --- /dev/null +++ b/interface/vcos/vcos_lowlevel_thread.h @@ -0,0 +1,129 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - low level thread support +=============================================================================*/ + +#ifndef VCOS_LOWLEVEL_THREAD_H +#define VCOS_LOWLEVEL_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#ifndef VCOS_PLATFORM_H +#include "vcos.h" +#endif + +/** + * \file + * + * This defines a low level thread API that is supported by *some* operating systems + * and can be used to construct the regular "joinable thread" API on those operating + * systems. + * + * Most clients will not need to use this code. + * + * \sa vcos_joinable_thread.h + */ + +/** + * \brief Create a thread. + * + * This creates a thread which can be stopped either by returning from the + * entry point function or by calling vcos_llthread_exit from within the entry + * point function. The thread must be cleaned up by calling + * vcos_llthread_delete. vcos_llthread_delete may or may not terminate the + * thread. + * + * The preemptible parameter familiar from Nucleus is removed, as it is unused in + * VideoCore code. Affinity is added, since we do use this. + * + * @param thread Filled in with thread instance + * @param name An optional name for the thread. "" may be used (but + * a name will aid in debugging). + * @param entry Entry point + * @param arg A single argument passed to the entry point function + * @param stack Pointer to stack address + * @param stacksz Size of stack in bytes + * @param priority Priority of task, between VCOS_PRI_LOW and VCOS_PRI_HIGH + * @param affinity CPU affinity + * + * @sa vcos_llthread_terminate vcos_llthread_delete + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_llthread_create(VCOS_LLTHREAD_T *thread, + const char *name, + VCOS_LLTHREAD_ENTRY_FN_T entry, + void *arg, + void *stack, + VCOS_UNSIGNED stacksz, + VCOS_UNSIGNED priority, + VCOS_UNSIGNED affinity, + VCOS_UNSIGNED timeslice, + VCOS_UNSIGNED autostart); + +/** + * \brief Exits the current thread. + */ +VCOSPRE_ void VCOSPOST_ vcos_llthread_exit(void); + +/** + * \brief Delete a thread. This must be called to cleanup after + * vcos_llthread_create. This may or may not terminate the thread. + * It does not clean up any resources that may have been + * allocated by the thread. + */ +VCOSPRE_ void VCOSPOST_ vcos_llthread_delete(VCOS_LLTHREAD_T *thread); + +/** + * \brief Return current lowlevel thread pointer. + */ +VCOS_INLINE_DECL +VCOS_LLTHREAD_T *vcos_llthread_current(void); + +/** + * Resume a thread. + */ +VCOS_INLINE_DECL +void vcos_llthread_resume(VCOS_LLTHREAD_T *thread); + +VCOSPRE_ int VCOSPOST_ vcos_llthread_running(VCOS_LLTHREAD_T *thread); + +/** + * \brief Create a VCOS_LLTHREAD_T for the current thread. This is so we can + * have VCOS_LLTHREAD_Ts even for threads not originally created by VCOS (eg + * the thread that calls vcos_init). + */ +extern VCOS_STATUS_T _vcos_llthread_create_attach(VCOS_LLTHREAD_T *thread); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_mem.h b/interface/vcos/vcos_mem.h new file mode 100755 index 0000000..03720df --- /dev/null +++ b/interface/vcos/vcos_mem.h @@ -0,0 +1,101 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - memory support +=============================================================================*/ + +#ifndef VCOS_MEM_H +#define VCOS_MEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** \file + * + * Memory allocation api (malloc/free equivalents) is for benefit of host + * applications. VideoCore code should use rtos_XXX functions. + * + */ + + +/** Allocate memory + * + * @param size Size of memory to allocate + * @param description Description, to aid in debugging. May be ignored internally on some platforms. + */ +VCOS_INLINE_DECL +void *vcos_malloc(VCOS_UNSIGNED size, const char *description); + +void *vcos_kmalloc(VCOS_UNSIGNED size, const char *description); +void *vcos_kcalloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description); + +/** Allocate cleared memory + * + * @param num Number of items to allocate. + * @param size Size of each item in bytes. + * @param description Description, to aid in debugging. May be ignored internally on some platforms. + */ +VCOS_INLINE_DECL +void *vcos_calloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description); + +/** Free memory + * + * Free memory that has been allocated. + */ +VCOS_INLINE_DECL +void vcos_free(void *ptr); + +void vcos_kfree(void *ptr); + +/** Allocate aligned memory + * + * Allocate memory aligned on the specified boundary. + * + * @param size Size of memory to allocate + * @param description Description, to aid in debugging. May be ignored internally on some platforms. + */ +VCOS_INLINE_DECL +void *vcos_malloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description); + +/** Return the amount of free heap memory + * + */ +VCOS_INLINE_DECL +unsigned long vcos_get_free_mem(void); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/interface/vcos/vcos_mempool.h b/interface/vcos/vcos_mempool.h new file mode 100755 index 0000000..4fb9596 --- /dev/null +++ b/interface/vcos/vcos_mempool.h @@ -0,0 +1,109 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - memory pool support +=============================================================================*/ + +#ifndef VCOS_MEMPOOL_H +#define VCOS_MEMPOOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** \file + * + * Memory pools - variable sized allocator. + * + * A very basic memory pool API. + * + * This interface is deliberately not thread safe - clients should add + * their own locking, if required. + * + * + * \fixme: Add fixed-size allocator. + * + */ + + +/** Initialize a memory pool. The control data is taken from the memory + * supplied itself. + * + * Note: the dmalloc pool uses the memory supplied for its control + * area. This is probably a bit broken, as it stops you creating + * a pool in some "special" area of memory, while leaving the control + * information in normal memory. + * + * @param pool Pointer to pool object. + * + * @param name Name for the pool. Used for diagnostics. + * + * @param start Starting address. Must be at least 8byte aligned. + * + * @param size Size of pool in bytes. + * + * @return VCOS_SUCCESS if pool was created. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_mempool_create(VCOS_MEMPOOL_T *pool, const char *name, void *start, VCOS_UNSIGNED size); + +/** Allocate some memory from a pool. If no memory is available, it + * returns NULL. + * + * @param pool Pool to allocate from + * @param len Length of memory to allocate + * + */ +VCOS_INLINE_DECL +void *vcos_mempool_alloc(VCOS_MEMPOOL_T *pool, VCOS_UNSIGNED len); + +/** Free some memory back to a pool. + * + * @param pool Pool to return to + * @param mem Memory to return + */ +VCOS_INLINE_DECL +void vcos_mempool_free(VCOS_MEMPOOL_T *pool, void *mem); + +/** Deinitialize a memory pool. + * + * @param pool Pool to return to + */ +VCOS_INLINE_DECL +void vcos_mempool_delete(VCOS_MEMPOOL_T *pool); + +#ifdef __cplusplus +} +#endif +#endif + + + diff --git a/interface/vcos/vcos_msgqueue.h b/interface/vcos/vcos_msgqueue.h new file mode 100755 index 0000000..99144d0 --- /dev/null +++ b/interface/vcos/vcos_msgqueue.h @@ -0,0 +1,280 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VCOS - packet-like messages, based loosely on those found in TRIPOS. + +In the simple case, only the server thread creates a message queue, and +clients wait for replies on a semaphore. In more complex cases, clients can +also create message queues (not yet implemented). + +Although it's possible for a thread to create multiple queues and listen +on them in turn, if you find yourself doing this it's probably a bug. +=============================================================================*/ + +#ifndef VCOS_MSGQUEUE_H +#define VCOS_MSGQUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "vcos_types.h" +#include "vcos.h" +#include "vcos_blockpool.h" + +/** + * \file + * + * Packet-like messages, based loosely on those found in TRIPOS and + * derivatives thereof. + * + * A task can send a message *pointer* to another task, where it is + * queued on a linked list and the task woken up. The receiving task + * consumes all of the messages on its input queue, and optionally + * sends back replies using the original message memory. + * + * A caller can wait for the reply to a specific message - any other + * messages that arrive in the meantime are queued separately. + * + * + * All messages have a standard common layout, but the payload area can + * be used freely to extend this. + */ + +#define VCOS_MSGQ_MAGIC 0x5147534d + +/** Map the payload portion of a message to a structure pointer. + */ +#define VCOS_MSG_DATA(_msg) (void*)((_msg)->data) + +/** Standard message ids - FIXME - these need to be done properly! */ +#define VCOS_MSG_N_QUIT 1 +#define VCOS_MSG_N_OPEN 2 +#define VCOS_MSG_N_CLOSE 3 +#define VCOS_MSG_N_PRIVATE (1<<20) + +#define VCOS_MSG_REPLY_BIT (1<<31) + +/** Make gnuc compiler be happy about pointer punning */ +#ifdef __GNUC__ +#define __VCOS_MAY_ALIAS __attribute__((__may_alias__)) +#else +#define __VCOS_MAY_ALIAS +#endif + +struct VCOS_MSG_T; + +/* Replies go to one of these objects. + */ +typedef struct VCOS_MSG_WAITER_T +{ + /* When the reply is sent, this function gets called with the + * address of the waiter. + */ + void (*on_reply)(struct VCOS_MSG_WAITER_T *waiter, + struct VCOS_MSG_T *msg); +} VCOS_MSG_WAITER_T; + +/** A single message queue. + */ +typedef struct VCOS_MSGQUEUE_T +{ + VCOS_MSG_WAITER_T waiter; /**< So we can wait on a queue */ + struct VCOS_MSG_T *head; /**< head of linked list of messages waiting on this queue */ + struct VCOS_MSG_T *tail; /**< tail of message queue */ + VCOS_SEMAPHORE_T sem; /**< thread waits on this for new messages */ + VCOS_MUTEX_T lock; /**< locks the messages list */ + int attached; /**< Is this attached to a thread? */ +} VCOS_MSGQUEUE_T; + +/** A single message + */ +typedef struct VCOS_MSG_T +{ + uint32_t magic; /**< Sanity checking */ + uint32_t code; /**< message code */ + struct VCOS_MSG_T *next; /**< next in queue */ + VCOS_THREAD_T *src_thread; /**< for debug */ + struct VCOS_MSG_WAITER_T *waiter; /**< client waiter structure */ + struct VCOS_MSGQ_POOL_T *pool; /**< Pool allocated from, or NULL */ +} VCOS_MSG_T; + +#define MSG_REPLY_BIT (1<<31) + +/** Initialize a VCOS_MSG_T. Can also use vcos_msg_init(). + */ +#define VCOS_MSG_INITIALIZER {VCOS_MSGQ_MAGIC, 0, NULL, NULL, NULL, 0} + +/** A pool of messages. This contains its own waiter and + * semaphore, as well as a blockpool for the actual memory + * management. + * + * When messages are returned to the waiter, it posts the + * semaphore. + * + * When waiting for a message, we just wait on the semaphore. + * When allocating without waiting, we just try-wait on the + * semaphore. + * + * If we managed to claim the semaphore, then by definition + * there must be at least that many free messages in the + * blockpool. + */ +typedef struct VCOS_MSGQ_POOL_T +{ + VCOS_MSG_WAITER_T waiter; + VCOS_BLOCKPOOL_T blockpool; + VCOS_SEMAPHORE_T sem; + uint32_t magic; +} VCOS_MSGQ_POOL_T; + +/** Initialise the library. Normally called from vcos_init(). + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_msgq_init(void); + +/** De-initialise the library. Normally called from vcos_deinit(). + */ +VCOSPRE_ void VCOSPOST_ vcos_msgq_deinit(void); + +/** Send a message. + * + * @param dest Destination message queue + * @param code Message code. + * @param msg Pointer to message to send. Must not go out of scope before + * message is received (do not declare on the stack). + */ +VCOSPRE_ void VCOSPOST_ vcos_msg_send(VCOS_MSGQUEUE_T *dest, uint32_t code, VCOS_MSG_T *msg); + +/** Send a message and wait for a reply. + * + * @param dest Destination message queue + * @param code Message code. + * @param msg Pointer to message to send. May be declared on the stack. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_msg_sendwait(VCOS_MSGQUEUE_T *queue, uint32_t code, VCOS_MSG_T *msg); + +/** Wait for a message on a queue. + */ +VCOSPRE_ VCOS_MSG_T * VCOSPOST_ vcos_msg_wait(VCOS_MSGQUEUE_T *queue); + +/** Peek for a message on this thread's endpoint. If a message is not + * available, NULL is returned. If a message is available it will be + * removed from the endpoint and returned. + */ +VCOSPRE_ VCOS_MSG_T * VCOSPOST_ vcos_msg_peek(VCOS_MSGQUEUE_T *queue); + +/** Send a reply to a message + */ +VCOSPRE_ void VCOSPOST_ vcos_msg_reply(VCOS_MSG_T *msg); + +/** Set the reply queue for a message. When the message is replied-to, it + * will return to the given queue. + * + * @param msg Message + * @param queue Message queue the message should return to + */ +VCOSPRE_ void VCOSPOST_ vcos_msg_set_source(VCOS_MSG_T *msg, VCOS_MSGQUEUE_T *queue); + +/** Initialise a newly allocated message. This only needs to be called + * for messages allocated on the stack, heap or statically. It is not + * needed for messages allocated from a pool. + */ +VCOSPRE_ void VCOSPOST_ vcos_msg_init(VCOS_MSG_T *msg); + +/** Create a message queue to wait on. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_msgq_create(VCOS_MSGQUEUE_T *queue, const char *name); + +/** Destroy a queue + */ +VCOSPRE_ void VCOSPOST_ vcos_msgq_delete(VCOS_MSGQUEUE_T *queue); + +/* + * Message pools + */ + +/** Create a pool of messages. Messages can be allocated from the pool and + * sent to a message queue. Replying to the message will automatically + * free it back to the pool. + * + * The pool is threadsafe. + * + * @param count number of messages in the pool + * @param payload_size maximum message payload size, not including MSG_T. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_msgq_pool_create( + VCOS_MSGQ_POOL_T *pool, + size_t count, + size_t payload_size, + const char *name); + +/** Destroy a message pool. + */ +VCOSPRE_ void VCOSPOST_ vcos_msgq_pool_delete(VCOS_MSGQ_POOL_T *pool); + +/** Allocate a message from a message pool. + * + * Note: + * + * If the alloc fails (returns NULL) then your worker thread has stopped + * servicing requests or your pool is too small for the latency in + * the system. Your best bet to handle this is to fail the call that + * needs to send the message. + * + * The returned message payload area is initialised to zero. + * + * @param pool Pool to allocate from. + * @return Message or NULL if pool exhausted. + */ +VCOSPRE_ VCOS_MSG_T *VCOSPOST_ vcos_msgq_pool_alloc(VCOS_MSGQ_POOL_T *pool); + +/** Wait for a message from a message pool. Waits until a + * message is available in the pool and then allocates it. If + * one is already available, returns immediately. + * + * This call can never fail. + * + * The returned message payload area is initialised to zero. + * + * @param pool Pool to allocate from. + * @return Message + */ +VCOSPRE_ VCOS_MSG_T *VCOSPOST_ vcos_msgq_pool_wait(VCOS_MSGQ_POOL_T *pool); + +/** Explicitly free a message and return it to its pool. + * + * @param msg Message to free. No-op if NULL. + */ +VCOSPRE_ void VCOSPOST_ vcos_msgq_pool_free(VCOS_MSG_T *msg); + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/interface/vcos/vcos_mutex.h b/interface/vcos/vcos_mutex.h new file mode 100755 index 0000000..4825074 --- /dev/null +++ b/interface/vcos/vcos_mutex.h @@ -0,0 +1,112 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - mutex public header file +=============================================================================*/ + +#ifndef VCOS_MUTEX_H +#define VCOS_MUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file vcos_mutex.h + * + * Mutex API. Mutexes are not re-entrant, as supporting this adds extra code + * that slows down clients which have been written sensibly. + * + * \sa vcos_reentrant_mutex.h + * + */ + +/** Create a mutex. + * + * @param m Filled in with mutex on return + * @param name A non-null name for the mutex, used for diagnostics. + * + * @return VCOS_SUCCESS if mutex was created, or error code. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *m, const char *name); + +/** Delete the mutex. + */ +VCOS_INLINE_DECL +void vcos_mutex_delete(VCOS_MUTEX_T *m); + +/** + * \brief Wait to claim the mutex. + * + * On most platforms this always returns VCOS_SUCCESS, and so would ideally be + * a void function, however some platforms allow a wait to be interrupted so + * it remains non-void. + * + * Try to obtain the mutex. + * @param m Mutex to wait on + * @return VCOS_SUCCESS - mutex was taken. + * VCOS_EAGAIN - could not take mutex. + */ +#ifndef vcos_mutex_lock +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *m); + +/** Release the mutex. + */ +VCOS_INLINE_DECL +void vcos_mutex_unlock(VCOS_MUTEX_T *m); +#endif + +/** Test if the mutex is already locked. + * + * @return 1 if mutex is locked, 0 if it is unlocked. + */ +VCOS_INLINE_DECL +int vcos_mutex_is_locked(VCOS_MUTEX_T *m); + +/** Obtain the mutex if possible. + * + * @param m the mutex to try to obtain + * + * @return VCOS_SUCCESS if mutex is successfully obtained, or VCOS_EAGAIN + * if it is already in use by another thread. + */ +#ifndef vcos_mutex_trylock +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m); +#endif + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_named_semaphore.h b/interface/vcos/vcos_named_semaphore.h new file mode 100755 index 0000000..a477577 --- /dev/null +++ b/interface/vcos/vcos_named_semaphore.h @@ -0,0 +1,113 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - named semaphores +=============================================================================*/ + +#ifndef VCOS_NAMED_SEMAPHORE_H +#define VCOS_NAMED_SEMAPHORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file + * + * Create a named semaphore, or open an existing one by name. + * + */ + +/** + * \brief Create a named semaphore. + * + * Semaphores are not re-entrant. + * + * @param sem Pointer to memory to be initialized + * @param name A name for this semaphore. + * @param count The initial count for the semaphore. + * + * @return VCOS_SUCCESS if the semaphore was created. + * + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count); + +/** + * \brief Wait on a named semaphore. + * + * There is no timeout option on a semaphore, as adding this will slow down + * implementations on some platforms. If you need that kind of behaviour, use + * an event group. + * + * This always returns VCOS_SUCCESS and so should really be a void function. However + * too many lines of code would need to be changed in non-trivial ways, so for now + * it is non-void. + * + * @param sem Semaphore to wait on + * @return VCOS_SUCCESS - semaphore was taken. + * + */ +VCOS_INLINE_DECL +void vcos_named_semaphore_wait(VCOS_NAMED_SEMAPHORE_T *sem); + +/** + * \brief Try to wait for a semaphore. + * + * Try to obtain the semaphore. If it is already taken, return VCOS_TIMEOUT. + * @param sem Semaphore to wait on + * @return VCOS_SUCCESS - semaphore was taken. + * VCOS_EAGAIN - could not take semaphore + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_named_semaphore_trywait(VCOS_NAMED_SEMAPHORE_T *sem); + +/** + * \brief Post a semaphore. + * + * @param sem Semaphore to wait on + */ +VCOS_INLINE_DECL +void vcos_named_semaphore_post(VCOS_NAMED_SEMAPHORE_T *sem); + +/** + * \brief Delete a semaphore, releasing any resources consumed by it. + * + * @param sem Semaphore to wait on + */ +void vcos_named_semaphore_delete(VCOS_NAMED_SEMAPHORE_T *sem); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_once.h b/interface/vcos/vcos_once.h new file mode 100755 index 0000000..f5d82c1 --- /dev/null +++ b/interface/vcos/vcos_once.h @@ -0,0 +1,62 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - 'once' +=============================================================================*/ + +#ifndef VCOS_ONCE_H +#define VCOS_ONCE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file vcos_once.h + * + * Ensure something is called only once. + * + * Initialize once_control to VCOS_ONCE_INIT. The first + * time this is called, the init_routine will be called. Thereafter + * it won't. + * + * \sa pthread_once() + * + */ + +VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control, + void (*init_routine)(void)); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_queue.h b/interface/vcos/vcos_queue.h new file mode 100755 index 0000000..7515860 --- /dev/null +++ b/interface/vcos/vcos_queue.h @@ -0,0 +1,105 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - Queue public header file +=============================================================================*/ + +#ifndef VCOS_QUEUE_H +#define VCOS_QUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** \file vcos_queue.h + * + * API for accessing a fixed length queue. + * + * Nucleus offers variable length items, but this feature is not used + * in the current code base, so is withdrawn to simplify the API. + */ + +/** Create a fixed length queue. + * + * @param queue Pointer to queue control block + * @param name Name of queue + * @param message_size Size of each queue message item in words (words are sizeof VCOS_UNSIGNED). + * @param queue_start Start address of queue area + * @param queue_size Size in words (words are sizeof VCOS_UNSIGNED) of queue + * + */ + +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_queue_create(VCOS_QUEUE_T *queue, + const char *name, + VCOS_UNSIGNED message_size, + void *queue_start, + VCOS_UNSIGNED queue_size); + +/** Delete a queue. + * @param queue The queue to delete + */ +VCOS_INLINE_DECL +void vcos_queue_delete(VCOS_QUEUE_T *queue); + +/** Send an item to a queue. If there is no space, the call with + * either block waiting for space, or return an error, depending + * on the value of the wait parameter. + * + * @param queue The queue to send to + * @param src The data to send (length set when queue was created) + * @param wait Whether to wait for space (VCOS_SUSPEND) or fail if + * no space (VCOS_NO_SUSPEND). + * + * @return If space available, returns VCOS_SUCCESS. Otherwise returns + * VCOS_EAGAIN if no space available before timeout expires. + * + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_queue_send(VCOS_QUEUE_T *queue, const void *src, VCOS_UNSIGNED wait); + +/** Receive an item from a queue. + * @param queue The queue to receive from + * @param dst Where to write the data to + * @param wait Whether to wait (VCOS_SUSPEND) or fail if + * empty (VCOS_NO_SUSPEND). + * + * @return If an item is available, returns VCOS_SUCCESS. Otherwise returns + * VCOS_EAGAIN if no item available before timeout expires. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_queue_receive(VCOS_QUEUE_T *queue, void *dst, VCOS_UNSIGNED wait); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_quickslow_mutex.h b/interface/vcos/vcos_quickslow_mutex.h new file mode 100755 index 0000000..2a4465d --- /dev/null +++ b/interface/vcos/vcos_quickslow_mutex.h @@ -0,0 +1,101 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - mutex public header file +=============================================================================*/ + +#ifndef VCOS_QUICKSLOW_MUTEX_H +#define VCOS_QUICKSLOW_MUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file vcos_quickslow_mutex.h + * + * "Quick/Slow" Mutex API. This is a mutex which supports an additional "quick" + * (spinlock-based) locking mechanism. While in this quick locked state, other + * operating system commands will be unavailable and the caller should complete + * whatever it has to do in a short, bounded length of time (as the spinlock + * completely locks out other system activity). + * + * \sa vcos_mutex.h + * + */ + +/** Create a mutex. + * + * @param m Filled in with mutex on return + * @param name A non-null name for the mutex, used for diagnostics. + * + * @return VCOS_SUCCESS if mutex was created, or error code. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_quickslow_mutex_create(VCOS_QUICKSLOW_MUTEX_T *m, const char *name); + +/** Delete the mutex. + */ +VCOS_INLINE_DECL +void vcos_quickslow_mutex_delete(VCOS_QUICKSLOW_MUTEX_T *m); + +/** + * \brief Wait to claim the mutex ("slow" mode). + * + * Obtain the mutex. + */ +VCOS_INLINE_DECL +void vcos_quickslow_mutex_lock(VCOS_QUICKSLOW_MUTEX_T *m); + +/** Release the mutex ("slow" mode). + */ +VCOS_INLINE_DECL +void vcos_quickslow_mutex_unlock(VCOS_QUICKSLOW_MUTEX_T *m); + +/** + * \brief Wait to claim the mutex ("quick" mode). + * + * Obtain the mutex. The caller must not call any OS functions or do anything + * "slow" before the corresponding call to vcos_mutex_quickslow_unlock_quick. + */ +VCOS_INLINE_DECL +void vcos_quickslow_mutex_lock_quick(VCOS_QUICKSLOW_MUTEX_T *m); + +/** Release the mutex ("quick" mode). + */ +VCOS_INLINE_DECL +void vcos_quickslow_mutex_unlock_quick(VCOS_QUICKSLOW_MUTEX_T *m); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_reentrant_mutex.h b/interface/vcos/vcos_reentrant_mutex.h new file mode 100755 index 0000000..69777d8 --- /dev/null +++ b/interface/vcos/vcos_reentrant_mutex.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - reentrant mutex public header file +=============================================================================*/ + +#ifndef VCOS_REENTRANT_MUTEX_H +#define VCOS_REENTRANT_MUTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file + * + * Reentrant Mutex API. You can take one of these mutexes even if you've already + * taken it. Just to make sure. + * + * A re-entrant mutex may be slower on some platforms than a regular one. + * + * \sa vcos_mutex.h + * + */ + +/** Create a mutex. + * + * @param m Filled in with mutex on return + * @param name A non-null name for the mutex, used for diagnostics. + * + * @return VCOS_SUCCESS if mutex was created, or error code. + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name); + +/** Delete the mutex. + */ +VCOS_INLINE_DECL +void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m); + +/** Wait to claim the mutex. Must not have already been claimed by the current thread. + */ +#ifndef vcos_reentrant_mutexlock +VCOS_INLINE_DECL +void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m); + +/** Release the mutex. + */ +VCOS_INLINE_DECL +void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m); +#endif + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_semaphore.h b/interface/vcos/vcos_semaphore.h new file mode 100755 index 0000000..839faac --- /dev/null +++ b/interface/vcos/vcos_semaphore.h @@ -0,0 +1,158 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +#ifndef VCOS_SEMAPHORE_H +#define VCOS_SEMAPHORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#ifndef VCOS_PLATFORM_H +#include "vcos.h" +#endif + +/** + * \file vcos_semaphore.h + * + * \section sem Semaphores + * + * This provides counting semaphores. Semaphores are not re-entrant. On sensible + * operating systems a semaphore can always be posted but can only be taken in + * thread (not interrupt) context. Under Nucleus, a LISR cannot post a semaphore, + * although it would not be hard to lift this restriction. + * + * \subsection timeout Timeout + * + * On both Nucleus and ThreadX a semaphore can be taken with a timeout. This is + * not supported by VCOS because it makes the non-timeout code considerably more + * complicated (and hence slower). In the unlikely event that you need a timeout + * with a semaphore, and you cannot simply redesign your code to avoid it, use + * an event flag (vcos_event_flags.h). + * + * \subsection sem_nucleus Changes from Nucleus: + * + * Semaphores are always "FIFO" - i.e. sleeping threads are woken in FIFO order. That's + * because: + * \arg there's no support for NU_PRIORITY in threadx (though it can be emulated, slowly) + * \arg we don't appear to actually consciously use it - for example, Dispmanx uses + * it, but all threads waiting are the same priority. + * + */ + +/** + * \brief Create a semaphore. + * + * Create a semaphore. + * + * @param sem Pointer to memory to be initialized + * @param name A name for this semaphore. The name may be truncated internally. + * @param count The initial count for the semaphore. + * + * @return VCOS_SUCCESS if the semaphore was created. + * + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_semaphore_create(VCOS_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count); + +/** + * \brief Wait on a semaphore. + * + * There is no timeout option on a semaphore, as adding this will slow down + * implementations on some platforms. If you need that kind of behaviour, use + * an event group. + * + * On most platforms this always returns VCOS_SUCCESS, and so would ideally be + * a void function, however some platforms allow a wait to be interrupted so + * it remains non-void. + * + * @param sem Semaphore to wait on + * @return VCOS_SUCCESS - semaphore was taken. + * VCOS_EAGAIN - could not take semaphore + * + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_semaphore_wait(VCOS_SEMAPHORE_T *sem); + +/** + * \brief Wait on a semaphore with a timeout. + * + * Note that this function may not be implemented on all + * platforms, and may not be efficient on all platforms + * (see comment in vcos_semaphore_wait) + * + * Try to obtain the semaphore. If it is already taken, return + * VCOS_EAGAIN. + * @param sem Semaphore to wait on + * @param timeout Number of milliseconds to wait before + * returning if the semaphore can't be acquired. + * @return VCOS_SUCCESS - semaphore was taken. + * VCOS_EAGAIN - could not take semaphore (i.e. timeout + * expired) + * VCOS_EINVAL - Some other error (most likely bad + * parameters). + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_semaphore_wait_timeout(VCOS_SEMAPHORE_T *sem, VCOS_UNSIGNED timeout); + +/** + * \brief Try to wait for a semaphore. + * + * Try to obtain the semaphore. If it is already taken, return VCOS_TIMEOUT. + * @param sem Semaphore to wait on + * @return VCOS_SUCCESS - semaphore was taken. + * VCOS_EAGAIN - could not take semaphore + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_semaphore_trywait(VCOS_SEMAPHORE_T *sem); + +/** + * \brief Post a semaphore. + * + * @param sem Semaphore to wait on + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_semaphore_post(VCOS_SEMAPHORE_T *sem); + +/** + * \brief Delete a semaphore, releasing any resources consumed by it. + * + * @param sem Semaphore to wait on + */ +VCOS_INLINE_DECL +void vcos_semaphore_delete(VCOS_SEMAPHORE_T *sem); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/interface/vcos/vcos_stdbool.h b/interface/vcos/vcos_stdbool.h new file mode 100755 index 0000000..25b0e09 --- /dev/null +++ b/interface/vcos/vcos_stdbool.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VCOS_STDBOOL_H +#define VCOS_STDBOOL_H + +#ifndef __cplusplus + +#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L) +#include +#else +/* sizeof(bool) == 1. hopefully this matches up with c++ (so structures and + * such containing bool are binary compatible), but the c++ standard doesn't + * require sizeof(bool) == 1, so there's no guarantee */ +typedef unsigned char bool; +enum { + false, + true +}; +#endif + +#endif /* __cplusplus */ + +#endif diff --git a/interface/vcos/vcos_stdint.h b/interface/vcos/vcos_stdint.h new file mode 100755 index 0000000..6b5851a --- /dev/null +++ b/interface/vcos/vcos_stdint.h @@ -0,0 +1,107 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCOS_STDINT_H +#define VCOS_STDINT_H + +/** \file + * Attempt to provide the types defined in stdint.h. + * + * Except for use with lcc, this simply includes stdint.h, which should find + * the system/toolchain version if present, otherwise falling back to the + * version in interface/vcos/. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (VCMODS_LCC) + +#include + +typedef signed char int8_t; +typedef unsigned char uint8_t; + +typedef signed short int16_t; +typedef unsigned short uint16_t; + +typedef signed long int32_t; +typedef unsigned long uint32_t; + +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; + +typedef int32_t intmax_t; +typedef uint32_t uintmax_t; + +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; + +#define INT8_MIN SCHAR_MIN +#define INT8_MAX SCHAR_MAX +#define UINT8_MAX UCHAR_MAX + +#define INT16_MIN SHRT_MIN +#define INT16_MAX SHRT_MAX +#define UINT16_MAX USHRT_MAX + +#define INT32_MIN LONG_MIN +#define INT32_MAX LONG_MAX +#define UINT32_MAX ULONG_MAX + +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX + +#define INTMAX_MIN INT32_MIN +#define INTMAX_MAX INT32_MAX +#define UINTMAX_MAX UINT32_MAX + +/* N.B. 64-bit integer types are not currently supported by lcc. + * However, these symbols are referenced in header files included by files + * compiled by lcc for VCE, so removing them would break the build. + * The solution here then is to define them, as the correct size, but in a + * way that should make them unusable in normal arithmetic operations. + */ +typedef struct { uint32_t a; uint32_t b; } int64_t; +typedef struct { uint32_t a; uint32_t b; } uint64_t; + +#else + +#include + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* VCOS_STDINT_H */ diff --git a/interface/vcos/vcos_string.h b/interface/vcos/vcos_string.h new file mode 100755 index 0000000..7a6b938 --- /dev/null +++ b/interface/vcos/vcos_string.h @@ -0,0 +1,129 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +#ifndef VCOS_STRING_H +#define VCOS_STRING_H + +/** + * \file + * + * String functions. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +/** Case insensitive string comparison. + * + */ + +VCOS_INLINE_DECL +int vcos_strcasecmp(const char *s1, const char *s2); + +VCOS_INLINE_DECL +int vcos_strncasecmp(const char *s1, const char *s2, size_t n); + +VCOSPRE_ int VCOSPOST_ vcos_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap); + +VCOSPRE_ int VCOSPOST_ vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...); + +/** Like vsnprintf, except it places the output at the specified offset. + * Output is truncated to fit in buflen bytes, and is guaranteed to be NUL-terminated. + * Returns the string length before/without truncation. + */ +VCOSPRE_ size_t VCOSPOST_ vcos_safe_vsprintf(char *buf, size_t buflen, size_t offset, const char *fmt, va_list ap); + +#define VCOS_SAFE_VSPRINTF(buf, offset, fmt, ap) \ + vcos_safe_vsprintf(buf, sizeof(buf) + ((char (*)[sizeof(buf)])buf - &(buf)), offset, fmt, ap) + +/** Like snprintf, except it places the output at the specified offset. + * Output is truncated to fit in buflen bytes, and is guaranteed to be NUL-terminated. + * Returns the string length before/without truncation. + */ +VCOSPRE_ size_t VCOSPOST_ vcos_safe_sprintf(char *buf, size_t buflen, size_t offset, const char *fmt, ...); + +/* The Metaware compiler currently has a bug in its variadic macro handling which + causes it to append a spurious command to the end of its __VA_ARGS__ data. + Do not use until this has been fixed (and this comment has been deleted). */ + +#define VCOS_SAFE_SPRINTF(buf, offset, ...) \ + vcos_safe_sprintf(buf, sizeof(buf) + ((char (*)[sizeof(buf)])buf - &(buf)), offset, __VA_ARGS__) + +/** Copies string src to dst at the specified offset. + * Output is truncated to fit in dstlen bytes, i.e. the string is at most + * (buflen - 1) characters long. Unlike strncpy, exactly one NUL is written + * to dst, which is always NUL-terminated. + * Returns the string length before/without truncation. + */ +VCOSPRE_ size_t VCOSPOST_ vcos_safe_strcpy(char *dst, const char *src, size_t dstlen, size_t offset); + +#define VCOS_SAFE_STRCPY(dst, src, offset) \ + vcos_safe_strcpy(dst, src, sizeof(dst) + ((char (*)[sizeof(dst)])dst - &(dst)), offset) + +VCOS_STATIC_INLINE +int vcos_strlen(const char *s) { return (int)strlen(s); } + +VCOS_STATIC_INLINE +int vcos_strcmp(const char *s1, const char *s2) { return strcmp(s1,s2); } + +VCOS_STATIC_INLINE +int vcos_strncmp(const char *cs, const char *ct, size_t count) { return strncmp(cs, ct, count); } + +VCOS_STATIC_INLINE +char *vcos_strcpy(char *dst, const char *src) { return strcpy(dst, src); } + +VCOS_STATIC_INLINE +char *vcos_strncpy(char *dst, const char *src, size_t count) { return strncpy(dst, src, count); } + +VCOS_STATIC_INLINE +void *vcos_memcpy(void *dst, const void *src, size_t n) { memcpy(dst, src, n); return dst; } + +VCOS_STATIC_INLINE +void *vcos_memset(void *p, int c, size_t n) { return memset(p, c, n); } + +VCOS_STATIC_INLINE +int vcos_memcmp(const void *ptr1, const void *ptr2, size_t count) { return memcmp(ptr1, ptr2, count); } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_thread.h b/interface/vcos/vcos_thread.h new file mode 100755 index 0000000..e43d591 --- /dev/null +++ b/interface/vcos/vcos_thread.h @@ -0,0 +1,282 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - public header file +=============================================================================*/ + +#ifndef VCOS_THREAD_H +#define VCOS_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + +/** + * \file vcos_thread.h + * + * \section thread Threads + * + * Under Nucleus, a thread is created by NU_Create_Task, passing in the stack + * and various other parameters. To stop the thread, NU_Terminate_Thread() and + * NU_Delete_Thread() are called. + * + * Unfortunately it's not possible to emulate this API under some fairly common + * operating systems. Under Windows you can't pass in the stack, and you can't + * safely terminate a thread. + * + * Therefore, an API which is similar to the pthreads API is used instead. This + * API can (mostly) be emulated under all interesting operating systems. + * + * Obviously this makes the code somewhat more complicated on VideoCore than it + * would otherwise be - we end up with an extra mutex per thread, and some code + * that waits for it. The benefit is that we have a single way of creating + * threads that works consistently on all platforms (apart from stack supplying). + * + * \subsection stack Stack + * + * It's still not possible to pass in the stack address, but this can be made + * much more obvious in the API: the relevant function is missing and the + * CPP symbol VCOS_CAN_SET_STACK_ADDR is zero rather than one. + * + * \subsection thr_create Creating a thread + * + * The simplest way to create a thread is with vcos_thread_create() passing in a + * NULL thread parameter argument. To wait for the thread to exit, call + * vcos_thread_join(). + * + * \subsection back Backward compatibility + * + * To ease migration, a "classic" thread creation API is provided for code + * that used to make use of Nucleus, vcos_thread_create_classic(). The + * arguments are not exactly the same, as the PREEMPT parameter is dropped. + * + */ + +#define VCOS_AFFINITY_CPU0 _VCOS_AFFINITY_CPU0 +#define VCOS_AFFINITY_CPU1 _VCOS_AFFINITY_CPU1 +#define VCOS_AFFINITY_MASK _VCOS_AFFINITY_MASK +#define VCOS_AFFINITY_DEFAULT _VCOS_AFFINITY_DEFAULT +#define VCOS_AFFINITY_THISCPU _VCOS_AFFINITY_THISCPU + +/** Report whether or not we have an RTOS at all, and hence the ability to + * create threads. + */ +VCOSPRE_ int VCOSPOST_ vcos_have_rtos(void); + +/** Create a thread. It must be cleaned up by calling vcos_thread_join(). + * + * @param thread Filled in on return with thread + * @param name A name for the thread. May be the empty string. + * @param attrs Attributes; default attributes will be used if this is NULL. + * @param entry Entry point. + * @param arg Argument passed to the entry point. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create(VCOS_THREAD_T *thread, + const char *name, + VCOS_THREAD_ATTR_T *attrs, + VCOS_THREAD_ENTRY_FN_T entry, + void *arg); + +/** Exit the thread from within the thread function itself. + * Resources must still be cleaned up via a call to thread_join(). + * + * The thread can also be terminated by simply exiting the thread function. + * + * @param data Data passed to thread_join. May be NULL. + */ +VCOSPRE_ void VCOSPOST_ vcos_thread_exit(void *data); + +/** Wait for a thread to terminate and then clean up its resources. + * + * @param thread Thread to wait for + * @param pData Updated to point at data provided in vcos_thread_exit or exit + * code of thread function. + */ +VCOSPRE_ void VCOSPOST_ vcos_thread_join(VCOS_THREAD_T *thread, + void **pData); + + +/** + * \brief Create a thread using an API similar to the one "traditionally" + * used under Nucleus. + * + * This creates a thread which must be cleaned up by calling vcos_thread_join(). + * The thread cannot be simply terminated (as in Nucleus and ThreadX) as thread + * termination is not universally supported. + * + * @param thread Filled in with thread instance + * @param name An optional name for the thread. NULL or "" may be used (but + * a name will aid in debugging). + * @param entry Entry point + * @param arg A single argument passed to the entry point function + * @param stack Pointer to stack address + * @param stacksz Size of stack in bytes + * @param priaff Priority of task, between VCOS_PRI_LOW and VCOS_PRI_HIGH, ORed with the CPU affinity + * @param autostart If non-zero the thread will start immediately. + * @param timeslice Timeslice (system ticks) for this thread. + * + * @sa vcos_thread_terminate vcos_thread_delete + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create_classic(VCOS_THREAD_T *thread, + const char *name, + void *(*entry)(void *arg), + void *arg, + void *stack, + VCOS_UNSIGNED stacksz, + VCOS_UNSIGNED priaff, + VCOS_UNSIGNED timeslice, + VCOS_UNSIGNED autostart); + +/** + * \brief Set a thread's priority + * + * Set the priority for a thread. + * + * @param thread The thread + * @param pri Thread priority in VCOS_PRI_MASK bits; affinity in VCOS_AFFINITY_MASK bits. + */ +VCOS_INLINE_DECL +void vcos_thread_set_priority(VCOS_THREAD_T *thread, VCOS_UNSIGNED pri); + +/** + * \brief Return the currently executing thread. + * + */ +VCOS_INLINE_DECL +VCOS_THREAD_T *vcos_thread_current(void); + +/** + * \brief Return the thread's priority. + */ +VCOS_INLINE_DECL +VCOS_UNSIGNED vcos_thread_get_priority(VCOS_THREAD_T *thread); + +/** + * \brief Return the thread's cpu affinity. + */ +VCOS_INLINE_DECL +VCOS_UNSIGNED vcos_thread_get_affinity(VCOS_THREAD_T *thread); + +/** + * \brief Set the thread's cpu affinity. + */ + +VCOS_INLINE_DECL +void vcos_thread_set_affinity(VCOS_THREAD_T *thread, VCOS_UNSIGNED affinity); + +/** + * \brief Query whether we are in an interrupt. + * + * @return 1 if in interrupt context. + */ +VCOS_INLINE_DECL +int vcos_in_interrupt(void); + +/** + * \brief Sleep a while. + * + * @param ms Number of milliseconds to sleep for + * + * This may actually sleep a whole number of ticks. + */ +VCOS_INLINE_DECL +void vcos_sleep(uint32_t ms); + +/** + * \brief Return the value of the hardware microsecond counter. + * + */ +VCOS_INLINE_DECL +uint32_t vcos_getmicrosecs(void); + +VCOS_INLINE_DECL +uint64_t vcos_getmicrosecs64(void); + +#define vcos_get_ms() (vcos_getmicrosecs()/1000) + +/** + * \brief Return a unique identifier for the current process + * + */ +VCOS_INLINE_DECL +VCOS_UNSIGNED vcos_process_id_current(void); + +/** Relinquish this time slice. */ +VCOS_INLINE_DECL +void vcos_thread_relinquish(void); + +/** Return the name of the given thread. + */ +VCOSPRE_ const char * VCOSPOST_ vcos_thread_get_name(const VCOS_THREAD_T *thread); + +/** Change preemption. This is almost certainly not what you want, as it won't + * work reliably in a multicore system: although you can affect the preemption + * on *this* core, you won't affect what's happening on the other core(s). + * + * It's mainly here to ease migration. If you're using it in new code, you + * probably need to think again. + * + * @param pe New preemption, VCOS_PREEMPT or VCOS_NO_PREEMPT + * @return Old value of preemption. + */ +VCOS_INLINE_DECL +VCOS_UNSIGNED vcos_change_preemption(VCOS_UNSIGNED pe); + +/** Is a thread still running, or has it exited? + * + * Note: this exists for some fairly scary code in the video codec tests. Don't + * try to use it for anything else, as it may well not do what you expect. + * + * @param thread thread to query + * @return non-zero if thread is running, or zero if it has exited. + */ +VCOS_INLINE_DECL +int vcos_thread_running(VCOS_THREAD_T *thread); + +/** Resume a thread. + * + * @param thread thread to resume + */ +VCOS_INLINE_DECL +void vcos_thread_resume(VCOS_THREAD_T *thread); + +/* + * Internal APIs - may not always be present and should not be used in + * client code. + */ + +extern void _vcos_task_timer_set(void (*pfn)(void*), void *, VCOS_UNSIGNED ms); +extern void _vcos_task_timer_cancel(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_thread_attr.h b/interface/vcos/vcos_thread_attr.h new file mode 100755 index 0000000..9eddd69 --- /dev/null +++ b/interface/vcos/vcos_thread_attr.h @@ -0,0 +1,96 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - thread attributes +=============================================================================*/ + +#ifndef VCOS_THREAD_ATTR_H +#define VCOS_THREAD_ATTR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * + * Attributes for thread creation. + * + */ + +/** Initialize thread attribute struct. This call does not allocate memory, + * and so cannot fail. + * + */ +VCOSPRE_ void VCOSPOST_ vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs); + +/** Set the stack address and size. If not set, a stack will be allocated automatically. + * + * This can only be set on some platforms. It will always be possible to set the stack + * address on VideoCore, but on host platforms, support may well not be available. + */ +#if VCOS_CAN_SET_STACK_ADDR +VCOS_INLINE_DECL +void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attrs, void *addr, VCOS_UNSIGNED sz); +#endif + +/** Set the stack size. If not set, a default size will be used. Attempting to call this after having + * set the stack location with vcos_thread_attr_setstack() will result in undefined behaviour. + */ +VCOS_INLINE_DECL +void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED sz); + +/** Set the task priority. If not set, a default value will be used. + */ +VCOS_INLINE_DECL +void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED pri); + +/** Set the task cpu affinity. If not set, the default will be used. + */ +VCOS_INLINE_DECL +void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED aff); + +/** Set the timeslice. If not set the default will be used. + */ +VCOS_INLINE_DECL +void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts); + +/** The thread entry function takes (argc,argv), as per Nucleus, with + * argc being 0. This may be withdrawn in a future release and should not + * be used in new code. + */ +VCOS_INLINE_DECL +void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy); + +VCOS_INLINE_DECL +void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_timer.h b/interface/vcos/vcos_timer.h new file mode 100755 index 0000000..d61391c --- /dev/null +++ b/interface/vcos/vcos_timer.h @@ -0,0 +1,117 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - timer support +=============================================================================*/ + +#ifndef VCOS_TIMER_H +#define VCOS_TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#ifndef VCOS_PLATFORM_H +#include "vcos.h" +#endif + +/** \file vcos_timer.h + * + * Timers are single shot. + * + * Timer times are in milliseconds. + * + * \note that timer callback functions are called from an arbitrary thread + * context. The expiration function should do its work as quickly as possible; + * blocking should be avoided. + * + * \note On Windows, the separate function vcos_timer_init() must be called + * as timer initialization from DllMain is not possible. + */ + +/** Perform timer subsystem initialization. This function is not needed + * on non-Windows platforms but is still present so that it can be + * called. On Windows it is needed because vcos_init() gets called + * from DLL initialization where it is not possible to create a + * time queue (deadlock occurs if you try). + * + * @return VCOS_SUCCESS on success. VCOS_EEXIST if this has already been called + * once. VCOS_ENOMEM if resource allocation failed. + */ +VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_timer_init(void); + +/** Create a timer in a disabled state. + * + * The timer is initially disabled. + * + * @param timer timer handle + * @param name name for timer + * @param expiration_routine function to call when timer expires + * @param context context passed to expiration routine + * + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_timer_create(VCOS_TIMER_T *timer, + const char *name, + void (*expiration_routine)(void *context), + void *context); + + + +/** Start a timer running. + * + * Timer must be stopped. + * + * @param timer timer handle + * @param delay Delay to wait for, in ms + */ +VCOS_INLINE_DECL +void vcos_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay); + +/** Stop an already running timer. + * + * @param timer timer handle + */ +VCOS_INLINE_DECL +void vcos_timer_cancel(VCOS_TIMER_T *timer); + +/** Stop a timer and restart it. + * @param timer timer handle + * @param delay delay in ms + */ +VCOS_INLINE_DECL +void vcos_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay); + +VCOS_INLINE_DECL +void vcos_timer_delete(VCOS_TIMER_T *timer); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/interface/vcos/vcos_tls.h b/interface/vcos/vcos_tls.h new file mode 100755 index 0000000..a0df40c --- /dev/null +++ b/interface/vcos/vcos_tls.h @@ -0,0 +1,84 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - thread local storage +=============================================================================*/ + +#ifndef VCOS_TLS_H +#define VCOS_TLS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "interface/vcos/vcos_types.h" +#include "vcos.h" + + +/** Create a new thread local storage data key visible to all threads in + * the current process. + * + * @param key The key to create + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_tls_create(VCOS_TLS_KEY_T *key); + +/** Delete an existing TLS data key. + */ +VCOS_INLINE_DECL +void vcos_tls_delete(VCOS_TLS_KEY_T tls); + +/** Set the value seen by the current thread. + * + * @param key The key to update + * @param v The value to set for the current thread. + * + * @return VCOS_SUCCESS, or VCOS_ENOMEM if memory for this slot + * could not be allocated. + * + * If TLS is being emulated via VCOS then the memory required + * can be preallocated at thread creation time + */ +VCOS_INLINE_DECL +VCOS_STATUS_T vcos_tls_set(VCOS_TLS_KEY_T tls, void *v); + +/** Get the value for the current thread. + * + * @param key The key to update + * + * @return The current value for this thread. + */ +VCOS_INLINE_DECL +void *vcos_tls_get(VCOS_TLS_KEY_T tls); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/interface/vcos/vcos_types.h b/interface/vcos/vcos_types.h new file mode 100755 index 0000000..835a77f --- /dev/null +++ b/interface/vcos/vcos_types.h @@ -0,0 +1,293 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +VideoCore OS Abstraction Layer - basic types +=============================================================================*/ + +#ifndef VCOS_TYPES_H +#define VCOS_TYPES_H + +#define VCOS_VERSION 1 + +#include +#if defined(__unix__) && !defined(__ANDROID__) +#include "interface/vcos/pthreads/vcos_platform_types.h" +#else +#include "vcos_platform_types.h" +#endif +#include "interface/vcos/vcos_attr.h" + +#if !defined(VCOSPRE_) || !defined(VCOSPOST_) +#error VCOSPRE_ or VCOSPOST_ not defined! +#endif + +/* Redefine these here; this means that existing header files can carry on + * using the VCHPOST/VCHPRE macros rather than having huge changes, which + * could cause nasty merge problems. + */ +#ifndef VCHPOST_ +#define VCHPOST_ VCOSPOST_ +#endif +#ifndef VCHPRE_ +#define VCHPRE_ VCOSPRE_ +#endif + +/** Entry function for a lowlevel thread. + * + * Returns void for consistency with Nucleus/ThreadX. + */ +typedef void (*VCOS_LLTHREAD_ENTRY_FN_T)(void *); + +/** Thread entry point. Returns a void* for consistency + * with pthreads. + */ +typedef void *(*VCOS_THREAD_ENTRY_FN_T)(void*); + + +/* Error return codes - chosen to be similar to errno values */ +typedef enum +{ + VCOS_SUCCESS, + VCOS_EAGAIN, + VCOS_ENOENT, + VCOS_ENOSPC, + VCOS_EINVAL, + VCOS_EACCESS, + VCOS_ENOMEM, + VCOS_ENOSYS, + VCOS_EEXIST, + VCOS_ENXIO, + VCOS_EINTR +} VCOS_STATUS_T; + +/* Some compilers (MetaWare) won't inline with -g turned on, which then results + * in a lot of code bloat. To overcome this, inline functions are forward declared + * with the prefix VCOS_INLINE_DECL, and implemented with the prefix VCOS_INLINE_IMPL. + * + * That then means that in a release build, "static inline" can be used in the obvious + * way, but in a debug build the implementations can be skipped in all but one file, + * by using VCOS_INLINE_BODIES. + * + * VCOS_INLINE_DECL - put this at the start of an inline forward declaration of a VCOS + * function. + * + * VCOS_INLINE_IMPL - put this at the start of an inlined implementation of a VCOS + * function. + * + */ + +/* VCOS_EXPORT - it turns out that in some circumstances we need the implementation of + * a function even if it is usually inlined. + * + * In particular, if we have a codec that is usually provided in object form, if it + * was built for a debug build it will be full of calls to vcos_XXX(). If this is used + * in a *release* build, then there won't be any of these calls around in the main image + * as they will all have been inlined. The problem also exists for vcos functions called + * from assembler. + * + * VCOS_EXPORT ensures that the named function will be emitted as a regular (not static-inline) + * function inside vcos_.c so that it can be linked against. Doing this for every + * VCOS function would be a bit code-bloat-tastic, so it is only done for those that need it. + * + */ + +#ifdef __cplusplus +#define _VCOS_INLINE inline +#else +#define _VCOS_INLINE __inline +#endif + +#if defined(NDEBUG) + +#ifdef __GNUC__ +# define VCOS_INLINE_DECL extern __inline__ +# define VCOS_INLINE_IMPL static __inline__ +#else +# define VCOS_INLINE_DECL static _VCOS_INLINE /* declare a func */ +# define VCOS_INLINE_IMPL static _VCOS_INLINE /* implement a func inline */ +#endif + +# if defined(VCOS_WANT_IMPL) +# define VCOS_EXPORT +# else +# define VCOS_EXPORT VCOS_INLINE_IMPL +# endif /* VCOS_WANT_IMPL */ + +#define VCOS_INLINE_BODIES + +#else /* NDEBUG */ + +#if !defined(VCOS_INLINE_DECL) + #define VCOS_INLINE_DECL extern +#endif +#if !defined(VCOS_INLINE_IMPL) + #define VCOS_INLINE_IMPL +#endif +#define VCOS_EXPORT VCOS_INLINE_IMPL +#endif + +#define VCOS_STATIC_INLINE static _VCOS_INLINE + +#if defined(__HIGHC__) || defined(__HIGHC_ANSI__) +#define _VCOS_METAWARE +#endif + +/** It seems that __FUNCTION__ isn't standard! + */ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 || defined(__VIDEOCORE__) +# define VCOS_FUNCTION __FUNCTION__ +# else +# define VCOS_FUNCTION "" +# endif +#else +# define VCOS_FUNCTION __func__ +#endif + +#define _VCOS_MS_PER_TICK (1000/VCOS_TICKS_PER_SECOND) + +/* Convert a number of milliseconds to a tick count. Internal use only - fails to + * convert VCOS_SUSPEND correctly. + */ +#define _VCOS_MS_TO_TICKS(ms) (((ms)+_VCOS_MS_PER_TICK-1)/_VCOS_MS_PER_TICK) + +#define VCOS_TICKS_TO_MS(ticks) ((ticks) * _VCOS_MS_PER_TICK) + +/** VCOS version of DATESTR, from pcdisk.h. Used by the hostreq service. + */ +typedef struct vcos_datestr +{ + uint8_t cmsec; /**< Centesimal mili second */ + uint16_t date; /**< Date */ + uint16_t time; /**< Time */ + +} VCOS_DATESTR; + +/* Compile-time assert - declares invalid array length if condition + * not met, or array of length one if OK. + */ +#define VCOS_CASSERT(e) extern char vcos_compile_time_check[1/(e)] + +#define vcos_min(x,y) ((x) < (y) ? (x) : (y)) +#define vcos_max(x,y) ((x) > (y) ? (x) : (y)) + +/** Return the count of an array. FIXME: under gcc we could make + * this report an error for pointers using __builtin_types_compatible(). + */ +#define vcos_countof(x) (sizeof((x)) / sizeof((x)[0])) + +/* for backward compatibility */ +#define countof(x) (sizeof((x)) / sizeof((x)[0])) + +#define VCOS_ALIGN_DOWN(p,n) (((ptrdiff_t)(p)) & ~((n)-1)) +#define VCOS_ALIGN_UP(p,n) VCOS_ALIGN_DOWN((ptrdiff_t)(p)+(n)-1,(n)) + +#ifdef _MSC_VER + #define vcos_alignof(T) __alignof(T) +#elif defined(__GNUC__) + #define vcos_alignof(T) __alignof__(T) +#else + #define vcos_alignof(T) (sizeof(struct { T t; char ch; }) - sizeof(T)) +#endif + +/** bool_t is not a POSIX type so cannot rely on it. Define it here. + * It's not even defined in stdbool.h. + */ +typedef int32_t vcos_bool_t; +typedef int32_t vcos_fourcc_t; + +#define VCOS_FALSE 0 +#define VCOS_TRUE (!VCOS_FALSE) + +/** Mark unused arguments to keep compilers quiet */ +#define vcos_unused(x) (void)(x) + +/** For backward compatibility */ +typedef vcos_fourcc_t fourcc_t; +typedef vcos_fourcc_t FOURCC_T; + +#ifdef __cplusplus +#define VCOS_EXTERN_C_BEGIN extern "C" { +#define VCOS_EXTERN_C_END } +#else +#define VCOS_EXTERN_C_BEGIN +#define VCOS_EXTERN_C_END +#endif + +/** Variable attribute indicating the variable must be emitted even if it appears unused. */ +#if defined(__GNUC__) || defined(_VCOS_METAWARE) +# define VCOS_ATTR_USED __attribute__ ((used)) +#else +# define VCOS_ATTR_USED +#endif + +/** Variable attribute requiring specific alignment. */ +#if defined(__GNUC__) || defined(_VCOS_METAWARE) +# define VCOS_ATTR_ALIGNED(n) __attribute__ ((aligned(n))) +#else +# define VCOS_ATTR_ALIGNED(n) +#endif + +/** Define a function as a weak alias to another function. + * @param ret_type Function return type. + * @param alias_name Name of the alias. + * @param param_list Function parameter list, including the parentheses. + * @param target_name Target function (bare function name, not a string). + */ +#if defined(__GNUC__) || defined(_VCOS_METAWARE) + /* N.B. gcc allows __attribute__ after parameter list, but hcvc seems to silently ignore it. */ +# define VCOS_WEAK_ALIAS(ret_type, alias_name, param_list, target_name) \ + __attribute__ ((weak, alias(#target_name))) ret_type alias_name param_list +#else +# define VCOS_WEAK_ALIAS(ret_type, alias, params, target) VCOS_CASSERT(0) +#endif + +/** Define a function as a weak alias to another function, specified as a string. + * @param ret_type Function return type. + * @param alias_name Name of the alias. + * @param param_list Function parameter list, including the parentheses. + * @param target_name Target function name as a string. + * @note Prefer the use of VCOS_WEAK_ALIAS - it is likely to be more portable. + * Only use VCOS_WEAK_ALIAS_STR if you need to do pre-processor mangling of the target + * symbol. + */ +#if defined(__GNUC__) || defined(_VCOS_METAWARE) + /* N.B. gcc allows __attribute__ after parameter list, but hcvc seems to silently ignore it. */ +# define VCOS_WEAK_ALIAS_STR(ret_type, alias_name, param_list, target_name) \ + __attribute__ ((weak, alias(target_name))) ret_type alias_name param_list +#else +# define VCOS_WEAK_ALIAS_STR(ret_type, alias, params, target) VCOS_CASSERT(0) +#endif + +#if defined(__GNUC__) +#define VCOS_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define VCOS_DEPRECATED(msg) +#endif + +#endif diff --git a/interface/vctypes/vc_display_types.h b/interface/vctypes/vc_display_types.h new file mode 100755 index 0000000..dfde9d6 --- /dev/null +++ b/interface/vctypes/vc_display_types.h @@ -0,0 +1,114 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*============================================================================= +Common image types used by the vc_image library. +=============================================================================*/ + +#ifndef INTERFACE_VC_DISPLAY_TYPES_H +#define INTERFACE_VC_DISPLAY_TYPES_H + +//enums of display input format +typedef enum +{ + VCOS_DISPLAY_INPUT_FORMAT_INVALID = 0, + VCOS_DISPLAY_INPUT_FORMAT_RGB888, + VCOS_DISPLAY_INPUT_FORMAT_RGB565 +} +VCOS_DISPLAY_INPUT_FORMAT_T; + +/** For backward compatibility */ +#define DISPLAY_INPUT_FORMAT_INVALID VCOS_DISPLAY_INPUT_FORMAT_INVALID +#define DISPLAY_INPUT_FORMAT_RGB888 VCOS_DISPLAY_INPUT_FORMAT_RGB888 +#define DISPLAY_INPUT_FORMAT_RGB565 VCOS_DISPLAY_INPUT_FORMAT_RGB565 +typedef VCOS_DISPLAY_INPUT_FORMAT_T DISPLAY_INPUT_FORMAT_T; + +// Enum determining how image data for 3D displays has to be supplied +typedef enum +{ + DISPLAY_3D_UNSUPPORTED = 0, // default + DISPLAY_3D_INTERLEAVED, // For autosteroscopic displays + DISPLAY_3D_SBS_FULL_AUTO, // Side-By-Side, Full Width (also used by some autostereoscopic displays) + DISPLAY_3D_SBS_HALF_HORIZ, // Side-By-Side, Half Width, Horizontal Subsampling (see HDMI spec) + DISPLAY_3D_TB_HALF, // Top-bottom 3D + DISPLAY_3D_FRAME_PACKING, // Frame Packed 3D + DISPLAY_3D_FRAME_SEQUENTIAL, // Output left on even frames and right on odd frames (typically 120Hz) + DISPLAY_3D_FORMAT_MAX +} DISPLAY_3D_FORMAT_T; + +//enums of display types +typedef enum +{ + DISPLAY_INTERFACE_MIN, + DISPLAY_INTERFACE_SMI, + DISPLAY_INTERFACE_DPI, + DISPLAY_INTERFACE_DSI, + DISPLAY_INTERFACE_LVDS, + DISPLAY_INTERFACE_MAX + +} DISPLAY_INTERFACE_T; + +/* display dither setting, used on B0 */ +typedef enum { + DISPLAY_DITHER_NONE = 0, /* default if not set */ + DISPLAY_DITHER_RGB666 = 1, + DISPLAY_DITHER_RGB565 = 2, + DISPLAY_DITHER_RGB555 = 3, + DISPLAY_DITHER_MAX +} DISPLAY_DITHER_T; + +//info struct +typedef struct +{ + //type + DISPLAY_INTERFACE_T type; + //width / height + uint32_t width; + uint32_t height; + //output format + DISPLAY_INPUT_FORMAT_T input_format; + //interlaced? + uint32_t interlaced; + /* output dither setting (if required) */ + DISPLAY_DITHER_T output_dither; + /* Pixel frequency */ + uint32_t pixel_freq; + /* Line rate in lines per second */ + uint32_t line_rate; + // Format required for image data for 3D displays + DISPLAY_3D_FORMAT_T format_3d; + // If display requires PV1 (e.g. DSI1), special config is required in HVS + uint32_t use_pixelvalve_1; + // Set for DSI displays which use video mode. + uint32_t dsi_video_mode; + // Select HVS channel (usually 0). + uint32_t hvs_channel; +} DISPLAY_INFO_T; + +#endif /* __VC_INCLUDE_IMAGE_TYPES_H__ */ + + diff --git a/interface/vctypes/vc_image_structs.h b/interface/vctypes/vc_image_structs.h new file mode 100755 index 0000000..f877e1a --- /dev/null +++ b/interface/vctypes/vc_image_structs.h @@ -0,0 +1,245 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef INTERFACE_VC_IMAGE_STRUCTS_H +#define INTERFACE_VC_IMAGE_STRUCTS_H + +/* This file gets included by the VCE compiler, which gets confused + * easily by the VCOS headers. So cannot include vcos.h here. + */ +#include "interface/vcos/vcos_stdint.h" +#include "interface/vcos/vcos_attr.h" + +#include "helpers/debug_utils/debug_writer.h" + +#include "interface/vctypes/vc_image_types.h" + + /* Format specific infos for vc images */ + + /* YUV information, co-sited h/v flags & colour space words */ + typedef enum { + VC_IMAGE_YUVINFO_UNSPECIFIED = 0, /* Unknown or unset - defaults to BT601 interstitial */ + + /* colour-space conversions data [4 bits] */ + /* Note that colour conversions for SMPTE 170M are identical to BT.601 */ + VC_IMAGE_YUVINFO_CSC_ITUR_BT601 = 1, /* ITU-R BT.601-5 [SDTV] (compatible with VideoCore-II) */ + VC_IMAGE_YUVINFO_CSC_ITUR_BT709 = 2, /* ITU-R BT.709-3 [HDTV] */ + VC_IMAGE_YUVINFO_CSC_JPEG_JFIF = 3, /* JPEG JFIF */ + VC_IMAGE_YUVINFO_CSC_FCC = 4, /* Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */ + VC_IMAGE_YUVINFO_CSC_SMPTE_240M = 5, /* Society of Motion Picture and Television Engineers 240M (1999) */ + VC_IMAGE_YUVINFO_CSC_ITUR_BT470_2_M = 6, /* ITU-R BT.470-2 System M */ + VC_IMAGE_YUVINFO_CSC_ITUR_BT470_2_BG = 7, /* ITU-R BT.470-2 System B,G */ + VC_IMAGE_YUVINFO_CSC_JPEG_JFIF_Y16_255 = 8, /* JPEG JFIF, but with 16..255 luma */ + VC_IMAGE_YUVINFO_CSC_CUSTOM = 15, /* Custom colour matrix follows header */ + VC_IMAGE_YUVINFO_CSC_SMPTE_170M = VC_IMAGE_YUVINFO_CSC_ITUR_BT601, + + /* co-sited flags, assumed interstitial if not co-sited [2 bits] */ + VC_IMAGE_YUVINFO_H_COSITED = 256, + VC_IMAGE_YUVINFO_V_COSITED = 512, + + VC_IMAGE_YUVINFO_TOP_BOTTOM = 1024, + VC_IMAGE_YUVINFO_DECIMATED = 2048, + VC_IMAGE_YUVINFO_PACKED = 4096, + + /* Certain YUV image formats can either be V/U interleaved or U/V interleaved */ + VC_IMAGE_YUVINFO_IS_VU = 0x8000, + + /* Force Metaware to use 16 bits */ + VC_IMAGE_YUVINFO_FORCE_ENUM_16BIT = 0xffff, + } VC_IMAGE_YUVINFO_T; + +#define VC_IMAGE_YUV_UV_STRIPE_WIDTH_LOG2 7 +#define VC_IMAGE_YUV_UV_STRIPE_WIDTH (1 << VC_IMAGE_YUV_UV_STRIPE_WIDTH_LOG2) + +#define VC_IMAGE_YUV_UV32_STRIPE_WIDTH_LOG2 5 +#define VC_IMAGE_YUV_UV32_STRIPE_WIDTH (1 << VC_IMAGE_YUV_UV32_STRIPE_WIDTH_LOG2) + +/* 64 pixel wide stripes, 128 byte wide as 16bits/component */ +#define VC_IMAGE_YUV_UV_16_STRIPE_WIDTH_LOG2 6 +#define VC_IMAGE_YUV_UV_16_STRIPE_WIDTH (1 << VC_IMAGE_YUV_UV_16_STRIPE_WIDTH_LOG2) +#define VC_IMAGE_YUV_UV_16_STRIPE_STRIDE_LOG2 7 +#define VC_IMAGE_YUV_UV_16_STRIPE_STRIDE (1 << VC_IMAGE_YUV_UV_16_STRIPE_STRIDE_LOG2) + + /* The image structure. */ + typedef struct vc_image_extra_uv_s { + void *u, *v; + int vpitch; + } VC_IMAGE_EXTRA_UV_T; + + typedef struct vc_image_extra_rgba_s { + unsigned component_order : 24, /* diagnostic use only */ + normalised_alpha : 1, + transparent_colour: 1, + unused_26_31 : 6; + unsigned int arg; + } VC_IMAGE_EXTRA_RGBA_T; + + typedef struct vc_image_extra_pal_s { + short *palette; + int palette32 : 1; + } VC_IMAGE_EXTRA_PAL_T; + +// These fields are subject to change / being moved around + typedef struct vc_image_extra_tf_s { +signed int mipmap_levels : 8; +unsigned int xxx : 23; +unsigned int cube_map : 1; + void *palette; + } VC_IMAGE_EXTRA_TF_T; + + typedef struct vc_image_extra_bayer_s { + unsigned short order; + unsigned short format; + int block_length; + } VC_IMAGE_EXTRA_BAYER_T; + +//The next block can be used with Visual C++ +//which treats enums as long ints + typedef struct vc_image_extra_msbayer_s { + unsigned char order; + unsigned char format; + unsigned char dummy1; + unsigned char dummy2; + int block_length; + } VC_IMAGE_EXTRA_MSBAYER_T; + + typedef struct vc_image_extra_codec_s { + int fourcc; + int maxsize; //NB this will be copied to image.size in parmalloc() + } VC_IMAGE_EXTRA_CODEC_T; + +#define VC_IMAGE_OPENGL_RGBA32 0x14011908 //GL_UNSIGNED_BYTE GL_RGBA +#define VC_IMAGE_OPENGL_RGB24 0x14011907 //GL_UNSIGNED_BYTE GL_RGB +#define VC_IMAGE_OPENGL_RGBA16 0x80331908 //GL_UNSIGNED_SHORT_4_4_4_4 GL_RGBA +#define VC_IMAGE_OPENGL_RGBA5551 0x80341908 //GL_UNSIGNED_SHORT_5_5_5_1 GL_RGBA +#define VC_IMAGE_OPENGL_RGB565 0x83631907 //GL_UNSIGNED_SHORT_5_6_5 GL_RGB +#define VC_IMAGE_OPENGL_YA88 0x1401190A //GL_UNSIGNED_BYTE GL_LUMINANCE_ALPHA +#define VC_IMAGE_OPENGL_Y8 0x14011909 //GL_UNSIGNED_BYTE GL_LUMINANCE +#define VC_IMAGE_OPENGL_A8 0x14011906 //GL_UNSIGNED_BYTE GL_ALPHA +#define VC_IMAGE_OPENGL_ETC1 0x8D64 //GL_ETC1_RGB8_OES +#define VC_IMAGE_OPENGL_PALETTE4_RGB24 0x8B90 //GL_PALETTE4_RGB8_OES +#define VC_IMAGE_OPENGL_PALETTE4_RGBA32 0x8B91 //GL_PALETTE4_RGBA8_OES +#define VC_IMAGE_OPENGL_PALETTE4_RGB565 0x8B92 //GL_PALETTE4_R5_G6_B5_OES +#define VC_IMAGE_OPENGL_PALETTE4_RGBA16 0x8B93 //GL_PALETTE4_RGBA4_OES +#define VC_IMAGE_OPENGL_PALETTE4_RGB5551 0x8B94 //GL_PALETTE4_RGB5_A1_OES +#define VC_IMAGE_OPENGL_PALETTE8_RGB24 0x8B95 //GL_PALETTE8_RGB8_OES +#define VC_IMAGE_OPENGL_PALETTE8_RGBA32 0x8B96 //GL_PALETTE8_RGBA8_OES +#define VC_IMAGE_OPENGL_PALETTE8_RGB565 0x8B97 //GL_PALETTE8_R5_G6_B5_OES +#define VC_IMAGE_OPENGL_PALETTE8_RGBA16 0x8B98 //GL_PALETTE8_RGBA4_OES +#define VC_IMAGE_OPENGL_PALETTE8_RGB5551 0x8B99 //GL_PALETTE8_RGB5_A1_OES + + typedef struct vc_image_extra_opengl_s { + unsigned int format_and_type; + void const *palette; + } VC_IMAGE_EXTRA_OPENGL_T; + + typedef union { + VC_IMAGE_EXTRA_UV_T uv; + VC_IMAGE_EXTRA_RGBA_T rgba; + VC_IMAGE_EXTRA_PAL_T pal; + VC_IMAGE_EXTRA_TF_T tf; + VC_IMAGE_EXTRA_BAYER_T bayer; + VC_IMAGE_EXTRA_MSBAYER_T msbayer; + VC_IMAGE_EXTRA_CODEC_T codec; + VC_IMAGE_EXTRA_OPENGL_T opengl; + } VC_IMAGE_EXTRA_T; + + /* structure containing various colour meta-data for each format */ + typedef union { +#ifdef __HIGHC__ + VC_IMAGE_YUVINFO_T yuv; /* We know Metaware will use 16 bits for this enum, so use the correct type for debug info */ +#else + unsigned short yuv; /* Information pertinent to all YUV implementations */ +#endif + unsigned short info; /* dummy, force size to min 16 bits */ + } VC_IMAGE_INFO_T; + + + /** + * Image handle object, which must be locked before image data becomes + * accessible. + * + * A handle to an image where the image data does not have a guaranteed + * storage location. A call to \c vc_image_lock() must be made to convert + * this into a \c VC_IMAGE_BUF_T, which guarantees that image data can + * be accessed safely. + * + * This type will also be used in cases where it's unclear whether or not + * the buffer is already locked, and in legacy code. + */ + struct VC_IMAGE_T { +#ifdef __HIGHC__ + VC_IMAGE_TYPE_T type; /* Metaware will use 16 bits for this enum + so use the correct type for debug info */ +#else + unsigned short type; /* should restrict to 16 bits */ +#endif + VC_IMAGE_INFO_T info; /* format-specific info; zero for VC02 behaviour */ + unsigned short width; /* width in pixels */ + unsigned short height; /* height in pixels */ + int pitch; /* pitch of image_data array in bytes */ + int size; /* number of bytes available in image_data array */ + void *image_data; /* pixel data */ + VC_IMAGE_EXTRA_T extra; /* extra data like palette pointer */ + struct vc_metadata_header_s *metadata; /* metadata header for the image */ + struct opaque_vc_pool_object_s *pool_object; /* nonNULL if image was allocated from a vc_pool */ + uint32_t mem_handle; /* the mem handle for relocatable memory storage */ + int metadata_size; /* size of metadata of each channel in bytes */ + int channel_offset; /* offset of consecutive channels in bytes */ + uint32_t video_timestamp;/* 90000 Hz RTP times domain - derived from audio timestamp */ + uint8_t num_channels; /* number of channels (2 for stereo) */ + uint8_t current_channel;/* the channel this header is currently pointing to */ + uint8_t linked_multichann_flag;/* Indicate the header has the linked-multichannel structure*/ + uint8_t is_channel_linked; /* Track if the above structure is been used to link the header + into a linked-mulitchannel image */ + uint8_t channel_index; /* index of the channel this header represents while + it is being linked. */ + uint8_t _dummy[3]; /* pad struct to 64 bytes */ + }; + + +# ifdef __COVERITY__ + /* Currently battling with the size of enums when running through static analysis stage */ + typedef int vc_image_t_size_check[(sizeof(VC_IMAGE_T) == 68) * 2 - 1]; +# else + /* compile time assert to ensure size of VC_IMAGE_T is as expected, if the + compiler kicks out a "negative subscript" message then the size + of VC_IMAGE_T is *not* 64 bytes, which is a problem ... */ + typedef int vc_image_t_size_check[(sizeof(VC_IMAGE_T) == 64) * 2 - 1]; +#endif + +/****************************************************************************** + Debugging rules (defined in camera_debug.c) + *****************************************************************************/ +extern DEBUG_WRITE_ENUM_LOOKUP_T vc_image_type_lookup[]; +extern DEBUG_WRITE_ENUM_LOOKUP_T vc_image_bayer_order_lookup[]; +extern DEBUG_WRITE_ENUM_LOOKUP_T vc_image_bayer_format_lookup[]; +extern DEBUG_WRITE_RULE_T vc_image_info_rule[]; +extern DEBUG_WRITE_RULE_T vc_image_rule[]; + +#endif /* __VC_INCLUDE_IMAGE_TYPES_H__ */ + diff --git a/interface/vctypes/vc_image_types.h b/interface/vctypes/vc_image_types.h new file mode 100755 index 0000000..922cd23 --- /dev/null +++ b/interface/vctypes/vc_image_types.h @@ -0,0 +1,173 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Common image types used by the vc_image library + +#ifndef INTERFACE_VC_IMAGE_TYPES_H +#define INTERFACE_VC_IMAGE_TYPES_H + +/* This file gets included by the VCE compiler, which gets confused + * easily by the VCOS headers. So cannot include vcos.h here. + */ +#include "interface/vcos/vcos_stdint.h" + +/* We have so many rectangle types; let's try to introduce a common one. */ +typedef struct tag_VC_RECT_T { + int32_t x; + int32_t y; + int32_t width; + int32_t height; +} VC_RECT_T; + +struct VC_IMAGE_T; +typedef struct VC_IMAGE_T VC_IMAGE_T; + +/* Types of image supported. */ +/* Please add any new types to the *end* of this list. Also update + * case_VC_IMAGE_ANY_xxx macros (below), and the vc_image_type_info table in + * vc_image/vc_image_helper.c. + */ +typedef enum +{ + VC_IMAGE_MIN = 0, //bounds for error checking + + VC_IMAGE_RGB565 = 1, + VC_IMAGE_1BPP, + VC_IMAGE_YUV420, + VC_IMAGE_48BPP, + VC_IMAGE_RGB888, + VC_IMAGE_8BPP, + VC_IMAGE_4BPP, // 4bpp palettised image + VC_IMAGE_3D32, /* A separated format of 16 colour/light shorts followed by 16 z values */ + VC_IMAGE_3D32B, /* 16 colours followed by 16 z values */ + VC_IMAGE_3D32MAT, /* A separated format of 16 material/colour/light shorts followed by 16 z values */ + VC_IMAGE_RGB2X9, /* 32 bit format containing 18 bits of 6.6.6 RGB, 9 bits per short */ + VC_IMAGE_RGB666, /* 32-bit format holding 18 bits of 6.6.6 RGB */ + VC_IMAGE_PAL4_OBSOLETE, // 4bpp palettised image with embedded palette + VC_IMAGE_PAL8_OBSOLETE, // 8bpp palettised image with embedded palette + VC_IMAGE_RGBA32, /* RGB888 with an alpha byte after each pixel */ /* xxx: isn't it BEFORE each pixel? */ + VC_IMAGE_YUV422, /* a line of Y (32-byte padded), a line of U (16-byte padded), and a line of V (16-byte padded) */ + VC_IMAGE_RGBA565, /* RGB565 with a transparent patch */ + VC_IMAGE_RGBA16, /* Compressed (4444) version of RGBA32 */ + VC_IMAGE_YUV_UV, /* VCIII codec format */ + VC_IMAGE_TF_RGBA32, /* VCIII T-format RGBA8888 */ + VC_IMAGE_TF_RGBX32, /* VCIII T-format RGBx8888 */ + VC_IMAGE_TF_FLOAT, /* VCIII T-format float */ + VC_IMAGE_TF_RGBA16, /* VCIII T-format RGBA4444 */ + VC_IMAGE_TF_RGBA5551, /* VCIII T-format RGB5551 */ + VC_IMAGE_TF_RGB565, /* VCIII T-format RGB565 */ + VC_IMAGE_TF_YA88, /* VCIII T-format 8-bit luma and 8-bit alpha */ + VC_IMAGE_TF_BYTE, /* VCIII T-format 8 bit generic sample */ + VC_IMAGE_TF_PAL8, /* VCIII T-format 8-bit palette */ + VC_IMAGE_TF_PAL4, /* VCIII T-format 4-bit palette */ + VC_IMAGE_TF_ETC1, /* VCIII T-format Ericsson Texture Compressed */ + VC_IMAGE_BGR888, /* RGB888 with R & B swapped */ + VC_IMAGE_BGR888_NP, /* RGB888 with R & B swapped, but with no pitch, i.e. no padding after each row of pixels */ + VC_IMAGE_BAYER, /* Bayer image, extra defines which variant is being used */ + VC_IMAGE_CODEC, /* General wrapper for codec images e.g. JPEG from camera */ + VC_IMAGE_YUV_UV32, /* VCIII codec format */ + VC_IMAGE_TF_Y8, /* VCIII T-format 8-bit luma */ + VC_IMAGE_TF_A8, /* VCIII T-format 8-bit alpha */ + VC_IMAGE_TF_SHORT,/* VCIII T-format 16-bit generic sample */ + VC_IMAGE_TF_1BPP, /* VCIII T-format 1bpp black/white */ + VC_IMAGE_OPENGL, + VC_IMAGE_YUV444I, /* VCIII-B0 HVS YUV 4:4:4 interleaved samples */ + VC_IMAGE_YUV422PLANAR, /* Y, U, & V planes separately (VC_IMAGE_YUV422 has them interleaved on a per line basis) */ + VC_IMAGE_ARGB8888, /* 32bpp with 8bit alpha at MS byte, with R, G, B (LS byte) */ + VC_IMAGE_XRGB8888, /* 32bpp with 8bit unused at MS byte, with R, G, B (LS byte) */ + + VC_IMAGE_YUV422YUYV, /* interleaved 8 bit samples of Y, U, Y, V */ + VC_IMAGE_YUV422YVYU, /* interleaved 8 bit samples of Y, V, Y, U */ + VC_IMAGE_YUV422UYVY, /* interleaved 8 bit samples of U, Y, V, Y */ + VC_IMAGE_YUV422VYUY, /* interleaved 8 bit samples of V, Y, U, Y */ + + VC_IMAGE_RGBX32, /* 32bpp like RGBA32 but with unused alpha */ + VC_IMAGE_RGBX8888, /* 32bpp, corresponding to RGBA with unused alpha */ + VC_IMAGE_BGRX8888, /* 32bpp, corresponding to BGRA with unused alpha */ + + VC_IMAGE_YUV420SP, /* Y as a plane, then UV byte interleaved in plane with with same pitch, half height */ + + VC_IMAGE_YUV444PLANAR, /* Y, U, & V planes separately 4:4:4 */ + + VC_IMAGE_TF_U8, /* T-format 8-bit U - same as TF_Y8 buf from U plane */ + VC_IMAGE_TF_V8, /* T-format 8-bit U - same as TF_Y8 buf from V plane */ + + VC_IMAGE_YUV420_16, /* YUV4:2:0 planar, 16bit values */ + VC_IMAGE_YUV_UV_16, /* YUV4:2:0 codec format, 16bit values */ + + VC_IMAGE_MAX, //bounds for error checking + VC_IMAGE_FORCE_ENUM_16BIT = 0xffff, +} VC_IMAGE_TYPE_T; + +/* Image transformations (flips and 90 degree rotations). + These are made out of 3 primitives (transpose is done first). + These must match the DISPMAN and Media Player definitions. */ + +#define TRANSFORM_HFLIP (1<<0) +#define TRANSFORM_VFLIP (1<<1) +#define TRANSFORM_TRANSPOSE (1<<2) + +typedef enum { + VC_IMAGE_ROT0 = 0, + VC_IMAGE_MIRROR_ROT0 = TRANSFORM_HFLIP, + VC_IMAGE_MIRROR_ROT180 = TRANSFORM_VFLIP, + VC_IMAGE_ROT180 = TRANSFORM_HFLIP|TRANSFORM_VFLIP, + VC_IMAGE_MIRROR_ROT90 = TRANSFORM_TRANSPOSE, + VC_IMAGE_ROT270 = TRANSFORM_TRANSPOSE|TRANSFORM_HFLIP, + VC_IMAGE_ROT90 = TRANSFORM_TRANSPOSE|TRANSFORM_VFLIP, + VC_IMAGE_MIRROR_ROT270 = TRANSFORM_TRANSPOSE|TRANSFORM_HFLIP|TRANSFORM_VFLIP, +} VC_IMAGE_TRANSFORM_T; + +typedef enum +{ //defined to be identical to register bits + VC_IMAGE_BAYER_RGGB = 0, + VC_IMAGE_BAYER_GBRG = 1, + VC_IMAGE_BAYER_BGGR = 2, + VC_IMAGE_BAYER_GRBG = 3 +} VC_IMAGE_BAYER_ORDER_T; + +typedef enum +{ //defined to be identical to register bits + VC_IMAGE_BAYER_RAW6 = 0, + VC_IMAGE_BAYER_RAW7 = 1, + VC_IMAGE_BAYER_RAW8 = 2, + VC_IMAGE_BAYER_RAW10 = 3, + VC_IMAGE_BAYER_RAW12 = 4, + VC_IMAGE_BAYER_RAW14 = 5, + VC_IMAGE_BAYER_RAW16 = 6, + VC_IMAGE_BAYER_RAW10_8 = 7, + VC_IMAGE_BAYER_RAW12_8 = 8, + VC_IMAGE_BAYER_RAW14_8 = 9, + VC_IMAGE_BAYER_RAW10L = 11, + VC_IMAGE_BAYER_RAW12L = 12, + VC_IMAGE_BAYER_RAW14L = 13, + VC_IMAGE_BAYER_RAW16_BIG_ENDIAN = 14, + VC_IMAGE_BAYER_RAW4 = 15, +} VC_IMAGE_BAYER_FORMAT_T; + +#endif /* __VC_INCLUDE_IMAGE_TYPES_H__ */ + diff --git a/interface/vmcs_host/CMakeLists.txt b/interface/vmcs_host/CMakeLists.txt new file mode 100755 index 0000000..fde18da --- /dev/null +++ b/interface/vmcs_host/CMakeLists.txt @@ -0,0 +1,35 @@ + +# interface/vmcs_host + +# not working in release build +# add_definitions(-Werror) + +# vc_vchi_gencmd.c has a type-punning problem in vc_gencmd_read_response +add_definitions(-fno-strict-aliasing) + +include_directories(${VMCS_TARGET}/vcfiled) + +add_library(vchostif + ${VMCS_TARGET}/vcfilesys.c ${VMCS_TARGET}/vcmisc.c + vc_vchi_gencmd.c vc_vchi_filesys.c vc_vchi_gpuserv.c + vc_vchi_tvservice.c vc_vchi_cecservice.c + vc_vchi_dispmanx.c vc_service_common.c) +# ${VMCS_TARGET}/vmcs_main.c +# vc_vchi_haud.c +#add_library(bufman vc_vchi_bufman.c ) + +# OpenMAX/IL component service +add_library(vcilcs + vcilcs.c vcilcs_in.c vcilcs_out.c vcilcs_common.c) + +# ILCS pulls in EGL for the ILCS/EGL surface API support +target_link_libraries(vcilcs brcmEGL brcmGLESv2 khrn_client vchiq_arm vcos) + +# vchostif needs ilcore as well (vmcs_main pulls it in) +target_link_libraries(vchostif vchiq_arm vcos vcfiled_check) + +#target_link_libraries(bufman WFC) + +add_subdirectory(linux/vcfiled) +install(TARGETS vchostif vcilcs DESTINATION lib) + diff --git a/interface/vmcs_host/khronos/IL/OMX_Audio.h b/interface/vmcs_host/khronos/IL/OMX_Audio.h new file mode 100755 index 0000000..75d83c8 --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Audio.h @@ -0,0 +1,1391 @@ +/* + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** @file OMX_Audio.h - OpenMax IL version 1.1.2 + * The structures needed by Audio components to exchange + * parameters and configuration data with the componenmilts. + */ + +#ifndef OMX_Audio_h +#define OMX_Audio_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* Each OMX header must include all required header files to allow the + * header to compile without errors. The includes below are required + * for this header file to compile successfully + */ + +#include "OMX_Core.h" + +/** @defgroup midi MIDI + * @ingroup audio + */ + +/** @defgroup effects Audio effects + * @ingroup audio + */ + +/** @defgroup audio OpenMAX IL Audio Domain + * Structures for OpenMAX IL Audio domain + * @{ + */ + +/** Enumeration used to define the possible audio codings. + * If "OMX_AUDIO_CodingUnused" is selected, the coding selection must + * be done in a vendor specific way. Since this is for an audio + * processing element this enum is relevant. However, for another + * type of component other enums would be in this area. + */ +typedef enum OMX_AUDIO_CODINGTYPE { + OMX_AUDIO_CodingUnused = 0, /**< Placeholder value when coding is N/A */ + OMX_AUDIO_CodingAutoDetect, /**< auto detection of audio format */ + OMX_AUDIO_CodingPCM, /**< Any variant of PCM coding */ + OMX_AUDIO_CodingADPCM, /**< Any variant of ADPCM encoded data */ + OMX_AUDIO_CodingAMR, /**< Any variant of AMR encoded data */ + OMX_AUDIO_CodingGSMFR, /**< Any variant of GSM fullrate (i.e. GSM610) */ + OMX_AUDIO_CodingGSMEFR, /**< Any variant of GSM Enhanced Fullrate encoded data*/ + OMX_AUDIO_CodingGSMHR, /**< Any variant of GSM Halfrate encoded data */ + OMX_AUDIO_CodingPDCFR, /**< Any variant of PDC Fullrate encoded data */ + OMX_AUDIO_CodingPDCEFR, /**< Any variant of PDC Enhanced Fullrate encoded data */ + OMX_AUDIO_CodingPDCHR, /**< Any variant of PDC Halfrate encoded data */ + OMX_AUDIO_CodingTDMAFR, /**< Any variant of TDMA Fullrate encoded data (TIA/EIA-136-420) */ + OMX_AUDIO_CodingTDMAEFR, /**< Any variant of TDMA Enhanced Fullrate encoded data (TIA/EIA-136-410) */ + OMX_AUDIO_CodingQCELP8, /**< Any variant of QCELP 8kbps encoded data */ + OMX_AUDIO_CodingQCELP13, /**< Any variant of QCELP 13kbps encoded data */ + OMX_AUDIO_CodingEVRC, /**< Any variant of EVRC encoded data */ + OMX_AUDIO_CodingSMV, /**< Any variant of SMV encoded data */ + OMX_AUDIO_CodingG711, /**< Any variant of G.711 encoded data */ + OMX_AUDIO_CodingG723, /**< Any variant of G.723 dot 1 encoded data */ + OMX_AUDIO_CodingG726, /**< Any variant of G.726 encoded data */ + OMX_AUDIO_CodingG729, /**< Any variant of G.729 encoded data */ + OMX_AUDIO_CodingAAC, /**< Any variant of AAC encoded data */ + OMX_AUDIO_CodingMP3, /**< Any variant of MP3 encoded data */ + OMX_AUDIO_CodingSBC, /**< Any variant of SBC encoded data */ + OMX_AUDIO_CodingVORBIS, /**< Any variant of VORBIS encoded data */ + OMX_AUDIO_CodingWMA, /**< Any variant of WMA encoded data */ + OMX_AUDIO_CodingRA, /**< Any variant of RA encoded data */ + OMX_AUDIO_CodingMIDI, /**< Any variant of MIDI encoded data */ + OMX_AUDIO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + +#define OMX_AUDIO_CodingFLAC_Supported 1 + OMX_AUDIO_CodingFLAC, /**< Any variant of FLAC */ +#define OMX_AUDIO_CodingDDP_Supported 1 + OMX_AUDIO_CodingDDP, /**< Any variant of Dolby Digital Plus */ +#define OMX_AUDIO_CodingDTS_Supported 1 + OMX_AUDIO_CodingDTS, /**< Any variant of DTS */ +#define OMX_AUDIO_CodingWMAPRO_Supported 1 + OMX_AUDIO_CodingWMAPRO, /**< Any variant of WMA Professional */ +#define OMX_AUDIO_CodingATRAC3_Supported 1 + OMX_AUDIO_CodingATRAC3, /**< Sony ATRAC-3 variants */ +#define OMX_AUDIO_CodingATRACX_Supported 1 + OMX_AUDIO_CodingATRACX, /**< Sony ATRAC-X variants */ +#define OMX_AUDIO_CodingATRACAAL_Supported 1 + OMX_AUDIO_CodingATRACAAL, /**< Sony ATRAC advanced-lossless variants */ + + OMX_AUDIO_CodingMax = 0x7FFFFFFF +} OMX_AUDIO_CODINGTYPE; + + +/** The PortDefinition structure is used to define all of the parameters + * necessary for the compliant component to setup an input or an output audio + * path. If additional information is needed to define the parameters of the + * port (such as frequency), additional structures must be sent such as the + * OMX_AUDIO_PARAM_PCMMODETYPE structure to supply the extra parameters for the port. + */ +typedef struct OMX_AUDIO_PORTDEFINITIONTYPE { + OMX_STRING cMIMEType; /**< MIME type of data for the port */ + OMX_NATIVE_DEVICETYPE pNativeRender; /** < platform specific reference + for an output device, + otherwise this field is 0 */ + OMX_BOOL bFlagErrorConcealment; /**< Turns on error concealment if it is + supported by the OMX component */ + OMX_AUDIO_CODINGTYPE eEncoding; /**< Type of data expected for this + port (e.g. PCM, AMR, MP3, etc) */ +} OMX_AUDIO_PORTDEFINITIONTYPE; + + +/** Port format parameter. This structure is used to enumerate + * the various data input/output format supported by the port. + */ +typedef struct OMX_AUDIO_PARAM_PORTFORMATTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Indicates which port to set */ + OMX_U32 nIndex; /**< Indicates the enumeration index for the format from 0x0 to N-1 */ + OMX_AUDIO_CODINGTYPE eEncoding; /**< Type of data expected for this port (e.g. PCM, AMR, MP3, etc) */ +} OMX_AUDIO_PARAM_PORTFORMATTYPE; + + +/** PCM mode type */ +typedef enum OMX_AUDIO_PCMMODETYPE { + OMX_AUDIO_PCMModeLinear = 0, /**< Linear PCM encoded data */ + OMX_AUDIO_PCMModeALaw, /**< A law PCM encoded data (G.711) */ + OMX_AUDIO_PCMModeMULaw, /**< Mu law PCM encoded data (G.711) */ + OMX_AUDIO_PCMModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_PCMModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_PCMModeMax = 0x7FFFFFFF +} OMX_AUDIO_PCMMODETYPE; + + +typedef enum OMX_AUDIO_CHANNELTYPE { + OMX_AUDIO_ChannelNone = 0x0, /**< Unused or empty */ + OMX_AUDIO_ChannelLF = 0x1, /**< Left front */ + OMX_AUDIO_ChannelRF = 0x2, /**< Right front */ + OMX_AUDIO_ChannelCF = 0x3, /**< Center front */ + OMX_AUDIO_ChannelLS = 0x4, /**< Left surround */ + OMX_AUDIO_ChannelRS = 0x5, /**< Right surround */ + OMX_AUDIO_ChannelLFE = 0x6, /**< Low frequency effects */ + OMX_AUDIO_ChannelCS = 0x7, /**< Back surround */ + OMX_AUDIO_ChannelLR = 0x8, /**< Left rear. */ + OMX_AUDIO_ChannelRR = 0x9, /**< Right rear. */ + OMX_AUDIO_ChannelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_ChannelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_ChannelMax = 0x7FFFFFFF +} OMX_AUDIO_CHANNELTYPE; + +#define OMX_AUDIO_MAXCHANNELS 16 /**< maximum number distinct audio channels that a buffer may contain */ +#define OMX_MIN_PCMPAYLOAD_MSEC 5 /**< Minimum audio buffer payload size for uncompressed (PCM) audio */ + +/** PCM format description */ +typedef struct OMX_AUDIO_PARAM_PCMMODETYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels (e.g. 2 for stereo) */ + OMX_NUMERICALDATATYPE eNumData; /**< indicates PCM data as signed or unsigned */ + OMX_ENDIANTYPE eEndian; /**< indicates PCM data as little or big endian */ + OMX_BOOL bInterleaved; /**< True for normal interleaved data; false for + non-interleaved data (e.g. block data) */ + OMX_U32 nBitPerSample; /**< Bit per sample */ + OMX_U32 nSamplingRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_AUDIO_PCMMODETYPE ePCMMode; /**< PCM mode enumeration */ + OMX_AUDIO_CHANNELTYPE eChannelMapping[OMX_AUDIO_MAXCHANNELS]; /**< Slot i contains channel defined by eChannelMap[i] */ + +} OMX_AUDIO_PARAM_PCMMODETYPE; + + +/** Audio channel mode. This is used by both AAC and MP3, although the names are more appropriate + * for the MP3. For example, JointStereo for MP3 is CouplingChannels for AAC. + */ +typedef enum OMX_AUDIO_CHANNELMODETYPE { + OMX_AUDIO_ChannelModeStereo = 0, /**< 2 channels, the bitrate allocation between those + two channels changes accordingly to each channel information */ + OMX_AUDIO_ChannelModeJointStereo, /**< mode that takes advantage of what is common between + 2 channels for higher compression gain */ + OMX_AUDIO_ChannelModeDual, /**< 2 mono-channels, each channel is encoded with half + the bitrate of the overall bitrate */ + OMX_AUDIO_ChannelModeMono, /**< Mono channel mode */ + OMX_AUDIO_ChannelModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_ChannelModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_ChannelModeMax = 0x7FFFFFFF +} OMX_AUDIO_CHANNELMODETYPE; + + +typedef enum OMX_AUDIO_MP3STREAMFORMATTYPE { + OMX_AUDIO_MP3StreamFormatMP1Layer3 = 0, /**< MP3 Audio MPEG 1 Layer 3 Stream format */ + OMX_AUDIO_MP3StreamFormatMP2Layer3, /**< MP3 Audio MPEG 2 Layer 3 Stream format */ + OMX_AUDIO_MP3StreamFormatMP2_5Layer3, /**< MP3 Audio MPEG2.5 Layer 3 Stream format */ + OMX_AUDIO_MP3StreamFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_MP3StreamFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_MP3StreamFormatMax = 0x7FFFFFFF +} OMX_AUDIO_MP3STREAMFORMATTYPE; + +/** MP3 params */ +typedef struct OMX_AUDIO_PARAM_MP3TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should + limit the audio signal. Use 0 to let encoder decide */ + OMX_AUDIO_CHANNELMODETYPE eChannelMode; /**< Channel mode enumeration */ + OMX_AUDIO_MP3STREAMFORMATTYPE eFormat; /**< MP3 stream format */ +} OMX_AUDIO_PARAM_MP3TYPE; + +typedef enum OMX_AUDIO_DDPBITSTREAMID { + OMX_AUDIO_DDPBitStreamIdAC3 = 8, + OMX_AUDIO_DDPBitStreamIdEAC3 = 16, + OMX_AUDIO_DDPBitStreamIdKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_DDPBitStreamIdVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_DDPBitStreamIdMax = 0x7FFFFFFF +} OMX_AUDIO_DDPBITSTREAMID; + +typedef enum OMX_AUDIO_DDPBITSTREAMMODE { + OMX_AUDIO_DDPBitStreamModeCM = 0, /**< DDP any main audio service: complete main (CM) */ + OMX_AUDIO_DDPBitStreamModeME, /**< DDP any main audio service: music and effects (ME) */ + OMX_AUDIO_DDPBitStreamModeVI, /**< DDP any associated service: visually impaired (VI) */ + OMX_AUDIO_DDPBitStreamModeHI, /**< DDP any associated service: hearing impaired (HI) */ + OMX_AUDIO_DDPBitStreamModeD, /**< DDP any associated service: dialogue (D) */ + OMX_AUDIO_DDPBitStreamModeC, /**< DDP any associated service: commentary (C) */ + OMX_AUDIO_DDPBitStreamModeE, /**< DDP any associated service: emergency (E) */ + OMX_AUDIO_DDPBitStreamModeVO, /**< DDP associated service: voice over (VO) */ + OMX_AUDIO_DDPBitStreamModeK, /**< DDP main audio service: karaoke */ + OMX_AUDIO_DDPBitStreamModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_DDPBitStreamModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_DDPBitStreamModeMax = 0x7FFFFFFF +} OMX_AUDIO_DDPBITSTREAMMODE; + +typedef enum OMX_AUDIO_DDPDOLBYSURROUNDMODE { + OMX_AUDIO_DDPDolbySurroundModeNotIndicated = 0, /**< Not indicated */ + OMX_AUDIO_DDPDolbySurroundModeNotDolbySurround, /**< Not Dolby Surround */ + OMX_AUDIO_DDPDolbySurroundModeDolbySurroundEncoded, /**< Dolby Surround encoded */ + OMX_AUDIO_DDPDolbySurroundModeReserverd, /**< Reserved */ + OMX_AUDIO_DDPDolbySurroundModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_DDPDolbySurroundModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_DDPDolbySurroundModeMax = 0x7FFFFFFF +} OMX_AUDIO_DDPDOLBYSURROUNDMODE; + +/** DDP params */ +typedef struct OMX_AUDIO_PARAM_DDPTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_AUDIO_DDPBITSTREAMID eBitStreamId; + OMX_AUDIO_DDPBITSTREAMMODE eBitStreamMode; + OMX_AUDIO_DDPDOLBYSURROUNDMODE eDolbySurroundMode; + OMX_AUDIO_CHANNELTYPE eChannelMapping[OMX_AUDIO_MAXCHANNELS]; /**< Slot i contains channel defined by eChannelMapping[i] */ +} OMX_AUDIO_PARAM_DDPTYPE; + +/** DTS params */ +typedef struct OMX_AUDIO_PARAM_DTSTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_U32 nDtsType; /** DTS type 1, 2, or 3. */ + OMX_U32 nFormat; /** DTS stream is either big/little endian and 16/14 bit packing */ + OMX_U32 nDtsFrameSizeBytes; /** DTS frame size in bytes */ + OMX_AUDIO_CHANNELTYPE eChannelMapping[OMX_AUDIO_MAXCHANNELS]; /**< Slot i contains channel defined by eChannelMapping[i] */ +} OMX_AUDIO_PARAM_DTSTYPE; + +typedef enum OMX_AUDIO_AACSTREAMFORMATTYPE { + OMX_AUDIO_AACStreamFormatMP2ADTS = 0, /**< AAC Audio Data Transport Stream 2 format */ + OMX_AUDIO_AACStreamFormatMP4ADTS, /**< AAC Audio Data Transport Stream 4 format */ + OMX_AUDIO_AACStreamFormatMP4LOAS, /**< AAC Low Overhead Audio Stream format */ + OMX_AUDIO_AACStreamFormatMP4LATM, /**< AAC Low overhead Audio Transport Multiplex */ + OMX_AUDIO_AACStreamFormatADIF, /**< AAC Audio Data Interchange Format */ + OMX_AUDIO_AACStreamFormatMP4FF, /**< AAC inside MPEG-4/ISO File Format */ + OMX_AUDIO_AACStreamFormatRAW, /**< AAC Raw Format */ + OMX_AUDIO_AACStreamFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_AACStreamFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_AACStreamFormatMax = 0x7FFFFFFF +} OMX_AUDIO_AACSTREAMFORMATTYPE; + + +/** AAC mode type. Note that the term profile is used with the MPEG-2 + * standard and the term object type and profile is used with MPEG-4 */ +typedef enum OMX_AUDIO_AACPROFILETYPE{ + OMX_AUDIO_AACObjectNull = 0, /**< Null, not used */ + OMX_AUDIO_AACObjectMain = 1, /**< AAC Main object */ + OMX_AUDIO_AACObjectLC, /**< AAC Low Complexity object (AAC profile) */ + OMX_AUDIO_AACObjectSSR, /**< AAC Scalable Sample Rate object */ + OMX_AUDIO_AACObjectLTP, /**< AAC Long Term Prediction object */ + OMX_AUDIO_AACObjectHE, /**< AAC High Efficiency (object type SBR, HE-AAC profile) */ + OMX_AUDIO_AACObjectScalable, /**< AAC Scalable object */ + OMX_AUDIO_AACObjectERLC = 17, /**< ER AAC Low Complexity object (Error Resilient AAC-LC) */ + OMX_AUDIO_AACObjectLD = 23, /**< AAC Low Delay object (Error Resilient) */ + OMX_AUDIO_AACObjectHE_PS = 29, /**< AAC High Efficiency with Parametric Stereo coding (HE-AAC v2, object type PS) */ + OMX_AUDIO_AACObjectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_AACObjectVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_AACObjectMax = 0x7FFFFFFF +} OMX_AUDIO_AACPROFILETYPE; + + +/** AAC tool usage (for nAACtools in OMX_AUDIO_PARAM_AACPROFILETYPE). + * Required for encoder configuration and optional as decoder info output. + * For MP3, OMX_AUDIO_CHANNELMODETYPE is sufficient. */ +#define OMX_AUDIO_AACToolNone 0x00000000 /**< no AAC tools allowed (encoder config) or active (decoder info output) */ +#define OMX_AUDIO_AACToolMS 0x00000001 /**< MS: Mid/side joint coding tool allowed or active */ +#define OMX_AUDIO_AACToolIS 0x00000002 /**< IS: Intensity stereo tool allowed or active */ +#define OMX_AUDIO_AACToolTNS 0x00000004 /**< TNS: Temporal Noise Shaping tool allowed or active */ +#define OMX_AUDIO_AACToolPNS 0x00000008 /**< PNS: MPEG-4 Perceptual Noise substitution tool allowed or active */ +#define OMX_AUDIO_AACToolLTP 0x00000010 /**< LTP: MPEG-4 Long Term Prediction tool allowed or active */ +#define OMX_AUDIO_AACToolAll 0x7FFFFFFF /**< all AAC tools allowed or active (*/ + +/** MPEG-4 AAC error resilience (ER) tool usage (for nAACERtools in OMX_AUDIO_PARAM_AACPROFILETYPE). + * Required for ER encoder configuration and optional as decoder info output */ +#define OMX_AUDIO_AACERNone 0x00000000 /**< no AAC ER tools allowed/used */ +#define OMX_AUDIO_AACERVCB11 0x00000001 /**< VCB11: Virtual Code Books for AAC section data */ +#define OMX_AUDIO_AACERRVLC 0x00000002 /**< RVLC: Reversible Variable Length Coding */ +#define OMX_AUDIO_AACERHCR 0x00000004 /**< HCR: Huffman Codeword Reordering */ +#define OMX_AUDIO_AACERAll 0x7FFFFFFF /**< all AAC ER tools allowed/used */ + + +/** AAC params */ +typedef struct OMX_AUDIO_PARAM_AACPROFILETYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should + limit the audio signal. Use 0 to let encoder decide */ + OMX_U32 nFrameLength; /**< Frame length (in audio samples per channel) of the codec. + Can be 1024 or 960 (AAC-LC), 2048 (HE-AAC), 480 or 512 (AAC-LD). + Use 0 to let encoder decide */ + OMX_U32 nAACtools; /**< AAC tool usage */ + OMX_U32 nAACERtools; /**< MPEG-4 AAC error resilience tool usage */ + OMX_AUDIO_AACPROFILETYPE eAACProfile; /**< AAC profile enumeration */ + OMX_AUDIO_AACSTREAMFORMATTYPE eAACStreamFormat; /**< AAC stream format enumeration */ + OMX_AUDIO_CHANNELMODETYPE eChannelMode; /**< Channel mode enumeration */ +} OMX_AUDIO_PARAM_AACPROFILETYPE; + + +/** VORBIS params */ +typedef struct OMX_AUDIO_PARAM_VORBISTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate of the encoded data data. Use 0 for variable + rate or unknown bit rates. Encoding is set to the + bitrate closest to specified value (in bps) */ + OMX_U32 nMinBitRate; /**< Sets minimum bitrate (in bps). */ + OMX_U32 nMaxBitRate; /**< Sets maximum bitrate (in bps). */ + + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should + limit the audio signal. Use 0 to let encoder decide */ + OMX_S32 nQuality; /**< Sets encoding quality to n, between -1 (low) and 10 (high). + In the default mode of operation, the quality level is 3. + Normal quality range is 0 - 10. */ + OMX_BOOL bManaged; /**< Set bitrate management mode. This turns off the + normal VBR encoding, but allows hard or soft bitrate + constraints to be enforced by the encoder. This mode can + be slower, and may also be lower quality. It is + primarily useful for streaming. */ + OMX_BOOL bDownmix; /**< Downmix input from stereo to mono (has no effect on + non-stereo streams). Useful for lower-bitrate encoding. */ +} OMX_AUDIO_PARAM_VORBISTYPE; + + +/** WMA Version */ +typedef enum OMX_AUDIO_WMAFORMATTYPE { + OMX_AUDIO_WMAFormatUnused = 0, /**< format unused or unknown */ + OMX_AUDIO_WMAFormat7, /**< Windows Media Audio format 7 */ + OMX_AUDIO_WMAFormat8, /**< Windows Media Audio format 8 */ + OMX_AUDIO_WMAFormat9, /**< Windows Media Audio format 9 */ + OMX_AUDIO_WMAFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_WMAFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_WMAFormatMax = 0x7FFFFFFF +} OMX_AUDIO_WMAFORMATTYPE; + + +/** WMA Profile */ +typedef enum OMX_AUDIO_WMAPROFILETYPE { + OMX_AUDIO_WMAProfileUnused = 0, /**< profile unused or unknown */ + OMX_AUDIO_WMAProfileL1, /**< Windows Media audio version 9 profile L1 */ + OMX_AUDIO_WMAProfileL2, /**< Windows Media audio version 9 profile L2 */ + OMX_AUDIO_WMAProfileL3, /**< Windows Media audio version 9 profile L3 */ + OMX_AUDIO_WMAProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_WMAProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_WMAProfileMax = 0x7FFFFFFF +} OMX_AUDIO_WMAPROFILETYPE; + + +/** WMA params */ +typedef struct OMX_AUDIO_PARAM_WMATYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U16 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_AUDIO_WMAFORMATTYPE eFormat; /**< Version of WMA stream / data */ + OMX_AUDIO_WMAPROFILETYPE eProfile; /**< Profile of WMA stream / data */ + OMX_U32 nSamplingRate; /**< Sampling rate of the source data */ + OMX_U16 nBlockAlign; /**< is the block alignment, or block size, in bytes of the audio codec */ + OMX_U16 nEncodeOptions; /**< WMA Type-specific data */ + OMX_U32 nSuperBlockAlign; /**< WMA Type-specific data */ +} OMX_AUDIO_PARAM_WMATYPE; + +/** + * RealAudio format + */ +typedef enum OMX_AUDIO_RAFORMATTYPE { + OMX_AUDIO_RAFormatUnused = 0, /**< Format unused or unknown */ + OMX_AUDIO_RA8, /**< RealAudio 8 codec */ + OMX_AUDIO_RA9, /**< RealAudio 9 codec */ + OMX_AUDIO_RA10_AAC, /**< MPEG-4 AAC codec for bitrates of more than 128kbps */ + OMX_AUDIO_RA10_CODEC, /**< RealAudio codec for bitrates less than 128 kbps */ + OMX_AUDIO_RA10_LOSSLESS, /**< RealAudio Lossless */ + OMX_AUDIO_RA10_MULTICHANNEL, /**< RealAudio Multichannel */ + OMX_AUDIO_RA10_VOICE, /**< RealAudio Voice for bitrates below 15 kbps */ + OMX_AUDIO_RAFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_RAFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_RAFormatMax = 0x7FFFFFFF +} OMX_AUDIO_RAFORMATTYPE; + +/** RA (Real Audio) params */ +typedef struct OMX_AUDIO_PARAM_RATYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nSamplingRate; /**< is the sampling rate of the source data */ + OMX_U32 nBitsPerFrame; /**< is the value for bits per frame */ + OMX_U32 nSamplePerFrame; /**< is the value for samples per frame */ + OMX_U32 nCouplingQuantBits; /**< is the number of coupling quantization bits in the stream */ + OMX_U32 nCouplingStartRegion; /**< is the coupling start region in the stream */ + OMX_U32 nNumRegions; /**< is the number of regions value */ + OMX_AUDIO_RAFORMATTYPE eFormat; /**< is the RealAudio audio format */ +} OMX_AUDIO_PARAM_RATYPE; + + +/** SBC Allocation Method Type */ +typedef enum OMX_AUDIO_SBCALLOCMETHODTYPE { + OMX_AUDIO_SBCAllocMethodLoudness, /**< Loudness allocation method */ + OMX_AUDIO_SBCAllocMethodSNR, /**< SNR allocation method */ + OMX_AUDIO_SBCAllocMethodKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_SBCAllocMethodVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_SBCAllocMethodMax = 0x7FFFFFFF +} OMX_AUDIO_SBCALLOCMETHODTYPE; + + +/** SBC params */ +typedef struct OMX_AUDIO_PARAM_SBCTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ + OMX_U32 nBlocks; /**< Number of blocks */ + OMX_U32 nSubbands; /**< Number of subbands */ + OMX_U32 nBitPool; /**< Bitpool value */ + OMX_BOOL bEnableBitrate; /**< Use bitrate value instead of bitpool */ + OMX_AUDIO_CHANNELMODETYPE eChannelMode; /**< Channel mode enumeration */ + OMX_AUDIO_SBCALLOCMETHODTYPE eSBCAllocType; /**< SBC Allocation method type */ +} OMX_AUDIO_PARAM_SBCTYPE; + + +/** ADPCM stream format parameters */ +typedef struct OMX_AUDIO_PARAM_ADPCMTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_U32 nBitsPerSample; /**< Number of bits in each sample */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ +} OMX_AUDIO_PARAM_ADPCMTYPE; + + +/** G723 rate */ +typedef enum OMX_AUDIO_G723RATE { + OMX_AUDIO_G723ModeUnused = 0, /**< AMRNB Mode unused / unknown */ + OMX_AUDIO_G723ModeLow, /**< 5300 bps */ + OMX_AUDIO_G723ModeHigh, /**< 6300 bps */ + OMX_AUDIO_G723ModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_G723ModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_G723ModeMax = 0x7FFFFFFF +} OMX_AUDIO_G723RATE; + + +/** G723 - Sample rate must be 8 KHz */ +typedef struct OMX_AUDIO_PARAM_G723TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_AUDIO_G723RATE eBitRate; /**< todo: Should this be moved to a config? */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ + OMX_BOOL bPostFilter; /**< Enable Post Filter */ +} OMX_AUDIO_PARAM_G723TYPE; + + +/** ITU G726 (ADPCM) rate */ +typedef enum OMX_AUDIO_G726MODE { + OMX_AUDIO_G726ModeUnused = 0, /**< G726 Mode unused / unknown */ + OMX_AUDIO_G726Mode16, /**< 16 kbps */ + OMX_AUDIO_G726Mode24, /**< 24 kbps */ + OMX_AUDIO_G726Mode32, /**< 32 kbps, most common rate, also G721 */ + OMX_AUDIO_G726Mode40, /**< 40 kbps */ + OMX_AUDIO_G726ModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_G726ModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_G726ModeMax = 0x7FFFFFFF +} OMX_AUDIO_G726MODE; + + +/** G.726 stream format parameters - must be at 8KHz */ +typedef struct OMX_AUDIO_PARAM_G726TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_AUDIO_G726MODE eG726Mode; +} OMX_AUDIO_PARAM_G726TYPE; + + +/** G729 coder type */ +typedef enum OMX_AUDIO_G729TYPE { + OMX_AUDIO_G729 = 0, /**< ITU G.729 encoded data */ + OMX_AUDIO_G729A, /**< ITU G.729 annex A encoded data */ + OMX_AUDIO_G729B, /**< ITU G.729 with annex B encoded data */ + OMX_AUDIO_G729AB, /**< ITU G.729 annexes A and B encoded data */ + OMX_AUDIO_G729KhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_G729VendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_G729Max = 0x7FFFFFFF +} OMX_AUDIO_G729TYPE; + + +/** G729 stream format parameters - fixed 6KHz sample rate */ +typedef struct OMX_AUDIO_PARAM_G729TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_AUDIO_G729TYPE eBitType; +} OMX_AUDIO_PARAM_G729TYPE; + + +/** AMR Frame format */ +typedef enum OMX_AUDIO_AMRFRAMEFORMATTYPE { + OMX_AUDIO_AMRFrameFormatConformance = 0, /**< Frame Format is AMR Conformance + (Standard) Format */ + OMX_AUDIO_AMRFrameFormatIF1, /**< Frame Format is AMR Interface + Format 1 */ + OMX_AUDIO_AMRFrameFormatIF2, /**< Frame Format is AMR Interface + Format 2*/ + OMX_AUDIO_AMRFrameFormatFSF, /**< Frame Format is AMR File Storage + Format */ + OMX_AUDIO_AMRFrameFormatRTPPayload, /**< Frame Format is AMR Real-Time + Transport Protocol Payload Format */ + OMX_AUDIO_AMRFrameFormatITU, /**< Frame Format is ITU Format (added at Motorola request) */ + OMX_AUDIO_AMRFrameFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_AMRFrameFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_AMRFrameFormatMax = 0x7FFFFFFF +} OMX_AUDIO_AMRFRAMEFORMATTYPE; + + +/** AMR band mode */ +typedef enum OMX_AUDIO_AMRBANDMODETYPE { + OMX_AUDIO_AMRBandModeUnused = 0, /**< AMRNB Mode unused / unknown */ + OMX_AUDIO_AMRBandModeNB0, /**< AMRNB Mode 0 = 4750 bps */ + OMX_AUDIO_AMRBandModeNB1, /**< AMRNB Mode 1 = 5150 bps */ + OMX_AUDIO_AMRBandModeNB2, /**< AMRNB Mode 2 = 5900 bps */ + OMX_AUDIO_AMRBandModeNB3, /**< AMRNB Mode 3 = 6700 bps */ + OMX_AUDIO_AMRBandModeNB4, /**< AMRNB Mode 4 = 7400 bps */ + OMX_AUDIO_AMRBandModeNB5, /**< AMRNB Mode 5 = 7950 bps */ + OMX_AUDIO_AMRBandModeNB6, /**< AMRNB Mode 6 = 10200 bps */ + OMX_AUDIO_AMRBandModeNB7, /**< AMRNB Mode 7 = 12200 bps */ + OMX_AUDIO_AMRBandModeWB0, /**< AMRWB Mode 0 = 6600 bps */ + OMX_AUDIO_AMRBandModeWB1, /**< AMRWB Mode 1 = 8850 bps */ + OMX_AUDIO_AMRBandModeWB2, /**< AMRWB Mode 2 = 12650 bps */ + OMX_AUDIO_AMRBandModeWB3, /**< AMRWB Mode 3 = 14250 bps */ + OMX_AUDIO_AMRBandModeWB4, /**< AMRWB Mode 4 = 15850 bps */ + OMX_AUDIO_AMRBandModeWB5, /**< AMRWB Mode 5 = 18250 bps */ + OMX_AUDIO_AMRBandModeWB6, /**< AMRWB Mode 6 = 19850 bps */ + OMX_AUDIO_AMRBandModeWB7, /**< AMRWB Mode 7 = 23050 bps */ + OMX_AUDIO_AMRBandModeWB8, /**< AMRWB Mode 8 = 23850 bps */ + OMX_AUDIO_AMRBandModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_AMRBandModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_AMRBandModeMax = 0x7FFFFFFF +} OMX_AUDIO_AMRBANDMODETYPE; + + +/** AMR Discontinuous Transmission mode */ +typedef enum OMX_AUDIO_AMRDTXMODETYPE { + OMX_AUDIO_AMRDTXModeOff = 0, /**< AMR Discontinuous Transmission Mode is disabled */ + OMX_AUDIO_AMRDTXModeOnVAD1, /**< AMR Discontinuous Transmission Mode using + Voice Activity Detector 1 (VAD1) is enabled */ + OMX_AUDIO_AMRDTXModeOnVAD2, /**< AMR Discontinuous Transmission Mode using + Voice Activity Detector 2 (VAD2) is enabled */ + OMX_AUDIO_AMRDTXModeOnAuto, /**< The codec will automatically select between + Off, VAD1 or VAD2 modes */ + + OMX_AUDIO_AMRDTXasEFR, /**< DTX as EFR instead of AMR standard (3GPP 26.101, frame type =8,9,10) */ + + OMX_AUDIO_AMRDTXModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_AMRDTXModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_AMRDTXModeMax = 0x7FFFFFFF +} OMX_AUDIO_AMRDTXMODETYPE; + + +/** AMR params */ +typedef struct OMX_AUDIO_PARAM_AMRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nBitRate; /**< Bit rate read only field */ + OMX_AUDIO_AMRBANDMODETYPE eAMRBandMode; /**< AMR Band Mode enumeration */ + OMX_AUDIO_AMRDTXMODETYPE eAMRDTXMode; /**< AMR DTX Mode enumeration */ + OMX_AUDIO_AMRFRAMEFORMATTYPE eAMRFrameFormat; /**< AMR frame format enumeration */ +} OMX_AUDIO_PARAM_AMRTYPE; + + +/** GSM_FR (ETSI 06.10, 3GPP 46.010) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_GSMFRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_GSMFRTYPE; + + +/** GSM-HR (ETSI 06.20, 3GPP 46.020) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_GSMHRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_GSMHRTYPE; + + +/** GSM-EFR (ETSI 06.60, 3GPP 46.060) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_GSMEFRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_GSMEFRTYPE; + + +/** TDMA FR (TIA/EIA-136-420, VSELP 7.95kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_TDMAFRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_TDMAFRTYPE; + + +/** TDMA EFR (TIA/EIA-136-410, ACELP 7.4kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_TDMAEFRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_TDMAEFRTYPE; + + +/** PDC FR ( RCR-27, VSELP 6.7kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_PDCFRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_PDCFRTYPE; + + +/** PDC EFR ( RCR-27, ACELP 6.7kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_PDCEFRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_PDCEFRTYPE; + +/** PDC HR ( RCR-27, PSI-CELP 3.45kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_PDCHRTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ + OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ +} OMX_AUDIO_PARAM_PDCHRTYPE; + + +/** CDMA Rate types */ +typedef enum OMX_AUDIO_CDMARATETYPE { + OMX_AUDIO_CDMARateBlank = 0, /**< CDMA encoded frame is blank */ + OMX_AUDIO_CDMARateFull, /**< CDMA encoded frame in full rate */ + OMX_AUDIO_CDMARateHalf, /**< CDMA encoded frame in half rate */ + OMX_AUDIO_CDMARateQuarter, /**< CDMA encoded frame in quarter rate */ + OMX_AUDIO_CDMARateEighth, /**< CDMA encoded frame in eighth rate (DTX)*/ + OMX_AUDIO_CDMARateErasure, /**< CDMA erasure frame */ + OMX_AUDIO_CDMARateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_CDMARateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_CDMARateMax = 0x7FFFFFFF +} OMX_AUDIO_CDMARATETYPE; + + +/** QCELP8 (TIA/EIA-96, up to 8kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_QCELP8TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable + rate or unknown bit rates */ + OMX_AUDIO_CDMARATETYPE eCDMARate; /**< Frame rate */ + OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 */ + OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 */ +} OMX_AUDIO_PARAM_QCELP8TYPE; + + +/** QCELP13 ( CDMA, EIA/TIA-733, 13.3kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_QCELP13TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_AUDIO_CDMARATETYPE eCDMARate; /**< Frame rate */ + OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 */ + OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 */ +} OMX_AUDIO_PARAM_QCELP13TYPE; + + +/** EVRC ( CDMA, EIA/TIA-127, RCELP up to 8.55kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_EVRCTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_AUDIO_CDMARATETYPE eCDMARate; /**< actual Frame rate */ + OMX_BOOL bRATE_REDUCon; /**< RATE_REDUCtion is requested for this frame */ + OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 */ + OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 */ + OMX_BOOL bHiPassFilter; /**< Enable encoder's High Pass Filter */ + OMX_BOOL bNoiseSuppressor; /**< Enable encoder's noise suppressor pre-processing */ + OMX_BOOL bPostFilter; /**< Enable decoder's post Filter */ +} OMX_AUDIO_PARAM_EVRCTYPE; + + +/** SMV ( up to 8.55kbps coder) stream format parameters */ +typedef struct OMX_AUDIO_PARAM_SMVTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels in the data stream (not + necessarily the same as the number of channels + to be rendered. */ + OMX_AUDIO_CDMARATETYPE eCDMARate; /**< Frame rate */ + OMX_BOOL bRATE_REDUCon; /**< RATE_REDUCtion is requested for this frame */ + OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 ??*/ + OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 ??*/ + OMX_BOOL bHiPassFilter; /**< Enable encoder's High Pass Filter ??*/ + OMX_BOOL bNoiseSuppressor; /**< Enable encoder's noise suppressor pre-processing */ + OMX_BOOL bPostFilter; /**< Enable decoder's post Filter ??*/ +} OMX_AUDIO_PARAM_SMVTYPE; + + +/** MIDI Format + * @ingroup midi + */ +typedef enum OMX_AUDIO_MIDIFORMATTYPE +{ + OMX_AUDIO_MIDIFormatUnknown = 0, /**< MIDI Format unknown or don't care */ + OMX_AUDIO_MIDIFormatSMF0, /**< Standard MIDI File Type 0 */ + OMX_AUDIO_MIDIFormatSMF1, /**< Standard MIDI File Type 1 */ + OMX_AUDIO_MIDIFormatSMF2, /**< Standard MIDI File Type 2 */ + OMX_AUDIO_MIDIFormatSPMIDI, /**< SP-MIDI */ + OMX_AUDIO_MIDIFormatXMF0, /**< eXtensible Music Format type 0 */ + OMX_AUDIO_MIDIFormatXMF1, /**< eXtensible Music Format type 1 */ + OMX_AUDIO_MIDIFormatMobileXMF, /**< Mobile XMF (eXtensible Music Format type 2) */ + OMX_AUDIO_MIDIFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_MIDIFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_MIDIFormatMax = 0x7FFFFFFF +} OMX_AUDIO_MIDIFORMATTYPE; + + +/** MIDI params + * @ingroup midi + */ +typedef struct OMX_AUDIO_PARAM_MIDITYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nFileSize; /**< size of the MIDI file in bytes, where the entire + MIDI file passed in, otherwise if 0x0, the MIDI data + is merged and streamed (instead of passed as an + entire MIDI file) */ + OMX_BU32 sMaxPolyphony; /**< Specifies the maximum simultaneous polyphonic + voices. A value of zero indicates that the default + polyphony of the device is used */ + OMX_BOOL bLoadDefaultSound; /**< Whether to load default sound + bank at initialization */ + OMX_AUDIO_MIDIFORMATTYPE eMidiFormat; /**< Version of the MIDI file */ +} OMX_AUDIO_PARAM_MIDITYPE; + + +/** Type of the MIDI sound bank + * @ingroup midi + */ +typedef enum OMX_AUDIO_MIDISOUNDBANKTYPE { + OMX_AUDIO_MIDISoundBankUnused = 0, /**< unused/unknown soundbank type */ + OMX_AUDIO_MIDISoundBankDLS1, /**< DLS version 1 */ + OMX_AUDIO_MIDISoundBankDLS2, /**< DLS version 2 */ + OMX_AUDIO_MIDISoundBankMobileDLSBase, /**< Mobile DLS, using the base functionality */ + OMX_AUDIO_MIDISoundBankMobileDLSPlusOptions, /**< Mobile DLS, using the specification-defined optional feature set */ + OMX_AUDIO_MIDISoundBankKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_MIDISoundBankVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_MIDISoundBankMax = 0x7FFFFFFF +} OMX_AUDIO_MIDISOUNDBANKTYPE; + + +/** Bank Layout describes how bank MSB & LSB are used in the DLS instrument definitions sound bank + * @ingroup midi + */ +typedef enum OMX_AUDIO_MIDISOUNDBANKLAYOUTTYPE { + OMX_AUDIO_MIDISoundBankLayoutUnused = 0, /**< unused/unknown soundbank type */ + OMX_AUDIO_MIDISoundBankLayoutGM, /**< GS layout (based on bank MSB 0x00) */ + OMX_AUDIO_MIDISoundBankLayoutGM2, /**< General MIDI 2 layout (using MSB 0x78/0x79, LSB 0x00) */ + OMX_AUDIO_MIDISoundBankLayoutUser, /**< Does not conform to any bank numbering standards */ + OMX_AUDIO_MIDISoundBankLayoutKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_MIDISoundBankLayoutVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_MIDISoundBankLayoutMax = 0x7FFFFFFF +} OMX_AUDIO_MIDISOUNDBANKLAYOUTTYPE; + + +/** MIDI params to load/unload user soundbank + * @ingroup midi + */ +typedef struct OMX_AUDIO_PARAM_MIDILOADUSERSOUNDTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nDLSIndex; /**< DLS file index to be loaded */ + OMX_U32 nDLSSize; /**< Size in bytes */ + OMX_PTR pDLSData; /**< Pointer to DLS file data */ + OMX_AUDIO_MIDISOUNDBANKTYPE eMidiSoundBank; /**< Midi sound bank type enumeration */ + OMX_AUDIO_MIDISOUNDBANKLAYOUTTYPE eMidiSoundBankLayout; /**< Midi sound bank layout enumeration */ +} OMX_AUDIO_PARAM_MIDILOADUSERSOUNDTYPE; + + +/** Structure for Live MIDI events and MIP messages. + * (MIP = Maximum Instantaneous Polyphony; part of the SP-MIDI standard.) + * @ingroup midi + */ +typedef struct OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_U32 nMidiEventSize; /**< Size of immediate MIDI events or MIP message in bytes */ + OMX_U8 nMidiEvents[1]; /**< MIDI event array to be rendered immediately, or an + array for the MIP message buffer, where the size is + indicated by nMidiEventSize */ +} OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE; + + +/** MIDI sound bank/ program pair in a given channel + * @ingroup midi + */ +typedef struct OMX_AUDIO_CONFIG_MIDISOUNDBANKPROGRAMTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_U32 nChannel; /**< Valid channel values range from 1 to 16 */ + OMX_U16 nIDProgram; /**< Valid program ID range is 1 to 128 */ + OMX_U16 nIDSoundBank; /**< Sound bank ID */ + OMX_U32 nUserSoundBankIndex;/**< User soundbank index, easier to access soundbanks + by index if multiple banks are present */ +} OMX_AUDIO_CONFIG_MIDISOUNDBANKPROGRAMTYPE; + + +/** MIDI control + * @ingroup midi + */ +typedef struct OMX_AUDIO_CONFIG_MIDICONTROLTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BS32 sPitchTransposition; /**< Pitch transposition in semitones, stored as Q22.10 + format based on JAVA MMAPI (JSR-135) requirement */ + OMX_BU32 sPlayBackRate; /**< Relative playback rate, stored as Q14.17 fixed-point + number based on JSR-135 requirement */ + OMX_BU32 sTempo ; /**< Tempo in beats per minute (BPM), stored as Q22.10 + fixed-point number based on JSR-135 requirement */ + OMX_U32 nMaxPolyphony; /**< Specifies the maximum simultaneous polyphonic + voices. A value of zero indicates that the default + polyphony of the device is used */ + OMX_U32 nNumRepeat; /**< Number of times to repeat playback */ + OMX_U32 nStopTime; /**< Time in milliseconds to indicate when playback + will stop automatically. Set to zero if not used */ + OMX_U16 nChannelMuteMask; /**< 16 bit mask for channel mute status */ + OMX_U16 nChannelSoloMask; /**< 16 bit mask for channel solo status */ + OMX_U32 nTrack0031MuteMask; /**< 32 bit mask for track mute status. Note: This is for tracks 0-31 */ + OMX_U32 nTrack3263MuteMask; /**< 32 bit mask for track mute status. Note: This is for tracks 32-63 */ + OMX_U32 nTrack0031SoloMask; /**< 32 bit mask for track solo status. Note: This is for tracks 0-31 */ + OMX_U32 nTrack3263SoloMask; /**< 32 bit mask for track solo status. Note: This is for tracks 32-63 */ + +} OMX_AUDIO_CONFIG_MIDICONTROLTYPE; + + +/** MIDI Playback States + * @ingroup midi + */ +typedef enum OMX_AUDIO_MIDIPLAYBACKSTATETYPE { + OMX_AUDIO_MIDIPlayBackStateUnknown = 0, /**< Unknown state or state does not map to + other defined states */ + OMX_AUDIO_MIDIPlayBackStateClosedEngaged, /**< No MIDI resource is currently open. + The MIDI engine is currently processing + MIDI events. */ + OMX_AUDIO_MIDIPlayBackStateParsing, /**< A MIDI resource is open and is being + primed. The MIDI engine is currently + processing MIDI events. */ + OMX_AUDIO_MIDIPlayBackStateOpenEngaged, /**< A MIDI resource is open and primed but + not playing. The MIDI engine is currently + processing MIDI events. The transition to + this state is only possible from the + OMX_AUDIO_MIDIPlayBackStatePlaying state, + when the 'playback head' reaches the end + of media data or the playback stops due + to stop time set.*/ + OMX_AUDIO_MIDIPlayBackStatePlaying, /**< A MIDI resource is open and currently + playing. The MIDI engine is currently + processing MIDI events.*/ + OMX_AUDIO_MIDIPlayBackStatePlayingPartially, /**< Best-effort playback due to SP-MIDI/DLS + resource constraints */ + OMX_AUDIO_MIDIPlayBackStatePlayingSilently, /**< Due to system resource constraints and + SP-MIDI content constraints, there is + no audible MIDI content during playback + currently. The situation may change if + resources are freed later.*/ + OMX_AUDIO_MIDIPlayBackStateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_MIDIPlayBackStateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_MIDIPlayBackStateMax = 0x7FFFFFFF +} OMX_AUDIO_MIDIPLAYBACKSTATETYPE; + + +/** MIDI status + * @ingroup midi + */ +typedef struct OMX_AUDIO_CONFIG_MIDISTATUSTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U16 nNumTracks; /**< Number of MIDI tracks in the file, read only field. + NOTE: May not return a meaningful value until the entire + file is parsed and buffered. */ + OMX_U32 nDuration; /**< The length of the currently open MIDI resource + in milliseconds. NOTE: May not return a meaningful value + until the entire file is parsed and buffered. */ + OMX_U32 nPosition; /**< Current Position of the MIDI resource being played + in milliseconds */ + OMX_BOOL bVibra; /**< Does Vibra track exist? NOTE: May not return a meaningful + value until the entire file is parsed and buffered. */ + OMX_U32 nNumMetaEvents; /**< Total number of MIDI Meta Events in the currently + open MIDI resource. NOTE: May not return a meaningful value + until the entire file is parsed and buffered. */ + OMX_U32 nNumActiveVoices; /**< Number of active voices in the currently playing + MIDI resource. NOTE: May not return a meaningful value until + the entire file is parsed and buffered. */ + OMX_AUDIO_MIDIPLAYBACKSTATETYPE eMIDIPlayBackState; /**< MIDI playback state enumeration, read only field */ +} OMX_AUDIO_CONFIG_MIDISTATUSTYPE; + + +/** MIDI Meta Event structure one per Meta Event. + * MIDI Meta Events are like audio metadata, except that they are interspersed + * with the MIDI content throughout the file and are not localized in the header. + * As such, it is necessary to retrieve information about these Meta Events from + * the engine, as it encounters these Meta Events within the MIDI content. + * For example, SMF files can have up to 14 types of MIDI Meta Events (copyright, + * author, default tempo, etc.) scattered throughout the file. + * @ingroup midi + */ +typedef struct OMX_AUDIO_CONFIG_MIDIMETAEVENTTYPE{ + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nIndex; /**< Index of Meta Event */ + OMX_U8 nMetaEventType; /**< Meta Event Type, 7bits (i.e. 0 - 127) */ + OMX_U32 nMetaEventSize; /**< size of the Meta Event in bytes */ + OMX_U32 nTrack; /**< track number for the meta event */ + OMX_U32 nPosition; /**< Position of the meta-event in milliseconds */ +} OMX_AUDIO_CONFIG_MIDIMETAEVENTTYPE; + + +/** MIDI Meta Event Data structure - one per Meta Event. + * @ingroup midi + */ +typedef struct OMX_AUDIO_CONFIG_MIDIMETAEVENTDATATYPE{ + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nIndex; /**< Index of Meta Event */ + OMX_U32 nMetaEventSize; /**< size of the Meta Event in bytes */ + OMX_U8 nData[1]; /**< array of one or more bytes of meta data + as indicated by the nMetaEventSize field */ +} OMX_AUDIO_CONFIG__MIDIMETAEVENTDATATYPE; + + +/** Audio Volume adjustment for a port */ +typedef struct OMX_AUDIO_CONFIG_VOLUMETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port index indicating which port to + set. Select the input port to set + just that port's volume. Select the + output port to adjust the master + volume. */ + OMX_BOOL bLinear; /**< Is the volume to be set in linear (0.100) + or logarithmic scale (mB) */ + OMX_BS32 sVolume; /**< Volume linear setting in the 0..100 range, OR + Volume logarithmic setting for this port. The values + for volume are in mB (millibels = 1/100 dB) relative + to a gain of 1 (e.g. the output is the same as the + input level). Values are in mB from nMax + (maximum volume) to nMin mB (typically negative). + Since the volume is "voltage" + and not a "power", it takes a setting of + -600 mB to decrease the volume by 1/2. If + a component cannot accurately set the + volume to the requested value, it must + set the volume to the closest value BELOW + the requested value. When getting the + volume setting, the current actual volume + must be returned. */ +} OMX_AUDIO_CONFIG_VOLUMETYPE; + + +/** Audio Volume adjustment for a channel */ +typedef struct OMX_AUDIO_CONFIG_CHANNELVOLUMETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port index indicating which port to + set. Select the input port to set + just that port's volume. Select the + output port to adjust the master + volume. */ + OMX_U32 nChannel; /**< channel to select from 0 to N-1, + using OMX_ALL to apply volume settings + to all channels */ + OMX_BOOL bLinear; /**< Is the volume to be set in linear (0.100) or + logarithmic scale (mB) */ + OMX_BS32 sVolume; /**< Volume linear setting in the 0..100 range, OR + Volume logarithmic setting for this port. + The values for volume are in mB + (millibels = 1/100 dB) relative to a gain + of 1 (e.g. the output is the same as the + input level). Values are in mB from nMax + (maximum volume) to nMin mB (typically negative). + Since the volume is "voltage" + and not a "power", it takes a setting of + -600 mB to decrease the volume by 1/2. If + a component cannot accurately set the + volume to the requested value, it must + set the volume to the closest value BELOW + the requested value. When getting the + volume setting, the current actual volume + must be returned. */ + OMX_BOOL bIsMIDI; /**< TRUE if nChannel refers to a MIDI channel, + FALSE otherwise */ +} OMX_AUDIO_CONFIG_CHANNELVOLUMETYPE; + + +/** Audio balance setting */ +typedef struct OMX_AUDIO_CONFIG_BALANCETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port index indicating which port to + set. Select the input port to set + just that port's balance. Select the + output port to adjust the master + balance. */ + OMX_S32 nBalance; /**< balance setting for this port + (-100 to 100, where -100 indicates + all left, and no right */ +} OMX_AUDIO_CONFIG_BALANCETYPE; + + +/** Audio Port mute */ +typedef struct OMX_AUDIO_CONFIG_MUTETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port index indicating which port to + set. Select the input port to set + just that port's mute. Select the + output port to adjust the master + mute. */ + OMX_BOOL bMute; /**< Mute setting for this port */ +} OMX_AUDIO_CONFIG_MUTETYPE; + + +/** Audio Channel mute */ +typedef struct OMX_AUDIO_CONFIG_CHANNELMUTETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannel; /**< channel to select from 0 to N-1, + using OMX_ALL to apply mute settings + to all channels */ + OMX_BOOL bMute; /**< Mute setting for this channel */ + OMX_BOOL bIsMIDI; /**< TRUE if nChannel refers to a MIDI channel, + FALSE otherwise */ +} OMX_AUDIO_CONFIG_CHANNELMUTETYPE; + + + +/** Enable / Disable for loudness control, which boosts bass and to a + * smaller extent high end frequencies to compensate for hearing + * ability at the extreme ends of the audio spectrum + */ +typedef struct OMX_AUDIO_CONFIG_LOUDNESSTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bLoudness; /**< Enable/disable for loudness */ +} OMX_AUDIO_CONFIG_LOUDNESSTYPE; + + +/** Enable / Disable for bass, which controls low frequencies + */ +typedef struct OMX_AUDIO_CONFIG_BASSTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bEnable; /**< Enable/disable for bass control */ + OMX_S32 nBass; /**< bass setting for the port, as a + continuous value from -100 to 100 + (0 means no change in bass level)*/ +} OMX_AUDIO_CONFIG_BASSTYPE; + + +/** Enable / Disable for treble, which controls high frequencies tones + */ +typedef struct OMX_AUDIO_CONFIG_TREBLETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bEnable; /**< Enable/disable for treble control */ + OMX_S32 nTreble; /**< treble setting for the port, as a + continuous value from -100 to 100 + (0 means no change in treble level) */ +} OMX_AUDIO_CONFIG_TREBLETYPE; + + +/** An equalizer is typically used for two reasons: to compensate for an + * sub-optimal frequency response of a system to make it sound more natural + * or to create intentionally some unnatural coloring to the sound to create + * an effect. + * @ingroup effects + */ +typedef struct OMX_AUDIO_CONFIG_EQUALIZERTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bEnable; /**< Enable/disable for equalizer */ + OMX_BU32 sBandIndex; /**< Band number to be set. Upper Limit is + N-1, where N is the number of bands, lower limit is 0 */ + OMX_BU32 sCenterFreq; /**< Center frequecies in Hz. This is a + read only element and is used to determine + the lower, center and upper frequency of + this band. */ + OMX_BS32 sBandLevel; /**< band level in millibels */ +} OMX_AUDIO_CONFIG_EQUALIZERTYPE; + + +/** Stereo widening mode type + * @ingroup effects + */ +typedef enum OMX_AUDIO_STEREOWIDENINGTYPE { + OMX_AUDIO_StereoWideningHeadphones, /**< Stereo widening for loudspeakers */ + OMX_AUDIO_StereoWideningLoudspeakers, /**< Stereo widening for closely spaced loudspeakers */ + OMX_AUDIO_StereoWideningKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_StereoWideningVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_StereoWideningMax = 0x7FFFFFFF +} OMX_AUDIO_STEREOWIDENINGTYPE; + + +/** Control for stereo widening, which is a special 2-channel + * case of the audio virtualizer effect. For example, for 5.1-channel + * output, it translates to virtual surround sound. + * @ingroup effects + */ +typedef struct OMX_AUDIO_CONFIG_STEREOWIDENINGTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bEnable; /**< Enable/disable for stereo widening control */ + OMX_AUDIO_STEREOWIDENINGTYPE eWideningType; /**< Stereo widening algorithm type */ + OMX_U32 nStereoWidening; /**< stereo widening setting for the port, + as a continuous value from 0 to 100 */ +} OMX_AUDIO_CONFIG_STEREOWIDENINGTYPE; + + +/** The chorus effect (or ``choralizer'') is any signal processor which makes + * one sound source (such as a voice) sound like many such sources singing + * (or playing) in unison. Since performance in unison is never exact, chorus + * effects simulate this by making independently modified copies of the input + * signal. Modifications may include (1) delay, (2) frequency shift, and + * (3) amplitude modulation. + * @ingroup effects + */ +typedef struct OMX_AUDIO_CONFIG_CHORUSTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bEnable; /**< Enable/disable for chorus */ + OMX_BU32 sDelay; /**< average delay in milliseconds */ + OMX_BU32 sModulationRate; /**< rate of modulation in millihertz */ + OMX_U32 nModulationDepth; /**< depth of modulation as a percentage of + delay (i.e. 0 to 100) */ + OMX_BU32 nFeedback; /**< Feedback from chorus output to input in percentage */ +} OMX_AUDIO_CONFIG_CHORUSTYPE; + + +/** Reverberation is part of the reflected sound that follows the early + * reflections. In a typical room, this consists of a dense succession of + * echoes whose energy decays exponentially. The reverberation effect structure + * as defined here includes both (early) reflections as well as (late) reverberations. + * @ingroup effects + */ +typedef struct OMX_AUDIO_CONFIG_REVERBERATIONTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bEnable; /**< Enable/disable for reverberation control */ + OMX_BS32 sRoomLevel; /**< Intensity level for the whole room effect + (i.e. both early reflections and late + reverberation) in millibels */ + OMX_BS32 sRoomHighFreqLevel; /**< Attenuation at high frequencies + relative to the intensity at low + frequencies in millibels */ + OMX_BS32 sReflectionsLevel; /**< Intensity level of early reflections + (relative to room value), in millibels */ + OMX_BU32 sReflectionsDelay; /**< Delay time of the first reflection relative + to the direct path, in milliseconds */ + OMX_BS32 sReverbLevel; /**< Intensity level of late reverberation + relative to room level, in millibels */ + OMX_BU32 sReverbDelay; /**< Time delay from the first early reflection + to the beginning of the late reverberation + section, in milliseconds */ + OMX_BU32 sDecayTime; /**< Late reverberation decay time at low + frequencies, in milliseconds */ + OMX_BU32 nDecayHighFreqRatio; /**< Ratio of high frequency decay time relative + to low frequency decay time in percent */ + OMX_U32 nDensity; /**< Modal density in the late reverberation decay, + in percent (i.e. 0 - 100) */ + OMX_U32 nDiffusion; /**< Echo density in the late reverberation decay, + in percent (i.e. 0 - 100) */ + OMX_BU32 sReferenceHighFreq; /**< Reference high frequency in Hertz. This is + the frequency used as the reference for all + the high-frequency settings above */ + +} OMX_AUDIO_CONFIG_REVERBERATIONTYPE; + + +/** Possible settings for the Echo Cancelation structure to use + * @ingroup effects + */ +typedef enum OMX_AUDIO_ECHOCANTYPE { + OMX_AUDIO_EchoCanOff = 0, /**< Echo Cancellation is disabled */ + OMX_AUDIO_EchoCanNormal, /**< Echo Cancellation normal operation - + echo from plastics and face */ + OMX_AUDIO_EchoCanHFree, /**< Echo Cancellation optimized for + Hands Free operation */ + OMX_AUDIO_EchoCanCarKit, /**< Echo Cancellation optimized for + Car Kit (longer echo) */ + OMX_AUDIO_EchoCanKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_AUDIO_EchoCanVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_AUDIO_EchoCanMax = 0x7FFFFFFF +} OMX_AUDIO_ECHOCANTYPE; + + +/** Enable / Disable for echo cancelation, which removes undesired echo's + * from the audio + * @ingroup effects + */ +typedef struct OMX_AUDIO_CONFIG_ECHOCANCELATIONTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_AUDIO_ECHOCANTYPE eEchoCancelation; /**< Echo cancelation settings */ +} OMX_AUDIO_CONFIG_ECHOCANCELATIONTYPE; + + +/** Enable / Disable for noise reduction, which undesired noise from + * the audio + * @ingroup effects + */ +typedef struct OMX_AUDIO_CONFIG_NOISEREDUCTIONTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BOOL bNoiseReduction; /**< Enable/disable for noise reduction */ +} OMX_AUDIO_CONFIG_NOISEREDUCTIONTYPE; + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ + diff --git a/interface/vmcs_host/khronos/IL/OMX_Broadcom.h b/interface/vmcs_host/khronos/IL/OMX_Broadcom.h new file mode 100755 index 0000000..697f1ca --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Broadcom.h @@ -0,0 +1,2685 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// OpenMAX IL - Broadcom specific types + +#ifndef OMX_Broadcom_h +#define OMX_Broadcom_h + +#include "OMX_Component.h" + +// for use in buffer headers - marks the contained data +// as being a codec header +#define OMX_BUFFERFLAG_TIME_UNKNOWN 0x00000100 + +//for use in buffer headers - marks the buffer as being the +//snapshot preview image from a still capture. +//Mainly to be used with the DisplayFunction callback from camera. +#define OMX_BUFFERFLAG_CAPTURE_PREVIEW 0x00000200 + +/* Mark the end of a NAL unit produced by a video encoder. + */ +#define OMX_BUFFERFLAG_ENDOFNAL 0x00000400 + +/* Marks pBuffer in OMX_BUFFERHEADERTYPE as containing a fragment list instead of the actual buffer + */ +#define OMX_BUFFERFLAG_FRAGMENTLIST 0x00000800 + +/* Marks the start of a new sequence of data following any kind of seek operation. + */ +#define OMX_BUFFERFLAG_DISCONTINUITY 0x00001000 + +/** Codec side information Flag: +* OMX_BUFFERFLAG_CODECSIDEINFO is an optional flag that is set by an +* output port when all bytes in the buffer form part or all of a set of +* codec specific side information. For example, distortion information +* estimated by H.264 encoder can be sent using this flag to signal +* the decoder quality +*/ +#define OMX_BUFFERFLAG_CODECSIDEINFO 0x00002000 + +// for use in buffer headers - indicated the timestamp is a DTS rather than PTS +#define OMX_BUFFERFLAG_TIME_IS_DTS 0x000004000 + +// for use in buffer headers - signals that a video picture is interlaced +#define OMX_BUFFERFLAG_INTERLACED 0x000010000 + +// Signals that the top field of the current interlaced frame should be displayed first +#define OMX_BUFFERFLAG_TOP_FIELD_FIRST 0x000020000 + +/** + * Macros to convert to OMX_TICKS from a signed 64 bit value and + * vice-versa. These macros don't actually do anything unless OMX_TICKS + * is defined as a two-part structure (instead of a native signed 64-bit type). + **/ +#ifndef OMX_SKIP64BIT + #define omx_ticks_from_s64(s) (s) + #define omx_ticks_to_s64(t) (t) +#else + static inline OMX_TICKS omx_ticks_from_s64(signed long long s) { OMX_TICKS t; t.nLowPart = (OMX_U32)s; t.nHighPart = (OMX_U32)(s>>32); return t; } + #define omx_ticks_to_s64(t) ((t).nLowPart | ((uint64_t)((t).nHighPart) << 32)) +#endif /* OMX_SKIP64BIT */ + +/* Buffer fragment descriptor */ +typedef struct OMX_BUFFERFRAGMENTTYPE { + OMX_PTR pBuffer; /**< Pointer to actual block of memory that is acting as the fragment buffer */ + OMX_U32 nLen; /**< number of bytes in the buffer */ +} OMX_BUFFERFRAGMENTTYPE; + +/* OMX_IndexParamBrcmEnableIJGTableScaling: JPEG Quality Table Setting. */ +typedef struct OMX_PARAM_IJGSCALINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnabled; +} OMX_PARAM_IJGSCALINGTYPE; +/* +The boolean \code{bEnabled} value determines whether the component uses +the standard IJG quality tables when encoding images. +*/ + + +/* OMX_IndexConfigTimeInvalidStartTime: Invalid Start Times */ +/* +This allows clock clients to supply a start time notification to the +clock whilst indicating that this time is invalid. +*/ + +/* OMX_IndexParamBrcmMaxFrameSkips: Frame timestamp jumps */ +/* +This number represents the number of times a jump in frame timestamps +has been observed that is greater than expected. +*/ + +/* OMX_IndexConfigAsynchronousFailureURI: Asynchronous Failure Filename */ +/* +This allows the client to query for the filename that cause an asynchronous +output error. +*/ + +/* OMX_IndexParamAsynchronousOutput: Asynchronous Output */ +/* +The allows the client to specify to a component that is writing files +that this writing may happen asynchronously, including opening and closing +of files. +*/ + +/* OMX_IndexConfigClockAdjustment: Clock Adjustment */ +/* +This allows the client to read from the clock the total time +adjustment made to the clock whilst running by the reference clock. +If the reference clock sends a time that causes the media time to jump +this difference is added to the total, which can be reported via this +index. When the stream restarts by setting the clock state to +\code{OMX_TIME_ClockStateRunning} or +\code{OMX_TIME_ClockStateWaitingForStartTime} this adjustment total is +set to zero. +*/ + +/* OMX_IndexParamBrcmDataUnit: Data Unit */ +/* +The data unit is an indication to components connected to this +component of the type of data delivery available. +\code{OMX_DataUnitCodedPicture} indicates that we are able to give +framing information, using the \code{OMX_BUFFERFLAG_ENDOFFRAME} flag to +indicate that the data contained finishes a complete +frame. \code{OMX_DataUnitArbitraryStreamSection} indicates that no +end-of-frame markers will be present, and the decoder should perform +the steps necessary to decode the stream. The other enum values are +not used. +*/ + +/* OMX_IndexConfigPresentationOffset: Presentation Offset */ +/* +The value of \code{nTimestamp} is added to the offset requested for +each new input frame. Takes effect for all new input frames, and has +no effect on the offset used for currently-queued frames. A positive +value will make the requested port earlier relative to other streams, +a negative value will make the requested port later relative to other +streams. +*/ + +/* OMX_IndexConfigSingleStep: Single Step */ +/* +When setting this config on a paused clock, where the \code{nU32} +value is non-zero and \code{nPortIndex} is OMX_ALL, the media clock +will advance through the next \code{nU32} next requested media +times. A paused clock is in running state but has a time scale of +0. This will trigger the display of some video frames, so allowing +single-stepping functionality. This config can be set multiple times, +and will buffer up stepping requests until we have media requests to +fulfil, or the clock is stopped or un-paused. + +This config can also be used on some video output ports and, if +\code{nU32} is non-zero, requests that the output port forwards the +next \code{nU32} frames appending an EOS marker on the last frame, and +then ceases to forward data on this port. If \code{nU32} is zero, any +previous request to forward a limited number of frames is cancelled +and the default behaviour of this port will resume. +*/ + +/* OMX_IndexParamCameraCamplusId: Camera Subsystem Identification */ +/* +This parameter allows the configuration of the identifier to be used +to initialise the Broadcom Camplus subsystem that sits beneath the +camera component. If only one instance of the camera component is +used, the default value can be used. If more than one instance is +required, they must each have their own unique values for this +parameter. It is also used to tie the component to the image pool +created with \code{OMX_Set upCamPools}. +*/ + +/* OMX_IndexConfigAudioRenderingLatency: Audio Rendering Latency */ +/* +This config allows the client to query the current latency of audio +rendering. The latency is returned as the number of samples that +an audio rendering component has received but have not been played. +*/ + +/* OMX_IndexConfigBrcmPoolMemAllocSize: Pool memory usage values */ +/* +This config allows the client to query how much memory is being used by +the component for any image pools. +*/ + +/* OMX_IndexConfigDisplayRegion: Display Region */ +typedef enum OMX_DISPLAYTRANSFORMTYPE{ + OMX_DISPLAY_ROT0 = 0, + OMX_DISPLAY_MIRROR_ROT0 = 1, + OMX_DISPLAY_MIRROR_ROT180 = 2, + OMX_DISPLAY_ROT180 = 3, + OMX_DISPLAY_MIRROR_ROT90 = 4, + OMX_DISPLAY_ROT270 = 5, + OMX_DISPLAY_ROT90 = 6, + OMX_DISPLAY_MIRROR_ROT270 = 7, + OMX_DISPLAY_DUMMY = 0x7FFFFFFF +} OMX_DISPLAYTRANSFORMTYPE; + +typedef struct OMX_DISPLAYRECTTYPE { + OMX_S16 x_offset; + OMX_S16 y_offset; + OMX_S16 width; + OMX_S16 height; +} OMX_DISPLAYRECTTYPE; + +typedef enum OMX_DISPLAYMODETYPE { + OMX_DISPLAY_MODE_FILL = 0, + OMX_DISPLAY_MODE_LETTERBOX = 1, + // these allow a left eye source->dest to be specified and the right eye mapping will be inferred by symmetry + OMX_DISPLAY_MODE_STEREO_LEFT_TO_LEFT = 2, + OMX_DISPLAY_MODE_STEREO_TOP_TO_TOP = 3, + OMX_DISPLAY_MODE_STEREO_LEFT_TO_TOP = 4, + OMX_DISPLAY_MODE_STEREO_TOP_TO_LEFT = 5, + OMX_DISPLAY_MODE_DUMMY = 0x7FFFFFFF +} OMX_DISPLAYMODETYPE; + +typedef enum OMX_DISPLAYSETTYPE { + OMX_DISPLAY_SET_NONE = 0, + OMX_DISPLAY_SET_NUM = 1, + OMX_DISPLAY_SET_FULLSCREEN = 2, + OMX_DISPLAY_SET_TRANSFORM = 4, + OMX_DISPLAY_SET_DEST_RECT = 8, + OMX_DISPLAY_SET_SRC_RECT = 0x10, + OMX_DISPLAY_SET_MODE = 0x20, + OMX_DISPLAY_SET_PIXEL = 0x40, + OMX_DISPLAY_SET_NOASPECT = 0x80, + OMX_DISPLAY_SET_LAYER = 0x100, + OMX_DISPLAY_SET_COPYPROTECT = 0x200, + OMX_DISPLAY_SET_ALPHA = 0x400, + OMX_DISPLAY_SET_DUMMY = 0x7FFFFFFF +} OMX_DISPLAYSETTYPE; + +typedef struct OMX_CONFIG_DISPLAYREGIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_DISPLAYSETTYPE set; + OMX_U32 num; + OMX_BOOL fullscreen; + OMX_DISPLAYTRANSFORMTYPE transform; + OMX_DISPLAYRECTTYPE dest_rect; + OMX_DISPLAYRECTTYPE src_rect; + OMX_BOOL noaspect; + OMX_DISPLAYMODETYPE mode; + OMX_U32 pixel_x; + OMX_U32 pixel_y; + OMX_S32 layer; + OMX_BOOL copyprotect_required; + OMX_U32 alpha; + OMX_U32 wfc_context_width; + OMX_U32 wfc_context_height; +} OMX_CONFIG_DISPLAYREGIONTYPE; +/* +This config sets the output display device, as well as the region used +on the output display, any display transformation, and some flags to +indicate how to scale the image. + +The structure uses a bitfield, \code{set}, to indicate which fields are set +and should be used. All other fields will maintain their current +value. + +\code{num} describes the display output device, with 0 typically being +a directly connected LCD display. + +\code{fullscreen} indicates that we are using the full device screen +area, rather than a window of the display. If fullscreen is false, +then dest_rect is used to specify a region of the display to use. + +\code{transform} indicates any rotation or flipping used to map frames +onto the natural display orientation. + +The \code{src_rect} indicates which area of the frame to display. If +all values are zero, the whole frame will be used. + +The \code{noaspect} flag, if set, indicates that any display scaling +should disregard the aspect ratio of the frame region being displayed. + +\code{mode} indicates how the image should be scaled to fit the +display. \code{OMX_DISPLAY_MODE_FILL} indicates that the image should +fill the screen by potentially cropping the frames. Setting +\code{mode} to \code{OMX_DISPLAY_MODE_LETTERBOX} indicates that all +the source region should be displayed and black bars added if +necessary. + +The \code{pixel_x} and \code{pixel_y} values, if non-zero, are used to +describe the size of a source pixel. If values are zero, then pixels +default to being square. + +Set the \code{layer} that the image will appear on with the +\code{layer} field. +*/ + + + +/* OMX_IndexParamSource: Source Image Configuration */ +typedef enum OMX_SOURCETYPE { + OMX_SOURCE_WHITE = 0, // all white images + OMX_SOURCE_BLACK = 1, // all black images + OMX_SOURCE_DIAGONAL = 2, // greyscale diagonal stripes + OMX_SOURCE_NOISE = 3, // random pixel values + OMX_SOURCE_RANDOM = 4, // a shaded random pattern of colours + OMX_SOURCE_COLOUR = 5, // a solid colour determined by nParam + OMX_SOURCE_BLOCKS = 6, // random coloured blocks of 16x16 size + OMX_SOURCE_SWIRLY, // a swirly pattern used for encode testing + OMX_SOURCE_DUMMY = 0x7FFFFFFF +} OMX_SOURCETYPE; + +typedef struct OMX_PARAM_SOURCETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_SOURCETYPE eType; + OMX_U32 nParam; + OMX_U32 nFrameCount; + OMX_U32 xFrameRate; +} OMX_PARAM_SOURCETYPE; +/* +The source type determines the kind of image that is produced. Not all +combinations of source type and image type are supported. The +\code{OMX_SOURCE_SWIRLY} setting can only be used with YUV420 packed +planar image formats. When producing RGB565 image format, the +\code{OMX_SOURCE_DIAGONAL} and \code{OMX_SOURCE_RANDOM} modes are +treated as \code{OMX_SOURCE_NOISE}. + +The \code{nParam} field is used to specify the colour for the source +colour mode, and the offset of the diagonal pattern for diagonal mode. +For the blocks mode, \code{nParam} is used as the seed for the random +colour generator. + +The \code{nFrameCount} parameter determines how many frames to send. +If it is zero, then frames are sent continuously. For any other value, +it counts down until it has sent that many frames, and then stops, +sending out an EOS. The \code{xFrameRate} setting is used to determine +the timestamp for each frame produced, or can be set to zero if +timestamps should all remain at zero. +*/ + +/* OMX_IndexParamSourceSeed: Source Random Seed */ +typedef struct OMX_PARAM_SOURCESEEDTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U16 nData[16]; +} OMX_PARAM_SOURCESEEDTYPE; +/* +This structure sets the current state of the random number generator +used for \code{OMX_SOURCE_RANDOM} source type, allowing repeatable +random image creation. +*/ + +/* OMX_IndexParamResize: Resize Control */ +typedef enum OMX_RESIZEMODETYPE { + OMX_RESIZE_NONE, + OMX_RESIZE_CROP, + OMX_RESIZE_BOX, + OMX_RESIZE_BYTES, + OMX_RESIZE_DUMMY = 0x7FFFFFFF +} OMX_RESIZEMODETYPE; + +typedef struct OMX_PARAM_RESIZETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_RESIZEMODETYPE eMode; + OMX_U32 nMaxWidth; + OMX_U32 nMaxHeight; + OMX_U32 nMaxBytes; + OMX_BOOL bPreserveAspectRatio; + OMX_BOOL bAllowUpscaling; +} OMX_PARAM_RESIZETYPE; +/* +The mode determines the kind of resize. \code{OMX_RESIZE_BOX} allow +the \code{nMaxWidth} and \code{nMaxHeight} to set a bounding box into +which the output must fit. \code{OMX_RESIZE_BYTES} allows +\code{nMaxBytes} to set the maximum number of bytes into which the +full output frame must fit. Two flags aid the setting of the output +size. \code{bPreseveAspectRatio} sets whether the resize should +preserve the aspect ratio of the incoming +image. \code{bAllowUpscaling} sets whether the resize is allowed to +increase the size of the output image compared to the size of the +input image. +*/ + +typedef struct OMX_PARAM_TESTINTERFACETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bTest; + OMX_BOOL bSetExtra; + OMX_U32 nExtra; + OMX_BOOL bSetError; + OMX_BOOL stateError[2]; +} OMX_PARAM_TESTINTERFACETYPE; + +/* OMX_IndexConfigVisualisation: Visualisation Mode */ +typedef struct OMX_CONFIG_VISUALISATIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U8 name[16]; + OMX_U8 property[64]; +} OMX_CONFIG_VISUALISATIONTYPE; + +/* +\code{name} is a string of characters specifying the type of +visualization. The component appends \code{"_vis.vll"} to the name +provided, and attempts to load a visualisation library contained in +this VLL. \code{property} contains configuration parameters and +values, which is interpreted by the visualisation library. Typically +all visualisations will accept a property string containing +\code{'mode='}, where \code{} may be a random 32 bit +integer in decimal format. If provided, this may select a random mode +from that visualisation library. +*/ + +/* +This parameter is used when creating proprietary communication with +the display component, and provides the display function for passing +images to be displayed, together with a function used to flush all +pending image updates and release all images. +*/ + +/* OMX_IndexConfigBrcmAudioDestination: Audio Destination */ +typedef struct OMX_CONFIG_BRCMAUDIODESTINATIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U8 sName[16]; +} OMX_CONFIG_BRCMAUDIODESTINATIONTYPE; +/* +This config sets the platform-specific audio destination or output +device for audio sink components (e.g. audio_render). + +\code{sName} describes the audio destination, with \code{"local"} +typically being directly connected to headphones. +*/ + +/* OMX_IndexConfigBrcmAudioSource: Audio Source */ +typedef struct OMX_CONFIG_BRCMAUDIOSOURCETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U8 sName[16]; +} OMX_CONFIG_BRCMAUDIOSOURCETYPE; +/* +This config sets the platform-specific audio source or input device +for audio source components (e.g. audio_capture). + +\code{sName} describes the audio source, with \code{"local"} +typically being directly connected to microphone. +*/ + +/* OMX_IndexConfigBrcmAudioDownmixCoefficients: Audio Downmix Coefficients */ +typedef struct OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 coeff[16]; +} OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS; +/* +This config sets the platform-specific audio downmixing coefficients for the +audio mixer component. The coefficients are 16.16 fixed point. +The even coefficients contribute to the left channel. +The odd coefficients contribute to the right channel. +L' = coeff[0] * sample[N] + coeff[2] * sample[N+1] + coeff[4] * sample[N+2] + coeff[6] * sample[N+3] + + coeff[8] * sample[N+4] + coeff[10] * sample[N+5] + coeff[12] * sample[N+6] + coeff[14] * sample[N+7] +R' = coeff[1] * sample[N] + coeff[3] * sample[N+1] + coeff[5] * sample[N+2] + coeff[7] * sample[N+3] + + coeff[9] * sample[N+4] + coeff[11] * sample[N+5] + coeff[13] * sample[N+6] + coeff[15] * sample[N+7] + +\code{coeff} describes the downmixing coefficients +*/ + +/* OMX_IndexConfigBrcmAudioDownmixCoefficients8x8: Audio Downmix Coefficient matrix */ +typedef struct OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS8x8 { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 coeff[64]; +} OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS8x8; +/* +This config sets the platform-specific audio downmixing coefficients for the +audio mixer component. The coefficients are 16.16 fixed point. +The coefficients are a 8*8 mixing matrix from 8 input channels to 8 outputs channels + +\code{coeff} describes the downmixing coefficients +*/ + +/* OMX_IndexConfigBrcmAudioMaxSample: Maximum sample seen */ +typedef struct OMX_CONFIG_BRCMAUDIOMAXSAMPLE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nMaxSample; + OMX_TICKS nTimeStamp; +} OMX_CONFIG_BRCMAUDIOMAXSAMPLE; +/* +This gets the largest sample produced (after downmixing with OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS8x8) +since this config was last read. The nTimestamp is the earliest timestamp processed. +This can be used for DRC schemes + +\code{coeff} maximum sample seen in current block +*/ + +/* OMX_IndexConfigPlayMode: Play Mode */ +typedef enum OMX_PLAYMODETYPE { + OMX_PLAYMODE_NORMAL, + OMX_PLAYMODE_FF, + OMX_PLAYMODE_REW, + OMX_PLAYMODE_DUMMY = 0x7FFFFFFF +} OMX_PLAYMODETYPE; + +typedef struct OMX_CONFIG_PLAYMODETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_PLAYMODETYPE eMode; +} OMX_CONFIG_PLAYMODETYPE; +/* +The playmode affects which frames are extracted from the media file +and passed on the output ports. \code{OMX_PLAYMODE_NORMAL} will +extract all frames, \code{OMX_PLAYMODE_FF} extracts only IDR frames +when video is present, or only occasional packets of audio if no video +is present. \code{OMX_PLAYMODE_REW} is similar to +\code{OMX_PLAYMODE_FF} but extracts packets in reverse time +order. +*/ + +typedef enum OMX_DELIVERYFORMATTYPE { + OMX_DELIVERYFORMAT_STREAM, // no framing information is known + OMX_DELIVERYFORMAT_SINGLE_PACKET, // packetised, at most one frame per buffer + OMX_DELIVERYFORMAT_DUMMY = 0x7FFFFFFF +} OMX_DELIVERYFORMATTYPE; + +typedef struct OMX_PARAM_DELIVERYFORMATTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_DELIVERYFORMATTYPE eFormat; +} OMX_PARAM_DELIVERYFORMATTYPE; + +/* OMX_IndexParamCodecConfig: Codec Configuration */ + +typedef struct OMX_PARAM_CODECCONFIGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 bCodecConfigIsComplete; + OMX_U8 nData[1]; +} OMX_PARAM_CODECCONFIGTYPE; + +/* +This parameter contains opaque data in a format specified by Broadcom +and allows out-of-band information such as cropping rectangles, aspect +ratio information, codec-specific header bytes, and other essential +information to be passed between connected components. + +\code{bCodecConfigIsCompete} specifies if the codec config is fully +contained in here and there is no need to wait for OMX_BUFFERFLAG_CODECCONFIG +buffers +*/ + +typedef struct OMX_PARAM_STILLSFUNCTIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bBuffer; + OMX_PTR (*pOpenFunc)(void); + OMX_PTR (*pCloseFunc)(void); + OMX_PTR (*pReadFunc)(void); + OMX_PTR (*pSeekFunc)(void); + OMX_PTR (*pWriteFunc)(void); +} OMX_PARAM_STILLSFUNCTIONTYPE; + +typedef void* OMX_BUFFERADDRESSHANDLETYPE; + +typedef struct OMX_PARAM_BUFFERADDRESSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nAllocLen; + OMX_BUFFERADDRESSHANDLETYPE handle; +} OMX_PARAM_BUFFERADDRESSTYPE; + +typedef struct OMX_PARAM_TUNNELSETUPTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_TUNNELSETUPTYPE sSetup; +} OMX_PARAM_TUNNELSETUPTYPE; + +/* OMX_IndexParamBrcmPortEGL: Used for querying whether a port is an EGL port or not. */ +typedef struct OMX_PARAM_BRCMPORTEGLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bPortIsEGL; +} OMX_PARAM_BRCMPORTEGLTYPE; +/* +*/ + +#define OMX_CONFIG_IMAGEFILTERPARAMS_MAXPARAMS 6 +/* OMX_IndexConfigCommonImageFilterParameters: Parameterized Image Filter */ +typedef struct OMX_CONFIG_IMAGEFILTERPARAMSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_IMAGEFILTERTYPE eImageFilter; + OMX_U32 nNumParams; + OMX_U32 nParams[OMX_CONFIG_IMAGEFILTERPARAMS_MAXPARAMS]; +} OMX_CONFIG_IMAGEFILTERPARAMSTYPE; +/* +This structure contains optional parameters for some image +filters. The following table lists all image filters that support +parameters. + + +
FilterParametersNotes + +
\code{OMX_ImageFilterSolarize} +\code{[x0 y0 y1 y2]} +Linear mapping of \code{[0,x0]} to \code{[0,y0>]} +and \code{[x0,255]} to \code{[y1,y2]}. +Default is \code{"128 128 128 0"}. + +
\code{OMX_ImageFilterSharpen} +\code{[sz [str [th]]} +\code{sz} size of filter, either 1 or 2. +\code{str} strength of filter. +\code{th} threshold of filter. +Default is \code{"1 40 20"}. + +
\code{OMX_ImageFilterFilm} +\code{[[str] [u v]]} +\code{str} strength of effect. +\code{u} sets u to constant value. +\code{v} sets v to constant value. +Default is \code{"24"}. + +
\code{OMX_ImageFilterBlur} +\code{[sz]} +\code{sz} size of filter, either 1 or 2. +Default is \code{"2"}. + +
\code{OMX_ImageFilterSaturation} +\code{[str]} +\code{str} strength of effect, in 8.8 fixed point format. u/v value +differences from 128 are multiplied by \code{str}. +Default is \code{"272"}. +
+*/ + + +/* OMX_IndexConfigTransitionControl: Transition Control */ +typedef struct OMX_CONFIG_TRANSITIONCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nPosStart; + OMX_U32 nPosEnd; + OMX_S32 nPosIncrement; + OMX_TICKS nFrameIncrement; + OMX_BOOL bSwapInputs; + OMX_U8 name[16]; + OMX_U8 property[64]; +} OMX_CONFIG_TRANSITIONCONTROLTYPE; +/* +This structure represents the internal configuration of the +transition. Transitions are generated by a loadable plug-in described +by the \code{name} field. The component appends \code{"_tran.vll"} to +the name provided, and attempts to load a transition library contained +in this VLL. The exact type of transition is configured in a +plug-in-dependent manner with the \code{property} field. All plug-ins +should accept a \code{property} field equal to +\code{"flags="}, where \code{} can be a random 32 bit +number. If \code{bSwapInputs} is false, then the start image is on +port 210, the stop image on port 211. These are reversed if +\code{bSwapInputs} is true. + +Transition frames are generated from the plug-in by referencing a +frame position in [0,65536], where position 0 is the start image, +position 65536 is the stop image. The first frame position generated +is \code{nPosStart}. The last frame position generated is +\code{nPosEnd}. Each frame will increment the position by +\code{nPosIncrement}. The timestamp attached to each frame will +increment by \code{nFrameIncrement}. +*/ + + +/* +This parameter is used to provide a callback function pointer for +release events. It is used for internal clients on VideoCore. +*/ + + +/* OMX_IndexConfigAudioMonoTrackControl: Dual Mono Control */ +typedef enum OMX_AUDIOMONOTRACKOPERATIONSTYPE { + OMX_AUDIOMONOTRACKOPERATIONS_NOP, + OMX_AUDIOMONOTRACKOPERATIONS_L_TO_R, + OMX_AUDIOMONOTRACKOPERATIONS_R_TO_L, + OMX_AUDIOMONOTRACKOPERATIONS_DUMMY = 0x7FFFFFFF +} OMX_AUDIOMONOTRACKOPERATIONSTYPE ; + +typedef struct OMX_CONFIG_AUDIOMONOTRACKCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_AUDIOMONOTRACKOPERATIONSTYPE eMode; +} OMX_CONFIG_AUDIOMONOTRACKCONTROLTYPE; +/* +This config controls the options to support dual mono audio +streams. The output can be unchanged, or the left channel copied over +the right channel, or the right channel copied over the left +channel. This config can be applied at any time with stereo +16-bit-per-sample data. Since audio output is typically buffered, any +change will not be audible until that buffering has been played out. +*/ + +/* OMX_IndexParamCameraImagePool: Camera Image Pools */ +typedef enum OMX_CAMERAIMAGEPOOLINPUTMODETYPE { + OMX_CAMERAIMAGEPOOLINPUTMODE_ONEPOOL, /*All input images are allocated from one pool + Works for simple stills capture use cases + Can not be used with parallel stills capture + and video encode, as the pool will be sized for + capture or viewfinder, not both simultaneously. + The pool wouldn't divide sensibly in this mode + anyway. + */ + OMX_CAMERAIMAGEPOOLINPUTMODE_TWOPOOLS, /*All stills & video input images are allocated + from two separate pools. + This ensures that parallel capture can work, but + would consume more memory if used on a simple + stills capture use case. + */ +} OMX_CAMERAIMAGEPOOLINPUTMODETYPE; + +typedef struct OMX_PARAM_CAMERAIMAGEPOOLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nNumHiResVideoFrames; + OMX_U32 nHiResVideoWidth; + OMX_U32 nHiResVideoHeight; + OMX_COLOR_FORMATTYPE eHiResVideoType; + OMX_U32 nNumHiResStillsFrames; + OMX_U32 nHiResStillsWidth; + OMX_U32 nHiResStillsHeight; + OMX_COLOR_FORMATTYPE eHiResStillsType; + OMX_U32 nNumLoResFrames; + OMX_U32 nLoResWidth; + OMX_U32 nLoResHeight; + OMX_COLOR_FORMATTYPE eLoResType; + OMX_U32 nNumSnapshotFrames; + OMX_COLOR_FORMATTYPE eSnapshotType; + OMX_CAMERAIMAGEPOOLINPUTMODETYPE eInputPoolMode; + OMX_U32 nNumInputVideoFrames; + OMX_U32 nInputVideoWidth; + OMX_U32 nInputVideoHeight; + OMX_COLOR_FORMATTYPE eInputVideoType; + OMX_U32 nNumInputStillsFrames; + OMX_U32 nInputStillsWidth; + OMX_U32 nInputStillsHeight; + OMX_COLOR_FORMATTYPE eInputStillsType; +} OMX_PARAM_CAMERAIMAGEPOOLTYPE; +/* +\sloppy This parameter specifies the size, type, and number, of images to +allow in the images pools required by Camplus. Supported types are +\code{OMX_COLOR_FormatYUV420PackedPlanar}, +\code{OMX_COLOR_FormatYUV422PackedPlanar}, +\code{OMX_COLOR_FormatRawBayer8bit}, +\code{OMX_COLOR_FormatRawBayer10bit}, +\code{OMX_COLOR_FormatRawBayer8bitcompressed}, and 0 (reserved for the +Broadcom-specific format required by the video encoder). The input +pool width, height, and type can be set as 0 to make the component +query Camplus for the sensor mode that would correspond to the largest +of the viewfinder port definition, the capture port definition, or the +high resolution image pool. +*/ + +/* OMX_IndexParamImagePoolSize: Specifying Image Pool Properties */ +typedef struct OMX_PARAM_IMAGEPOOLSIZETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 width; + OMX_U32 height; + OMX_U32 num_pages; +} OMX_PARAM_IMAGEPOOLSIZETYPE; +/* +This parameter is used to control the size of pool that the component +will allocate in the absence of setting an external pool. The default +can be reset by setting this parameter with all three fields set to +zero. +*/ + + +/* OMX_IndexParamImagePoolExternal: Client Allocated Image Pools */ +struct opaque_vc_pool_s; +typedef struct opaque_vc_pool_s OMX_BRCM_POOL_T; + +typedef struct OMX_PARAM_IMAGEPOOLEXTERNALTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BRCM_POOL_T *image_pool; + OMX_BRCM_POOL_T *image_pool2; + OMX_BRCM_POOL_T *image_pool3; + OMX_BRCM_POOL_T *image_pool4; + OMX_BRCM_POOL_T *image_pool5; +} OMX_PARAM_IMAGEPOOLEXTERNALTYPE; +/* +This config allows the client to pass in handles to pre-allocated +image pools for use within the component. +*/ + + +struct _IL_FIFO_T; +typedef struct OMX_PARAM_RUTILFIFOINFOTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + struct _IL_FIFO_T *pILFifo; +} OMX_PARAM_RUTILFIFOINFOTYPE; + +/* OMX_IndexParamILFifoConfig: Allows configuration of the FIFO settings. */ +typedef struct OMX_PARAM_ILFIFOCONFIG { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nDataSize; /**< The size of the FIFO's data area */ + OMX_U32 nHeaderCount; /**< The number of headers allocated */ +} OMX_PARAM_ILFIFOCONFIG; +/** + * Allows configuring the size of the ILFIFO used in a component. + */ + +/* OMX_IndexConfigCameraSensorModes: Camera Sensor Mode */ +typedef struct OMX_CONFIG_CAMERASENSORMODETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nModeIndex; + OMX_U32 nNumModes; + OMX_U32 nWidth; + OMX_U32 nHeight; + OMX_U32 nPaddingRight; + OMX_U32 nPaddingDown; + OMX_COLOR_FORMATTYPE eColorFormat; + OMX_U32 nFrameRateMax; + OMX_U32 nFrameRateMin; +} OMX_CONFIG_CAMERASENSORMODETYPE; +/* +This parameter is used by clients to determine the sensor mode, and +hence the memory usage, of the camera module. This is primarily used +for determining the size of the input image pool. + +It can be used in two ways dependent on \code{nPortIndex}. If +\code{nPortIndex} is \code{OMX_ALL}, it returns the sensor mode +corresponding to \code{nModeIndex}, and the number of modes in +\code{nNumModes}. If \code{nModeIndex} is greater than or equal to +\code{nNumModes} only \code{nNumModes} is returned. If +\code{nPortIndex} is equal to a camera video output port index, it +returns the sensor mode that would be selected for the values +currently in \code{OMX_IndexParamPortDefinition} for that port. + +The \code{nPaddingRight} and \code{nPaddingDown} values determine the +extra padding the sensor adds to the image. These values must be added +to \code{nWidth} and \code{nHeight} respectively if the client is +specifying the input image pool size. +*/ + +typedef struct OMX_BRCMBUFFERSTATSTYPE { + OMX_U32 nOrdinal; + OMX_TICKS nTimeStamp; + OMX_U32 nFilledLen; + OMX_U32 nFlags; + union + { + OMX_U32 nU32; + struct + { + OMX_U32 nYpart; + OMX_U32 nUVpart; + } image; + } crc; +} OMX_BRCMBUFFERSTATSTYPE; + +/* +Ports that gather statistics for debugging and diagnostics +might also collect information about buffer header fields +and data. + +Note that: + +The \code{nOrdinal} field increases monotonically whenever +a new buffer is received or emitted and shall not be reset +upon a port flush. + +The \code{nFilledLen} might indicate the size of a data area +larger than the data area that actually contributed to the +checksums (e.g. when image data is provided with cropping +information). +*/ + +/* OMX_IndexConfigBrcmPortBufferStats: Query port buffer stats history */ +typedef struct OMX_CONFIG_BRCMPORTBUFFERSTATSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nCount; + OMX_BRCMBUFFERSTATSTYPE sData[1]; +} OMX_CONFIG_BRCMPORTBUFFERSTATSTYPE; +/* +Ports that gather statistics for debugging and diagnostics +might also collect information about buffer header fields +and data. + +The \code{sStatsData} field is a variable length array and +the number of items is denoted by \code{nStatsCount}. +*/ + +/* OMX_IndexConfigBrcmPortStats: Query port statistics */ +typedef struct OMX_CONFIG_BRCMPORTSTATSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nImageCount; + OMX_U32 nBufferCount; + OMX_U32 nFrameCount; + OMX_U32 nFrameSkips; + OMX_U32 nDiscards; + OMX_U32 nEOS; + OMX_U32 nMaxFrameSize; + + OMX_TICKS nByteCount; + OMX_TICKS nMaxTimeDelta; + OMX_U32 nCorruptMBs; /**< Number of corrupt macroblocks in the stream */ +} OMX_CONFIG_BRCMPORTSTATSTYPE; +/* +Some ports gather various statistics that can be used by clients for +debugging purposes. This structure is the set of all statistics that +are gathered. + +The \code{nFrameSkips} field indicates the number of frames that did +not have an expected PTS value based on the port frame rate. + +The \code{nByteCount} field is a 64 bit value, that will either use a +64 bit type or two 32 bit types, similarly to \code{OMX_TICKS}. +*/ + +/* OMX_IndexConfigBrcmClockMissCount: Missed clock request accounting */ +/* +For each port on the clock component, requests for media times may be +made. These are typically done one per video frame to allow for +scheduling the display of that frame at the correct time. If a +request is made after the time has occurred, then that frame will be +displayed late, and the clock component keeps a per-port record of the +number of times this occurs. This record can be read using this +index. +*/ + +typedef struct OMX_CONFIG_BRCMCAMERASTATSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nOutFrameCount; + OMX_U32 nDroppedFrameCount; +} OMX_CONFIG_BRCMCAMERASTATSTYPE; + +// for backward compatibility +typedef struct OMX_CONFIG_BRCMCAMERASTATSTYPE OMX_CONFIG_BRCMCAMERASTATS; + + +#define OMX_BRCM_MAXIOPERFBANDS 10 +typedef struct { + OMX_U32 count[OMX_BRCM_MAXIOPERFBANDS]; + OMX_U32 num[OMX_BRCM_MAXIOPERFBANDS]; +} OMX_BRCM_PERFSTATS; + +/* OMX_IndexConfigBrcmIOPerfStats: Query I/O performance statistics */ +typedef struct OMX_CONFIG_BRCMIOPERFSTATSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bEnabled; /**< Enable/disable I/O performance statistics */ + OMX_BRCM_PERFSTATS write; /**< count:bytes num:microseconds */ + OMX_BRCM_PERFSTATS flush; /**< count:frequency num:microseconds waiting to flush data */ + OMX_BRCM_PERFSTATS wait; /**< count:frequency num:microseconds waiting in calling function */ +} OMX_CONFIG_BRCMIOPERFSTATSTYPE; +/* +A sink component can gather various statistics about I/O (eg. file media) performance that can be used by +clients for debugging purposes. The \code{bEnabled} field is used to turn the gathering of statistics +on/off. +*/ + +typedef struct OMX_CONFIG_SHARPNESSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nSharpness; +} OMX_CONFIG_SHARPNESSTYPE; + +/* OMX_IndexConfigCommonFlickerCancellation: Flicker cancellation */ +typedef enum OMX_COMMONFLICKERCANCELTYPE { + OMX_COMMONFLICKERCANCEL_OFF, + OMX_COMMONFLICKERCANCEL_AUTO, + OMX_COMMONFLICKERCANCEL_50, + OMX_COMMONFLICKERCANCEL_60, + OMX_COMMONFLICKERCANCEL_DUMMY = 0x7FFFFFFF +} OMX_COMMONFLICKERCANCELTYPE; + +typedef struct OMX_CONFIG_FLICKERCANCELTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_COMMONFLICKERCANCELTYPE eFlickerCancel; +} OMX_CONFIG_FLICKERCANCELTYPE; +/* +Query / set the flicker cancellation frequency. Values are defined for Off, +50Hz, 60Hz, or auto. The method for auto detecting the flicker frequency is +not defined, and currently results in the feature being turned off. +*/ + +/* OMX_IndexConfigCommonRedEyeRemoval: Red eye removal/reduction */ +typedef enum OMX_REDEYEREMOVALTYPE { + OMX_RedEyeRemovalNone, /**< No red eye removal */ + OMX_RedEyeRemovalOn, /**< Red eye removal on */ + OMX_RedEyeRemovalAuto, /**< Red eye removal will be done automatically when detected */ + OMX_RedEyeRemovalKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_RedEyeRemovalVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_RedEyeRemovalSimple, /**< Use simple red eye reduction mechanism if supported by algorithm */ + OMX_RedEyeRemovalMax = 0x7FFFFFFF +} OMX_REDEYEREMOVALTYPE; + +typedef struct OMX_CONFIG_REDEYEREMOVALTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_REDEYEREMOVALTYPE eMode; +} OMX_CONFIG_REDEYEREMOVALTYPE; +/* + Configures the red eye reduction algorithm in the camera processing + pipeline. The stage is only enabled if the flash mode is not FlashOff. + The OMX_RedEyeRemovalSimple mode requests that the algorithm uses a + reduced complexity algorithm to reduce the processing time. +*/ + + +typedef enum OMX_FACEDETECTIONCONTROLTYPE { + OMX_FaceDetectionControlNone, /**< Disables face detection */ + OMX_FaceDetectionControlOn, /**< Enables face detection */ + OMX_FaceDetectionControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_FaceDetectionControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_FaceDetectionControlMax = 0x7FFFFFFF +} OMX_FACEDETECTIONCONTROLTYPE; + +typedef struct OMX_CONFIG_FACEDETECTIONCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_FACEDETECTIONCONTROLTYPE eMode; + OMX_U32 nFrames; /**< number of frames to apply this setting for, + 0 for unlimited */ + OMX_U32 nMaxRegions; /**< maximum number of regions to detect, 0 for unlimited */ + OMX_U32 nQuality; /**< hint for algorithmic complexity, range is 0-100. + 0 for simplest algorithm, 100 for best quality */ +} OMX_CONFIG_FACEDETECTIONCONTROLTYPE; + +typedef enum OMX_FACEREGIONFLAGSTYPE { + OMX_FaceRegionFlagsNone = 0, + OMX_FaceRegionFlagsBlink = 1, + OMX_FaceRegionFlagsSmile = 2, + OMX_FaceRegionFlagsKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_FaceRegionFlagsVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_FaceRegionFlagsMax = 0x7FFFFFFF +} OMX_FACEREGIONFLAGSTYPE; + +typedef struct OMX_FACEREGIONTYPE { + OMX_S16 nLeft; /**< X Coordinate of the top left corner of the rectangle */ + OMX_S16 nTop; /**< Y Coordinate of the top left corner of the rectangle */ + OMX_U16 nWidth; /**< Width of the rectangle */ + OMX_U16 nHeight; /**< Height of the rectangle */ + OMX_FACEREGIONFLAGSTYPE nFlags; /**< Flags for the region */ +#ifndef OMX_SKIP64BIT + OMX_U64 nFaceRecognitionId; /**< ID returned by face recognition for this face */ +#else + struct + { + OMX_U32 nLowPart; /**< low bits of the signed 64 bit value */ + OMX_U32 nHighPart; /**< high bits of the signed 64 bit value */ + } nFaceRecognitionId; +#endif +} OMX_FACEREGIONTYPE; + +typedef struct OMX_CONFIG_FACEDETECTIONREGIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; /**< index of port with face detection enabled */ + OMX_U32 nIndex; /**< first requested region number, allowing retrieval of many regions + over several requests */ + OMX_U32 nDetectedRegions; /**< total number of detected regions */ + OMX_S32 nValidRegions; /**< number of valid regions in sRegion array + When getting, the client sets this to the number of regions available. + The component writes region data and updates this field with how many + regions have been written to. */ + OMX_U32 nImageWidth; /**< Width of the image, hence reference for the face coordinates */ + OMX_U32 nImageHeight; /**< Height of the image, hence reference for the face coordinates */ + OMX_FACEREGIONTYPE sRegion[1]; /**< variable length array of face regions */ +} OMX_CONFIG_FACEDETECTIONREGIONTYPE; + +typedef enum OMX_INTERLACETYPE { + OMX_InterlaceProgressive, /**< The data is not interlaced, it is progressive scan */ + OMX_InterlaceFieldSingleUpperFirst, /**< The data is interlaced, fields sent + separately in temporal order, with upper field first */ + OMX_InterlaceFieldSingleLowerFirst, /**< The data is interlaced, fields sent + separately in temporal order, with lower field first */ + OMX_InterlaceFieldsInterleavedUpperFirst, /**< The data is interlaced, two fields sent together line + interleaved, with the upper field temporally earlier */ + OMX_InterlaceFieldsInterleavedLowerFirst, /**< The data is interlaced, two fields sent together line + interleaved, with the lower field temporally earlier */ + OMX_InterlaceMixed, /**< The stream may contain a mixture of progressive + and interlaced frames */ + OMX_InterlaceKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_InterlaceVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_InterlaceMax = 0x7FFFFFFF +} OMX_INTERLACETYPE; + +typedef struct OMX_CONFIG_INTERLACETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; /**< index of port emitting or accepting the content */ + OMX_INTERLACETYPE eMode; /**< The interlace type of the content */ + OMX_BOOL bRepeatFirstField; /**< Whether to repeat the first field */ +} OMX_CONFIG_INTERLACETYPE; + +/* OMX_IndexParamIspTuner: Custom ISP tuner */ +typedef struct OMX_PARAM_CAMERAISPTUNERTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U8 tuner_name[64]; +} OMX_PARAM_CAMERAISPTUNERTYPE; +/* +This parameter allows a custom ISP tuner to be loaded instead of +the default one specified for the camera module. Setting an empty +string uses the default value. +*/ + +/* OMX_IndexConfigCameraInputFrame: Pointer to the raw input image */ +typedef struct OMX_CONFIG_IMAGEPTRTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_PTR pImage; +} OMX_CONFIG_IMAGEPTRTYPE; +/* +This parameter parameter allows the return of a pointer to a +VideoCore image resource. +*/ + +/* OMX_IndexConfigAFAssistLight: Autofocus assist light mode selection */ +typedef enum OMX_AFASSISTTYPE { + OMX_AFAssistAuto, + OMX_AFAssistOn, + OMX_AFAssistOff, + OMX_AFAssistTorch, + OMX_AFAssistKhronosExtensions = 0x6F000000, + OMX_AFAssistVendorStartUnused = 0x7F000000, + OMX_AFAssistMax = 0x7FFFFFFF +} OMX_AFASSISTTYPE; + +typedef struct OMX_CONFIG_AFASSISTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_AFASSISTTYPE eMode; +} OMX_CONFIG_AFASSISTTYPE; +/* +Set the mode to adopt for the autofocus assist light. +\code{OMX_AFAssistTorch} will turn the AF assist light on permanently, allowing +it to be used as a torch. +*/ + +/* OMX_IndexConfigInputCropPercentage: Specify input crop as a percentage */ +typedef struct OMX_CONFIG_INPUTCROPTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 xLeft; /**< Fraction of the width for the top left corner of the rectangle */ + OMX_U32 xTop; /**< Fraction of the height for the top left corner of the rectangle */ + OMX_U32 xWidth; /**< Fraction of the image width desired */ + OMX_U32 xHeight; /**< Fraction of the image height desired */ +} OMX_CONFIG_INPUTCROPTYPE; +/* +This parameter allows the input cropping to be specified as a +percentage of the current width/height. Required for the camera +component where the output resolution varies dependent on the port. +All percentage values are as 16p16 fixed point numbers (0x10000 = +100\%) +*/ + +/* OMX_IndexParamCodecRequirements: Advanced codec requirements */ +typedef struct OMX_PARAM_CODECREQUIREMENTSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nCallbackID; + OMX_BOOL bTryHWCodec; +} OMX_PARAM_CODECREQUIREMENTSTYPE; +/* +This parameter allows internal users of RIL components controlling +video codecs to request that the component loads the component and +queries for requirements. The component will perform a callback with +the given nCallbackID value passing a pointer to the requirements +structure as the data field. +*/ + +/* OMX_IndexConfigBrcmEGLImageMemHandle: Mapping from an EGLImage to a VideoCore mem handle */ +typedef struct OMX_CONFIG_BRCMEGLIMAGEMEMHANDLETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_PTR eglImage; + OMX_PTR memHandle; +} OMX_CONFIG_BRCMEGLIMAGEMEMHANDLETYPE; +/* +This config allows the EGL server to notify a RIL component that an +EGLImage is available for rendering into and to provide a mapping from +an EGLImage to a mem handle. +*/ + +/* OMX_IndexConfigPrivacyIndicator: Privacy indicator control */ +typedef enum OMX_PRIVACYINDICATORTYPE { + OMX_PrivacyIndicatorOff, + OMX_PrivacyIndicatorOn, + OMX_PrivacyIndicatorForceOn, + OMX_PrivacyIndicatorKhronosExtensions = 0x6F000000, + OMX_PrivacyIndicatorVendorStartUnused = 0x7F000000, + OMX_PrivacyIndicatorMax = 0x7FFFFFFF +} OMX_PRIVACYINDICATORTYPE; + +typedef struct OMX_CONFIG_PRIVACYINDICATORTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_PRIVACYINDICATORTYPE ePrivacyIndicatorMode; +} OMX_CONFIG_PRIVACYINDICATORTYPE; +/* +This config allows control over the privacy indicator light. This +light indicates when a capture is in progress. + +\code{OMX_PrivacyIndicatorOff} indicator is disabled. + +\code{OMX_PrivacyIndicatorOn} indicator will be +turned on whenever an image is being captured as determined by the +capturing bit. Minimum on duration of approx 200ms. + +\code{OMX_PrivacyIndicatorForceOn} results in turning the indicator on +immediately, whether an image is being captured or not. The mode will +automatically revert to \code{OMX_PrivacyIndicatorOff} once the +indicator has been turned on, so \code{OMX_PrivacyIndicatorForceOn} +must be requested at least every 200ms if the indicator is to remain +on. +*/ + + +/* OMX_IndexParamCameraFlashType: Select flash type */ +typedef enum OMX_CAMERAFLASHTYPE { + OMX_CameraFlashDefault, + OMX_CameraFlashXenon, + OMX_CameraFlashLED, + OMX_CameraFlashNone, + OMX_CameraFlashKhronosExtensions = 0x6F000000, + OMX_CameraFlashVendorStartUnused = 0x7F000000, + OMX_CameraFlashMax = 0x7FFFFFFF +} OMX_CAMERAFLASHTYPE; + +typedef struct OMX_PARAM_CAMERAFLASHTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_CAMERAFLASHTYPE eFlashType; + OMX_BOOL bRedEyeUsesTorchMode; +} OMX_PARAM_CAMERAFLASHTYPE; +/* +This parameter allows the selection of xenon or LED flash devices +to be used with the currently selected camera. If that device is not +available, then the component will revert back to whatever flash +device is available for that camera. +\code{bRedEyeUsesTorchMode} allows the blinking for red eye reduction to +be switched between using the indicator mode, and the torch mode for the +flash driver. +*/ + +/* OMX_IndexConfigCameraFlashConfig: Flash cycle configuration */ +typedef enum OMX_CAMERAFLASHCONFIGSYNCTYPE { + OMX_CameraFlashConfigSyncFrontSlow, + OMX_CameraFlashConfigSyncRearSlow, + OMX_CameraFlashConfigSyncFrontFast, + OMX_CameraFlashConfigSyncKhronosExtensions = 0x6F000000, + OMX_CameraFlashConfigSyncVendorStartUnused = 0x7F000000, + OMX_CameraFlashConfigSyncMax = 0x7FFFFFFF +} OMX_CAMERAFLASHCONFIGSYNCTYPE; + +typedef struct OMX_CONFIG_CAMERAFLASHCONFIGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bUsePreFlash; + OMX_BOOL bUseFocusDistanceInfo; + OMX_CAMERAFLASHCONFIGSYNCTYPE eFlashSync; + OMX_BOOL bIgnoreChargeState; +} OMX_CONFIG_CAMERAFLASHCONFIGTYPE; +/* +This parameter allows the configuration of various parameters relating to +the flash cycle. Some of the options are only applicable to xenon flash. + +\code{bUsePreFlash} uses a low intensity pre-flash to determine flash intensity. This setting +is recommended for almost all flash situations. + +\code{bUseFocusDistanceInfo} uses the distance of the subject, as measured by the AF algorithm +to set the intensity of the flash. + +\code{eFlashSync} configures which edge of the shutter is used to synchronise the flash, and +the duration of the exposure. + +\code{eIgnoreChargeState} will make the flash fire, even if it is not fully charged. +*/ + +/* OMX_IndexConfigBrcmAudioTrackGaplessPlayback: Encoder/decoder delay and padding information for gapless playback. */ +typedef struct OMX_CONFIG_BRCMAUDIOTRACKGAPLESSPLAYBACKTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nDelay; /**< number of samples delay added by the codec */ + OMX_U32 nPadding; /**< number of silent samples added to the end */ +} OMX_CONFIG_BRCMAUDIOTRACKGAPLESSPLAYBACKTYPE; +/* +This config allows communication between components to facilitate gapless playback. +*/ + + +/* OMX_IndexConfigBrcmAudioTrackChangeControl: Configure gapless/crossfaded audio track change. */ +typedef struct OMX_CONFIG_BRCMAUDIOTRACKCHANGECONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nSrcPortIndex; + OMX_U32 nDstPortIndex; + OMX_U32 nXFade; +} OMX_CONFIG_BRCMAUDIOTRACKCHANGECONTROLTYPE; +/* +This config allows the client to specify the gapless or crossfade +parameters to be used on a track change. If \code{nXFade} is 0, then +a normal or gapless track change will result, otherwise a crossfade of +\code{nXFade} ms is used. +*/ + +/* OMX_IndexParamBrcmPixelValueRange: Describing the pixel value range */ +typedef enum OMX_BRCMPIXELVALUERANGETYPE +{ + OMX_PixelValueRangeUnspecified = 0, + OMX_PixelValueRangeITU_R_BT601, + OMX_PixelValueRangeFull8Bit, + OMX_PixelValueRangeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_PixelValueRangeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_PixelValueRangeMax = 0x7FFFFFFF +} OMX_BRCMPIXELVALUERANGETYPE; + +typedef struct OMX_PARAM_BRCMPIXELVALUERANGETYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BRCMPIXELVALUERANGETYPE ePixelValueRange; +} OMX_PARAM_BRCMPIXELVALUERANGETYPE; +/* +This structure allows a description of the range that pixel values may +have. This is typically useful since some standards use the full 8 +bit range, whereas others introduce pedastals which reduce the range +at the top and bottom end. +*/ + +/* OMX_IndexParamCameraDisableAlgorithm: Disabling camera processing stages. */ +typedef enum OMX_CAMERADISABLEALGORITHMTYPE { + OMX_CameraDisableAlgorithmFacetracking, + OMX_CameraDisableAlgorithmRedEyeReduction, + OMX_CameraDisableAlgorithmVideoStabilisation, + OMX_CameraDisableAlgorithmWriteRaw, + OMX_CameraDisableAlgorithmVideoDenoise, + OMX_CameraDisableAlgorithmStillsDenoise, + OMX_CameraDisableAlgorithmAntiShake, + OMX_CameraDisableAlgorithmImageEffects, + OMX_CameraDisableAlgorithmDarkSubtract, + OMX_CameraDisableAlgorithmDynamicRangeExpansion, + OMX_CameraDisableAlgorithmFaceRecognition, + OMX_CameraDisableAlgorithmFaceBeautification, + OMX_CameraDisableAlgorithmSceneDetection, + OMX_CameraDisableAlgorithmHighDynamicRange, + OMX_CameraDisableAlgorithmKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_CameraDisableAlgorithmVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_CameraDisableAlgorithmMax = 0x7FFFFFFF +} OMX_CAMERADISABLEALGORITHMTYPE; + +typedef struct OMX_PARAM_CAMERADISABLEALGORITHMTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_CAMERADISABLEALGORITHMTYPE eAlgorithm; + OMX_BOOL bDisabled; +} OMX_PARAM_CAMERADISABLEALGORITHMTYPE; +/* +Allows plugin algorithms to be disabled to save memory +within the camera component +*/ + +/* OMX_IndexConfigBrcmAudioEffectControl: Audio Effect Control */ +typedef struct OMX_CONFIG_BRCMAUDIOEFFECTCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnable; + OMX_U8 name[16]; + OMX_U8 property[256]; +} OMX_CONFIG_BRCMAUDIOEFFECTCONTROLTYPE; +/* +This structure represents the internal configuration of an audio effect. +The audio effect is provided by a loadable plug-in described +in the \code{name} field and is configured in a plug-in-dependent +manner with the \code{property} field. The \code{bEnable} field is used to +turn the effect on/off. +*/ + +/* OMX_IndexConfigBrcmMinimumProcessingLatency: Processing Latency Bound */ +typedef struct OMX_CONFIG_BRCMMINIMUMPROCESSINGLATENCY { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_TICKS nOffset; +} OMX_CONFIG_BRCMMINIMUMPROCESSINGLATENCY; +/* +Query/set the difference between the actual media time and when the +component receives request fulfillments for media time requests. This +can be used with e.g. splitter/mixer components to control when the +component stops waiting for input or output packets from active +streams and continues with processing (to maintain a constant +processing rate). +*/ + +/** Enable or disable Supplemental Enhancment Information (SEI) messages to be inserted in + * the H.264 bitstream. + */ +typedef struct OMX_PARAM_BRCMVIDEOAVCSEIENABLETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnable; +} OMX_PARAM_BRCMVIDEOAVCSEIENABLETYPE; + +/* OMX_IndexParamBrcmAllowMemChange: Allowing changing memory allocation on state transition */ +typedef struct OMX_PARAM_BRCMALLOWMEMCHANGETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bEnable; +} OMX_PARAM_BRCMALLOWMEMCHANGETYPE; +/* +Let the component change the amount of memory it has allocated when +going from LOADED to IDLE. By default this is enabled, but if it is +disabled the component will fail to transition to IDLE if the +component requires more memory than has already been allocated. This +might occur if (for example) the component was configured, taken to +IDLE, then taken back to LOADED, the profile increased and the +component taken back to IDLE. +*/ + +typedef enum OMX_CONFIG_CAMERAUSECASE { + OMX_CameraUseCaseAuto, + OMX_CameraUseCaseVideo, + OMX_CameraUseCaseStills, + OMX_CameraUseCaseKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_CameraUseCaseVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_CameraUseCaseMax = 0x7FFFFFFF +} OMX_CONFIG_CAMERAUSECASE; + +typedef struct OMX_CONFIG_CAMERAUSECASETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_CONFIG_CAMERAUSECASE eUseCase; +} OMX_CONFIG_CAMERAUSECASETYPE; + +/* OMX_IndexParamBrcmDisableProprietaryTunnels: Disabling proprietary tunnelling */ +typedef struct OMX_PARAM_BRCMDISABLEPROPRIETARYTUNNELSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bUseBuffers; +} OMX_PARAM_BRCMDISABLEPROPRIETARYTUNNELSTYPE; +/* +Tell a source component to refuse to support proprietary tunnelling. Buffers will be used instead. +*/ + + +// +// Control for memory allocation and component-internal buffering +// + +/* OMX_IndexParamBrcmRetainMemory: Controlling memory use on state transition */ +typedef struct OMX_PARAM_BRCMRETAINMEMORYTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bEnable; +} OMX_PARAM_BRCMRETAINMEMORYTYPE; +/* +Ask a component to retain its memory when going from IDLE to LOADED, if possible. +This has the benefit that you are then guaranteed that the transition to IDLE cannot +fail due to lack of memory, but has the disadvantage that you cannot leave the component +lying around in LOADED, unused, since it is using significant amounts of memory. +*/ + +/** Tell write media how large the output buffer should be. This is a hint, and + * may be ignored. A good size is bandwidth*, which works out at + * around 1Mbyte for up to 16Mbit/s. Sizes may (and probably will) be rounded down + * to the nearest power of 2. + */ +typedef struct OMX_PARAM_BRCMOUTPUTBUFFERSIZETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nBufferSize; +} OMX_PARAM_BRCMOUTPUTBUFFERSIZETYPE; + +/* OMX_IndexConfigCameraInfo: Camera device driver information */ +#define OMX_CONFIG_CAMERAINFOTYPE_NAME_LEN 16 +typedef struct OMX_CONFIG_LENSCALIBRATIONVALUETYPE +{ + OMX_U16 nShutterDelayTime; + OMX_U16 nNdTransparency; + OMX_U16 nPwmPulseNearEnd; /**< Num pulses to move lens 1um at near end */ + OMX_U16 nPwmPulseFarEnd; /**< Num pulses to move lens 1um at far end */ + OMX_U16 nVoltagePIOutNearEnd[3]; + OMX_U16 nVoltagePIOut10cm[3]; + OMX_U16 nVoltagePIOutInfinity[3]; + OMX_U16 nVoltagePIOutFarEnd[3]; + OMX_U32 nAdcConversionNearEnd; + OMX_U32 nAdcConversionFarEnd; +} OMX_CONFIG_LENSCALIBRATIONVALUETYPE; +/* +Ask the camera component for the driver info on the current camera device +*/ + +#define OMX_CONFIG_CAMERAINFOTYPE_NAME_LEN 16 +#define OMX_CONFIG_CAMERAINFOTYPE_SERIALNUM_LEN 20 +#define OMX_CONFIG_CAMERAINFOTYPE_EPROMVER_LEN 8 +typedef struct OMX_CONFIG_CAMERAINFOTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U8 cameraname[OMX_CONFIG_CAMERAINFOTYPE_NAME_LEN]; + OMX_U8 lensname[OMX_CONFIG_CAMERAINFOTYPE_NAME_LEN]; + OMX_U16 nModelId; + OMX_U8 nManufacturerId; + OMX_U8 nRevNum; + OMX_U8 sSerialNumber[OMX_CONFIG_CAMERAINFOTYPE_SERIALNUM_LEN]; + OMX_U8 sEpromVersion[OMX_CONFIG_CAMERAINFOTYPE_EPROMVER_LEN]; + OMX_CONFIG_LENSCALIBRATIONVALUETYPE sLensCalibration; + OMX_U32 xFNumber; + OMX_U32 xFocalLength; +} OMX_CONFIG_CAMERAINFOTYPE; + + +typedef enum OMX_CONFIG_CAMERAFEATURESSHUTTER { + OMX_CameraFeaturesShutterUnknown, + OMX_CameraFeaturesShutterNotPresent, + OMX_CameraFeaturesShutterPresent, + OMX_CameraFeaturesShutterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_CameraFeaturesShutterVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_CameraFeaturesShutterMax = 0x7FFFFFFF +} OMX_CONFIG_CAMERAFEATURESSHUTTER; + +typedef struct OMX_CONFIG_CAMERAFEATURESTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_CONFIG_CAMERAFEATURESSHUTTER eHasMechanicalShutter; + OMX_BOOL bHasLens; +} OMX_CONFIG_CAMERAFEATURESTYPE; + + +//Should be added to the spec as part of IL416c +/* OMX_IndexConfigRequestCallback: Enable config change notifications. */ +typedef struct OMX_CONFIG_REQUESTCALLBACKTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_INDEXTYPE nIndex; + OMX_BOOL bEnable; +} OMX_CONFIG_REQUESTCALLBACKTYPE; +/* +This config implements IL416c to allow clients to request notification +of when a config or parameter is changed. When the parameter specified +in \code{nIndex} for port \code{nPortIndex} changes, an +\code{OMX_EventParamOrConfigChanged} event is generated for the client. +*/ + +/* OMX_IndexConfigCommonFocusRegionXY: Define focus regions */ +typedef enum OMX_FOCUSREGIONTYPE { + OMX_FocusRegionNormal, + OMX_FocusRegionFace, + OMX_FocusRegionMax +} OMX_FOCUSREGIONTYPE; + +typedef struct OMX_FOCUSREGIONXY { + OMX_U32 xLeft; + OMX_U32 xTop; + OMX_U32 xWidth; + OMX_U32 xHeight; + OMX_U32 nWeight; + OMX_U32 nMask; + OMX_FOCUSREGIONTYPE eType; +} OMX_FOCUSREGIONXY; + +typedef struct OMX_CONFIG_FOCUSREGIONXYTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nIndex; + OMX_U32 nTotalRegions; + OMX_S32 nValidRegions; + OMX_BOOL bLockToFaces; + OMX_U32 xFaceTolerance; + OMX_FOCUSREGIONXY sRegion[1]; +} OMX_CONFIG_FOCUSREGIONXYTYPE; +/* +Query / set the focus regions to use as a set of x/y/width/height boxes relative +to the overall image. + +\code{nIndex} - first region number being set/read, allowing retrieval/setting +of many regions over several requests. + +\code{nTotalRegions} - total number of regions currently defined. + +\code{nValidRegions} - number of valid regions in the \code{sRegion} array. +When getting, the client sets this to the number of regions available. +The component writes region data and updates this field with how many +regions have been written to. +When setting, this is the number of regions defined with this structure + +\code{bLockToFaces} - compare the region(s) given to the latest face tracking results. +If a face is found within xFaceTolerance of the defined region, then amend the +region to correspond to the face. + +\code{xFaceTolerance} - 0p16 value to define the max difference between the region centre +and face tracking result centre to take the FT results + +\code{sRegions} - variable length array of focus regions. +*/ + +typedef struct OMX_CONFIG_U8TYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U8 nU8; /**< U8 value */ +} OMX_PARAM_U8TYPE; + +typedef struct OMX_CONFIG_CAMERASETTINGSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nExposure; + OMX_U32 nAnalogGain; + OMX_U32 nDigitalGain; + OMX_U32 nLux; + OMX_U32 nRedGain; + OMX_U32 nBlueGain; + OMX_U32 nFocusPosition; +} OMX_CONFIG_CAMERASETTINGSTYPE; + +/* OMX_IndexConfigDrawBoxLineParams: Face box style parameters. */ +typedef struct OMX_YUVCOLOUR { + OMX_U8 nY; + OMX_U8 nU; + OMX_U8 nV; +} OMX_YUVCOLOUR; + +typedef struct OMX_CONFIG_DRAWBOXLINEPARAMS { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port to which this config applies */ + OMX_U32 xCornerSize; /**< Size of the corners as a fraction of the complete side */ + OMX_U32 nPrimaryFaceLineWidth; /**< Width of the box line for the primary face in pixels */ + OMX_U32 nOtherFaceLineWidth; /**< Width of the box line for other faces in pixels */ + OMX_U32 nFocusRegionLineWidth; /**< Width of the box line for focus regions in pixels */ + OMX_YUVCOLOUR sPrimaryFaceColour; /**< YUV colour for the primary face */ + OMX_YUVCOLOUR sPrimaryFaceSmileColour; /**< YUV colour for the primary face if smiling */ + OMX_YUVCOLOUR sPrimaryFaceBlinkColour; /**< YUV colour for the primary face if blinking */ + OMX_YUVCOLOUR sOtherFaceColour; /**< YUV colour for the all other faces */ + OMX_YUVCOLOUR sOtherFaceSmileColour; /**< YUV colour for the all other faces if smiling */ + OMX_YUVCOLOUR sOtherFaceBlinkColour; /**< YUV colour for the all other faces if blinking */ + OMX_BOOL bShowFocusRegionsWhenIdle; /**< Are focus regions displayed when just in viewfinder/AF idle */ + OMX_YUVCOLOUR sFocusRegionColour; /**< YUV colour for focus regions */ + OMX_BOOL bShowAfState; /**< Change to the colours specified below if AF cycle has run */ + OMX_BOOL bShowOnlyPrimaryAfState; /**< Only show the primary face when displaying the AF status */ + OMX_BOOL bCombineNonFaceRegions; /**< Combine all regions not defined as faces into one single box covering them all */ + OMX_YUVCOLOUR sAfLockPrimaryFaceColour; /**< YUV colour for the primary face */ + OMX_YUVCOLOUR sAfLockOtherFaceColour; /**< YUV colour for the all other faces */ + OMX_YUVCOLOUR sAfLockFocusRegionColour; /**< YUV colour for focus regions */ + OMX_YUVCOLOUR sAfFailPrimaryFaceColour; /**< YUV colour for the primary face */ + OMX_YUVCOLOUR sAfFailOtherFaceColour; /**< YUV colour for the all other faces */ + OMX_YUVCOLOUR sAfFailFocusRegionColour; /**< YUV colour for focus regions */ + } OMX_CONFIG_DRAWBOXLINEPARAMS; +/* +Query / set the parameters for the box to be drawn around faces/focus regions. +*/ + + #define OMX_PARAM_CAMERARMITYPE_RMINAME_LEN 16 + //OMX_IndexParamCameraRmiControl + typedef struct OMX_PARAM_CAMERARMITYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bEnabled; + OMX_U8 sRmiName[OMX_PARAM_CAMERARMITYPE_RMINAME_LEN]; + OMX_U32 nInputBufferHeight; + OMX_U32 nRmiBufferSize; + OMX_BRCM_POOL_T *pImagePool; + } OMX_PARAM_CAMERARMITYPE; + +/* OMX_IndexConfigBrcmSyncOutput: Forcing a write sync */ +typedef struct OMX_CONFIG_BRCMSYNCOUTPUTTYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ +} OMX_CONFIG_BRCMSYNCOUTPUTTYPE; +/* +Setting this config forces a sync of data to the filesystem. +*/ + +/* OMX_IndexConfigDrmView: View information for DRM rental files */ +typedef struct OMX_CONFIG_DRMVIEWTYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nCurrentView; /**< Current view count */ + OMX_U32 nMaxView; /**< Max. no. of view allowed */ +} OMX_CONFIG_DRMVIEWTYPE; +/* +This structure contains information about the number of available +views in the selected DRM rental file, which typically have a given +maximum view count. It allows the user to explicitly agree to playing +the file, which will increment the number of current views the file +has had. +*/ + +typedef struct OMX_PARAM_BRCMU64TYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nLowPart; /**< low bits of the unsigned 64 bit value */ + OMX_U32 nHighPart; /**< high bits of the unsigned 64 bit value */ +} OMX_PARAM_BRCMU64TYPE; + +/* OMX_IndexParamBrcmDisableEXIF: Disable generation of EXIF data */ +/* +This parameter is used by clients to control the generation of exif +data in JPEG images. +*/ + +/* OMX_IndexParamBrcmThumbnail: Control generation of thumbnail */ +typedef struct OMX_PARAM_BRCMTHUMBNAILTYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_BOOL bEnable; /**< Enable generation of thumbnails during still capture */ + OMX_BOOL bUsePreview; /**< Use the preview image (as is) as thumbnail */ + OMX_U32 nWidth; /**< Desired width of the thumbnail */ + OMX_U32 nHeight; /**< Desired height of the thumbnail */ +} OMX_PARAM_BRCMTHUMBNAILTYPE; +/* +This parameter is used by clients to control how thumbnails are +generated when creating still images. + +Thumbnail generation will be turned on or off depending on the +\code{bEnable} field. + +The \code{bUsePreview} field will let the component know whether it +should use the low resolution preview image (if the component has one +available) as is for the thumbnail. When this is set to true, it should +make the generation of thumbnails faster (if a preview image is available) +and should use less memory as well. + +The \code{nWidth} and \code{nHeight} fields allow the client to +specify the dimensions of the thumbnail. If both \code{nWidth} and +\code{nHeight} are 0, we will calculate a sensible size for the +thumbnail. +*/ + +typedef struct OMX_PARAM_BRCMASPECTRATIOTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nWidth; + OMX_U32 nHeight; +} OMX_PARAM_BRCMASPECTRATIOTYPE; + +/* OMX_IndexParamBrcmVideoDecodeErrorConcealment: Control error concealment for video decode */ +typedef struct OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bStartWithValidFrame; /**< Decoder will only start emitting frames from a non-corrupted frame */ +} OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE; +/* + This parameter is used by clients to control the type of error concealment + that will be done by the video decoder. + */ + +#define OMX_CONFIG_FLASHINFOTYPE_NAME_LEN 16 +typedef struct OMX_CONFIG_FLASHINFOTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U8 sFlashName[OMX_CONFIG_FLASHINFOTYPE_NAME_LEN]; + OMX_CAMERAFLASHTYPE eFlashType; + OMX_U8 nDeviceId; + OMX_U8 nDeviceVersion; +} OMX_CONFIG_FLASHINFOTYPE; + +/* OMX_IndexParamBrcmInterpolateMissingTimestamps: Configure component to interpolate missing timestamps */ +/* +Configures a component so that it tries to timestamp all the buffers it outputs. +If the timestamp information is missing from the original buffer, the +component will try its best to interpolate a value for the missing timestamp. + */ + +/* OMX_IndexParamBrcmSetCodecPerformanceMonitoring: Configure component to output performance statistics */ +/* +Configures a codec component so that it outputs performance statistics to +the given DECODE_PROGRESS_REPORT_T structure (passed as a pointer). +This structure can then be read by the client to find out where the codec is +at in its processing. + */ + +/* OMX_IndexConfigDynamicRangeExpansion: Configure image dynamic range expansion processing */ +typedef enum OMX_DYNAMICRANGEEXPANSIONMODETYPE { + OMX_DynRangeExpOff, + OMX_DynRangeExpLow, + OMX_DynRangeExpMedium, + OMX_DynRangeExpHigh, + OMX_DynRangeExpKhronosExtensions = 0x6F000000, + OMX_DynRangeExpVendorStartUnused = 0x7F000000, + OMX_DynRangeExpMax = 0x7FFFFFFF +} OMX_DYNAMICRANGEEXPANSIONMODETYPE; + +typedef struct OMX_CONFIG_DYNAMICRANGEEXPANSIONTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_DYNAMICRANGEEXPANSIONMODETYPE eMode; +} OMX_CONFIG_DYNAMICRANGEEXPANSIONTYPE; +/* +Configures the intensity of an image dynamic range expansion processing stage +*/ + +/* OMX_IndexParamBrcmTransposeBufferCount: Configure the number of pre-allocated transpose buffers */ +/* +This config allows the client to explicitly set the number of destination buffers pre-allocated for +ports that support 90/270 degree rotation (e.g. in video_render). The buffers will be pre-allocated during +a state transition from LOADED to IDLE (the transition will fail if there is not enough memory available +for the buffers). +. +*/ + + +/* OMX_IndexParamBrcmThreadAffinity: Control the CPU affinity of component thread(s) */ +typedef enum OMX_BRCMTHREADAFFINITYTYPE { + OMX_BrcmThreadAffinityCPU0, + OMX_BrcmThreadAffinityCPU1, + OMX_BrcmThreadAffinityMax = 0x7FFFFFFF +} OMX_BRCMTHREADAFFINITYTYPE; + +typedef struct OMX_PARAM_BRCMTHREADAFFINITYTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BRCMTHREADAFFINITYTYPE eAffinity; /**< Thread CPU affinity */ +} OMX_PARAM_BRCMTHREADAFFINITYTYPE; +/* + This parameter is used by clients to hint the CPU that a component thread should run on. + */ + + /* OMX_IndexConfigCommonSceneDetected: Reports the scene type detected by a scene detection algorithm. */ +typedef enum OMX_SCENEDETECTTYPE { + OMX_SceneDetectUnknown, + OMX_SceneDetectLandscape, + OMX_SceneDetectPortrait, + OMX_SceneDetectMacro, + OMX_SceneDetectNight, + OMX_SceneDetectPortraitNight, + OMX_SceneDetectBacklit, + OMX_SceneDetectPortraitBacklit, + OMX_SceneDetectSunset, + OMX_SceneDetectBeach, + OMX_SceneDetectSnow, + OMX_SceneDetectFireworks, + OMX_SceneDetectMax = 0x7FFFFFFF +} OMX_SCENEDETECTTYPE; + +/* OMX_IndexConfigCommonSceneDetected: Reports the scene type detected by a scene detection algorithm. */ +typedef struct OMX_CONFIG_SCENEDETECTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_SCENEDETECTTYPE eScene; /**< Scene type detected */ +} OMX_CONFIG_SCENEDETECTTYPE; +/* + This config is used to report to clients the scene type that has been detected. + */ + +/* OMX_IndexParamNalStreamFormat: Control the NAL unit packaging. This is a Khronos extension. */ +typedef enum OMX_INDEXEXTTYPE { + /* Video parameters and configurations */ + OMX_IndexExtVideoStartUnused = OMX_IndexKhronosExtensions + 0x00600000, + OMX_IndexParamNalStreamFormatSupported, /**< reference: OMX_NALSTREAMFORMATTYPE */ + OMX_IndexParamNalStreamFormat, /**< reference: OMX_NALSTREAMFORMATTYPE */ + OMX_IndexParamNalStreamFormatSelect, /**< reference: OMX_NALSTREAMFORMATTYPE */ + + OMX_IndexExtMax = 0x7FFFFFFF +} OMX_INDEXEXTTYPE; + +/* OMX_IndexParamNalStreamFormat: Control the NAL unit packaging. This is a Khronos extension. */ +typedef enum OMX_NALUFORMATSTYPE { + OMX_NaluFormatStartCodes = 1, + OMX_NaluFormatOneNaluPerBuffer = 2, + OMX_NaluFormatOneByteInterleaveLength = 4, + OMX_NaluFormatTwoByteInterleaveLength = 8, + OMX_NaluFormatFourByteInterleaveLength = 16, + OMX_NaluFormatCodingMax = 0x7FFFFFFF +} OMX_NALUFORMATSTYPE; + +/* OMX_IndexParamNalStreamFormat: Control the NAL unit packaging. This is a Khronos extension. */ +typedef struct OMX_NALSTREAMFORMATTYPE{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_NALUFORMATSTYPE eNaluFormat; +} OMX_NALSTREAMFORMATTYPE; +/* + This parameter is used to control the NAL unit packaging of an H264 video port. + */ + +/* OMX_IndexParamVideoMvc: MVC codec parameters */ +typedef struct OMX_VIDEO_PARAM_AVCTYPE OMX_VIDEO_PARAM_MVCTYPE; +/* +This parameter is currently identical to the AVC parameter type. +*/ + + /* OMX_IndexConfigBrcmDrawStaticBox: Define static box to be drawn */ +typedef enum OMX_STATICBOXTYPE { + OMX_StaticBoxNormal, + OMX_StaticBoxPrimaryFaceAfIdle, + OMX_StaticBoxNonPrimaryFaceAfIdle, + OMX_StaticBoxFocusRegionAfIdle, + OMX_StaticBoxPrimaryFaceAfSuccess, + OMX_StaticBoxNonPrimaryFaceAfSuccess, + OMX_StaticBoxFocusRegionAfSuccess, + OMX_StaticBoxPrimaryFaceAfFail, + OMX_StaticBoxNonPrimaryFaceAfFail, + OMX_StaticBoxFocusRegionAfFail, + OMX_StaticBoxMax +} OMX_STATICBOXTYPE; + +typedef struct OMX_STATICBOX { + OMX_U32 xLeft; + OMX_U32 xTop; + OMX_U32 xWidth; + OMX_U32 xHeight; + OMX_STATICBOXTYPE eType; +} OMX_STATICBOX; + +typedef struct OMX_CONFIG_STATICBOXTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nIndex; + OMX_U32 nTotalBoxes; + OMX_S32 nValidBoxes; + OMX_BOOL bDrawOtherBoxes; + OMX_STATICBOX sBoxes[1]; +} OMX_CONFIG_STATICBOXTYPE; +/* +Query / set the parameters for a box to always be drawn on viewfinder images +The x/y/width/height values for the boxes are relative to the overall image. + +\code{nIndex} - first box number being set/read, allowing retrieval/setting +of many boxes over several requests. + +\code{nValidBoxes} - total number of boxes currently defined. + +\code{nValidBoxes} - number of valid boxes in the \code{sBoxes} array. +When getting, the client sets this to the number of boxes available. +The component writes box data and updates this field with how many +boxes have been written to. +When setting, this is the number of boxes defined with this structure + +\code{sBoxes} - variable length array of static boxes. +*/ + +/* OMX_IndexConfigPortCapturing: Per-port capturing state */ +typedef struct OMX_CONFIG_PORTBOOLEANTYPE{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnabled; +} OMX_CONFIG_PORTBOOLEANTYPE; +/* +This is proposed in IL533f for controlling +which ports of a multi-port camera component are capturing frames. +*/ + +/* OMX_IndexConfigCaptureMode: Capturing mode type */ +typedef enum OMX_CAMERACAPTUREMODETYPE { + OMX_CameraCaptureModeWaitForCaptureEnd, + OMX_CameraCaptureModeWaitForCaptureEndAndUsePreviousInputImage, + OMX_CameraCaptureModeResumeViewfinderImmediately, + OMX_CameraCaptureModeMax, +} OMX_CAMERACAPTUREMODETYPE; + +typedef struct OMX_PARAM_CAMERACAPTUREMODETYPE{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_CAMERACAPTUREMODETYPE eMode; +} OMX_PARAM_CAMERACAPTUREMODETYPE; +/* +This controls the mode of operation for +still image capture in the camera component. +*/ + +/* OMX_IndexParamBrcmDrmEncryption: Set DRM encryption scheme */ +typedef enum OMX_BRCMDRMENCRYPTIONTYPE +{ + OMX_DrmEncryptionNone = 0, + OMX_DrmEncryptionHdcp2, + OMX_DrmEncryptionKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DrmEncryptionVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_DrmEncryptionRangeMax = 0x7FFFFFFF +} OMX_BRCMDRMENCRYPTIONTYPE; + +typedef struct OMX_PARAM_BRCMDRMENCRYPTIONTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BRCMDRMENCRYPTIONTYPE eEncryption; + OMX_U32 nConfigDataLen; + OMX_U8 configData[1]; +} OMX_PARAM_BRCMDRMENCRYPTIONTYPE; +/* +Query/set the DRM encryption scheme used by a port writing out data. +*/ + + +/* OMX_IndexConfigBufferStall: Advertise buffer stall state */ +typedef struct OMX_CONFIG_BUFFERSTALLTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bStalled; /**< Whether we are stalled */ + OMX_U32 nDelay; /**< Delay in real time (us) from last buffer to current time */ +} OMX_CONFIG_BUFFERSTALLTYPE; +/* +Query/set the buffer stall threashold. When set the \code{nDelay} +parameter specifies a time to class whether buffer output is stalled. +When get, the \code{nDelay} parameter indicates the current buffer +delay, and the {bStalled} parameter indicates whether this time is +over a previously set threashold. When +\code{OMX_IndexConfigRequestCallback} is used with this index, a +notification is given when \code{bStalled} changes. +*/ + +/* OMX_IndexConfigLatencyTarget: Maintain target latency by adjusting clock speed */ +typedef struct OMX_CONFIG_LATENCYTARGETTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnabled; /**< whether this mode is enabled */ + OMX_U32 nFilter; /**< number of latency samples to filter on, good value: 1 */ + OMX_U32 nTarget; /**< target latency, us */ + OMX_U32 nShift; /**< shift for storing latency values, good value: 7 */ + OMX_S32 nSpeedFactor; /**< multiplier for speed changes, in 24.8 format, good value: 256-512 */ + OMX_S32 nInterFactor; /**< divider for comparing latency versus gradiant, good value: 300 */ + OMX_S32 nAdjCap; /**< limit for speed change before nSpeedFactor is applied, good value: 100 */ +} OMX_CONFIG_LATENCYTARGETTYPE; +/* +Query/set parameters used when adjusting clock speed to match the +measured latency to a specified value. +*/ + +/* OMX_IndexConfigBrcmUseProprietaryCallback: Force use of proprietary callback */ +typedef struct OMX_CONFIG_BRCMUSEPROPRIETARYCALLBACKTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnable; +} OMX_CONFIG_BRCMUSEPROPRIETARYCALLBACKTYPE; +/* +Disable/enable the use of proprietary callbacks rather than OpenMAX IL buffer handling. +*/ + +/* OMX_IndexParamCommonUseStcTimestamps: Select timestamp mode */ +typedef enum OMX_TIMESTAMPMODETYPE +{ + OMX_TimestampModeZero = 0, /**< Use a timestamp of 0 */ + OMX_TimestampModeRawStc, /**< Use the raw STC as the timestamp */ + OMX_TimestampModeResetStc, /**< Store the STC when video capture port goes active, and subtract that from STC for the timestamp */ + OMX_TimestampModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_TimestampModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_TimestampModeMax = 0x7FFFFFFF +} OMX_TIMESTAMPMODETYPE; + +typedef struct OMX_PARAM_TIMESTAMPMODETYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_TIMESTAMPMODETYPE eTimestampMode; +} OMX_PARAM_TIMESTAMPMODETYPE; +/* + Specifies what to use as timestamps in the absence of a clock component. +*/ + +/* EGL image buffer for passing to video port. + * Used when port color format is OMX_COLOR_FormatBRCMEGL. + */ +typedef struct OMX_BRCMVEGLIMAGETYPE +{ + /* Passed between ARM + VC; use fixed width types. */ + OMX_U32 nWidth; + OMX_U32 nHeight; + OMX_U32 nStride; + OMX_U32 nUmemHandle; + OMX_U32 nUmemOffset; + OMX_U32 nFlipped; /* Non-zero -> vertically flipped image */ +} OMX_BRCMVEGLIMAGETYPE; + +/* Provides field of view + */ +typedef struct OMX_CONFIG_BRCMFOVTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 xFieldOfViewHorizontal; /**< Horizontal field of view in degrees. 16p16 value */ + OMX_U32 xFieldOfViewVertical; /**< Vertical field of view in degrees. 16p16 value */ +} OMX_CONFIG_BRCMFOVTYPE; + +/* OMX_IndexConfigBrcmDecoderPassThrough: Enabling Audio Passthrough */ +/* +This allows an audio decoder to disable decoding the stream and pass through correctly framed +data to enable playback of compressed audio to supported output devices. +*/ + +/* OMX_IndexConfigBrcmClockReferenceSource: Select Clock Reference Source */ +/* +This control allows communicating directly to an audio renderer component whether it should +act as a clock reference source or act as a slave. +*/ + +/* OMX_IndexConfigEncLevelExtension: AVC Override encode capabilities */ +typedef struct OMX_VIDEO_CONFIG_LEVEL_EXTEND { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nCustomMaxMBPS; /**< Specifies maximum macro-blocks per second */ + OMX_U32 nCustomMaxFS; /**< Specifies maximum frame size (macro-blocks per frame) */ + OMX_U32 nCustomMaxBRandCPB; /**< Specifies maximum bitrate in units of 1000 bits/s and Codec Picture Buffer (CPB derived from bitrate) */ +} OMX_VIDEO_CONFIG_LEVEL_EXTEND; +/* +This allows finer control of the H264 encode internal parameters. +*/ + +/* OMX_IndexParamBrcmEEDEEnable: Enable/Disable end to end distortion estimator */ +typedef struct OMX_VIDEO_EEDE_ENABLE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 enable; +} OMX_VIDEO_EEDE_ENABLE; +/* +This enables or disables the use of end to end distortion estimation. +*/ + +/* OMX_IndexParamBrcmEEDELossRate: Loss rate configuration for end to end distortion */ +typedef struct OMX_VIDEO_EEDE_LOSSRATE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 loss_rate; /**< loss rate, 5 means 5% */ +} OMX_VIDEO_EEDE_LOSSRATE; +/* +Set the packet loss rate used by the end to end distortion estimator. +*/ + +/* OMX_IndexParamColorSpace: Colour space information */ +typedef enum OMX_COLORSPACETYPE +{ + OMX_COLORSPACE_UNKNOWN, + OMX_COLORSPACE_JPEG_JFIF, + OMX_COLORSPACE_ITU_R_BT601, + OMX_COLORSPACE_ITU_R_BT709, + OMX_COLORSPACE_FCC, + OMX_COLORSPACE_SMPTE240M, + OMX_COLORSPACE_BT470_2_M, + OMX_COLORSPACE_BT470_2_BG, + OMX_COLORSPACE_JFIF_Y16_255, + OMX_COLORSPACE_MAX = 0x7FFFFFFF +} OMX_COLORSPACETYPE; + +typedef struct OMX_PARAM_COLORSPACETYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_COLORSPACETYPE eColorSpace; +} OMX_PARAM_COLORSPACETYPE; +/* +Sets the colourspace with which pixel buffers should be generated / interpreted. +*/ + +typedef enum OMX_CAPTURESTATETYPE +{ + OMX_NotCapturing, + OMX_CaptureStarted, + OMX_CaptureComplete, + OMX_CaptureMax = 0x7FFFFFFF +} OMX_CAPTURESTATETYPE; + +typedef struct OMX_PARAM_CAPTURESTATETYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_CAPTURESTATETYPE eCaptureState; +} OMX_PARAM_CAPTURESTATETYPE; + +/* +Provides information on the colour space that's in use during image/video processing. +*/ + +/* OMX_IndexConfigMinimiseFragmentation: Minimising Fragmentation */ +/* +This control can be supported to enable the client to request that the component works +to minimise fragmentation of output buffers. +*/ + +/* OMX_IndexConfigBrcmBufferFlagFilter: Filter buffers based on flags */ +/* +This control can be set to request that buffers are conditionally forwarded on +output ports based on matching flags set on that buffer. +*/ + +/* OMX_IndexParamPortMaxFrameSize: Specifying maximum frame size */ +/* +This control can be used to control the maximum frame size allowed on an output port. +*/ + +/* OMX_IndexConfigBrcmCameraRnDPreprocess: Enable use of development ISP software stage */ +/* +This control can be used to enable a developmental software stage to be inserted into +the preprocessor stage of the ISP. +*/ + +/* OMX_IndexConfigBrcmCameraRnDPostprocess: Enable use of development ISP software stage */ +/* +This control can be used to enable a developmental software stage to be inserted into +the postprocessor stage of the ISP. +*/ + +/* OMX_IndexParamDisableVllPool: Controlling use of memory for loadable modules */ +/* +This control can be used to control whether loadable modules used a dedicated memory +pool or use heap allocated memory. +*/ + +/* OMX_IndexParamBrcmVideoPrecodeForQP: Pre-code 1st frame for QP.*/ +/* +This control selects a pre-encode of the first frame to set up a better initial QP value. +*/ + +/* OMX_IndexParamBrcmVideoTimestampFifo: Video timestamp FIFO mode. */ +/* +When enabled, the timestamp fifo mode will change the way +incoming timestamps are associated with output images so the incoming timestamps +get used without re-ordering on output images. +*/ + +/* OMX_IndexParamCameraCustomSensorConfig: Custom camera sensor configuration. */ +/* +This parameter is passed down to the camera sensor driver to be interpreted as a +request for a different configuration to normal. How the configuration varies is +sensor specific. +*/ + +/* OMX_IndexParamCameraDeviceNumber: Camera device selection .*/ +/* +Controls which camera driver, or camera peripheral, to use. +*/ + +/* OMX_IndexParamBrcmMaxNumCallbacks: Codec callback limit. */ +/* +The codec can queue up a significant number of frames internally if the sink is +not consuming the output fast enough. This control limits the number of frames +that can be queued up. +*/ + +typedef struct OMX_PARAM_BRCMCONFIGFILETYPE { + OMX_U32 nSize; /**< size of the structure in bytes, including + actual URI name */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 fileSize; /**< Size of complete file data */ +} OMX_PARAM_BRCMCONFIGFILETYPE; + +typedef struct OMX_PARAM_BRCMCONFIGFILECHUNKTYPE { + OMX_U32 nSize; /**< size of the structure in bytes, including + actual chunk data */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 size; /**< Number of bytes being transferred in this chunk */ + OMX_U32 offset; /**< Offset of this chunk in the file */ + OMX_U8 data[1]; /**< Chunk data */ +} OMX_PARAM_BRCMCONFIGFILECHUNKTYPE; + +typedef struct OMX_PARAM_BRCMFRAMERATERANGETYPE { + OMX_U32 nSize; /**< size of the structure in bytes, including + actual chunk data */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; + OMX_U32 xFramerateLow; /**< Low end of framerate range. Q16 format */ + OMX_U32 xFramerateHigh; /**< High end of framerate range. Q16 format */ +} OMX_PARAM_BRCMFRAMERATERANGETYPE; + +typedef struct OMX_PARAM_S32TYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_S32 nS32; /**< S32 value */ +} OMX_PARAM_S32TYPE; + +typedef struct OMX_PARAM_BRCMVIDEODRMPROTECTBUFFERTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 size_wanted; /**< Input. Zero size means internal video decoder buffer, + mem_handle and phys_addr not returned in this case */ + OMX_U32 protect; /**< Input. 1 = protect, 0 = unprotect */ + + OMX_U32 mem_handle; /**< Output. Handle for protected buffer */ + OMX_PTR phys_addr; /**< Output. Physical memory address of protected buffer */ +} OMX_PARAM_BRCMVIDEODRMPROTECTBUFFERTYPE; + +typedef struct OMX_CONFIG_ZEROSHUTTERLAGTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 bZeroShutterMode; /**< Select ZSL mode from the camera. */ + OMX_U32 bConcurrentCapture; /**< Perform concurrent captures for full ZSL. */ + +} OMX_CONFIG_ZEROSHUTTERLAGTYPE; + +/* OMX_IndexParamBrcmVideoDecodeConfigVD3: VDec3 configuration. */ +typedef struct OMX_PARAM_BRCMVIDEODECODECONFIGVD3TYPE { + OMX_U32 nSize; /**< size of the structure in bytes, including + configuration data */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U8 config[1]; /**< Configuration data (a VD3_CONFIGURE_T) */ +} OMX_PARAM_BRCMVIDEODECODECONFIGVD3TYPE; +/* +Codec specific configuration block to set up internal state in a non-standard manner. +*/ + +/* OMX_IndexConfigCustomAwbGains: Manual AWB Gains. */ +typedef struct OMX_CONFIG_CUSTOMAWBGAINSTYPE { + OMX_U32 nSize; /**< size of the structure in bytes, including + configuration data */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 xGainR; /**< Red gain - 16p16 */ + OMX_U32 xGainB; /**< Blue gain - 16p16 */ +} OMX_CONFIG_CUSTOMAWBGAINSTYPE; + +/* OMX_IndexConfigCustomAwbGains: Manual AWB Gains. */ + +/* OMX_IndexConfigBrcmRenderStats: Render port statistics */ +typedef struct OMX_CONFIG_BRCMRENDERSTATSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL nValid; + OMX_U32 nMatch; + OMX_U32 nPeriod; + OMX_U32 nPhase; + OMX_U32 nPixelClockNominal; + OMX_U32 nPixelClock; + OMX_U32 nHvsStatus; + OMX_U32 dummy0[2]; +} OMX_CONFIG_BRCMRENDERSTATSTYPE; +/* +This provides statistics from the renderer to allow more accurate synchronisation +between the scheduler and display VSYNC. +*/ + +#define OMX_BRCM_MAXANNOTATETEXTLEN 256 +typedef struct OMX_CONFIG_BRCMANNOTATETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bEnable; + OMX_BOOL bShowShutter; + OMX_BOOL bShowAnalogGain; + OMX_BOOL bShowLens; + OMX_BOOL bShowCaf; + OMX_BOOL bShowMotion; + OMX_BOOL bShowFrameNum; + OMX_BOOL bEnableBackground; + OMX_BOOL bCustomBackgroundColour; + OMX_U8 nBackgroundY; + OMX_U8 nBackgroundU; + OMX_U8 nBackgroundV; + OMX_U8 dummy1; + OMX_BOOL bCustomTextColour; + OMX_U8 nTextY; + OMX_U8 nTextU; + OMX_U8 nTextV; + OMX_U8 nTextSize; /**< Text size: 6-150 pixels */ + OMX_U8 sText[OMX_BRCM_MAXANNOTATETEXTLEN]; +} OMX_CONFIG_BRCMANNOTATETYPE; + +/* OMX_IndexParamBrcmStereoscopicMode: Stereoscopic camera support */ +typedef enum OMX_BRCMSTEREOSCOPICMODETYPE { + OMX_STEREOSCOPIC_NONE = 0, + OMX_STEREOSCOPIC_SIDEBYSIDE = 1, + OMX_STEREOSCOPIC_TOPBOTTOM = 2, + OMX_STEREOSCOPIC_MAX = 0x7FFFFFFF, +} OMX_BRCMSTEREOSCOPICMODETYPE; + +typedef struct OMX_CONFIG_BRCMSTEREOSCOPICMODETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BRCMSTEREOSCOPICMODETYPE eMode; /**< Packing mode */ + OMX_BOOL bDecimate; /**< Half/half mode + (pixel aspect ratio = 1:2 or 2:1 if set. 1:1 if not set) */ + OMX_BOOL bSwapEyes; /**< False = left eye first. True = right eye first. */ +} OMX_CONFIG_BRCMSTEREOSCOPICMODETYPE; +/* +This control sets up how stereoscopic images should be generated. +*/ + +/* OMX_IndexParamCameraInterface: Camera interface type. */ +typedef enum OMX_CAMERAINTERFACETYPE { + OMX_CAMERAINTERFACE_CSI = 0, + OMX_CAMERAINTERFACE_CCP2 = 1, + OMX_CAMERAINTERFACE_CPI = 2, + OMX_CAMERAINTERFACE_MAX = 0x7FFFFFFF, +} OMX_CAMERAINTERFACETYPE; + +typedef struct OMX_PARAM_CAMERAINTERFACETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_CAMERAINTERFACETYPE eMode; /**< Interface mode */ +} OMX_PARAM_CAMERAINTERFACETYPE; +/* +This configures the physical camera interface type. +*/ + +typedef enum OMX_CAMERACLOCKINGMODETYPE { + OMX_CAMERACLOCKINGMODE_STROBE = 0, + OMX_CAMERACLOCKINGMODE_CLOCK = 1, + OMX_CAMERACLOCKINGMODE_MAX = 0x7FFFFFFF, +} OMX_CAMERACLOCKINGMODETYPE; + +typedef struct OMX_PARAM_CAMERACLOCKINGMODETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_CAMERACLOCKINGMODETYPE eMode; /**< Clocking mode */ +} OMX_PARAM_CAMERACLOCKINGMODETYPE; + +/* OMX_IndexParamCameraRxConfig: Camera receiver configuration */ +typedef enum OMX_CAMERARXDECODETYPE { + OMX_CAMERARXDECODE_NONE = 0, + OMX_CAMERARXDECODE_DPCM8TO10 = 1, + OMX_CAMERARXDECODE_DPCM7TO10 = 2, + OMX_CAMERARXDECODE_DPCM6TO10 = 3, + OMX_CAMERARXDECODE_DPCM8TO12 = 4, + OMX_CAMERARXDECODE_DPCM7TO12 = 5, + OMX_CAMERARXDECODE_DPCM6TO12 = 6, + OMX_CAMERARXDECODE_DPCM10TO14 = 7, + OMX_CAMERARXDECODE_DPCM8TO14 = 8, + OMX_CAMERARXDECODE_DPCM12TO16 = 9, + OMX_CAMERARXDECODE_DPCM10TO16 = 10, + OMX_CAMERARXDECODE_DPCM8TO16 = 11, + OMX_CAMERARXDECODE_MAX = 0x7FFFFFFF +} OMX_CAMERARXDECODETYPE; + +typedef enum OMX_CAMERARXENCODETYPE { + OMX_CAMERARXENCODE_NONE = 0, + OMX_CAMERARXENCODE_DPCM10TO8 = 1, + OMX_CAMERARXENCODE_DPCM12TO8 = 2, + OMX_CAMERARXENCODE_DPCM14TO8 = 3, + OMX_CAMERARXENCODE_MAX = 0x7FFFFFFF +} OMX_CAMERARXENCODETYPE; + +typedef enum OMX_CAMERARXUNPACKTYPE { + OMX_CAMERARXUNPACK_NONE = 0, + OMX_CAMERARXUNPACK_6 = 1, + OMX_CAMERARXUNPACK_7 = 2, + OMX_CAMERARXUNPACK_8 = 3, + OMX_CAMERARXUNPACK_10 = 4, + OMX_CAMERARXUNPACK_12 = 5, + OMX_CAMERARXUNPACK_14 = 6, + OMX_CAMERARXUNPACK_16 = 7, + OMX_CAMERARXUNPACK_MAX = 0x7FFFFFFF +} OMX_CAMERARXUNPACKYPE; + +typedef enum OMX_CAMERARXPACKTYPE { + OMX_CAMERARXPACK_NONE = 0, + OMX_CAMERARXPACK_8 = 1, + OMX_CAMERARXPACK_10 = 2, + OMX_CAMERARXPACK_12 = 3, + OMX_CAMERARXPACK_14 = 4, + OMX_CAMERARXPACK_16 = 5, + OMX_CAMERARXPACK_RAW10 = 6, + OMX_CAMERARXPACK_RAW12 = 7, + OMX_CAMERARXPACK_MAX = 0x7FFFFFFF +} OMX_CAMERARXPACKTYPE; + +typedef struct OMX_PARAM_CAMERARXCONFIG_TYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_CAMERARXDECODETYPE eDecode; + OMX_CAMERARXENCODETYPE eEncode; + OMX_CAMERARXUNPACKYPE eUnpack; + OMX_CAMERARXPACKTYPE ePack; + OMX_U32 nDataLanes; + OMX_U32 nEncodeBlockLength; + OMX_U32 nEmbeddedDataLines; + OMX_U32 nImageId; +} OMX_PARAM_CAMERARXCONFIG_TYPE; +/* +Configures the setup and processing options of the camera receiver peripheral. +*/ + +typedef struct OMX_PARAM_CAMERARXTIMING_TYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nTiming1; + OMX_U32 nTiming2; + OMX_U32 nTiming3; + OMX_U32 nTiming4; + OMX_U32 nTiming5; + OMX_U32 nTerm1; + OMX_U32 nTerm2; + OMX_U32 nCpiTiming1; + OMX_U32 nCpiTiming2; +} OMX_PARAM_CAMERARXTIMING_TYPE; + + +/* OMX_IndexParamBrcmBayerOrder: Bayer order */ +typedef enum OMX_BAYERORDERTYPE { + OMX_BayerOrderRGGB = 0, + OMX_BayerOrderGBRG = 1, + OMX_BayerOrderBGGR = 2, + OMX_BayerOrderGRBG = 3, + + OMX_BayerOrderMax = 0x7FFFFFFF +} OMX_BAYERORDERTYPE; + +typedef struct OMX_PARAM_BAYERORDERTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BAYERORDERTYPE eBayerOrder; +} OMX_PARAM_BAYERORDERTYPE; +/* +The IL standard does not support a way to specify the Bayer order of Bayer images. +This control adds that missing functionality. +*/ + +/* OMX_IndexParamBrcmLensShadingOverride: Override or set a lens shading table.*/ +/* +Allows the lens shading grid to be set. +Configuration is based on a similar system to the OMAP3 ISP. +A grid of gains is required for each of the 4 Bayer channels, with each value covering +a nGridCellSize square of pixels. +nWidth and nHeight should be equal or greater than the sensor resolution. In the +case of the camera component, the firmware will crop the table based on the preconfigured +mode set. nStride allows additional horizontal padding to be including in the table. +nMemHandleTable needs to be set to a MEM_HANDLE_T, allocated via VC-SM or similar allocator. +nRefTransform should be set to the transform in force when the reference table was +captured. This allows correct compensation when the sensor is subsequently used with +an alternate transform. +*/ +typedef struct OMX_PARAM_LENSSHADINGOVERRIDETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + + OMX_BOOL bEnabled; /**< Enable the override grid */ + OMX_U32 nGridCellSize; /**< size of each grid element. Assumes square grid */ + OMX_U32 nWidth; /**< grid width */ + OMX_U32 nStride; /**< grid stride (allows for padding) */ + OMX_U32 nHeight; /**< grid height */ + OMX_U32 nMemHandleTable; /**< Handle for grid */ + OMX_U32 nRefTransform; /**< Reference transform taken from raw header */ +} OMX_PARAM_LENSSHADINGOVERRIDETYPE; + +/* OMX_IndexConfigBrcmPowerMonitor: Deprecated.*/ +/* +Deprecated. Do not use. +*/ + +/* OMX_IndexParamBrcmZeroCopy: Deprecated */ +/* +Deprecated. Do not use. +*/ + +/* OMX_IndexParamBrcmSupportsSlices: Sliced processing support */ +/* +Mainly used by the MMAL framework. +Some components support an nSliceHeight value of 16, to allow images +to be passed in multiple chunks. All will support an nSliceHeight >= +nFrameHeight (with some extra constraints). +If a component supports nSliceHeight of 16, then it will respond to +OMX_GetParameter on this index with no error and bEnabled set to OMX_TRUE. +*/ + +/* OMX_IndexParamBrcmSupportsUnalignedSliceheight: Unaligned nSliceHeight support */ +/* +Most components require an nSliceHeight value which is a multiple of 16, but +some components accepting any value >= nFrameHeight. Those ports/components will +respond to OMX_GetParameter on this index with no error and bEnabled set to OMX_TRUE. +*/ + +typedef struct OMX_CCMTYPE { + OMX_S32 sCcm[3][3]; + OMX_S32 soffsets[3]; +} OMX_PARAM_CCMTYPE; + +typedef struct OMX_PARAM_CUSTOMCCMTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + + OMX_BOOL bEnabled; /**< Enable the custom CCM. */ + OMX_S32 xColorMatrix[3][3]; /**< Stored in signed Q16 format */ + OMX_S32 nColorOffset[3]; /**< CCM offsets */ +} OMX_PARAM_CUSTOMCCMTYPE; + +/* OMX_IndexConfigCameraDigitalGain: Manual digital gain. */ +/* +Configures the digital gain within the ISP pipeline. +*/ +typedef struct OMX_CONFIG_CAMERAGAINTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + + OMX_U32 xGain; /**< Gain to be applied, stored as Q16 format */ + OMX_BOOL bAutoGain; /**< Whether gain is set automatically */ +} OMX_CONFIG_CAMERAGAINTYPE; + +#endif +/* File EOF */ diff --git a/interface/vmcs_host/khronos/IL/OMX_Component.h b/interface/vmcs_host/khronos/IL/OMX_Component.h new file mode 100755 index 0000000..b0b9b81 --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Component.h @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** OMX_Component.h - OpenMax IL version 1.1.2 + * The OMX_Component header file contains the definitions used to define + * the public interface of a component. This header file is intended to + * be used by both the application and the component. + */ + +#ifndef OMX_Component_h +#define OMX_Component_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/* Each OMX header must include all required header files to allow the + * header to compile without errors. The includes below are required + * for this header file to compile successfully + */ + +#include "OMX_Audio.h" +#include "OMX_Video.h" +#include "OMX_Image.h" +#include "OMX_Other.h" + +/** @ingroup comp */ +typedef enum OMX_PORTDOMAINTYPE { + OMX_PortDomainAudio, + OMX_PortDomainVideo, + OMX_PortDomainImage, + OMX_PortDomainOther, + OMX_PortDomainKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_PortDomainVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_PortDomainMax = 0x7ffffff +} OMX_PORTDOMAINTYPE; + +/** @ingroup comp */ +typedef struct OMX_PARAM_PORTDEFINITIONTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port number the structure applies to */ + OMX_DIRTYPE eDir; /**< Direction (input or output) of this port */ + OMX_U32 nBufferCountActual; /**< The actual number of buffers allocated on this port */ + OMX_U32 nBufferCountMin; /**< The minimum number of buffers this port requires */ + OMX_U32 nBufferSize; /**< Size, in bytes, for buffers to be used for this channel */ + OMX_BOOL bEnabled; /**< Ports default to enabled and are enabled/disabled by + OMX_CommandPortEnable/OMX_CommandPortDisable. + When disabled a port is unpopulated. A disabled port + is not populated with buffers on a transition to IDLE. */ + OMX_BOOL bPopulated; /**< Port is populated with all of its buffers as indicated by + nBufferCountActual. A disabled port is always unpopulated. + An enabled port is populated on a transition to OMX_StateIdle + and unpopulated on a transition to loaded. */ + OMX_PORTDOMAINTYPE eDomain; /**< Domain of the port. Determines the contents of metadata below. */ + union { + OMX_AUDIO_PORTDEFINITIONTYPE audio; + OMX_VIDEO_PORTDEFINITIONTYPE video; + OMX_IMAGE_PORTDEFINITIONTYPE image; + OMX_OTHER_PORTDEFINITIONTYPE other; + } format; + OMX_BOOL bBuffersContiguous; + OMX_U32 nBufferAlignment; +} OMX_PARAM_PORTDEFINITIONTYPE; + +/** @ingroup comp */ +typedef struct OMX_PARAM_U32TYPE { + OMX_U32 nSize; /**< Size of this structure, in Bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nU32; /**< U32 value */ +} OMX_PARAM_U32TYPE; + +/** @ingroup rpm */ +typedef enum OMX_SUSPENSIONPOLICYTYPE { + OMX_SuspensionDisabled, /**< No suspension; v1.0 behavior */ + OMX_SuspensionEnabled, /**< Suspension allowed */ + OMX_SuspensionPolicyKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_SuspensionPolicyStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_SuspensionPolicyMax = 0x7fffffff +} OMX_SUSPENSIONPOLICYTYPE; + +/** @ingroup rpm */ +typedef struct OMX_PARAM_SUSPENSIONPOLICYTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_SUSPENSIONPOLICYTYPE ePolicy; +} OMX_PARAM_SUSPENSIONPOLICYTYPE; + +/** @ingroup rpm */ +typedef enum OMX_SUSPENSIONTYPE { + OMX_NotSuspended, /**< component is not suspended */ + OMX_Suspended, /**< component is suspended */ + OMX_SuspensionKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_SuspensionVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_SuspendMax = 0x7FFFFFFF +} OMX_SUSPENSIONTYPE; + +/** @ingroup rpm */ +typedef struct OMX_PARAM_SUSPENSIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_SUSPENSIONTYPE eType; +} OMX_PARAM_SUSPENSIONTYPE ; + +typedef struct OMX_CONFIG_BOOLEANTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bEnabled; +} OMX_CONFIG_BOOLEANTYPE; + +/* Parameter specifying the content uri to use. */ +/** @ingroup cp */ +typedef struct OMX_PARAM_CONTENTURITYPE +{ + OMX_U32 nSize; /**< size of the structure in bytes, including + actual URI name */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U8 contentURI[1]; /**< The URI name */ +} OMX_PARAM_CONTENTURITYPE; + +/* Parameter specifying the pipe to use. */ +/** @ingroup cp */ +typedef struct OMX_PARAM_CONTENTPIPETYPE +{ + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_HANDLETYPE hPipe; /**< The pipe handle*/ +} OMX_PARAM_CONTENTPIPETYPE; + +/** @ingroup rpm */ +typedef struct OMX_RESOURCECONCEALMENTTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_BOOL bResourceConcealmentForbidden; /**< disallow the use of resource concealment + methods (like degrading algorithm quality to + lower resource consumption or functional bypass) + on a component as a resolution to resource conflicts. */ +} OMX_RESOURCECONCEALMENTTYPE; + + +/** @ingroup metadata */ +typedef enum OMX_METADATACHARSETTYPE { + OMX_MetadataCharsetUnknown = 0, + OMX_MetadataCharsetASCII, + OMX_MetadataCharsetBinary, + OMX_MetadataCharsetCodePage1252, + OMX_MetadataCharsetUTF8, + OMX_MetadataCharsetJavaConformantUTF8, + OMX_MetadataCharsetUTF7, + OMX_MetadataCharsetImapUTF7, + OMX_MetadataCharsetUTF16LE, + OMX_MetadataCharsetUTF16BE, + OMX_MetadataCharsetGB12345, + OMX_MetadataCharsetHZGB2312, + OMX_MetadataCharsetGB2312, + OMX_MetadataCharsetGB18030, + OMX_MetadataCharsetGBK, + OMX_MetadataCharsetBig5, + OMX_MetadataCharsetISO88591, + OMX_MetadataCharsetISO88592, + OMX_MetadataCharsetISO88593, + OMX_MetadataCharsetISO88594, + OMX_MetadataCharsetISO88595, + OMX_MetadataCharsetISO88596, + OMX_MetadataCharsetISO88597, + OMX_MetadataCharsetISO88598, + OMX_MetadataCharsetISO88599, + OMX_MetadataCharsetISO885910, + OMX_MetadataCharsetISO885913, + OMX_MetadataCharsetISO885914, + OMX_MetadataCharsetISO885915, + OMX_MetadataCharsetShiftJIS, + OMX_MetadataCharsetISO2022JP, + OMX_MetadataCharsetISO2022JP1, + OMX_MetadataCharsetISOEUCJP, + OMX_MetadataCharsetSMS7Bit, + OMX_MetadataCharsetKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_MetadataCharsetVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_MetadataCharsetTypeMax= 0x7FFFFFFF +} OMX_METADATACHARSETTYPE; + +/** @ingroup metadata */ +typedef enum OMX_METADATASCOPETYPE +{ + OMX_MetadataScopeAllLevels, + OMX_MetadataScopeTopLevel, + OMX_MetadataScopePortLevel, + OMX_MetadataScopeNodeLevel, + OMX_MetadataScopeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_MetadataScopeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_MetadataScopeTypeMax = 0x7fffffff +} OMX_METADATASCOPETYPE; + +/** @ingroup metadata */ +typedef enum OMX_METADATASEARCHMODETYPE +{ + OMX_MetadataSearchValueSizeByIndex, + OMX_MetadataSearchItemByIndex, + OMX_MetadataSearchNextItemByKey, + OMX_MetadataSearchKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_MetadataSearchVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_MetadataSearchTypeMax = 0x7fffffff +} OMX_METADATASEARCHMODETYPE; +/** @ingroup metadata */ +typedef struct OMX_CONFIG_METADATAITEMCOUNTTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_METADATASCOPETYPE eScopeMode; + OMX_U32 nScopeSpecifier; + OMX_U32 nMetadataItemCount; +} OMX_CONFIG_METADATAITEMCOUNTTYPE; + +/** @ingroup metadata */ +typedef struct OMX_CONFIG_METADATAITEMTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_METADATASCOPETYPE eScopeMode; + OMX_U32 nScopeSpecifier; + OMX_U32 nMetadataItemIndex; + OMX_METADATASEARCHMODETYPE eSearchMode; + OMX_METADATACHARSETTYPE eKeyCharset; + OMX_U8 nKeySizeUsed; + OMX_U8 nKey[128]; + OMX_METADATACHARSETTYPE eValueCharset; + OMX_STRING sLanguageCountry; + OMX_U32 nValueMaxSize; + OMX_U32 nValueSizeUsed; + OMX_U8 nValue[1]; +} OMX_CONFIG_METADATAITEMTYPE; + +/* @ingroup metadata */ +typedef struct OMX_CONFIG_CONTAINERNODECOUNTTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bAllKeys; + OMX_U32 nParentNodeID; + OMX_U32 nNumNodes; +} OMX_CONFIG_CONTAINERNODECOUNTTYPE; + +/** @ingroup metadata */ +typedef struct OMX_CONFIG_CONTAINERNODEIDTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bAllKeys; + OMX_U32 nParentNodeID; + OMX_U32 nNodeIndex; + OMX_U32 nNodeID; + OMX_STRING cNodeName; + OMX_BOOL bIsLeafType; +} OMX_CONFIG_CONTAINERNODEIDTYPE; + +/** @ingroup metadata */ +typedef struct OMX_PARAM_METADATAFILTERTYPE +{ + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_BOOL bAllKeys; /* if true then this structure refers to all keys and + * the three key fields below are ignored */ + OMX_METADATACHARSETTYPE eKeyCharset; + OMX_U32 nKeySizeUsed; + OMX_U8 nKey [128]; + OMX_U32 nLanguageCountrySizeUsed; + OMX_U8 nLanguageCountry[128]; + OMX_BOOL bEnabled; /* if true then key is part of filter (e.g. + * retained for query later). If false then + * key is not part of filter */ +} OMX_PARAM_METADATAFILTERTYPE; + +/** The OMX_HANDLETYPE structure defines the component handle. The component + * handle is used to access all of the component's public methods and also + * contains pointers to the component's private data area. The component + * handle is initialized by the OMX core (with help from the component) + * during the process of loading the component. After the component is + * successfully loaded, the application can safely access any of the + * component's public functions (although some may return an error because + * the state is inappropriate for the access). + * + * @ingroup comp + */ +typedef struct OMX_COMPONENTTYPE +{ + /** The size of this structure, in bytes. It is the responsibility + of the allocator of this structure to fill in this value. Since + this structure is allocated by the GetHandle function, this + function will fill in this value. */ + OMX_U32 nSize; + + /** nVersion is the version of the OMX specification that the structure + is built against. It is the responsibility of the creator of this + structure to initialize this value and every user of this structure + should verify that it knows how to use the exact version of + this structure found herein. */ + OMX_VERSIONTYPE nVersion; + + /** pComponentPrivate is a pointer to the component private data area. + This member is allocated and initialized by the component when the + component is first loaded. The application should not access this + data area. */ + OMX_PTR pComponentPrivate; + + /** pApplicationPrivate is a pointer that is a parameter to the + OMX_GetHandle method, and contains an application private value + provided by the IL client. This application private data is + returned to the IL Client by OMX in all callbacks */ + OMX_PTR pApplicationPrivate; + + /** refer to OMX_GetComponentVersion in OMX_core.h or the OMX IL + specification for details on the GetComponentVersion method. + */ + OMX_ERRORTYPE (*GetComponentVersion)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_OUT OMX_STRING pComponentName, + OMX_OUT OMX_VERSIONTYPE* pComponentVersion, + OMX_OUT OMX_VERSIONTYPE* pSpecVersion, + OMX_OUT OMX_UUIDTYPE* pComponentUUID); + + /** refer to OMX_SendCommand in OMX_core.h or the OMX IL + specification for details on the SendCommand method. + */ + OMX_ERRORTYPE (*SendCommand)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_COMMANDTYPE Cmd, + OMX_IN OMX_U32 nParam1, + OMX_IN OMX_PTR pCmdData); + + /** refer to OMX_GetParameter in OMX_core.h or the OMX IL + specification for details on the GetParameter method. + */ + OMX_ERRORTYPE (*GetParameter)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_INOUT OMX_PTR pComponentParameterStructure); + + + /** refer to OMX_SetParameter in OMX_core.h or the OMX IL + specification for details on the SetParameter method. + */ + OMX_ERRORTYPE (*SetParameter)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nIndex, + OMX_IN OMX_PTR pComponentParameterStructure); + + + /** refer to OMX_GetConfig in OMX_core.h or the OMX IL + specification for details on the GetConfig method. + */ + OMX_ERRORTYPE (*GetConfig)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nIndex, + OMX_INOUT OMX_PTR pComponentConfigStructure); + + + /** refer to OMX_SetConfig in OMX_core.h or the OMX IL + specification for details on the SetConfig method. + */ + OMX_ERRORTYPE (*SetConfig)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nIndex, + OMX_IN OMX_PTR pComponentConfigStructure); + + + /** refer to OMX_GetExtensionIndex in OMX_core.h or the OMX IL + specification for details on the GetExtensionIndex method. + */ + OMX_ERRORTYPE (*GetExtensionIndex)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_STRING cParameterName, + OMX_OUT OMX_INDEXTYPE* pIndexType); + + + /** refer to OMX_GetState in OMX_core.h or the OMX IL + specification for details on the GetState method. + */ + OMX_ERRORTYPE (*GetState)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_OUT OMX_STATETYPE* pState); + + + /** The ComponentTunnelRequest method will interact with another OMX + component to determine if tunneling is possible and to setup the + tunneling. The return codes for this method can be used to + determine if tunneling is not possible, or if tunneling is not + supported. + + Base profile components (i.e. non-interop) do not support this + method and should return OMX_ErrorNotImplemented + + The interop profile component MUST support tunneling to another + interop profile component with a compatible port parameters. + A component may also support proprietary communication. + + If proprietary communication is supported the negotiation of + proprietary communication is done outside of OMX in a vendor + specific way. It is only required that the proper result be + returned and the details of how the setup is done is left + to the component implementation. + + When this method is invoked when nPort in an output port, the + component will: + 1. Populate the pTunnelSetup structure with the output port's + requirements and constraints for the tunnel. + + When this method is invoked when nPort in an input port, the + component will: + 1. Query the necessary parameters from the output port to + determine if the ports are compatible for tunneling + 2. If the ports are compatible, the component should store + the tunnel step provided by the output port + 3. Determine which port (either input or output) is the buffer + supplier, and call OMX_SetParameter on the output port to + indicate this selection. + + The component will return from this call within 5 msec. + + @param [in] hComp + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle method. + @param [in] nPort + nPort is used to select the port on the component to be used + for tunneling. + @param [in] hTunneledComp + Handle of the component to tunnel with. This is the component + handle returned by the call to the OMX_GetHandle method. When + this parameter is 0x0 the component should setup the port for + communication with the application / IL Client. + @param [in] nPortOutput + nPortOutput is used indicate the port the component should + tunnel with. + @param [in] pTunnelSetup + Pointer to the tunnel setup structure. When nPort is an output port + the component should populate the fields of this structure. When + When nPort is an input port the component should review the setup + provided by the component with the output port. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup tun + */ + + OMX_ERRORTYPE (*ComponentTunnelRequest)( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 nPort, + OMX_IN OMX_HANDLETYPE hTunneledComp, + OMX_IN OMX_U32 nTunneledPort, + OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup); + + /** refer to OMX_UseBuffer in OMX_core.h or the OMX IL + specification for details on the UseBuffer method. + @ingroup buf + */ + OMX_ERRORTYPE (*UseBuffer)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN OMX_U32 nSizeBytes, + OMX_IN OMX_U8* pBuffer); + + /** refer to OMX_AllocateBuffer in OMX_core.h or the OMX IL + specification for details on the AllocateBuffer method. + @ingroup buf + */ + OMX_ERRORTYPE (*AllocateBuffer)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN OMX_U32 nSizeBytes); + + /** refer to OMX_FreeBuffer in OMX_core.h or the OMX IL + specification for details on the FreeBuffer method. + @ingroup buf + */ + OMX_ERRORTYPE (*FreeBuffer)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + + /** refer to OMX_EmptyThisBuffer in OMX_core.h or the OMX IL + specification for details on the EmptyThisBuffer method. + @ingroup buf + */ + OMX_ERRORTYPE (*EmptyThisBuffer)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + + /** refer to OMX_FillThisBuffer in OMX_core.h or the OMX IL + specification for details on the FillThisBuffer method. + @ingroup buf + */ + OMX_ERRORTYPE (*FillThisBuffer)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + + /** The SetCallbacks method is used by the core to specify the callback + structure from the application to the component. This is a blocking + call. The component will return from this call within 5 msec. + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the GetHandle function. + @param [in] pCallbacks + pointer to an OMX_CALLBACKTYPE structure used to provide the + callback information to the component + @param [in] pAppData + pointer to an application defined value. It is anticipated that + the application will pass a pointer to a data structure or a "this + pointer" in this area to allow the callback (in the application) + to determine the context of the call + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + */ + OMX_ERRORTYPE (*SetCallbacks)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_CALLBACKTYPE* pCallbacks, + OMX_IN OMX_PTR pAppData); + + /** ComponentDeInit method is used to deinitialize the component + providing a means to free any resources allocated at component + initialization. NOTE: After this call the component handle is + not valid for further use. + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the GetHandle function. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + */ + OMX_ERRORTYPE (*ComponentDeInit)( + OMX_IN OMX_HANDLETYPE hComponent); + + /** @ingroup buf */ + OMX_ERRORTYPE (*UseEGLImage)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN void* eglImage); + + OMX_ERRORTYPE (*ComponentRoleEnum)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_OUT OMX_U8 *cRole, + OMX_IN OMX_U32 nIndex); + +} OMX_COMPONENTTYPE; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ diff --git a/interface/vmcs_host/khronos/IL/OMX_Core.h b/interface/vmcs_host/khronos/IL/OMX_Core.h new file mode 100755 index 0000000..d883d94 --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Core.h @@ -0,0 +1,1467 @@ +/* + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** OMX_Core.h - OpenMax IL version 1.1.2 + * The OMX_Core header file contains the definitions used by both the + * application and the component to access common items. + */ + +#ifndef OMX_Core_h +#define OMX_Core_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if !defined(OMX_SKIP64BIT) && !defined(_VIDEOCORE) + /* The Videocore compiler doesn't enforce 64 bit alignment on 64 bit variables, + * which is almost equivalent to OMX_SKIP64BIT. + * Annoyingly struct OMX_BUFFERHEADERTYPE doesn't do the sensible thing + * and add padding fields or similar to make it the same for all compilers, + * so all clients need to define this. + * Warn if this isn't set, as the GPU will not interpret your buffers correctly, + * or vice versa. + */ + #warning OMX_SKIP64BIT is not defined - this will be incompatible with the VC GPU code. +#endif + +/* Each OMX header shall include all required header files to allow the + * header to compile without errors. The includes below are required + * for this header file to compile successfully + */ + +#include "OMX_Index.h" + + +/** The OMX_COMMANDTYPE enumeration is used to specify the action in the + * OMX_SendCommand macro. + * @ingroup core + */ +typedef enum OMX_COMMANDTYPE +{ + OMX_CommandStateSet, /**< Change the component state */ + OMX_CommandFlush, /**< Flush the data queue(s) of a component */ + OMX_CommandPortDisable, /**< Disable a port on a component. */ + OMX_CommandPortEnable, /**< Enable a port on a component. */ + OMX_CommandMarkBuffer, /**< Mark a component/buffer for observation */ + OMX_CommandKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_CommandVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_CommandMax = 0X7FFFFFFF +} OMX_COMMANDTYPE; + + + +/** The OMX_STATETYPE enumeration is used to indicate or change the component + * state. This enumeration reflects the current state of the component when + * used with the OMX_GetState macro or becomes the parameter in a state change + * command when used with the OMX_SendCommand macro. + * + * The component will be in the Loaded state after the component is initially + * loaded into memory. In the Loaded state, the component is not allowed to + * allocate or hold resources other than to build it's internal parameter + * and configuration tables. The application will send one or more + * SetParameters/GetParameters and SetConfig/GetConfig commands to the + * component and the component will record each of these parameter and + * configuration changes for use later. When the application sends the + * Idle command, the component will acquire the resources needed for the + * specified configuration and will transition to the idle state if the + * allocation is successful. If the component cannot successfully + * transition to the idle state for any reason, the state of the component + * shall be fully rolled back to the Loaded state (e.g. all allocated + * resources shall be released). When the component receives the command + * to go to the Executing state, it shall begin processing buffers by + * sending all input buffers it holds to the application. While + * the component is in the Idle state, the application may also send the + * Pause command. If the component receives the pause command while in the + * Idle state, the component shall send all input buffers it holds to the + * application, but shall not begin processing buffers. This will allow the + * application to prefill buffers. + * + * @ingroup comp + */ + +typedef enum OMX_STATETYPE +{ + OMX_StateInvalid, /**< component has detected that it's internal data + structures are corrupted to the point that + it cannot determine it's state properly */ + OMX_StateLoaded, /**< component has been loaded but has not completed + initialization. The OMX_SetParameter macro + and the OMX_GetParameter macro are the only + valid macros allowed to be sent to the + component in this state. */ + OMX_StateIdle, /**< component initialization has been completed + successfully and the component is ready to + to start. */ + OMX_StateExecuting, /**< component has accepted the start command and + is processing data (if data is available) */ + OMX_StatePause, /**< component has received pause command */ + OMX_StateWaitForResources, /**< component is waiting for resources, either after + preemption or before it gets the resources requested. + See specification for complete details. */ + OMX_StateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_StateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_StateMax = 0X7FFFFFFF +} OMX_STATETYPE; + +/** The OMX_ERRORTYPE enumeration defines the standard OMX Errors. These + * errors should cover most of the common failure cases. However, + * vendors are free to add additional error messages of their own as + * long as they follow these rules: + * 1. Vendor error messages shall be in the range of 0x90000000 to + * 0x9000FFFF. + * 2. Vendor error messages shall be defined in a header file provided + * with the component. No error messages are allowed that are + * not defined. + */ +typedef enum OMX_ERRORTYPE +{ + OMX_ErrorNone = 0, + + /** There were insufficient resources to perform the requested operation */ + OMX_ErrorInsufficientResources = (OMX_S32) 0x80001000, + + /** There was an error, but the cause of the error could not be determined */ + OMX_ErrorUndefined = (OMX_S32) 0x80001001, + + /** The component name string was not valid */ + OMX_ErrorInvalidComponentName = (OMX_S32) 0x80001002, + + /** No component with the specified name string was found */ + OMX_ErrorComponentNotFound = (OMX_S32) 0x80001003, + + /** The component specified did not have a "OMX_ComponentInit" or + "OMX_ComponentDeInit entry point */ + OMX_ErrorInvalidComponent = (OMX_S32) 0x80001004, + + /** One or more parameters were not valid */ + OMX_ErrorBadParameter = (OMX_S32) 0x80001005, + + /** The requested function is not implemented */ + OMX_ErrorNotImplemented = (OMX_S32) 0x80001006, + + /** The buffer was emptied before the next buffer was ready */ + OMX_ErrorUnderflow = (OMX_S32) 0x80001007, + + /** The buffer was not available when it was needed */ + OMX_ErrorOverflow = (OMX_S32) 0x80001008, + + /** The hardware failed to respond as expected */ + OMX_ErrorHardware = (OMX_S32) 0x80001009, + + /** The component is in the state OMX_StateInvalid */ + OMX_ErrorInvalidState = (OMX_S32) 0x8000100A, + + /** Stream is found to be corrupt */ + OMX_ErrorStreamCorrupt = (OMX_S32) 0x8000100B, + + /** Ports being connected are not compatible */ + OMX_ErrorPortsNotCompatible = (OMX_S32) 0x8000100C, + + /** Resources allocated to an idle component have been + lost resulting in the component returning to the loaded state */ + OMX_ErrorResourcesLost = (OMX_S32) 0x8000100D, + + /** No more indicies can be enumerated */ + OMX_ErrorNoMore = (OMX_S32) 0x8000100E, + + /** The component detected a version mismatch */ + OMX_ErrorVersionMismatch = (OMX_S32) 0x8000100F, + + /** The component is not ready to return data at this time */ + OMX_ErrorNotReady = (OMX_S32) 0x80001010, + + /** There was a timeout that occurred */ + OMX_ErrorTimeout = (OMX_S32) 0x80001011, + + /** This error occurs when trying to transition into the state you are already in */ + OMX_ErrorSameState = (OMX_S32) 0x80001012, + + /** Resources allocated to an executing or paused component have been + preempted, causing the component to return to the idle state */ + OMX_ErrorResourcesPreempted = (OMX_S32) 0x80001013, + + /** A non-supplier port sends this error to the IL client (via the EventHandler callback) + during the allocation of buffers (on a transition from the LOADED to the IDLE state or + on a port restart) when it deems that it has waited an unusually long time for the supplier + to send it an allocated buffer via a UseBuffer call. */ + OMX_ErrorPortUnresponsiveDuringAllocation = (OMX_S32) 0x80001014, + + /** A non-supplier port sends this error to the IL client (via the EventHandler callback) + during the deallocation of buffers (on a transition from the IDLE to LOADED state or + on a port stop) when it deems that it has waited an unusually long time for the supplier + to request the deallocation of a buffer header via a FreeBuffer call. */ + OMX_ErrorPortUnresponsiveDuringDeallocation = (OMX_S32) 0x80001015, + + /** A supplier port sends this error to the IL client (via the EventHandler callback) + during the stopping of a port (either on a transition from the IDLE to LOADED + state or a port stop) when it deems that it has waited an unusually long time for + the non-supplier to return a buffer via an EmptyThisBuffer or FillThisBuffer call. */ + OMX_ErrorPortUnresponsiveDuringStop = (OMX_S32) 0x80001016, + + /** Attempting a state transtion that is not allowed */ + OMX_ErrorIncorrectStateTransition = (OMX_S32) 0x80001017, + + /* Attempting a command that is not allowed during the present state. */ + OMX_ErrorIncorrectStateOperation = (OMX_S32) 0x80001018, + + /** The values encapsulated in the parameter or config structure are not supported. */ + OMX_ErrorUnsupportedSetting = (OMX_S32) 0x80001019, + + /** The parameter or config indicated by the given index is not supported. */ + OMX_ErrorUnsupportedIndex = (OMX_S32) 0x8000101A, + + /** The port index supplied is incorrect. */ + OMX_ErrorBadPortIndex = (OMX_S32) 0x8000101B, + + /** The port has lost one or more of its buffers and it thus unpopulated. */ + OMX_ErrorPortUnpopulated = (OMX_S32) 0x8000101C, + + /** Component suspended due to temporary loss of resources */ + OMX_ErrorComponentSuspended = (OMX_S32) 0x8000101D, + + /** Component suspended due to an inability to acquire dynamic resources */ + OMX_ErrorDynamicResourcesUnavailable = (OMX_S32) 0x8000101E, + + /** When the macroblock error reporting is enabled the component returns new error + for every frame that has errors */ + OMX_ErrorMbErrorsInFrame = (OMX_S32) 0x8000101F, + + /** A component reports this error when it cannot parse or determine the format of an input stream. */ + OMX_ErrorFormatNotDetected = (OMX_S32) 0x80001020, + + /** The content open operation failed. */ + OMX_ErrorContentPipeOpenFailed = (OMX_S32) 0x80001021, + + /** The content creation operation failed. */ + OMX_ErrorContentPipeCreationFailed = (OMX_S32) 0x80001022, + + /** Separate table information is being used */ + OMX_ErrorSeperateTablesUsed = (OMX_S32) 0x80001023, + + /** Tunneling is unsupported by the component*/ + OMX_ErrorTunnelingUnsupported = (OMX_S32) 0x80001024, + + OMX_ErrorKhronosExtensions = (OMX_S32)0x8F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ErrorVendorStartUnused = (OMX_S32)0x90000000, /**< Reserved region for introducing Vendor Extensions */ + + /** Disk Full error */ + OMX_ErrorDiskFull = (OMX_S32) 0x90000001, + + /** Max file size is reached */ + OMX_ErrorMaxFileSize = (OMX_S32) 0x90000002, + + /** Unauthorised to play a DRM protected file */ + OMX_ErrorDrmUnauthorised = (OMX_S32) 0x90000003, + + /** The DRM protected file has expired */ + OMX_ErrorDrmExpired = (OMX_S32) 0x90000004, + + /** Some other DRM library error */ + OMX_ErrorDrmGeneral = (OMX_S32) 0x90000005, + + OMX_ErrorMax = 0x7FFFFFFF +} OMX_ERRORTYPE; + +/** @ingroup core */ +typedef OMX_ERRORTYPE (* OMX_COMPONENTINITTYPE)(OMX_IN OMX_HANDLETYPE hComponent); + +/** @ingroup core */ +typedef struct OMX_COMPONENTREGISTERTYPE +{ + const char * pName; /* Component name, 128 byte limit (including '\0') applies */ + OMX_COMPONENTINITTYPE pInitialize; /* Component instance initialization function */ +} OMX_COMPONENTREGISTERTYPE; + +/** @ingroup core */ +extern OMX_COMPONENTREGISTERTYPE OMX_ComponentRegistered[]; + +/** @ingroup rpm */ +typedef struct OMX_PRIORITYMGMTTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nGroupPriority; /**< Priority of the component group */ + OMX_U32 nGroupID; /**< ID of the component group */ +} OMX_PRIORITYMGMTTYPE; + +/* Component name and Role names are limited to 128 characters including the terminating '\0'. */ +#define OMX_MAX_STRINGNAME_SIZE 128 + +/** @ingroup comp */ +typedef struct OMX_PARAM_COMPONENTROLETYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U8 cRole[OMX_MAX_STRINGNAME_SIZE]; /**< name of standard component which defines component role */ +} OMX_PARAM_COMPONENTROLETYPE; + +/** End of Stream Buffer Flag: + * + * A component sets EOS when it has no more data to emit on a particular + * output port. Thus an output port shall set EOS on the last buffer it + * emits. A component's determination of when an output port should + * cease sending data is implemenation specific. + * @ingroup buf + */ + +#define OMX_BUFFERFLAG_EOS 0x00000001 + +/** Start Time Buffer Flag: + * + * The source of a stream (e.g. a demux component) sets the STARTTIME + * flag on the buffer that contains the starting timestamp for the + * stream. The starting timestamp corresponds to the first data that + * should be displayed at startup or after a seek. + * The first timestamp of the stream is not necessarily the start time. + * For instance, in the case of a seek to a particular video frame, + * the target frame may be an interframe. Thus the first buffer of + * the stream will be the intra-frame preceding the target frame and + * the starttime will occur with the target frame (with any other + * required frames required to reconstruct the target intervening). + * + * The STARTTIME flag is directly associated with the buffer's + * timestamp ' thus its association to buffer data and its + * propagation is identical to the timestamp's. + * + * When a Sync Component client receives a buffer with the + * STARTTIME flag it shall perform a SetConfig on its sync port + * using OMX_ConfigTimeClientStartTime and passing the buffer's + * timestamp. + * + * @ingroup buf + */ + +#define OMX_BUFFERFLAG_STARTTIME 0x00000002 + + + +/** Decode Only Buffer Flag: + * + * The source of a stream (e.g. a demux component) sets the DECODEONLY + * flag on any buffer that should shall be decoded but should not be + * displayed. This flag is used, for instance, when a source seeks to + * a target interframe that requires the decode of frames preceding the + * target to facilitate the target's reconstruction. In this case the + * source would emit the frames preceding the target downstream + * but mark them as decode only. + * + * The DECODEONLY is associated with buffer data and propagated in a + * manner identical to the buffer timestamp. + * + * A component that renders data should ignore all buffers with + * the DECODEONLY flag set. + * + * @ingroup buf + */ + +#define OMX_BUFFERFLAG_DECODEONLY 0x00000004 + + +/* Data Corrupt Flag: This flag is set when the IL client believes the data in the associated buffer is corrupt + * @ingroup buf + */ + +#define OMX_BUFFERFLAG_DATACORRUPT 0x00000008 + +/* End of Frame: The buffer contains exactly one end of frame and no data + * occurs after the end of frame. This flag is an optional hint. The absence + * of this flag does not imply the absence of an end of frame within the buffer. + * @ingroup buf +*/ +#define OMX_BUFFERFLAG_ENDOFFRAME 0x00000010 + +/* Sync Frame Flag: This flag is set when the buffer content contains a coded sync frame ' + * a frame that has no dependency on any other frame information + * @ingroup buf + */ +#define OMX_BUFFERFLAG_SYNCFRAME 0x00000020 + +/* Extra data present flag: there is extra data appended to the data stream + * residing in the buffer + * @ingroup buf + */ +#define OMX_BUFFERFLAG_EXTRADATA 0x00000040 + +/** Codec Config Buffer Flag: +* OMX_BUFFERFLAG_CODECCONFIG is an optional flag that is set by an +* output port when all bytes in the buffer form part or all of a set of +* codec specific configuration data. Examples include SPS/PPS nal units +* for OMX_VIDEO_CodingAVC or AudioSpecificConfig data for +* OMX_AUDIO_CodingAAC. Any component that for a given stream sets +* OMX_BUFFERFLAG_CODECCONFIG shall not mix codec configuration bytes +* with frame data in the same buffer, and shall send all buffers +* containing codec configuration bytes before any buffers containing +* frame data that those configurations bytes describe. +* If the stream format for a particular codec has a frame specific +* header at the start of each frame, for example OMX_AUDIO_CodingMP3 or +* OMX_AUDIO_CodingAAC in ADTS mode, then these shall be presented as +* normal without setting OMX_BUFFERFLAG_CODECCONFIG. + * @ingroup buf + */ +#define OMX_BUFFERFLAG_CODECCONFIG 0x00000080 + + + +/** @ingroup buf */ +typedef struct OMX_BUFFERHEADERTYPE +{ + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U8* pBuffer; /**< Pointer to actual block of memory + that is acting as the buffer */ + OMX_U32 nAllocLen; /**< size of the buffer allocated, in bytes */ + OMX_U32 nFilledLen; /**< number of bytes currently in the + buffer */ + OMX_U32 nOffset; /**< start offset of valid data in bytes from + the start of the buffer */ + OMX_PTR pAppPrivate; /**< pointer to any data the application + wants to associate with this buffer */ + OMX_PTR pPlatformPrivate; /**< pointer to any data the platform + wants to associate with this buffer */ + OMX_PTR pInputPortPrivate; /**< pointer to any data the input port + wants to associate with this buffer */ + OMX_PTR pOutputPortPrivate; /**< pointer to any data the output port + wants to associate with this buffer */ + OMX_HANDLETYPE hMarkTargetComponent; /**< The component that will generate a + mark event upon processing this buffer. */ + OMX_PTR pMarkData; /**< Application specific data associated with + the mark sent on a mark event to disambiguate + this mark from others. */ + OMX_U32 nTickCount; /**< Optional entry that the component and + application can update with a tick count + when they access the component. This + value should be in microseconds. Since + this is a value relative to an arbitrary + starting point, this value cannot be used + to determine absolute time. This is an + optional entry and not all components + will update it.*/ + OMX_TICKS nTimeStamp; /**< Timestamp corresponding to the sample + starting at the first logical sample + boundary in the buffer. Timestamps of + successive samples within the buffer may + be inferred by adding the duration of the + of the preceding buffer to the timestamp + of the preceding buffer.*/ + OMX_U32 nFlags; /**< buffer specific flags */ + OMX_U32 nOutputPortIndex; /**< The index of the output port (if any) using + this buffer */ + OMX_U32 nInputPortIndex; /**< The index of the input port (if any) using + this buffer */ +} OMX_BUFFERHEADERTYPE; + +/** The OMX_EXTRADATATYPE enumeration is used to define the + * possible extra data payload types. + * NB: this enum is binary backwards compatible with the previous + * OMX_EXTRADATA_QUANT define. This should be replaced with + * OMX_ExtraDataQuantization. + */ +typedef enum OMX_EXTRADATATYPE +{ + OMX_ExtraDataNone = 0, /**< Indicates that no more extra data sections follow */ + OMX_ExtraDataQuantization, /**< The data payload contains quantization data */ + OMX_ExtraDataKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ExtraDataVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + + OMX_ExtraDataSequenceGap, /**< Indicates a gap in sequence numbers, data is uint32_t + saying how many frames were lost */ + OMX_ExtraDataDecodeOnlyUntil, /**< Indicates a timestamp until which all data should be + decoded only, and the first packets after should generate + a client start time flag. data is int32_t of seek time + in milliseconds */ + + OMX_ExtraDataMax = 0x7FFFFFFF +} OMX_EXTRADATATYPE; + + +typedef struct OMX_OTHER_EXTRADATATYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_EXTRADATATYPE eType; /* Extra Data type */ + OMX_U32 nDataSize; /* Size of the supporting data to follow */ + OMX_U8 data[1]; /* Supporting data hint */ +} OMX_OTHER_EXTRADATATYPE; + +/** @ingroup comp */ +typedef struct OMX_PORT_PARAM_TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPorts; /**< The number of ports for this component */ + OMX_U32 nStartPortNumber; /** first port number for this type of port */ +} OMX_PORT_PARAM_TYPE; + +/** @ingroup comp */ +typedef enum OMX_EVENTTYPE +{ + OMX_EventCmdComplete, /**< component has sucessfully completed a command */ + OMX_EventError, /**< component has detected an error condition */ + OMX_EventMark, /**< component has detected a buffer mark */ + OMX_EventPortSettingsChanged, /**< component is reported a port settings change */ + OMX_EventBufferFlag, /**< component has detected an EOS */ + OMX_EventResourcesAcquired, /**< component has been granted resources and is + automatically starting the state change from + OMX_StateWaitForResources to OMX_StateIdle. */ + OMX_EventComponentResumed, /**< Component resumed due to reacquisition of resources */ + OMX_EventDynamicResourcesAvailable, /**< Component has acquired previously unavailable dynamic resources */ + OMX_EventPortFormatDetected, /**< Component has detected a supported format. */ + OMX_EventKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_EventVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_EventParamOrConfigChanged, /* Should be added to the main spec as part of IL416c */ + OMX_EventMax = 0x7FFFFFFF +} OMX_EVENTTYPE; + +typedef struct OMX_CALLBACKTYPE +{ + /** The EventHandler method is used to notify the application when an + event of interest occurs. Events are defined in the OMX_EVENTTYPE + enumeration. Please see that enumeration for details of what will + be returned for each type of event. Callbacks should not return + an error to the component, so if an error occurs, the application + shall handle it internally. This is a blocking call. + + The application should return from this call within 5 msec to avoid + blocking the component for an excessively long period of time. + + @param hComponent + handle of the component to access. This is the component + handle returned by the call to the GetHandle function. + @param pAppData + pointer to an application defined value that was provided in the + pAppData parameter to the OMX_GetHandle method for the component. + This application defined value is provided so that the application + can have a component specific context when receiving the callback. + @param eEvent + Event that the component wants to notify the application about. + @param nData1 + nData will be the OMX_ERRORTYPE for an error event and will be + an OMX_COMMANDTYPE for a command complete event and OMX_INDEXTYPE for a OMX_PortSettingsChanged event. + @param nData2 + nData2 will hold further information related to the event. Can be OMX_STATETYPE for + a OMX_CommandStateSet command or port index for a OMX_PortSettingsChanged event. + Default value is 0 if not used. ) + @param pEventData + Pointer to additional event-specific data (see spec for meaning). + */ + + OMX_ERRORTYPE (*EventHandler)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); + + /** The EmptyBufferDone method is used to return emptied buffers from an + input port back to the application for reuse. This is a blocking call + so the application should not attempt to refill the buffers during this + call, but should queue them and refill them in another thread. There + is no error return, so the application shall handle any errors generated + internally. + + The application should return from this call within 5 msec. + + @param hComponent + handle of the component to access. This is the component + handle returned by the call to the GetHandle function. + @param pAppData + pointer to an application defined value that was provided in the + pAppData parameter to the OMX_GetHandle method for the component. + This application defined value is provided so that the application + can have a component specific context when receiving the callback. + @param pBuffer + pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer + or AllocateBuffer indicating the buffer that was emptied. + @ingroup buf + */ + OMX_ERRORTYPE (*EmptyBufferDone)( + OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + + /** The FillBufferDone method is used to return filled buffers from an + output port back to the application for emptying and then reuse. + This is a blocking call so the application should not attempt to + empty the buffers during this call, but should queue the buffers + and empty them in another thread. There is no error return, so + the application shall handle any errors generated internally. The + application shall also update the buffer header to indicate the + number of bytes placed into the buffer. + + The application should return from this call within 5 msec. + + @param hComponent + handle of the component to access. This is the component + handle returned by the call to the GetHandle function. + @param pAppData + pointer to an application defined value that was provided in the + pAppData parameter to the OMX_GetHandle method for the component. + This application defined value is provided so that the application + can have a component specific context when receiving the callback. + @param pBuffer + pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer + or AllocateBuffer indicating the buffer that was filled. + @ingroup buf + */ + OMX_ERRORTYPE (*FillBufferDone)( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); + +} OMX_CALLBACKTYPE; + +/** The OMX_BUFFERSUPPLIERTYPE enumeration is used to dictate port supplier + preference when tunneling between two ports. + @ingroup tun buf +*/ +typedef enum OMX_BUFFERSUPPLIERTYPE +{ + OMX_BufferSupplyUnspecified = 0x0, /**< port supplying the buffers is unspecified, + or don't care */ + OMX_BufferSupplyInput, /**< input port supplies the buffers */ + OMX_BufferSupplyOutput, /**< output port supplies the buffers */ + OMX_BufferSupplyKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_BufferSupplyVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_BufferSupplyMax = 0x7FFFFFFF +} OMX_BUFFERSUPPLIERTYPE; + + +/** buffer supplier parameter + * @ingroup tun + */ +typedef struct OMX_PARAM_BUFFERSUPPLIERTYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_BUFFERSUPPLIERTYPE eBufferSupplier; /**< buffer supplier */ +} OMX_PARAM_BUFFERSUPPLIERTYPE; + + +/**< indicates that buffers received by an input port of a tunnel + may not modify the data in the buffers + @ingroup tun + */ +#define OMX_PORTTUNNELFLAG_READONLY 0x00000001 + + +/** The OMX_TUNNELSETUPTYPE structure is used to pass data from an output + port to an input port as part the two ComponentTunnelRequest calls + resulting from a OMX_SetupTunnel call from the IL Client. + @ingroup tun + */ +typedef struct OMX_TUNNELSETUPTYPE +{ + OMX_U32 nTunnelFlags; /**< bit flags for tunneling */ + OMX_BUFFERSUPPLIERTYPE eSupplier; /**< supplier preference */ +} OMX_TUNNELSETUPTYPE; + +/* OMX Component headers is included to enable the core to use + macros for functions into the component for OMX release 1.0. + Developers should not access any structures or data from within + the component header directly */ +/* TO BE REMOVED - #include */ + +/** GetComponentVersion will return information about the component. + This is a blocking call. This macro will go directly from the + application to the component (via a core macro). The + component will return from this call within 5 msec. + @param [in] hComponent + handle of component to execute the command + @param [out] pComponentName + pointer to an empty string of length 128 bytes. The component + will write its name into this string. The name will be + terminated by a single zero byte. The name of a component will + be 127 bytes or less to leave room for the trailing zero byte. + An example of a valid component name is "OMX.ABC.ChannelMixer\0". + @param [out] pComponentVersion + pointer to an OMX Version structure that the component will fill + in. The component will fill in a value that indicates the + component version. NOTE: the component version is NOT the same + as the OMX Specification version (found in all structures). The + component version is defined by the vendor of the component and + its value is entirely up to the component vendor. + @param [out] pSpecVersion + pointer to an OMX Version structure that the component will fill + in. The SpecVersion is the version of the specification that the + component was built against. Please note that this value may or + may not match the structure's version. For example, if the + component was built against the 2.0 specification, but the + application (which creates the structure is built against the + 1.0 specification the versions would be different. + @param [out] pComponentUUID + pointer to the UUID of the component which will be filled in by + the component. The UUID is a unique identifier that is set at + RUN time for the component and is unique to each instantion of + the component. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_GetComponentVersion( \ + hComponent, \ + pComponentName, \ + pComponentVersion, \ + pSpecVersion, \ + pComponentUUID) \ + ((OMX_COMPONENTTYPE*)hComponent)->GetComponentVersion( \ + hComponent, \ + pComponentName, \ + pComponentVersion, \ + pSpecVersion, \ + pComponentUUID) /* Macro End */ + + +/** Send a command to the component. This call is a non-blocking call. + The component should check the parameters and then queue the command + to the component thread to be executed. The component thread shall + send the EventHandler() callback at the conclusion of the command. + This macro will go directly from the application to the component (via + a core macro). The component will return from this call within 5 msec. + + When the command is "OMX_CommandStateSet" the component will queue a + state transition to the new state idenfied in nParam. + + When the command is "OMX_CommandFlush", to flush a port's buffer queues, + the command will force the component to return all buffers NOT CURRENTLY + BEING PROCESSED to the application, in the order in which the buffers + were received. + + When the command is "OMX_CommandPortDisable" or + "OMX_CommandPortEnable", the component's port (given by the value of + nParam) will be stopped or restarted. + + When the command "OMX_CommandMarkBuffer" is used to mark a buffer, the + pCmdData will point to a OMX_MARKTYPE structure containing the component + handle of the component to examine the buffer chain for the mark. nParam1 + contains the index of the port on which the buffer mark is applied. + + Specification text for more details. + + @param [in] hComponent + handle of component to execute the command + @param [in] Cmd + Command for the component to execute + @param [in] nParam + Parameter for the command to be executed. When Cmd has the value + OMX_CommandStateSet, value is a member of OMX_STATETYPE. When Cmd has + the value OMX_CommandFlush, value of nParam indicates which port(s) + to flush. -1 is used to flush all ports a single port index will + only flush that port. When Cmd has the value "OMX_CommandPortDisable" + or "OMX_CommandPortEnable", the component's port is given by + the value of nParam. When Cmd has the value "OMX_CommandMarkBuffer" + the components pot is given by the value of nParam. + @param [in] pCmdData + Parameter pointing to the OMX_MARKTYPE structure when Cmd has the value + "OMX_CommandMarkBuffer". + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_SendCommand( \ + hComponent, \ + Cmd, \ + nParam, \ + pCmdData) \ + ((OMX_COMPONENTTYPE*)hComponent)->SendCommand( \ + hComponent, \ + Cmd, \ + nParam, \ + pCmdData) /* Macro End */ + + +/** The OMX_GetParameter macro will get one of the current parameter + settings from the component. This macro cannot only be invoked when + the component is in the OMX_StateInvalid state. The nParamIndex + parameter is used to indicate which structure is being requested from + the component. The application shall allocate the correct structure + and shall fill in the structure size and version information before + invoking this macro. When the parameter applies to a port, the + caller shall fill in the appropriate nPortIndex value indicating the + port on which the parameter applies. If the component has not had + any settings changed, then the component should return a set of + valid DEFAULT parameters for the component. This is a blocking + call. + + The component should return from this call within 20 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] nParamIndex + Index of the structure to be filled. This value is from the + OMX_INDEXTYPE enumeration. + @param [in,out] pComponentParameterStructure + Pointer to application allocated structure to be filled by the + component. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_GetParameter( \ + hComponent, \ + nParamIndex, \ + pComponentParameterStructure) \ + ((OMX_COMPONENTTYPE*)hComponent)->GetParameter( \ + hComponent, \ + nParamIndex, \ + pComponentParameterStructure) /* Macro End */ + + +/** The OMX_SetParameter macro will send an initialization parameter + structure to a component. Each structure shall be sent one at a time, + in a separate invocation of the macro. This macro can only be + invoked when the component is in the OMX_StateLoaded state, or the + port is disabled (when the parameter applies to a port). The + nParamIndex parameter is used to indicate which structure is being + passed to the component. The application shall allocate the + correct structure and shall fill in the structure size and version + information (as well as the actual data) before invoking this macro. + The application is free to dispose of this structure after the call + as the component is required to copy any data it shall retain. This + is a blocking call. + + The component should return from this call within 20 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] nIndex + Index of the structure to be sent. This value is from the + OMX_INDEXTYPE enumeration. + @param [in] pComponentParameterStructure + pointer to application allocated structure to be used for + initialization by the component. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_SetParameter( \ + hComponent, \ + nParamIndex, \ + pComponentParameterStructure) \ + ((OMX_COMPONENTTYPE*)hComponent)->SetParameter( \ + hComponent, \ + nParamIndex, \ + pComponentParameterStructure) /* Macro End */ + + +/** The OMX_GetConfig macro will get one of the configuration structures + from a component. This macro can be invoked anytime after the + component has been loaded. The nParamIndex call parameter is used to + indicate which structure is being requested from the component. The + application shall allocate the correct structure and shall fill in the + structure size and version information before invoking this macro. + If the component has not had this configuration parameter sent before, + then the component should return a set of valid DEFAULT values for the + component. This is a blocking call. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] nIndex + Index of the structure to be filled. This value is from the + OMX_INDEXTYPE enumeration. + @param [in,out] pComponentConfigStructure + pointer to application allocated structure to be filled by the + component. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp +*/ +#define OMX_GetConfig( \ + hComponent, \ + nConfigIndex, \ + pComponentConfigStructure) \ + ((OMX_COMPONENTTYPE*)hComponent)->GetConfig( \ + hComponent, \ + nConfigIndex, \ + pComponentConfigStructure) /* Macro End */ + + +/** The OMX_SetConfig macro will send one of the configuration + structures to a component. Each structure shall be sent one at a time, + each in a separate invocation of the macro. This macro can be invoked + anytime after the component has been loaded. The application shall + allocate the correct structure and shall fill in the structure size + and version information (as well as the actual data) before invoking + this macro. The application is free to dispose of this structure after + the call as the component is required to copy any data it shall retain. + This is a blocking call. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] nConfigIndex + Index of the structure to be sent. This value is from the + OMX_INDEXTYPE enumeration above. + @param [in] pComponentConfigStructure + pointer to application allocated structure to be used for + initialization by the component. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_SetConfig( \ + hComponent, \ + nConfigIndex, \ + pComponentConfigStructure) \ + ((OMX_COMPONENTTYPE*)hComponent)->SetConfig( \ + hComponent, \ + nConfigIndex, \ + pComponentConfigStructure) /* Macro End */ + + +/** The OMX_GetExtensionIndex macro will invoke a component to translate + a vendor specific configuration or parameter string into an OMX + structure index. There is no requirement for the vendor to support + this command for the indexes already found in the OMX_INDEXTYPE + enumeration (this is done to save space in small components). The + component shall support all vendor supplied extension indexes not found + in the master OMX_INDEXTYPE enumeration. This is a blocking call. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the GetHandle function. + @param [in] cParameterName + OMX_STRING that shall be less than 128 characters long including + the trailing null byte. This is the string that will get + translated by the component into a configuration index. + @param [out] pIndexType + a pointer to a OMX_INDEXTYPE to receive the index value. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_GetExtensionIndex( \ + hComponent, \ + cParameterName, \ + pIndexType) \ + ((OMX_COMPONENTTYPE*)hComponent)->GetExtensionIndex( \ + hComponent, \ + cParameterName, \ + pIndexType) /* Macro End */ + + +/** The OMX_GetState macro will invoke the component to get the current + state of the component and place the state value into the location + pointed to by pState. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [out] pState + pointer to the location to receive the state. The value returned + is one of the OMX_STATETYPE members + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp + */ +#define OMX_GetState( \ + hComponent, \ + pState) \ + ((OMX_COMPONENTTYPE*)hComponent)->GetState( \ + hComponent, \ + pState) /* Macro End */ + + +/** The OMX_UseBuffer macro will request that the component use + a buffer (and allocate its own buffer header) already allocated + by another component, or by the IL Client. This is a blocking + call. + + The component should return from this call within 20 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [out] ppBuffer + pointer to an OMX_BUFFERHEADERTYPE structure used to receive the + pointer to the buffer header + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp buf + */ + +#define OMX_UseBuffer( \ + hComponent, \ + ppBufferHdr, \ + nPortIndex, \ + pAppPrivate, \ + nSizeBytes, \ + pBuffer) \ + ((OMX_COMPONENTTYPE*)hComponent)->UseBuffer( \ + hComponent, \ + ppBufferHdr, \ + nPortIndex, \ + pAppPrivate, \ + nSizeBytes, \ + pBuffer) + + +/** The OMX_AllocateBuffer macro will request that the component allocate + a new buffer and buffer header. The component will allocate the + buffer and the buffer header and return a pointer to the buffer + header. This is a blocking call. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [out] ppBuffer + pointer to an OMX_BUFFERHEADERTYPE structure used to receive + the pointer to the buffer header + @param [in] nPortIndex + nPortIndex is used to select the port on the component the buffer will + be used with. The port can be found by using the nPortIndex + value as an index into the Port Definition array of the component. + @param [in] pAppPrivate + pAppPrivate is used to initialize the pAppPrivate member of the + buffer header structure. + @param [in] nSizeBytes + size of the buffer to allocate. Used when bAllocateNew is true. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp buf + */ +#define OMX_AllocateBuffer( \ + hComponent, \ + ppBuffer, \ + nPortIndex, \ + pAppPrivate, \ + nSizeBytes) \ + ((OMX_COMPONENTTYPE*)hComponent)->AllocateBuffer( \ + hComponent, \ + ppBuffer, \ + nPortIndex, \ + pAppPrivate, \ + nSizeBytes) /* Macro End */ + + +/** The OMX_FreeBuffer macro will release a buffer header from the component + which was allocated using either OMX_AllocateBuffer or OMX_UseBuffer. If + the component allocated the buffer (see the OMX_UseBuffer macro) then + the component shall free the buffer and buffer header. This is a + blocking call. + + The component should return from this call within 20 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] nPortIndex + nPortIndex is used to select the port on the component the buffer will + be used with. + @param [in] pBuffer + pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer + or AllocateBuffer. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp buf + */ +#define OMX_FreeBuffer( \ + hComponent, \ + nPortIndex, \ + pBuffer) \ + ((OMX_COMPONENTTYPE*)hComponent)->FreeBuffer( \ + hComponent, \ + nPortIndex, \ + pBuffer) /* Macro End */ + + +/** The OMX_EmptyThisBuffer macro will send a buffer full of data to an + input port of a component. The buffer will be emptied by the component + and returned to the application via the EmptyBufferDone call back. + This is a non-blocking call in that the component will record the buffer + and return immediately and then empty the buffer, later, at the proper + time. As expected, this macro may be invoked only while the component + is in the OMX_StateExecuting. If nPortIndex does not specify an input + port, the component shall return an error. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] pBuffer + pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer + or AllocateBuffer. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp buf + */ +#define OMX_EmptyThisBuffer( \ + hComponent, \ + pBuffer) \ + ((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer( \ + hComponent, \ + pBuffer) /* Macro End */ + + +/** The OMX_FillThisBuffer macro will send an empty buffer to an + output port of a component. The buffer will be filled by the component + and returned to the application via the FillBufferDone call back. + This is a non-blocking call in that the component will record the buffer + and return immediately and then fill the buffer, later, at the proper + time. As expected, this macro may be invoked only while the component + is in the OMX_ExecutingState. If nPortIndex does not specify an output + port, the component shall return an error. + + The component should return from this call within 5 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [in] pBuffer + pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer + or AllocateBuffer. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp buf + */ +#define OMX_FillThisBuffer( \ + hComponent, \ + pBuffer) \ + ((OMX_COMPONENTTYPE*)hComponent)->FillThisBuffer( \ + hComponent, \ + pBuffer) /* Macro End */ + + + +/** The OMX_UseEGLImage macro will request that the component use + a EGLImage provided by EGL (and allocate its own buffer header) + This is a blocking call. + + The component should return from this call within 20 msec. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the OMX_GetHandle function. + @param [out] ppBuffer + pointer to an OMX_BUFFERHEADERTYPE structure used to receive the + pointer to the buffer header. Note that the memory location used + for this buffer is NOT visible to the IL Client. + @param [in] nPortIndex + nPortIndex is used to select the port on the component the buffer will + be used with. The port can be found by using the nPortIndex + value as an index into the Port Definition array of the component. + @param [in] pAppPrivate + pAppPrivate is used to initialize the pAppPrivate member of the + buffer header structure. + @param [in] eglImage + eglImage contains the handle of the EGLImage to use as a buffer on the + specified port. The component is expected to validate properties of + the EGLImage against the configuration of the port to ensure the component + can use the EGLImage as a buffer. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup comp buf + */ +#define OMX_UseEGLImage( \ + hComponent, \ + ppBufferHdr, \ + nPortIndex, \ + pAppPrivate, \ + eglImage) \ + ((OMX_COMPONENTTYPE*)hComponent)->UseEGLImage( \ + hComponent, \ + ppBufferHdr, \ + nPortIndex, \ + pAppPrivate, \ + eglImage) + +/** The OMX_Init method is used to initialize the OMX core. It shall be the + first call made into OMX and it should only be executed one time without + an interviening OMX_Deinit call. + + The core should return from this call within 20 msec. + + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void); + + +/** The OMX_Deinit method is used to deinitialize the OMX core. It shall be + the last call made into OMX. In the event that the core determines that + thare are components loaded when this call is made, the core may return + with an error rather than try to unload the components. + + The core should return from this call within 20 msec. + + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void); + + +/** The OMX_ComponentNameEnum method will enumerate through all the names of + recognised valid components in the system. This function is provided + as a means to detect all the components in the system run-time. There is + no strict ordering to the enumeration order of component names, although + each name will only be enumerated once. If the OMX core supports run-time + installation of new components, it is only requried to detect newly + installed components when the first call to enumerate component names + is made (i.e. when nIndex is 0x0). + + The core should return from this call in 20 msec. + + @param [out] cComponentName + pointer to a null terminated string with the component name. The + names of the components are strings less than 127 bytes in length + plus the trailing null for a maximum size of 128 bytes. An example + of a valid component name is "OMX.TI.AUDIO.DSP.MIXER\0". Names are + assigned by the vendor, but shall start with "OMX." and then have + the Vendor designation next. + @param [in] nNameLength + number of characters in the cComponentName string. With all + component name strings restricted to less than 128 characters + (including the trailing null) it is recomended that the caller + provide a input string for the cComponentName of 128 characters. + @param [in] nIndex + number containing the enumeration index for the component. + Multiple calls to OMX_ComponentNameEnum with increasing values + of nIndex will enumerate through the component names in the + system until OMX_ErrorNoMore is returned. The value of nIndex + is 0 to (N-1), where N is the number of valid installed components + in the system. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. When the value of nIndex exceeds the number of + components in the system minus 1, OMX_ErrorNoMore will be + returned. Otherwise the appropriate OMX error will be returned. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex); + + +/** The OMX_GetHandle method will locate the component specified by the + component name given, load that component into memory and then invoke + the component's methods to create an instance of the component. + + The core should return from this call within 20 msec. + + @param [out] pHandle + pointer to an OMX_HANDLETYPE pointer to be filled in by this method. + @param [in] cComponentName + pointer to a null terminated string with the component name. The + names of the components are strings less than 127 bytes in length + plus the trailing null for a maximum size of 128 bytes. An example + of a valid component name is "OMX.TI.AUDIO.DSP.MIXER\0". Names are + assigned by the vendor, but shall start with "OMX." and then have + the Vendor designation next. + @param [in] pAppData + pointer to an application defined value that will be returned + during callbacks so that the application can identify the source + of the callback. + @param [in] pCallBacks + pointer to a OMX_CALLBACKTYPE structure that will be passed to the + component to initialize it with. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks); + + +/** The OMX_FreeHandle method will free a handle allocated by the OMX_GetHandle + method. If the component reference count goes to zero, the component will + be unloaded from memory. + + The core should return from this call within 20 msec when the component is + in the OMX_StateLoaded state. + + @param [in] hComponent + Handle of the component to be accessed. This is the component + handle returned by the call to the GetHandle function. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent); + + + +/** The OMX_SetupTunnel method will handle the necessary calls to the components + to setup the specified tunnel the two components. NOTE: This is + an actual method (not a #define macro). This method will make calls into + the component ComponentTunnelRequest method to do the actual tunnel + connection. + + The ComponentTunnelRequest method on both components will be called. + This method shall not be called unless the component is in the + OMX_StateLoaded state except when the ports used for the tunnel are + disabled. In this case, the component may be in the OMX_StateExecuting, + OMX_StatePause, or OMX_StateIdle states. + + The core should return from this call within 20 msec. + + @param [in] hOutput + Handle of the component to be accessed. Also this is the handle + of the component whose port, specified in the nPortOutput parameter + will be used the source for the tunnel. This is the component handle + returned by the call to the OMX_GetHandle function. There is a + requirement that hOutput be the source for the data when + tunelling (i.e. nPortOutput is an output port). If 0x0, the component + specified in hInput will have it's port specified in nPortInput + setup for communication with the application / IL client. + @param [in] nPortOutput + nPortOutput is used to select the source port on component to be + used in the tunnel. + @param [in] hInput + This is the component to setup the tunnel with. This is the handle + of the component whose port, specified in the nPortInput parameter + will be used the destination for the tunnel. This is the component handle + returned by the call to the OMX_GetHandle function. There is a + requirement that hInput be the destination for the data when + tunelling (i.e. nPortInut is an input port). If 0x0, the component + specified in hOutput will have it's port specified in nPortPOutput + setup for communication with the application / IL client. + @param [in] nPortInput + nPortInput is used to select the destination port on component to be + used in the tunnel. + @return OMX_ERRORTYPE + If the command successfully executes, the return code will be + OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. + When OMX_ErrorNotImplemented is returned, one or both components is + a non-interop component and does not support tunneling. + + On failure, the ports of both components are setup for communication + with the application / IL Client. + @ingroup core tun + */ +OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput); + +/** @ingroup cp */ +OMX_API OMX_ERRORTYPE OMX_GetContentPipe( + OMX_OUT OMX_HANDLETYPE *hPipe, + OMX_IN OMX_STRING szURI); + +/** The OMX_GetComponentsOfRole method will return the number of components that support the given + role and (if the compNames field is non-NULL) the names of those components. The call will fail if + an insufficiently sized array of names is supplied. To ensure the array is sufficiently sized the + client should: + * first call this function with the compNames field NULL to determine the number of component names + * second call this function with the compNames field pointing to an array of names allocated + according to the number returned by the first call. + + The core should return from this call within 5 msec. + + @param [in] role + This is generic standard component name consisting only of component class + name and the type within that class (e.g. 'audio_decoder.aac'). + @param [inout] pNumComps + This is used both as input and output. + + If compNames is NULL, the input is ignored and the output specifies how many components support + the given role. + + If compNames is not NULL, on input it bounds the size of the input structure and + on output, it specifies the number of components string names listed within the compNames parameter. + @param [inout] compNames + If NULL this field is ignored. If non-NULL this points to an array of 128-byte strings which accepts + a list of the names of all physical components that implement the specified standard component name. + Each name is NULL terminated. numComps indicates the number of names. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames); + +/** The OMX_GetRolesOfComponent method will return the number of roles supported by the given + component and (if the roles field is non-NULL) the names of those roles. The call will fail if + an insufficiently sized array of names is supplied. To ensure the array is sufficiently sized the + client should: + * first call this function with the roles field NULL to determine the number of role names + * second call this function with the roles field pointing to an array of names allocated + according to the number returned by the first call. + + The core should return from this call within 5 msec. + + @param [in] compName + This is the name of the component being queried about. + @param [inout] pNumRoles + This is used both as input and output. + + If roles is NULL, the input is ignored and the output specifies how many roles the component supports. + + If compNames is not NULL, on input it bounds the size of the input structure and + on output, it specifies the number of roles string names listed within the roles parameter. + @param [out] roles + If NULL this field is ignored. If non-NULL this points to an array of 128-byte strings + which accepts a list of the names of all standard components roles implemented on the + specified component name. numComps indicates the number of names. + @ingroup core + */ +OMX_API OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ + diff --git a/interface/vmcs_host/khronos/IL/OMX_ILCS.h b/interface/vmcs_host/khronos/IL/OMX_ILCS.h new file mode 100755 index 0000000..434b471 --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_ILCS.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// OpenMAX IL - ILCS specific types + +#ifndef OMX_ILCS_h +#define OMX_ILCS_h + +typedef struct OMX_PARAM_PORTSUMMARYTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nNumPorts; /**< Total number of ports */ + OMX_U32 reqSet; /**< Which set of ports is details below, portIndex[0] is port reqSet*32 */ + OMX_U32 portDir; /**< Bitfield, 1 if output port, 0 if input port, max 256 ports */ + OMX_U32 portIndex[32]; /**< Port Indexes */ +} OMX_PARAM_PORTSUMMARYTYPE; + +typedef struct OMX_PARAM_MARKCOMPARISONTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_PTR mark; /**< Pointer to be used for mark comparisons */ +} OMX_PARAM_MARKCOMPARISONTYPE; + +typedef struct OMX_PARAM_BRCMRECURSIONUNSAFETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_S32 (*pRecursionUnsafe)(OMX_PTR param); + OMX_PTR param; +} OMX_PARAM_BRCMRECURSIONUNSAFETYPE; + +typedef struct OMX_PARAM_TUNNELSTATUSTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port being queried */ + OMX_U32 nIndex; /**< Query the nIndex'th port and fill in nPortIndex */ + OMX_BOOL bUseIndex; /**< If OMX_TRUE read nIndex, otherwise read nPortIndex */ + OMX_PTR hTunneledComponent; /**< Component currently tunnelling with */ + OMX_U32 nTunneledPort; /**< Port on tunnelled component */ +} OMX_PARAM_TUNNELSTATUSTYPE; + +#endif diff --git a/interface/vmcs_host/khronos/IL/OMX_IVCommon.h b/interface/vmcs_host/khronos/IL/OMX_IVCommon.h new file mode 100755 index 0000000..71c9eaa --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_IVCommon.h @@ -0,0 +1,1107 @@ +/** + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** + * @file OMX_IVCommon.h - OpenMax IL version 1.1.2 + * The structures needed by Video and Image components to exchange + * parameters and configuration data with the components. + */ +#ifndef OMX_IVCommon_h +#define OMX_IVCommon_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Each OMX header must include all required header files to allow the header + * to compile without errors. The includes below are required for this header + * file to compile successfully + */ + +#include "OMX_Core.h" + +/** @defgroup iv OpenMAX IL Imaging and Video Domain + * Common structures for OpenMAX IL Imaging and Video domains + * @{ + */ + + +/** + * Enumeration defining possible uncompressed image/video formats. + * + * ENUMS: + * Unused : Placeholder value when format is N/A + * Monochrome : black and white + * 8bitRGB332 : Red 7:5, Green 4:2, Blue 1:0 + * 12bitRGB444 : Red 11:8, Green 7:4, Blue 3:0 + * 16bitARGB4444 : Alpha 15:12, Red 11:8, Green 7:4, Blue 3:0 + * 16bitARGB1555 : Alpha 15, Red 14:10, Green 9:5, Blue 4:0 + * 16bitRGB565 : Red 15:11, Green 10:5, Blue 4:0 + * 16bitBGR565 : Blue 15:11, Green 10:5, Red 4:0 + * 18bitRGB666 : Red 17:12, Green 11:6, Blue 5:0 + * 18bitARGB1665 : Alpha 17, Red 16:11, Green 10:5, Blue 4:0 + * 19bitARGB1666 : Alpha 18, Red 17:12, Green 11:6, Blue 5:0 + * 24bitRGB888 : Red 24:16, Green 15:8, Blue 7:0 + * 24bitBGR888 : Blue 24:16, Green 15:8, Red 7:0 + * 24bitARGB1887 : Alpha 23, Red 22:15, Green 14:7, Blue 6:0 + * 25bitARGB1888 : Alpha 24, Red 23:16, Green 15:8, Blue 7:0 + * 32bitBGRA8888 : Blue 31:24, Green 23:16, Red 15:8, Alpha 7:0 + * 32bitARGB8888 : Alpha 31:24, Red 23:16, Green 15:8, Blue 7:0 + * YUV411Planar : U,Y are subsampled by a factor of 4 horizontally + * YUV411PackedPlanar : packed per payload in planar slices + * YUV420Planar : Three arrays Y,U,V. + * YUV420PackedPlanar : packed per payload in planar slices + * YUV420SemiPlanar : Two arrays, one is all Y, the other is U and V + * YUV422Planar : Three arrays Y,U,V. + * YUV422PackedPlanar : packed per payload in planar slices + * YUV422SemiPlanar : Two arrays, one is all Y, the other is U and V + * YCbYCr : Organized as 16bit YUYV (i.e. YCbYCr) + * YCrYCb : Organized as 16bit YVYU (i.e. YCrYCb) + * CbYCrY : Organized as 16bit UYVY (i.e. CbYCrY) + * CrYCbY : Organized as 16bit VYUY (i.e. CrYCbY) + * YUV444Interleaved : Each pixel contains equal parts YUV + * RawBayer8bit : SMIA camera output format + * RawBayer10bit : SMIA camera output format + * RawBayer8bitcompressed : SMIA camera output format + Vendor extensions + * 32bitABGR888 : Alpha 31:24, Blue 23:16, Green 15:8, Red 7:0 + */ +typedef enum OMX_COLOR_FORMATTYPE { + OMX_COLOR_FormatUnused, + OMX_COLOR_FormatMonochrome, + OMX_COLOR_Format8bitRGB332, + OMX_COLOR_Format12bitRGB444, + OMX_COLOR_Format16bitARGB4444, + OMX_COLOR_Format16bitARGB1555, + OMX_COLOR_Format16bitRGB565, + OMX_COLOR_Format16bitBGR565, + OMX_COLOR_Format18bitRGB666, + OMX_COLOR_Format18bitARGB1665, + OMX_COLOR_Format19bitARGB1666, + OMX_COLOR_Format24bitRGB888, + OMX_COLOR_Format24bitBGR888, + OMX_COLOR_Format24bitARGB1887, + OMX_COLOR_Format25bitARGB1888, + OMX_COLOR_Format32bitBGRA8888, + OMX_COLOR_Format32bitARGB8888, + OMX_COLOR_FormatYUV411Planar, + OMX_COLOR_FormatYUV411PackedPlanar, + OMX_COLOR_FormatYUV420Planar, + OMX_COLOR_FormatYUV420PackedPlanar, + OMX_COLOR_FormatYUV420SemiPlanar, + OMX_COLOR_FormatYUV422Planar, + OMX_COLOR_FormatYUV422PackedPlanar, + OMX_COLOR_FormatYUV422SemiPlanar, + OMX_COLOR_FormatYCbYCr, + OMX_COLOR_FormatYCrYCb, + OMX_COLOR_FormatCbYCrY, + OMX_COLOR_FormatCrYCbY, + OMX_COLOR_FormatYUV444Interleaved, + OMX_COLOR_FormatRawBayer8bit, + OMX_COLOR_FormatRawBayer10bit, + OMX_COLOR_FormatRawBayer8bitcompressed, + OMX_COLOR_FormatL2, + OMX_COLOR_FormatL4, + OMX_COLOR_FormatL8, + OMX_COLOR_FormatL16, + OMX_COLOR_FormatL24, + OMX_COLOR_FormatL32, + OMX_COLOR_FormatYUV420PackedSemiPlanar, + OMX_COLOR_FormatYUV422PackedSemiPlanar, + OMX_COLOR_Format18BitBGR666, + OMX_COLOR_Format24BitARGB6666, + OMX_COLOR_Format24BitABGR6666, + OMX_COLOR_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_COLOR_FormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_COLOR_Format32bitABGR8888, + OMX_COLOR_Format8bitPalette, + OMX_COLOR_FormatYUVUV128, + OMX_COLOR_FormatRawBayer12bit, + OMX_COLOR_FormatBRCMEGL, + OMX_COLOR_FormatBRCMOpaque, + OMX_COLOR_FormatYVU420PackedPlanar, + OMX_COLOR_FormatYVU420PackedSemiPlanar, + OMX_COLOR_FormatRawBayer16bit, + OMX_COLOR_FormatYUV420_16PackedPlanar, /**< YUV420, 16bit/component */ + OMX_COLOR_FormatYUVUV64_16, /**< YUVUV, 16bit/component */ + OMX_COLOR_FormatYUV420_10PackedPlanar, /**< YUV420, 10bit/component as least sig 10bits of 16 bit words */ + OMX_COLOR_FormatYUVUV64_10, /**< YUVUV, 10bit/component as least sig 10bits of 16 bit words */ +#ifdef TIZEN_FEATURE_OMX + OMX_EXT_COLOR_FormatNV12TPhysicalAddress = 0x7F000001, /**< Reserved region for introducing Vendor Extensions */ + OMX_EXT_COLOR_FormatNV12LPhysicalAddress = 0x7F000002, + OMX_EXT_COLOR_FormatNV12Tiled = 0x7FC00002, + OMX_EXT_COLOR_FormatNV12TFdValue = 0x7F000012, + OMX_EXT_COLOR_FormatNV12LFdValue = 0x7F000013, +#endif + OMX_COLOR_FormatMax = 0x7FFFFFFF +} OMX_COLOR_FORMATTYPE; + + +/** + * Defines the matrix for conversion from RGB to YUV or vice versa. + * iColorMatrix should be initialized with the fixed point values + * used in converting between formats. + */ +typedef struct OMX_CONFIG_COLORCONVERSIONTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version info */ + OMX_U32 nPortIndex; /**< Port that this struct applies to */ + OMX_S32 xColorMatrix[3][3]; /**< Stored in signed Q16 format */ + OMX_S32 xColorOffset[4]; /**< Stored in signed Q16 format */ +}OMX_CONFIG_COLORCONVERSIONTYPE; + + +/** + * Structure defining percent to scale each frame dimension. For example: + * To make the width 50% larger, use fWidth = 1.5 and to make the width + * 1/2 the original size, use fWidth = 0.5 + */ +typedef struct OMX_CONFIG_SCALEFACTORTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version info */ + OMX_U32 nPortIndex; /**< Port that this struct applies to */ + OMX_S32 xWidth; /**< Fixed point value stored as Q16 */ + OMX_S32 xHeight; /**< Fixed point value stored as Q16 */ +}OMX_CONFIG_SCALEFACTORTYPE; + + +/** + * Enumeration of possible image filter types + */ +typedef enum OMX_IMAGEFILTERTYPE { + OMX_ImageFilterNone, + OMX_ImageFilterNoise, + OMX_ImageFilterEmboss, + OMX_ImageFilterNegative, + OMX_ImageFilterSketch, + OMX_ImageFilterOilPaint, + OMX_ImageFilterHatch, + OMX_ImageFilterGpen, + OMX_ImageFilterAntialias, + OMX_ImageFilterDeRing, + OMX_ImageFilterSolarize, + OMX_ImageFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ImageFilterVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + + /* Broadcom specific image filters */ + OMX_ImageFilterWatercolor, + OMX_ImageFilterPastel, + OMX_ImageFilterSharpen, + OMX_ImageFilterFilm, + OMX_ImageFilterBlur, + OMX_ImageFilterSaturation, + + OMX_ImageFilterDeInterlaceLineDouble, + OMX_ImageFilterDeInterlaceAdvanced, + + OMX_ImageFilterColourSwap, + OMX_ImageFilterWashedOut, + OMX_ImageFilterColourPoint, + OMX_ImageFilterPosterise, + OMX_ImageFilterColourBalance, + OMX_ImageFilterCartoon, + + OMX_ImageFilterAnaglyph, + OMX_ImageFilterDeInterlaceFast, + OMX_ImageFilterMax = 0x7FFFFFFF +} OMX_IMAGEFILTERTYPE; + +typedef enum OMX_IMAGEFILTERANAGLYPHTYPE { + OMX_ImageFilterAnaglyphNone, + OMX_ImageFilterAnaglyphSBStoRedCyan, + OMX_ImageFilterAnaglyphSBStoCyanRed, + OMX_ImageFilterAnaglyphSBStoGreenMagenta, + OMX_ImageFilterAnaglyphSBStoMagentaGreen, + OMX_ImageFilterAnaglyphTABtoRedCyan, + OMX_ImageFilterAnaglyphTABtoCyanRed, + OMX_ImageFilterAnaglyphTABtoGreenMagenta, + OMX_ImageFilterAnaglyphTABtoMagentaGreen, +} OMX_IMAGEFILTERANAGLYPHTYPE; + +/** + * Image filter configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eImageFilter : Image filter type enumeration + */ +typedef struct OMX_CONFIG_IMAGEFILTERTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_IMAGEFILTERTYPE eImageFilter; +} OMX_CONFIG_IMAGEFILTERTYPE; + + +/** + * Customized U and V for color enhancement + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * bColorEnhancement : Enable/disable color enhancement + * nCustomizedU : Practical values: 16-240, range: 0-255, value set for + * U component + * nCustomizedV : Practical values: 16-240, range: 0-255, value set for + * V component + */ +typedef struct OMX_CONFIG_COLORENHANCEMENTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bColorEnhancement; + OMX_U8 nCustomizedU; + OMX_U8 nCustomizedV; +} OMX_CONFIG_COLORENHANCEMENTTYPE; + + +/** + * Define color key and color key mask + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nARGBColor : 32bit Alpha, Red, Green, Blue Color + * nARGBMask : 32bit Mask for Alpha, Red, Green, Blue channels + */ +typedef struct OMX_CONFIG_COLORKEYTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nARGBColor; + OMX_U32 nARGBMask; +} OMX_CONFIG_COLORKEYTYPE; + + +/** + * List of color blend types for pre/post processing + * + * ENUMS: + * None : No color blending present + * AlphaConstant : Function is (alpha_constant * src) + + * (1 - alpha_constant) * dst) + * AlphaPerPixel : Function is (alpha * src) + (1 - alpha) * dst) + * Alternate : Function is alternating pixels from src and dst + * And : Function is (src & dst) + * Or : Function is (src | dst) + * Invert : Function is ~src + */ +typedef enum OMX_COLORBLENDTYPE { + OMX_ColorBlendNone, + OMX_ColorBlendAlphaConstant, + OMX_ColorBlendAlphaPerPixel, + OMX_ColorBlendAlternate, + OMX_ColorBlendAnd, + OMX_ColorBlendOr, + OMX_ColorBlendInvert, + OMX_ColorBlendKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ColorBlendVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_ColorBlendMax = 0x7FFFFFFF +} OMX_COLORBLENDTYPE; + + +/** + * Color blend configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nRGBAlphaConstant : Constant global alpha values when global alpha is used + * eColorBlend : Color blend type enumeration + */ +typedef struct OMX_CONFIG_COLORBLENDTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nRGBAlphaConstant; + OMX_COLORBLENDTYPE eColorBlend; +} OMX_CONFIG_COLORBLENDTYPE; + + +/** + * Hold frame dimension + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nWidth : Frame width in pixels + * nHeight : Frame height in pixels + */ +typedef struct OMX_FRAMESIZETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nWidth; + OMX_U32 nHeight; +} OMX_FRAMESIZETYPE; + + +/** + * Rotation configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nRotation : +/- integer rotation value + */ +typedef struct OMX_CONFIG_ROTATIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nRotation; +} OMX_CONFIG_ROTATIONTYPE; + + +/** + * Possible mirroring directions for pre/post processing + * + * ENUMS: + * None : No mirroring + * Vertical : Vertical mirroring, flip on X axis + * Horizontal : Horizontal mirroring, flip on Y axis + * Both : Both vertical and horizontal mirroring + */ +typedef enum OMX_MIRRORTYPE { + OMX_MirrorNone = 0, + OMX_MirrorVertical, + OMX_MirrorHorizontal, + OMX_MirrorBoth, + OMX_MirrorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_MirrorVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_MirrorMax = 0x7FFFFFFF +} OMX_MIRRORTYPE; + + +/** + * Mirroring configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eMirror : Mirror type enumeration + */ +typedef struct OMX_CONFIG_MIRRORTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_MIRRORTYPE eMirror; +} OMX_CONFIG_MIRRORTYPE; + + +/** + * Position information only + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nX : X coordinate for the point + * nY : Y coordinate for the point + */ +typedef struct OMX_CONFIG_POINTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nX; + OMX_S32 nY; +} OMX_CONFIG_POINTTYPE; + + +/** + * Frame size plus position + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nLeft : X Coordinate of the top left corner of the rectangle + * nTop : Y Coordinate of the top left corner of the rectangle + * nWidth : Width of the rectangle + * nHeight : Height of the rectangle + */ +typedef struct OMX_CONFIG_RECTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nLeft; + OMX_S32 nTop; + OMX_U32 nWidth; + OMX_U32 nHeight; +} OMX_CONFIG_RECTTYPE; + + +/** + * Deblocking state; it is required to be set up before starting the codec + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * bDeblocking : Enable/disable deblocking mode + */ +typedef struct OMX_PARAM_DEBLOCKINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bDeblocking; +} OMX_PARAM_DEBLOCKINGTYPE; + + +/** + * Stabilization state + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * bStab : Enable/disable frame stabilization state + */ +typedef struct OMX_CONFIG_FRAMESTABTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bStab; +} OMX_CONFIG_FRAMESTABTYPE; + + +/** + * White Balance control type + * + * STRUCT MEMBERS: + * SunLight : Referenced in JSR-234 + * Flash : Optimal for device's integrated flash + */ +typedef enum OMX_WHITEBALCONTROLTYPE { + OMX_WhiteBalControlOff = 0, + OMX_WhiteBalControlAuto, + OMX_WhiteBalControlSunLight, + OMX_WhiteBalControlCloudy, + OMX_WhiteBalControlShade, + OMX_WhiteBalControlTungsten, + OMX_WhiteBalControlFluorescent, + OMX_WhiteBalControlIncandescent, + OMX_WhiteBalControlFlash, + OMX_WhiteBalControlHorizon, + OMX_WhiteBalControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_WhiteBalControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_WhiteBalControlMax = 0x7FFFFFFF +} OMX_WHITEBALCONTROLTYPE; + + +/** + * White Balance control configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eWhiteBalControl : White balance enumeration + */ +typedef struct OMX_CONFIG_WHITEBALCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_WHITEBALCONTROLTYPE eWhiteBalControl; +} OMX_CONFIG_WHITEBALCONTROLTYPE; + + +/** + * Exposure control type + */ +typedef enum OMX_EXPOSURECONTROLTYPE { + OMX_ExposureControlOff = 0, + OMX_ExposureControlAuto, + OMX_ExposureControlNight, + OMX_ExposureControlBackLight, + OMX_ExposureControlSpotLight, + OMX_ExposureControlSports, + OMX_ExposureControlSnow, + OMX_ExposureControlBeach, + OMX_ExposureControlLargeAperture, + OMX_ExposureControlSmallAperture, + OMX_ExposureControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_ExposureControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_ExposureControlVeryLong, + OMX_ExposureControlFixedFps, + OMX_ExposureControlNightWithPreview, + OMX_ExposureControlAntishake, + OMX_ExposureControlFireworks, + OMX_ExposureControlMax = 0x7FFFFFFF +} OMX_EXPOSURECONTROLTYPE; + + +/** + * White Balance control configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eExposureControl : Exposure control enumeration + */ +typedef struct OMX_CONFIG_EXPOSURECONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_EXPOSURECONTROLTYPE eExposureControl; +} OMX_CONFIG_EXPOSURECONTROLTYPE; + + +/** + * Defines sensor supported mode. + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nFrameRate : Single shot mode is indicated by a 0 + * bOneShot : Enable for single shot, disable for streaming + * sFrameSize : Framesize + */ +typedef struct OMX_PARAM_SENSORMODETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nFrameRate; + OMX_BOOL bOneShot; + OMX_FRAMESIZETYPE sFrameSize; +} OMX_PARAM_SENSORMODETYPE; + + +/** + * Defines contrast level + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nContrast : Values allowed for contrast -100 to 100, zero means no change + */ +typedef struct OMX_CONFIG_CONTRASTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nContrast; +} OMX_CONFIG_CONTRASTTYPE; + + +/** + * Defines brightness level + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nBrightness : 0-100% + */ +typedef struct OMX_CONFIG_BRIGHTNESSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nBrightness; +} OMX_CONFIG_BRIGHTNESSTYPE; + + +/** + * Defines backlight level configuration for a video sink, e.g. LCD panel + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nBacklight : Values allowed for backlight 0-100% + * nTimeout : Number of milliseconds before backlight automatically turns + * off. A value of 0x0 disables backight timeout + */ +typedef struct OMX_CONFIG_BACKLIGHTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nBacklight; + OMX_U32 nTimeout; +} OMX_CONFIG_BACKLIGHTTYPE; + + +/** + * Defines setting for Gamma + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nGamma : Values allowed for gamma -100 to 100, zero means no change + */ +typedef struct OMX_CONFIG_GAMMATYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nGamma; +} OMX_CONFIG_GAMMATYPE; + + +/** + * Define for setting saturation + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nSaturation : Values allowed for saturation -100 to 100, zero means + * no change + */ +typedef struct OMX_CONFIG_SATURATIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nSaturation; +} OMX_CONFIG_SATURATIONTYPE; + + +/** + * Define for setting Lightness + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nLightness : Values allowed for lightness -100 to 100, zero means no + * change + */ +typedef struct OMX_CONFIG_LIGHTNESSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_S32 nLightness; +} OMX_CONFIG_LIGHTNESSTYPE; + + +/** + * Plane blend configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Index of input port associated with the plane. + * nDepth : Depth of the plane in relation to the screen. Higher + * numbered depths are "behind" lower number depths. + * This number defaults to the Port Index number. + * nAlpha : Transparency blending component for the entire plane. + * See blending modes for more detail. + */ +typedef struct OMX_CONFIG_PLANEBLENDTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nDepth; + OMX_U32 nAlpha; +} OMX_CONFIG_PLANEBLENDTYPE; + + +/** + * Define interlace type + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * bEnable : Enable control variable for this functionality + * (see below) + * nInterleavePortIndex : Index of input or output port associated with + * the interleaved plane. + * pPlanarPortIndexes[4] : Index of input or output planar ports. + */ +typedef struct OMX_PARAM_INTERLEAVETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnable; + OMX_U32 nInterleavePortIndex; +} OMX_PARAM_INTERLEAVETYPE; + + +/** + * Defines the picture effect used for an input picture + */ +typedef enum OMX_TRANSITIONEFFECTTYPE { + OMX_EffectNone, + OMX_EffectFadeFromBlack, + OMX_EffectFadeToBlack, + OMX_EffectUnspecifiedThroughConstantColor, + OMX_EffectDissolve, + OMX_EffectWipe, + OMX_EffectUnspecifiedMixOfTwoScenes, + OMX_EffectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_EffectVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + + OMX_EffectReverseUnspecifiedMixOfTwoScenes, + +#ifndef __VIDEOCORE4__ + OMX_EffectDiagonalWipe, + OMX_EffectDiagonalWipeRotate, + OMX_EffectEllipticalWipe, + OMX_EffectEllipticalWipeRotate, + OMX_EffectInverseEllipticalWipe, + OMX_EffectInverseEllipticalWipeRotate, + OMX_EffectGlassWipe, + OMX_EffectGlassWipeRotate, + OMX_EffectWavyWipe, + OMX_EffectWavyWipeRotate, + OMX_EffectMunchingSquares, + OMX_EffectStripeWipe, + OMX_EffectStripeWipeRotate, + + OMX_EffectRotozoomUnmatched, + OMX_EffectRotozoomMatched, + OMX_EffectRotozoomGentle, +#endif + + OMX_EffectMunchRandom, + OMX_EffectMunchVRandom, + OMX_EffectMunchHRandom, + OMX_EffectMunchWipe, + OMX_EffectMunchMunch, + OMX_EffectMunchStripe, + OMX_EffectFadeRandom, + OMX_EffectFadeVRandom, + OMX_EffectFadeHRandom, + OMX_EffectFadeWipe, + OMX_EffectFadeMunch, + OMX_EffectFadeStripe, + OMX_EffectColourBlockRandom, + OMX_EffectColourBlockVRandom, + OMX_EffectColourBlockHRandom, + OMX_EffectColourBlockWipe, + OMX_EffectColourBlockMunch, + OMX_EffectColourBlockStripe, + OMX_EffectColourBlock2Random, + OMX_EffectColourBlock2VRandom, + OMX_EffectColourBlock2HRandom, + OMX_EffectColourBlock2Wipe, + OMX_EffectColourBlock2Munch, + OMX_EffectColourBlock2Stripe, + OMX_EffectShadeRandom, + OMX_EffectShadeVRandom, + OMX_EffectShadeHRandom, + OMX_EffectShadeWipe, + OMX_EffectShadeMunch, + OMX_EffectShadeStripe, + OMX_EffectBitmaskRandom, + OMX_EffectBitmaskVRandom, + OMX_EffectBitmaskHRandom, + OMX_EffectBitmaskWipe, + OMX_EffectBitmaskMunch, + OMX_EffectBitmaskStripe, + OMX_EffectBitmask2Random, + OMX_EffectBitmask2VRandom, + OMX_EffectBitmask2HRandom, + OMX_EffectBitmask2Wipe, + OMX_EffectBitmask2Munch, + OMX_EffectBitmask2Stripe, + OMX_EffectBitmask2ColourRandom, + OMX_EffectBitmask2ColourVRandom, + OMX_EffectBitmask2ColourHRandom, + OMX_EffectBitmask2ColourWipe, + OMX_EffectBitmask2ColourMunch, + OMX_EffectBitmask2ColourStripe, + + OMX_EffectPushRight, + OMX_EffectPushLeft, + OMX_EffectPushDown, + OMX_EffectPushUp, + OMX_EffectCoverRight, + OMX_EffectCoverLeft, + OMX_EffectCoverDown, + OMX_EffectCoverUp, + OMX_EffectRevealRight, + OMX_EffectRevealLeft, + OMX_EffectRevealDown, + OMX_EffectRevealUp, + OMX_EffectWipeRight, + OMX_EffectWipeLeft, + OMX_EffectWipeDown, + OMX_EffectWipeUp, + OMX_EffectSpeckle, + OMX_EffectCircle, + OMX_EffectSpiral, + OMX_EffectDiamond, + OMX_EffectVert, + OMX_EffectPlus, + OMX_EffectClock, + OMX_EffectPlasma, + OMX_EffectDisplace, + OMX_EffectGenie, + OMX_EffectSide, + OMX_EffectMaze, + OMX_EffectRipple, + OMX_EffectStar, + OMX_EffectAlpha, + OMX_EffectIntense, + OMX_EffectIntenseU, + OMX_EffectIntenseV, + OMX_EffectInverseIntense, + OMX_EffectInverseIntenseU, + OMX_EffectInverseIntenseV, + + OMX_EffectPageTurn, + + OMX_EffectFlipPlaneDown, + OMX_EffectFlipPlaneDownMid, + OMX_EffectFlipPlaneDownHigh, + OMX_EffectFlipPlaneLeft, + OMX_EffectFlipPlaneLeftMid, + OMX_EffectFlipPlaneLeftHigh, + OMX_EffectFlipCubeDown, + OMX_EffectFlipCubeDownMid, + OMX_EffectFlipCubeDownHigh, + OMX_EffectFlipCubeLeft, + OMX_EffectFlipCubeLeftMid, + OMX_EffectFlipCubeLeftHigh, + + OMX_EffectMax = 0x7FFFFFFF +} OMX_TRANSITIONEFFECTTYPE; + + +/** + * Structure used to configure current transition effect + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eEffect : Effect to enable + */ +typedef struct OMX_CONFIG_TRANSITIONEFFECTTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_TRANSITIONEFFECTTYPE eEffect; +} OMX_CONFIG_TRANSITIONEFFECTTYPE; + + +/** + * Defines possible data unit types for encoded video data. The data unit + * types are used both for encoded video input for playback as well as + * encoded video output from recording. + */ +typedef enum OMX_DATAUNITTYPE { + OMX_DataUnitCodedPicture, + OMX_DataUnitVideoSegment, + OMX_DataUnitSeveralSegments, + OMX_DataUnitArbitraryStreamSection, + OMX_DataUnitKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DataUnitVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_DataUnitMax = 0x7FFFFFFF +} OMX_DATAUNITTYPE; + + +/** + * Defines possible encapsulation types for coded video data unit. The + * encapsulation information is used both for encoded video input for + * playback as well as encoded video output from recording. + */ +typedef enum OMX_DATAUNITENCAPSULATIONTYPE { + OMX_DataEncapsulationElementaryStream, + OMX_DataEncapsulationGenericPayload, + OMX_DataEncapsulationRtpPayload, + OMX_DataEncapsulationKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DataEncapsulationVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_DataEncapsulationMax = 0x7FFFFFFF +} OMX_DATAUNITENCAPSULATIONTYPE; + + +/** + * Structure used to configure the type of being decoded/encoded + */ +typedef struct OMX_PARAM_DATAUNITTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_DATAUNITTYPE eUnitType; + OMX_DATAUNITENCAPSULATIONTYPE eEncapsulationType; +} OMX_PARAM_DATAUNITTYPE; + + +/** + * Defines dither types + */ +typedef enum OMX_DITHERTYPE { + OMX_DitherNone, + OMX_DitherOrdered, + OMX_DitherErrorDiffusion, + OMX_DitherOther, + OMX_DitherKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_DitherVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_DitherMax = 0x7FFFFFFF +} OMX_DITHERTYPE; + + +/** + * Structure used to configure current type of dithering + */ +typedef struct OMX_CONFIG_DITHERTYPE { + OMX_U32 nSize; /**< Size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_DITHERTYPE eDither; /**< Type of dithering to use */ +} OMX_CONFIG_DITHERTYPE; + +typedef struct OMX_CONFIG_CAPTUREMODETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; /**< Port that this structure applies to */ + OMX_BOOL bContinuous; /**< If true then ignore frame rate and emit capture + * data as fast as possible (otherwise obey port's frame rate). */ + OMX_BOOL bFrameLimited; /**< If true then terminate capture after the port emits the + * specified number of frames (otherwise the port does not + * terminate the capture until instructed to do so by the client). + * Even if set, the client may manually terminate the capture prior + * to reaching the limit. */ + OMX_U32 nFrameLimit; /**< Limit on number of frames emitted during a capture (only + * valid if bFrameLimited is set). */ +} OMX_CONFIG_CAPTUREMODETYPE; + +typedef enum OMX_METERINGTYPE { + + OMX_MeteringModeAverage, /**< Center-weighted average metering. */ + OMX_MeteringModeSpot, /**< Spot (partial) metering. */ + OMX_MeteringModeMatrix, /**< Matrix or evaluative metering. */ + + OMX_MeteringKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_MeteringVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_MeteringModeBacklit, + OMX_EVModeMax = 0x7fffffff +} OMX_METERINGTYPE; + +typedef struct OMX_CONFIG_EXPOSUREVALUETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_METERINGTYPE eMetering; + OMX_S32 xEVCompensation; /**< Fixed point value stored as Q16 */ + OMX_U32 nApertureFNumber; /**< e.g. nApertureFNumber = 2 implies "f/2" - Q16 format */ + OMX_BOOL bAutoAperture; /**< Whether aperture number is defined automatically */ + OMX_U32 nShutterSpeedMsec; /**< Shutterspeed in milliseconds */ + OMX_BOOL bAutoShutterSpeed; /**< Whether shutter speed is defined automatically */ + OMX_U32 nSensitivity; /**< e.g. nSensitivity = 100 implies "ISO 100" */ + OMX_BOOL bAutoSensitivity; /**< Whether sensitivity is defined automatically */ +} OMX_CONFIG_EXPOSUREVALUETYPE; + +/** + * Focus region configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * bCenter : Use center region as focus region of interest + * bLeft : Use left region as focus region of interest + * bRight : Use right region as focus region of interest + * bTop : Use top region as focus region of interest + * bBottom : Use bottom region as focus region of interest + * bTopLeft : Use top left region as focus region of interest + * bTopRight : Use top right region as focus region of interest + * bBottomLeft : Use bottom left region as focus region of interest + * bBottomRight : Use bottom right region as focus region of interest + */ +typedef struct OMX_CONFIG_FOCUSREGIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bCenter; + OMX_BOOL bLeft; + OMX_BOOL bRight; + OMX_BOOL bTop; + OMX_BOOL bBottom; + OMX_BOOL bTopLeft; + OMX_BOOL bTopRight; + OMX_BOOL bBottomLeft; + OMX_BOOL bBottomRight; +} OMX_CONFIG_FOCUSREGIONTYPE; + +/** + * Focus Status type + */ +typedef enum OMX_FOCUSSTATUSTYPE { + OMX_FocusStatusOff = 0, + OMX_FocusStatusRequest, + OMX_FocusStatusReached, + OMX_FocusStatusUnableToReach, + OMX_FocusStatusLost, + OMX_FocusStatusKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_FocusStatusVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_FocusStatusCafWatching, + OMX_FocusStatusCafSceneChanged, + OMX_FocusStatusMax = 0x7FFFFFFF +} OMX_FOCUSSTATUSTYPE; + +/** + * Focus status configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eFocusStatus : Specifies the focus status + * bCenterStatus : Use center region as focus region of interest + * bLeftStatus : Use left region as focus region of interest + * bRightStatus : Use right region as focus region of interest + * bTopStatus : Use top region as focus region of interest + * bBottomStatus : Use bottom region as focus region of interest + * bTopLeftStatus : Use top left region as focus region of interest + * bTopRightStatus : Use top right region as focus region of interest + * bBottomLeftStatus : Use bottom left region as focus region of interest + * bBottomRightStatus : Use bottom right region as focus region of interest + */ +typedef struct OMX_PARAM_FOCUSSTATUSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_FOCUSSTATUSTYPE eFocusStatus; + OMX_BOOL bCenterStatus; + OMX_BOOL bLeftStatus; + OMX_BOOL bRightStatus; + OMX_BOOL bTopStatus; + OMX_BOOL bBottomStatus; + OMX_BOOL bTopLeftStatus; + OMX_BOOL bTopRightStatus; + OMX_BOOL bBottomLeftStatus; + OMX_BOOL bBottomRightStatus; +} OMX_PARAM_FOCUSSTATUSTYPE; + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ diff --git a/interface/vmcs_host/khronos/IL/OMX_Image.h b/interface/vmcs_host/khronos/IL/OMX_Image.h new file mode 100755 index 0000000..4bf5b26 --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Image.h @@ -0,0 +1,347 @@ +/** + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file OMX_Image.h - OpenMax IL version 1.1.2 + * The structures needed by Image components to exchange parameters and + * configuration data with the components. + */ +#ifndef OMX_Image_h +#define OMX_Image_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/** + * Each OMX header must include all required header files to allow the + * header to compile without errors. The includes below are required + * for this header file to compile successfully + */ + +#include "OMX_IVCommon.h" + +/** @defgroup imaging OpenMAX IL Imaging Domain + * @ingroup iv + * Structures for OpenMAX IL Imaging domain + * @{ + */ + +/** + * Enumeration used to define the possible image compression coding. + */ +typedef enum OMX_IMAGE_CODINGTYPE { + OMX_IMAGE_CodingUnused, /**< Value when format is N/A */ + OMX_IMAGE_CodingAutoDetect, /**< Auto detection of image format */ + OMX_IMAGE_CodingJPEG, /**< JPEG/JFIF image format */ + OMX_IMAGE_CodingJPEG2K, /**< JPEG 2000 image format */ + OMX_IMAGE_CodingEXIF, /**< EXIF image format */ + OMX_IMAGE_CodingTIFF, /**< TIFF image format */ + OMX_IMAGE_CodingGIF, /**< Graphics image format */ + OMX_IMAGE_CodingPNG, /**< PNG image format */ + OMX_IMAGE_CodingLZW, /**< LZW image format */ + OMX_IMAGE_CodingBMP, /**< Windows Bitmap format */ + OMX_IMAGE_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_IMAGE_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + + OMX_IMAGE_CodingTGA, + OMX_IMAGE_CodingPPM, + + OMX_IMAGE_CodingMax = 0x7FFFFFFF +} OMX_IMAGE_CODINGTYPE; + + +/** + * Data structure used to define an image path. The number of image paths + * for input and output will vary by type of the image component. + * + * Input (aka Source) : Zero Inputs, one Output, + * Splitter : One Input, 2 or more Outputs, + * Processing Element : One Input, one output, + * Mixer : 2 or more inputs, one output, + * Output (aka Sink) : One Input, zero outputs. + * + * The PortDefinition structure is used to define all of the parameters + * necessary for the compliant component to setup an input or an output + * image path. If additional vendor specific data is required, it should + * be transmitted to the component using the CustomCommand function. + * Compliant components will prepopulate this structure with optimal + * values during the OMX_GetParameter() command. + * + * STRUCT MEMBERS: + * cMIMEType : MIME type of data for the port + * pNativeRender : Platform specific reference for a display if a + * sync, otherwise this field is 0 + * nFrameWidth : Width of frame to be used on port if + * uncompressed format is used. Use 0 for + * unknown, don't care or variable + * nFrameHeight : Height of frame to be used on port if + * uncompressed format is used. Use 0 for + * unknown, don't care or variable + * nStride : Number of bytes per span of an image (i.e. + * indicates the number of bytes to get from + * span N to span N+1, where negative stride + * indicates the image is bottom up + * nSliceHeight : Height used when encoding in slices + * bFlagErrorConcealment : Turns on error concealment if it is supported by + * the OMX component + * eCompressionFormat : Compression format used in this instance of + * the component. When OMX_IMAGE_CodingUnused is + * specified, eColorFormat is valid + * eColorFormat : Decompressed format used by this component + * pNativeWindow : Platform specific reference for a window object if a + * display sink , otherwise this field is 0x0. + */ +typedef struct OMX_IMAGE_PORTDEFINITIONTYPE { + OMX_STRING cMIMEType; + OMX_NATIVE_DEVICETYPE pNativeRender; + OMX_U32 nFrameWidth; + OMX_U32 nFrameHeight; + OMX_S32 nStride; + OMX_U32 nSliceHeight; + OMX_BOOL bFlagErrorConcealment; + OMX_IMAGE_CODINGTYPE eCompressionFormat; + OMX_COLOR_FORMATTYPE eColorFormat; + OMX_NATIVE_WINDOWTYPE pNativeWindow; +} OMX_IMAGE_PORTDEFINITIONTYPE; + + +/** + * Port format parameter. This structure is used to enumerate the various + * data input/output format supported by the port. + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Indicates which port to set + * nIndex : Indicates the enumeration index for the format from + * 0x0 to N-1 + * eCompressionFormat : Compression format used in this instance of the + * component. When OMX_IMAGE_CodingUnused is specified, + * eColorFormat is valid + * eColorFormat : Decompressed format used by this component + */ +typedef struct OMX_IMAGE_PARAM_PORTFORMATTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nIndex; + OMX_IMAGE_CODINGTYPE eCompressionFormat; + OMX_COLOR_FORMATTYPE eColorFormat; +} OMX_IMAGE_PARAM_PORTFORMATTYPE; + + +/** + * Flash control type + * + * ENUMS + * Torch : Flash forced constantly on + */ +typedef enum OMX_IMAGE_FLASHCONTROLTYPE { + OMX_IMAGE_FlashControlOn = 0, + OMX_IMAGE_FlashControlOff, + OMX_IMAGE_FlashControlAuto, + OMX_IMAGE_FlashControlRedEyeReduction, + OMX_IMAGE_FlashControlFillin, + OMX_IMAGE_FlashControlTorch, + OMX_IMAGE_FlashControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_IMAGE_FlashControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_IMAGE_FlashControlMax = 0x7FFFFFFF +} OMX_IMAGE_FLASHCONTROLTYPE; + + +/** + * Flash control configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eFlashControl : Flash control type + */ +typedef struct OMX_IMAGE_PARAM_FLASHCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_IMAGE_FLASHCONTROLTYPE eFlashControl; +} OMX_IMAGE_PARAM_FLASHCONTROLTYPE; + + +/** + * Focus control type + */ +typedef enum OMX_IMAGE_FOCUSCONTROLTYPE { + OMX_IMAGE_FocusControlOn = 0, + OMX_IMAGE_FocusControlOff, + OMX_IMAGE_FocusControlAuto, + OMX_IMAGE_FocusControlAutoLock, + OMX_IMAGE_FocusControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_IMAGE_FocusControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_IMAGE_FocusControlHyperfocal, /* Focus at the hyperfocal point of the lens. */ + OMX_IMAGE_FocusControlAutoMacro, /* CF over a macro range (eg 0-50cm) */ + OMX_IMAGE_FocusControlAutoInfinity, /* CF over distant range (eg 50cm to infinity) */ + OMX_IMAGE_FocusControlAutoLockMacro, /* AF over a macro range (eg 0-50cm) */ + OMX_IMAGE_FocusControlAutoLockInfinity, /* AF over distant range (eg 50cm to infinity) */ + OMX_IMAGE_FocusControlNearFixed, /* Focus at a fixed near focus point - (50cm-1m) */ + OMX_IMAGE_FocusControlAutoNear, /* CF over a near range (eg 0-200cm) */ + OMX_IMAGE_FocusControlAutoLockNear, /* AF over a near range (eg 0-200cm) */ + OMX_IMAGE_FocusControlInfinityFixed, /* Focus at infinity */ + OMX_IMAGE_FocusControlMacroFixed, /* Focus at a macro distance */ + OMX_IMAGE_FocusControlAutoFast, /* CF over a full range with fast response */ + OMX_IMAGE_FocusControlAutoMacroFast, /* CF over a macro range (eg 0-50cm) with fast response */ + OMX_IMAGE_FocusControlAutoNearFast, /* CF over a near range (eg 0-200cm) */ + OMX_IMAGE_FocusControlAutoInfinityFast, /* CF over distant range (eg 50cm to infinity) with fast response */ + OMX_IMAGE_FocusControlCurrentFixed, /* Stop the lens at the current position */ + OMX_IMAGE_FocusControlMax = 0x7FFFFFFF +} OMX_IMAGE_FOCUSCONTROLTYPE; + + +/** + * Focus control configuration + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eFocusControl : Focus control + * nFocusSteps : Focus can take on values from 0 mm to infinity. + * Interest is only in number of steps over this range. + * nFocusStepIndex : Current focus step index + */ +typedef struct OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_IMAGE_FOCUSCONTROLTYPE eFocusControl; + OMX_U32 nFocusSteps; + OMX_U32 nFocusStepIndex; +} OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE; + + +/** + * Q Factor for JPEG compression, which controls the tradeoff between image + * quality and size. Q Factor provides a more simple means of controlling + * JPEG compression quality, without directly programming Quantization + * tables for chroma and luma + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nQFactor : JPEG Q factor value in the range of 1-100. A factor of 1 + * produces the smallest, worst quality images, and a factor + * of 100 produces the largest, best quality images. A + * typical default is 75 for small good quality images + */ +typedef struct OMX_IMAGE_PARAM_QFACTORTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nQFactor; +} OMX_IMAGE_PARAM_QFACTORTYPE; + +/** + * Quantization table type + */ + +typedef enum OMX_IMAGE_QUANTIZATIONTABLETYPE { + OMX_IMAGE_QuantizationTableLuma = 0, + OMX_IMAGE_QuantizationTableChroma, + OMX_IMAGE_QuantizationTableChromaCb, + OMX_IMAGE_QuantizationTableChromaCr, + OMX_IMAGE_QuantizationTableKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_IMAGE_QuantizationTableVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_IMAGE_QuantizationTableMax = 0x7FFFFFFF +} OMX_IMAGE_QUANTIZATIONTABLETYPE; + +/** + * JPEG quantization tables are used to determine DCT compression for + * YUV data, as an alternative to specifying Q factor, providing exact + * control of compression + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eQuantizationTable : Quantization table type + * nQuantizationMatrix[64] : JPEG quantization table of coefficients stored + * in increasing columns then by rows of data (i.e. + * row 1, ... row 8). Quantization values are in + * the range 0-255 and stored in linear order + * (i.e. the component will zig-zag the + * quantization table data if required internally) + */ +typedef struct OMX_IMAGE_PARAM_QUANTIZATIONTABLETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_IMAGE_QUANTIZATIONTABLETYPE eQuantizationTable; + OMX_U8 nQuantizationMatrix[64]; +} OMX_IMAGE_PARAM_QUANTIZATIONTABLETYPE; + + +/** + * Huffman table type, the same Huffman table is applied for chroma and + * luma component + */ +typedef enum OMX_IMAGE_HUFFMANTABLETYPE { + OMX_IMAGE_HuffmanTableAC = 0, + OMX_IMAGE_HuffmanTableDC, + OMX_IMAGE_HuffmanTableACLuma, + OMX_IMAGE_HuffmanTableACChroma, + OMX_IMAGE_HuffmanTableDCLuma, + OMX_IMAGE_HuffmanTableDCChroma, + OMX_IMAGE_HuffmanTableKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_IMAGE_HuffmanTableVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_IMAGE_HuffmanTableMax = 0x7FFFFFFF +} OMX_IMAGE_HUFFMANTABLETYPE; + +/** + * JPEG Huffman table + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eHuffmanTable : Huffman table type + * nNumberOfHuffmanCodeOfLength[16] : 0-16, number of Huffman codes of each + * possible length + * nHuffmanTable[256] : 0-255, the size used for AC and DC + * HuffmanTable are 16 and 162 + */ +typedef struct OMX_IMAGE_PARAM_HUFFMANTTABLETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_IMAGE_HUFFMANTABLETYPE eHuffmanTable; + OMX_U8 nNumberOfHuffmanCodeOfLength[16]; + OMX_U8 nHuffmanTable[256]; +}OMX_IMAGE_PARAM_HUFFMANTTABLETYPE; + +/** @} */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ diff --git a/interface/vmcs_host/khronos/IL/OMX_Index.h b/interface/vmcs_host/khronos/IL/OMX_Index.h new file mode 100755 index 0000000..3ad2941 --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Index.h @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** @file OMX_Index.h - OpenMax IL version 1.1.2 + * The OMX_Index header file contains the definitions for both applications + * and components . + */ + + +#ifndef OMX_Index_h +#define OMX_Index_h + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* Each OMX header must include all required header files to allow the + * header to compile without errors. The includes below are required + * for this header file to compile successfully + */ +#include "OMX_Types.h" + + +/** The OMX_INDEXTYPE enumeration is used to select a structure when either + * getting or setting parameters and/or configuration data. Each entry in + * this enumeration maps to an OMX specified structure. When the + * OMX_GetParameter, OMX_SetParameter, OMX_GetConfig or OMX_SetConfig methods + * are used, the second parameter will always be an entry from this enumeration + * and the third entry will be the structure shown in the comments for the entry. + * For example, if the application is initializing a cropping function, the + * OMX_SetConfig command would have OMX_IndexConfigCommonInputCrop as the second parameter + * and would send a pointer to an initialized OMX_RECTTYPE structure as the + * third parameter. + * + * The enumeration entries named with the OMX_Config prefix are sent using + * the OMX_SetConfig command and the enumeration entries named with the + * OMX_PARAM_ prefix are sent using the OMX_SetParameter command. + */ +typedef enum OMX_INDEXTYPE { + + OMX_IndexComponentStartUnused = 0x01000000, + OMX_IndexParamPriorityMgmt, /**< reference: OMX_PRIORITYMGMTTYPE */ + OMX_IndexParamAudioInit, /**< reference: OMX_PORT_PARAM_TYPE */ + OMX_IndexParamImageInit, /**< reference: OMX_PORT_PARAM_TYPE */ + OMX_IndexParamVideoInit, /**< reference: OMX_PORT_PARAM_TYPE */ + OMX_IndexParamOtherInit, /**< reference: OMX_PORT_PARAM_TYPE */ + OMX_IndexParamNumAvailableStreams, /**< reference: OMX_PARAM_U32TYPE */ + OMX_IndexParamActiveStream, /**< reference: OMX_PARAM_U32TYPE */ + OMX_IndexParamSuspensionPolicy, /**< reference: OMX_PARAM_SUSPENSIONPOLICYTYPE */ + OMX_IndexParamComponentSuspended, /**< reference: OMX_PARAM_SUSPENSIONTYPE */ + OMX_IndexConfigCapturing, /**< reference: OMX_CONFIG_BOOLEANTYPE */ + OMX_IndexConfigCaptureMode, /**< reference: OMX_CONFIG_CAPTUREMODETYPE */ + OMX_IndexAutoPauseAfterCapture, /**< reference: OMX_CONFIG_BOOLEANTYPE */ + OMX_IndexParamContentURI, /**< reference: OMX_PARAM_CONTENTURITYPE */ + OMX_IndexParamCustomContentPipe, /**< reference: OMX_PARAM_CONTENTPIPETYPE */ + OMX_IndexParamDisableResourceConcealment, /**< reference: OMX_RESOURCECONCEALMENTTYPE */ + OMX_IndexConfigMetadataItemCount, /**< reference: OMX_CONFIG_METADATAITEMCOUNTTYPE */ + OMX_IndexConfigContainerNodeCount, /**< reference: OMX_CONFIG_CONTAINERNODECOUNTTYPE */ + OMX_IndexConfigMetadataItem, /**< reference: OMX_CONFIG_METADATAITEMTYPE */ + OMX_IndexConfigCounterNodeID, /**< reference: OMX_CONFIG_CONTAINERNODEIDTYPE */ + OMX_IndexParamMetadataFilterType, /**< reference: OMX_PARAM_METADATAFILTERTYPE */ + OMX_IndexParamMetadataKeyFilter, /**< reference: OMX_PARAM_METADATAFILTERTYPE */ + OMX_IndexConfigPriorityMgmt, /**< reference: OMX_PRIORITYMGMTTYPE */ + OMX_IndexParamStandardComponentRole, /**< reference: OMX_PARAM_COMPONENTROLETYPE */ + + OMX_IndexPortStartUnused = 0x02000000, + OMX_IndexParamPortDefinition, /**< reference: OMX_PARAM_PORTDEFINITIONTYPE */ + OMX_IndexParamCompBufferSupplier, /**< reference: OMX_PARAM_BUFFERSUPPLIERTYPE */ + OMX_IndexReservedStartUnused = 0x03000000, + + /* Audio parameters and configurations */ + OMX_IndexAudioStartUnused = 0x04000000, + OMX_IndexParamAudioPortFormat, /**< reference: OMX_AUDIO_PARAM_PORTFORMATTYPE */ + OMX_IndexParamAudioPcm, /**< reference: OMX_AUDIO_PARAM_PCMMODETYPE */ + OMX_IndexParamAudioAac, /**< reference: OMX_AUDIO_PARAM_AACPROFILETYPE */ + OMX_IndexParamAudioRa, /**< reference: OMX_AUDIO_PARAM_RATYPE */ + OMX_IndexParamAudioMp3, /**< reference: OMX_AUDIO_PARAM_MP3TYPE */ + OMX_IndexParamAudioAdpcm, /**< reference: OMX_AUDIO_PARAM_ADPCMTYPE */ + OMX_IndexParamAudioG723, /**< reference: OMX_AUDIO_PARAM_G723TYPE */ + OMX_IndexParamAudioG729, /**< reference: OMX_AUDIO_PARAM_G729TYPE */ + OMX_IndexParamAudioAmr, /**< reference: OMX_AUDIO_PARAM_AMRTYPE */ + OMX_IndexParamAudioWma, /**< reference: OMX_AUDIO_PARAM_WMATYPE */ + OMX_IndexParamAudioSbc, /**< reference: OMX_AUDIO_PARAM_SBCTYPE */ + OMX_IndexParamAudioMidi, /**< reference: OMX_AUDIO_PARAM_MIDITYPE */ + OMX_IndexParamAudioGsm_FR, /**< reference: OMX_AUDIO_PARAM_GSMFRTYPE */ + OMX_IndexParamAudioMidiLoadUserSound, /**< reference: OMX_AUDIO_PARAM_MIDILOADUSERSOUNDTYPE */ + OMX_IndexParamAudioG726, /**< reference: OMX_AUDIO_PARAM_G726TYPE */ + OMX_IndexParamAudioGsm_EFR, /**< reference: OMX_AUDIO_PARAM_GSMEFRTYPE */ + OMX_IndexParamAudioGsm_HR, /**< reference: OMX_AUDIO_PARAM_GSMHRTYPE */ + OMX_IndexParamAudioPdc_FR, /**< reference: OMX_AUDIO_PARAM_PDCFRTYPE */ + OMX_IndexParamAudioPdc_EFR, /**< reference: OMX_AUDIO_PARAM_PDCEFRTYPE */ + OMX_IndexParamAudioPdc_HR, /**< reference: OMX_AUDIO_PARAM_PDCHRTYPE */ + OMX_IndexParamAudioTdma_FR, /**< reference: OMX_AUDIO_PARAM_TDMAFRTYPE */ + OMX_IndexParamAudioTdma_EFR, /**< reference: OMX_AUDIO_PARAM_TDMAEFRTYPE */ + OMX_IndexParamAudioQcelp8, /**< reference: OMX_AUDIO_PARAM_QCELP8TYPE */ + OMX_IndexParamAudioQcelp13, /**< reference: OMX_AUDIO_PARAM_QCELP13TYPE */ + OMX_IndexParamAudioEvrc, /**< reference: OMX_AUDIO_PARAM_EVRCTYPE */ + OMX_IndexParamAudioSmv, /**< reference: OMX_AUDIO_PARAM_SMVTYPE */ + OMX_IndexParamAudioVorbis, /**< reference: OMX_AUDIO_PARAM_VORBISTYPE */ + + OMX_IndexConfigAudioMidiImmediateEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE */ + OMX_IndexConfigAudioMidiControl, /**< reference: OMX_AUDIO_CONFIG_MIDICONTROLTYPE */ + OMX_IndexConfigAudioMidiSoundBankProgram, /**< reference: OMX_AUDIO_CONFIG_MIDISOUNDBANKPROGRAMTYPE */ + OMX_IndexConfigAudioMidiStatus, /**< reference: OMX_AUDIO_CONFIG_MIDISTATUSTYPE */ + OMX_IndexConfigAudioMidiMetaEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIMETAEVENTTYPE */ + OMX_IndexConfigAudioMidiMetaEventData, /**< reference: OMX_AUDIO_CONFIG_MIDIMETAEVENTDATATYPE */ + OMX_IndexConfigAudioVolume, /**< reference: OMX_AUDIO_CONFIG_VOLUMETYPE */ + OMX_IndexConfigAudioBalance, /**< reference: OMX_AUDIO_CONFIG_BALANCETYPE */ + OMX_IndexConfigAudioChannelMute, /**< reference: OMX_AUDIO_CONFIG_CHANNELMUTETYPE */ + OMX_IndexConfigAudioMute, /**< reference: OMX_AUDIO_CONFIG_MUTETYPE */ + OMX_IndexConfigAudioLoudness, /**< reference: OMX_AUDIO_CONFIG_LOUDNESSTYPE */ + OMX_IndexConfigAudioEchoCancelation, /**< reference: OMX_AUDIO_CONFIG_ECHOCANCELATIONTYPE */ + OMX_IndexConfigAudioNoiseReduction, /**< reference: OMX_AUDIO_CONFIG_NOISEREDUCTIONTYPE */ + OMX_IndexConfigAudioBass, /**< reference: OMX_AUDIO_CONFIG_BASSTYPE */ + OMX_IndexConfigAudioTreble, /**< reference: OMX_AUDIO_CONFIG_TREBLETYPE */ + OMX_IndexConfigAudioStereoWidening, /**< reference: OMX_AUDIO_CONFIG_STEREOWIDENINGTYPE */ + OMX_IndexConfigAudioChorus, /**< reference: OMX_AUDIO_CONFIG_CHORUSTYPE */ + OMX_IndexConfigAudioEqualizer, /**< reference: OMX_AUDIO_CONFIG_EQUALIZERTYPE */ + OMX_IndexConfigAudioReverberation, /**< reference: OMX_AUDIO_CONFIG_REVERBERATIONTYPE */ + OMX_IndexConfigAudioChannelVolume, /**< reference: OMX_AUDIO_CONFIG_CHANNELVOLUMETYPE */ + + /* Image specific parameters and configurations */ + OMX_IndexImageStartUnused = 0x05000000, + OMX_IndexParamImagePortFormat, /**< reference: OMX_IMAGE_PARAM_PORTFORMATTYPE */ + OMX_IndexParamFlashControl, /**< reference: OMX_IMAGE_PARAM_FLASHCONTROLTYPE */ + OMX_IndexConfigFocusControl, /**< reference: OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE */ + OMX_IndexParamQFactor, /**< reference: OMX_IMAGE_PARAM_QFACTORTYPE */ + OMX_IndexParamQuantizationTable, /**< reference: OMX_IMAGE_PARAM_QUANTIZATIONTABLETYPE */ + OMX_IndexParamHuffmanTable, /**< reference: OMX_IMAGE_PARAM_HUFFMANTTABLETYPE */ + OMX_IndexConfigFlashControl, /**< reference: OMX_IMAGE_PARAM_FLASHCONTROLTYPE */ + + /* Video specific parameters and configurations */ + OMX_IndexVideoStartUnused = 0x06000000, + OMX_IndexParamVideoPortFormat, /**< reference: OMX_VIDEO_PARAM_PORTFORMATTYPE */ + OMX_IndexParamVideoQuantization, /**< reference: OMX_VIDEO_PARAM_QUANTIZATIONTYPE */ + OMX_IndexParamVideoFastUpdate, /**< reference: OMX_VIDEO_PARAM_VIDEOFASTUPDATETYPE */ + OMX_IndexParamVideoBitrate, /**< reference: OMX_VIDEO_PARAM_BITRATETYPE */ + OMX_IndexParamVideoMotionVector, /**< reference: OMX_VIDEO_PARAM_MOTIONVECTORTYPE */ + OMX_IndexParamVideoIntraRefresh, /**< reference: OMX_VIDEO_PARAM_INTRAREFRESHTYPE */ + OMX_IndexParamVideoErrorCorrection, /**< reference: OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE */ + OMX_IndexParamVideoVBSMC, /**< reference: OMX_VIDEO_PARAM_VBSMCTYPE */ + OMX_IndexParamVideoMpeg2, /**< reference: OMX_VIDEO_PARAM_MPEG2TYPE */ + OMX_IndexParamVideoMpeg4, /**< reference: OMX_VIDEO_PARAM_MPEG4TYPE */ + OMX_IndexParamVideoWmv, /**< reference: OMX_VIDEO_PARAM_WMVTYPE */ + OMX_IndexParamVideoRv, /**< reference: OMX_VIDEO_PARAM_RVTYPE */ + OMX_IndexParamVideoAvc, /**< reference: OMX_VIDEO_PARAM_AVCTYPE */ + OMX_IndexParamVideoH263, /**< reference: OMX_VIDEO_PARAM_H263TYPE */ + OMX_IndexParamVideoProfileLevelQuerySupported, /**< reference: OMX_VIDEO_PARAM_PROFILELEVELTYPE */ + OMX_IndexParamVideoProfileLevelCurrent, /**< reference: OMX_VIDEO_PARAM_PROFILELEVELTYPE */ + OMX_IndexConfigVideoBitrate, /**< reference: OMX_VIDEO_CONFIG_BITRATETYPE */ + OMX_IndexConfigVideoFramerate, /**< reference: OMX_CONFIG_FRAMERATETYPE */ + OMX_IndexConfigVideoIntraVOPRefresh, /**< reference: OMX_CONFIG_INTRAREFRESHVOPTYPE */ + OMX_IndexConfigVideoIntraMBRefresh, /**< reference: OMX_CONFIG_MACROBLOCKERRORMAPTYPE */ + OMX_IndexConfigVideoMBErrorReporting, /**< reference: OMX_CONFIG_MBERRORREPORTINGTYPE */ + OMX_IndexParamVideoMacroblocksPerFrame, /**< reference: OMX_PARAM_MACROBLOCKSTYPE */ + OMX_IndexConfigVideoMacroBlockErrorMap, /**< reference: OMX_CONFIG_MACROBLOCKERRORMAPTYPE */ + OMX_IndexParamVideoSliceFMO, /**< reference: OMX_VIDEO_PARAM_AVCSLICEFMO */ + OMX_IndexConfigVideoAVCIntraPeriod, /**< reference: OMX_VIDEO_CONFIG_AVCINTRAPERIOD */ + OMX_IndexConfigVideoNalSize, /**< reference: OMX_VIDEO_CONFIG_NALSIZE */ + + /* Image & Video common Configurations */ + OMX_IndexCommonStartUnused = 0x07000000, + OMX_IndexParamCommonDeblocking, /**< reference: OMX_PARAM_DEBLOCKINGTYPE */ + OMX_IndexParamCommonSensorMode, /**< reference: OMX_PARAM_SENSORMODETYPE */ + OMX_IndexParamCommonInterleave, /**< reference: OMX_PARAM_INTERLEAVETYPE */ + OMX_IndexConfigCommonColorFormatConversion, /**< reference: OMX_CONFIG_COLORCONVERSIONTYPE */ + OMX_IndexConfigCommonScale, /**< reference: OMX_CONFIG_SCALEFACTORTYPE */ + OMX_IndexConfigCommonImageFilter, /**< reference: OMX_CONFIG_IMAGEFILTERTYPE */ + OMX_IndexConfigCommonColorEnhancement, /**< reference: OMX_CONFIG_COLORENHANCEMENTTYPE */ + OMX_IndexConfigCommonColorKey, /**< reference: OMX_CONFIG_COLORKEYTYPE */ + OMX_IndexConfigCommonColorBlend, /**< reference: OMX_CONFIG_COLORBLENDTYPE */ + OMX_IndexConfigCommonFrameStabilisation,/**< reference: OMX_CONFIG_FRAMESTABTYPE */ + OMX_IndexConfigCommonRotate, /**< reference: OMX_CONFIG_ROTATIONTYPE */ + OMX_IndexConfigCommonMirror, /**< reference: OMX_CONFIG_MIRRORTYPE */ + OMX_IndexConfigCommonOutputPosition, /**< reference: OMX_CONFIG_POINTTYPE */ + OMX_IndexConfigCommonInputCrop, /**< reference: OMX_CONFIG_RECTTYPE */ + OMX_IndexConfigCommonOutputCrop, /**< reference: OMX_CONFIG_RECTTYPE */ + OMX_IndexConfigCommonDigitalZoom, /**< reference: OMX_CONFIG_SCALEFACTORTYPE */ + OMX_IndexConfigCommonOpticalZoom, /**< reference: OMX_CONFIG_SCALEFACTORTYPE*/ + OMX_IndexConfigCommonWhiteBalance, /**< reference: OMX_CONFIG_WHITEBALCONTROLTYPE */ + OMX_IndexConfigCommonExposure, /**< reference: OMX_CONFIG_EXPOSURECONTROLTYPE */ + OMX_IndexConfigCommonContrast, /**< reference: OMX_CONFIG_CONTRASTTYPE */ + OMX_IndexConfigCommonBrightness, /**< reference: OMX_CONFIG_BRIGHTNESSTYPE */ + OMX_IndexConfigCommonBacklight, /**< reference: OMX_CONFIG_BACKLIGHTTYPE */ + OMX_IndexConfigCommonGamma, /**< reference: OMX_CONFIG_GAMMATYPE */ + OMX_IndexConfigCommonSaturation, /**< reference: OMX_CONFIG_SATURATIONTYPE */ + OMX_IndexConfigCommonLightness, /**< reference: OMX_CONFIG_LIGHTNESSTYPE */ + OMX_IndexConfigCommonExclusionRect, /**< reference: OMX_CONFIG_RECTTYPE */ + OMX_IndexConfigCommonDithering, /**< reference: OMX_CONFIG_DITHERTYPE */ + OMX_IndexConfigCommonPlaneBlend, /**< reference: OMX_CONFIG_PLANEBLENDTYPE */ + OMX_IndexConfigCommonExposureValue, /**< reference: OMX_CONFIG_EXPOSUREVALUETYPE */ + OMX_IndexConfigCommonOutputSize, /**< reference: OMX_FRAMESIZETYPE */ + OMX_IndexParamCommonExtraQuantData, /**< reference: OMX_OTHER_EXTRADATATYPE */ + OMX_IndexConfigCommonFocusRegion, /**< reference: OMX_CONFIG_FOCUSREGIONTYPE */ + OMX_IndexConfigCommonFocusStatus, /**< reference: OMX_PARAM_FOCUSSTATUSTYPE */ + OMX_IndexConfigCommonTransitionEffect, /**< reference: OMX_CONFIG_TRANSITIONEFFECTTYPE */ + + /* Reserved Configuration range */ + OMX_IndexOtherStartUnused = 0x08000000, + OMX_IndexParamOtherPortFormat, /**< reference: OMX_OTHER_PARAM_PORTFORMATTYPE */ + OMX_IndexConfigOtherPower, /**< reference: OMX_OTHER_CONFIG_POWERTYPE */ + OMX_IndexConfigOtherStats, /**< reference: OMX_OTHER_CONFIG_STATSTYPE */ + + + /* Reserved Time range */ + OMX_IndexTimeStartUnused = 0x09000000, + OMX_IndexConfigTimeScale, /**< reference: OMX_TIME_CONFIG_SCALETYPE */ + OMX_IndexConfigTimeClockState, /**< reference: OMX_TIME_CONFIG_CLOCKSTATETYPE */ + OMX_IndexConfigTimeActiveRefClock, /**< reference: OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE */ + OMX_IndexConfigTimeCurrentMediaTime, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (read only) */ + OMX_IndexConfigTimeCurrentWallTime, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (read only) */ + OMX_IndexConfigTimeCurrentAudioReference, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (write only) */ + OMX_IndexConfigTimeCurrentVideoReference, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (write only) */ + OMX_IndexConfigTimeMediaTimeRequest, /**< reference: OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE (write only) */ + OMX_IndexConfigTimeClientStartTime, /** +#define STDINT_H_AVAILABLE +#endif + +/** OMX_U8 is an 8 bit unsigned quantity that is byte aligned */ +typedef unsigned char OMX_U8; + +/** OMX_S8 is an 8 bit signed quantity that is byte aligned */ +typedef signed char OMX_S8; + +/** OMX_U16 is a 16 bit unsigned quantity that is 16 bit word aligned */ +typedef unsigned short OMX_U16; + +/** OMX_S16 is a 16 bit signed quantity that is 16 bit word aligned */ +typedef signed short OMX_S16; + +/** OMX_U32 is a 32 bit unsigned quantity that is 32 bit word aligned */ +#ifdef STDINT_H_AVAILABLE +typedef uint32_t OMX_U32; +#else +typedef unsigned long OMX_U32; +#endif + +/** OMX_S32 is a 32 bit signed quantity that is 32 bit word aligned */ +#ifdef STDINT_H_AVAILABLE +typedef int32_t OMX_S32; +#else +typedef signed long OMX_S32; +#endif + + +/* Users with compilers that cannot accept the "long long" designation should + define the OMX_SKIP64BIT macro. It should be noted that this may cause + some components to fail to compile if the component was written to require + 64 bit integral types. However, these components would NOT compile anyway + since the compiler does not support the way the component was written. +*/ +#ifndef OMX_SKIP64BIT +#ifdef __SYMBIAN32__ +/** OMX_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ +typedef unsigned long long OMX_U64; + +/** OMX_S64 is a 64 bit signed quantity that is 64 bit word aligned */ +typedef signed long long OMX_S64; + +#elif defined(WIN32) + +/** OMX_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ +typedef unsigned __int64 OMX_U64; + +/** OMX_S64 is a 64 bit signed quantity that is 64 bit word aligned */ +typedef signed __int64 OMX_S64; + +#else /* WIN32 */ + +/** OMX_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ +typedef unsigned long long OMX_U64; + +/** OMX_S64 is a 64 bit signed quantity that is 64 bit word aligned */ +typedef signed long long OMX_S64; + +#endif /* WIN32 */ +#endif + + +/** The OMX_BOOL type is intended to be used to represent a true or a false + value when passing parameters to and from the OMX core and components. The + OMX_BOOL is a 32 bit quantity and is aligned on a 32 bit word boundary. + */ +typedef enum OMX_BOOL { + OMX_FALSE = 0, + OMX_TRUE = !OMX_FALSE, + OMX_BOOL_MAX = 0x7FFFFFFF +} OMX_BOOL; + +/** The OMX_PTR type is intended to be used to pass pointers between the OMX + applications and the OMX Core and components. This is a 32 bit pointer and + is aligned on a 32 bit boundary. + */ +typedef void* OMX_PTR; + +/** The OMX_STRING type is intended to be used to pass "C" type strings between + the application and the core and component. The OMX_STRING type is a 32 + bit pointer to a zero terminated string. The pointer is word aligned and + the string is byte aligned. + */ +typedef char* OMX_STRING; + +/** The OMX_BYTE type is intended to be used to pass arrays of bytes such as + buffers between the application and the component and core. The OMX_BYTE + type is a 32 bit pointer to a zero terminated string. The pointer is word + aligned and the string is byte aligned. + */ +typedef unsigned char* OMX_BYTE; + +/** OMX_UUIDTYPE is a very long unique identifier to uniquely identify + at runtime. This identifier should be generated by a component in a way + that guarantees that every instance of the identifier running on the system + is unique. */ +typedef unsigned char OMX_UUIDTYPE[128]; + +/** The OMX_DIRTYPE enumeration is used to indicate if a port is an input or + an output port. This enumeration is common across all component types. + */ +typedef enum OMX_DIRTYPE +{ + OMX_DirInput, /**< Port is an input port */ + OMX_DirOutput, /**< Port is an output port */ + OMX_DirMax = 0x7FFFFFFF +} OMX_DIRTYPE; + +/** The OMX_ENDIANTYPE enumeration is used to indicate the bit ordering + for numerical data (i.e. big endian, or little endian). + */ +typedef enum OMX_ENDIANTYPE +{ + OMX_EndianBig, /**< big endian */ + OMX_EndianLittle, /**< little endian */ + OMX_EndianMax = 0x7FFFFFFF +} OMX_ENDIANTYPE; + + +/** The OMX_NUMERICALDATATYPE enumeration is used to indicate if data + is signed or unsigned + */ +typedef enum OMX_NUMERICALDATATYPE +{ + OMX_NumericalDataSigned, /**< signed data */ + OMX_NumericalDataUnsigned, /**< unsigned data */ + OMX_NumercialDataMax = 0x7FFFFFFF +} OMX_NUMERICALDATATYPE; + + +/** Unsigned bounded value type */ +typedef struct OMX_BU32 { + OMX_U32 nValue; /**< actual value */ + OMX_U32 nMin; /**< minimum for value (i.e. nValue >= nMin) */ + OMX_U32 nMax; /**< maximum for value (i.e. nValue <= nMax) */ +} OMX_BU32; + + +/** Signed bounded value type */ +typedef struct OMX_BS32 { + OMX_S32 nValue; /**< actual value */ + OMX_S32 nMin; /**< minimum for value (i.e. nValue >= nMin) */ + OMX_S32 nMax; /**< maximum for value (i.e. nValue <= nMax) */ +} OMX_BS32; + + +/** Structure representing some time or duration in microseconds. This structure + * must be interpreted as a signed 64 bit value. The quantity is signed to accommodate + * negative deltas and preroll scenarios. The quantity is represented in microseconds + * to accomodate high resolution timestamps (e.g. DVD presentation timestamps based + * on a 90kHz clock) and to allow more accurate and synchronized delivery (e.g. + * individual audio samples delivered at 192 kHz). The quantity is 64 bit to + * accommodate a large dynamic range (signed 32 bit values would allow only for plus + * or minus 35 minutes). + * + * Implementations with limited precision may convert the signed 64 bit value to + * a signed 32 bit value internally but risk loss of precision. + */ +#ifndef OMX_SKIP64BIT +typedef OMX_S64 OMX_TICKS; +#else +typedef struct OMX_TICKS +{ + OMX_U32 nLowPart; /** low bits of the signed 64 bit tick value */ + OMX_U32 nHighPart; /** high bits of the signed 64 bit tick value */ +} OMX_TICKS; +#endif +#define OMX_TICKS_PER_SECOND 1000000 + +/** Define the public interface for the OMX Handle. The core will not use + this value internally, but the application should only use this value. + */ +typedef void* OMX_HANDLETYPE; + +typedef struct OMX_MARKTYPE +{ + OMX_HANDLETYPE hMarkTargetComponent; /**< The component that will + generate a mark event upon + processing the mark. */ + OMX_PTR pMarkData; /**< Application specific data associated with + the mark sent on a mark event to disambiguate + this mark from others. */ +} OMX_MARKTYPE; + + +/** OMX_NATIVE_DEVICETYPE is used to map a OMX video port to the + * platform & operating specific object used to reference the display + * or can be used by a audio port for native audio rendering */ +typedef void* OMX_NATIVE_DEVICETYPE; + +/** OMX_NATIVE_WINDOWTYPE is used to map a OMX video port to the + * platform & operating specific object used to reference the window */ +typedef void* OMX_NATIVE_WINDOWTYPE; + + +/** Define the OMX IL version that corresponds to this set of header files. + * We also define a combined version that can be used to write or compare + * values of the 32bit nVersion field, assuming a little endian architecture */ +#define OMX_VERSION_MAJOR 1 +#define OMX_VERSION_MINOR 1 +#define OMX_VERSION_REVISION 2 +#define OMX_VERSION_STEP 0 + +#define OMX_VERSION ((OMX_VERSION_STEP<<24) | (OMX_VERSION_REVISION<<16) | (OMX_VERSION_MINOR<<8) | OMX_VERSION_MAJOR) + + +/** The OMX_VERSIONTYPE union is used to specify the version for + a structure or component. For a component, the version is entirely + specified by the component vendor. Components doing the same function + from different vendors may or may not have the same version. For + structures, the version shall be set by the entity that allocates the + structure. For structures specified in the OMX 1.1 specification, the + value of the version shall be set to 1.1.0.0 in all cases. Access to the + OMX_VERSIONTYPE can be by a single 32 bit access (e.g. by nVersion) or + by accessing one of the structure elements to, for example, check only + the Major revision. + */ +typedef union OMX_VERSIONTYPE +{ + struct + { + OMX_U8 nVersionMajor; /**< Major version accessor element */ + OMX_U8 nVersionMinor; /**< Minor version accessor element */ + OMX_U8 nRevision; /**< Revision version accessor element */ + OMX_U8 nStep; /**< Step version accessor element */ + } s; + OMX_U32 nVersion; /**< 32 bit value to make accessing the + version easily done in a single word + size copy/compare operation */ +} OMX_VERSIONTYPE; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ diff --git a/interface/vmcs_host/khronos/IL/OMX_Video.h b/interface/vmcs_host/khronos/IL/OMX_Video.h new file mode 100755 index 0000000..cd941fc --- /dev/null +++ b/interface/vmcs_host/khronos/IL/OMX_Video.h @@ -0,0 +1,1082 @@ +/** + * Copyright (c) 2008 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** + * @file OMX_Video.h - OpenMax IL version 1.1.2 + * The structures is needed by Video components to exchange parameters + * and configuration data with OMX components. + */ +#ifndef OMX_Video_h +#define OMX_Video_h + +/** @defgroup video OpenMAX IL Video Domain + * @ingroup iv + * Structures for OpenMAX IL Video domain + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/** + * Each OMX header must include all required header files to allow the + * header to compile without errors. The includes below are required + * for this header file to compile successfully + */ + +#include "OMX_IVCommon.h" + + +/** + * Enumeration used to define the possible video compression codings. + * NOTE: This essentially refers to file extensions. If the coding is + * being used to specify the ENCODE type, then additional work + * must be done to configure the exact flavor of the compression + * to be used. For decode cases where the user application can + * not differentiate between MPEG-4 and H.264 bit streams, it is + * up to the codec to handle this. + */ +typedef enum OMX_VIDEO_CODINGTYPE { + OMX_VIDEO_CodingUnused, /**< Value when coding is N/A */ + OMX_VIDEO_CodingAutoDetect, /**< Autodetection of coding type */ + OMX_VIDEO_CodingMPEG2, /**< AKA: H.262 */ + OMX_VIDEO_CodingH263, /**< H.263 */ + OMX_VIDEO_CodingMPEG4, /**< MPEG-4 */ + OMX_VIDEO_CodingWMV, /**< all versions of Windows Media Video */ + OMX_VIDEO_CodingRV, /**< all versions of Real Video */ + OMX_VIDEO_CodingAVC, /**< H.264/AVC */ + OMX_VIDEO_CodingMJPEG, /**< Motion JPEG */ + OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + +#define OMX_AUDIO_CodingVP6_Supported 1 + OMX_VIDEO_CodingVP6, /**< On2 VP6 */ +#define OMX_AUDIO_CodingVP7_Supported 1 + OMX_VIDEO_CodingVP7, /**< On2 VP7 */ +#define OMX_AUDIO_CodingVP8_Supported 1 + OMX_VIDEO_CodingVP8, /**< On2 VP8 */ +#define OMX_AUDIO_CodingYUV_Supported 1 + OMX_VIDEO_CodingYUV, /* raw YUV video */ +#define OMX_AUDIO_CodingSorenson_Supported 1 + OMX_VIDEO_CodingSorenson, /**< Sorenson */ +#define OMX_AUDIO_CodingTheora_Supported 1 + OMX_VIDEO_CodingTheora, /**< Theora */ +#define OMX_AUDIO_CodingMVC_Supported 1 + OMX_VIDEO_CodingMVC, /**< H.264/MVC */ + + OMX_VIDEO_CodingMax = 0x7FFFFFFF +} OMX_VIDEO_CODINGTYPE; + + +/** + * Data structure used to define a video path. The number of Video paths for + * input and output will vary by type of the Video component. + * + * Input (aka Source) : zero Inputs, one Output, + * Splitter : one Input, 2 or more Outputs, + * Processing Element : one Input, one output, + * Mixer : 2 or more inputs, one output, + * Output (aka Sink) : one Input, zero outputs. + * + * The PortDefinition structure is used to define all of the parameters + * necessary for the compliant component to setup an input or an output video + * path. If additional vendor specific data is required, it should be + * transmitted to the component using the CustomCommand function. Compliant + * components will prepopulate this structure with optimal values during the + * GetDefaultInitParams command. + * + * STRUCT MEMBERS: + * cMIMEType : MIME type of data for the port + * pNativeRender : Platform specific reference for a display if a + * sync, otherwise this field is 0 + * nFrameWidth : Width of frame to be used on channel if + * uncompressed format is used. Use 0 for unknown, + * don't care or variable + * nFrameHeight : Height of frame to be used on channel if + * uncompressed format is used. Use 0 for unknown, + * don't care or variable + * nStride : Number of bytes per span of an image + * (i.e. indicates the number of bytes to get + * from span N to span N+1, where negative stride + * indicates the image is bottom up + * nSliceHeight : Height used when encoding in slices + * nBitrate : Bit rate of frame to be used on channel if + * compressed format is used. Use 0 for unknown, + * don't care or variable + * xFramerate : Frame rate to be used on channel if uncompressed + * format is used. Use 0 for unknown, don't care or + * variable. Units are Q16 frames per second. + * bFlagErrorConcealment : Turns on error concealment if it is supported by + * the OMX component + * eCompressionFormat : Compression format used in this instance of the + * component. When OMX_VIDEO_CodingUnused is + * specified, eColorFormat is used + * eColorFormat : Decompressed format used by this component + * pNativeWindow : Platform specific reference for a window object if a + * display sink , otherwise this field is 0x0. + */ +typedef struct OMX_VIDEO_PORTDEFINITIONTYPE { + OMX_STRING cMIMEType; + OMX_NATIVE_DEVICETYPE pNativeRender; + OMX_U32 nFrameWidth; + OMX_U32 nFrameHeight; + OMX_S32 nStride; + OMX_U32 nSliceHeight; + OMX_U32 nBitrate; + OMX_U32 xFramerate; + OMX_BOOL bFlagErrorConcealment; + OMX_VIDEO_CODINGTYPE eCompressionFormat; + OMX_COLOR_FORMATTYPE eColorFormat; + OMX_NATIVE_WINDOWTYPE pNativeWindow; +} OMX_VIDEO_PORTDEFINITIONTYPE; + +/** + * Port format parameter. This structure is used to enumerate the various + * data input/output format supported by the port. + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Indicates which port to set + * nIndex : Indicates the enumeration index for the format from + * 0x0 to N-1 + * eCompressionFormat : Compression format used in this instance of the + * component. When OMX_VIDEO_CodingUnused is specified, + * eColorFormat is used + * eColorFormat : Decompressed format used by this component + * xFrameRate : Indicates the video frame rate in Q16 format + */ +typedef struct OMX_VIDEO_PARAM_PORTFORMATTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nIndex; + OMX_VIDEO_CODINGTYPE eCompressionFormat; + OMX_COLOR_FORMATTYPE eColorFormat; + OMX_U32 xFramerate; +} OMX_VIDEO_PARAM_PORTFORMATTYPE; + + +/** + * This is a structure for configuring video compression quantization + * parameter values. Codecs may support different QP values for different + * frame types. + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version info + * nPortIndex : Port that this structure applies to + * nQpI : QP value to use for index frames + * nQpP : QP value to use for P frames + * nQpB : QP values to use for bidirectional frames + */ +typedef struct OMX_VIDEO_PARAM_QUANTIZATIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nQpI; + OMX_U32 nQpP; + OMX_U32 nQpB; +} OMX_VIDEO_PARAM_QUANTIZATIONTYPE; + + +/** + * Structure for configuration of video fast update parameters. + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version info + * nPortIndex : Port that this structure applies to + * bEnableVFU : Enable/Disable video fast update + * nFirstGOB : Specifies the number of the first macroblock row + * nFirstMB : specifies the first MB relative to the specified first GOB + * nNumMBs : Specifies the number of MBs to be refreshed from nFirstGOB + * and nFirstMB + */ +typedef struct OMX_VIDEO_PARAM_VIDEOFASTUPDATETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnableVFU; + OMX_U32 nFirstGOB; + OMX_U32 nFirstMB; + OMX_U32 nNumMBs; +} OMX_VIDEO_PARAM_VIDEOFASTUPDATETYPE; + + +/** + * Enumeration of possible bitrate control types + */ +typedef enum OMX_VIDEO_CONTROLRATETYPE { + OMX_Video_ControlRateDisable, + OMX_Video_ControlRateVariable, + OMX_Video_ControlRateConstant, + OMX_Video_ControlRateVariableSkipFrames, + OMX_Video_ControlRateConstantSkipFrames, + OMX_Video_ControlRateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_Video_ControlRateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_Video_ControlRateMax = 0x7FFFFFFF +} OMX_VIDEO_CONTROLRATETYPE; + + +/** + * Structure for configuring bitrate mode of a codec. + * + * STRUCT MEMBERS: + * nSize : Size of the struct in bytes + * nVersion : OMX spec version info + * nPortIndex : Port that this struct applies to + * eControlRate : Control rate type enum + * nTargetBitrate : Target bitrate to encode with + */ +typedef struct OMX_VIDEO_PARAM_BITRATETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_CONTROLRATETYPE eControlRate; + OMX_U32 nTargetBitrate; +} OMX_VIDEO_PARAM_BITRATETYPE; + + +/** + * Enumeration of possible motion vector (MV) types + */ +typedef enum OMX_VIDEO_MOTIONVECTORTYPE { + OMX_Video_MotionVectorPixel, + OMX_Video_MotionVectorHalfPel, + OMX_Video_MotionVectorQuarterPel, + OMX_Video_MotionVectorEighthPel, + OMX_Video_MotionVectorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_Video_MotionVectorVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_Video_MotionVectorMax = 0x7FFFFFFF +} OMX_VIDEO_MOTIONVECTORTYPE; + + +/** + * Structure for configuring the number of motion vectors used as well + * as their accuracy. + * + * STRUCT MEMBERS: + * nSize : Size of the struct in bytes + * nVersion : OMX spec version info + * nPortIndex : port that this structure applies to + * eAccuracy : Enumerated MV accuracy + * bUnrestrictedMVs : Allow unrestricted MVs + * bFourMV : Allow use of 4 MVs + * sXSearchRange : Search range in horizontal direction for MVs + * sYSearchRange : Search range in vertical direction for MVs + */ +typedef struct OMX_VIDEO_PARAM_MOTIONVECTORTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_MOTIONVECTORTYPE eAccuracy; + OMX_BOOL bUnrestrictedMVs; + OMX_BOOL bFourMV; + OMX_S32 sXSearchRange; + OMX_S32 sYSearchRange; +} OMX_VIDEO_PARAM_MOTIONVECTORTYPE; + + +/** + * Enumeration of possible methods to use for Intra Refresh + */ +typedef enum OMX_VIDEO_INTRAREFRESHTYPE { + OMX_VIDEO_IntraRefreshCyclic, /**< Cyclic intra refresh, bit 0 is set*/ + OMX_VIDEO_IntraRefreshAdaptive, /**< Adaptive intra refresh, bit 1 is set*/ + OMX_VIDEO_IntraRefreshBoth, /**< Cyclic + Adaptive intra refresh (no mrows since bit 2 is off)*/ + OMX_VIDEO_IntraRefreshKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_IntraRefreshVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_IntraRefreshCyclicMrows, /**< Cyclic intra refresh, multiple rows at a time bits 0 and 2 are set*/ + OMX_VIDEO_IntraRefreshPseudoRand, /**< Pseudo random intra refresh, uses bit 3*/ + OMX_VIDEO_IntraRefreshMax = 0x7FFFFFFF +} OMX_VIDEO_INTRAREFRESHTYPE; + + +/** + * Structure for configuring intra refresh mode + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eRefreshMode : Cyclic, Adaptive, or Both + * nAirMBs : Number of intra macroblocks to refresh in a frame when + * AIR is enabled + * nAirRef : Number of times a motion marked macroblock has to be + * intra coded + * nCirMBs : Number of consecutive macroblocks to be coded as "intra" + * when CIR is enabled + */ +typedef struct OMX_VIDEO_PARAM_INTRAREFRESHTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_INTRAREFRESHTYPE eRefreshMode; + OMX_U32 nAirMBs; + OMX_U32 nAirRef; + OMX_U32 nCirMBs; + OMX_U32 nPirMBs; +} OMX_VIDEO_PARAM_INTRAREFRESHTYPE; + + +/** + * Structure for enabling various error correction methods for video + * compression. + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * bEnableHEC : Enable/disable header extension codes (HEC) + * bEnableResync : Enable/disable resynchronization markers + * nResynchMarkerSpacing : Resynch markers interval (in bits) to be + * applied in the stream + * bEnableDataPartitioning : Enable/disable data partitioning + * bEnableRVLC : Enable/disable reversible variable length + * coding + */ +typedef struct OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnableHEC; + OMX_BOOL bEnableResync; + OMX_U32 nResynchMarkerSpacing; + OMX_BOOL bEnableDataPartitioning; + OMX_BOOL bEnableRVLC; +} OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE; + + +/** + * Configuration of variable block-size motion compensation (VBSMC) + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * b16x16 : Enable inter block search 16x16 + * b16x8 : Enable inter block search 16x8 + * b8x16 : Enable inter block search 8x16 + * b8x8 : Enable inter block search 8x8 + * b8x4 : Enable inter block search 8x4 + * b4x8 : Enable inter block search 4x8 + * b4x4 : Enable inter block search 4x4 + */ +typedef struct OMX_VIDEO_PARAM_VBSMCTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL b16x16; + OMX_BOOL b16x8; + OMX_BOOL b8x16; + OMX_BOOL b8x8; + OMX_BOOL b8x4; + OMX_BOOL b4x8; + OMX_BOOL b4x4; +} OMX_VIDEO_PARAM_VBSMCTYPE; + + +/** + * H.263 profile types, each profile indicates support for various + * performance bounds and different annexes. + * + * ENUMS: + * Baseline : Baseline Profile: H.263 (V1), no optional modes + * H320 Coding : H.320 Coding Efficiency Backward Compatibility + * Profile: H.263+ (V2), includes annexes I, J, L.4 + * and T + * BackwardCompatible : Backward Compatibility Profile: H.263 (V1), + * includes annex F + * ISWV2 : Interactive Streaming Wireless Profile: H.263+ + * (V2), includes annexes I, J, K and T + * ISWV3 : Interactive Streaming Wireless Profile: H.263++ + * (V3), includes profile 3 and annexes V and W.6.3.8 + * HighCompression : Conversational High Compression Profile: H.263++ + * (V3), includes profiles 1 & 2 and annexes D and U + * Internet : Conversational Internet Profile: H.263++ (V3), + * includes profile 5 and annex K + * Interlace : Conversational Interlace Profile: H.263++ (V3), + * includes profile 5 and annex W.6.3.11 + * HighLatency : High Latency Profile: H.263++ (V3), includes + * profile 6 and annexes O.1 and P.5 + */ +typedef enum OMX_VIDEO_H263PROFILETYPE { + OMX_VIDEO_H263ProfileBaseline = 0x01, + OMX_VIDEO_H263ProfileH320Coding = 0x02, + OMX_VIDEO_H263ProfileBackwardCompatible = 0x04, + OMX_VIDEO_H263ProfileISWV2 = 0x08, + OMX_VIDEO_H263ProfileISWV3 = 0x10, + OMX_VIDEO_H263ProfileHighCompression = 0x20, + OMX_VIDEO_H263ProfileInternet = 0x40, + OMX_VIDEO_H263ProfileInterlace = 0x80, + OMX_VIDEO_H263ProfileHighLatency = 0x100, + OMX_VIDEO_H263ProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_H263ProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_H263ProfileMax = 0x7FFFFFFF +} OMX_VIDEO_H263PROFILETYPE; + + +/** + * H.263 level types, each level indicates support for various frame sizes, + * bit rates, decoder frame rates. + */ +typedef enum OMX_VIDEO_H263LEVELTYPE { + OMX_VIDEO_H263Level10 = 0x01, + OMX_VIDEO_H263Level20 = 0x02, + OMX_VIDEO_H263Level30 = 0x04, + OMX_VIDEO_H263Level40 = 0x08, + OMX_VIDEO_H263Level45 = 0x10, + OMX_VIDEO_H263Level50 = 0x20, + OMX_VIDEO_H263Level60 = 0x40, + OMX_VIDEO_H263Level70 = 0x80, + OMX_VIDEO_H263LevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_H263LevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_H263LevelMax = 0x7FFFFFFF +} OMX_VIDEO_H263LEVELTYPE; + + +/** + * Specifies the picture type. These values should be OR'd to signal all + * pictures types which are allowed. + * + * ENUMS: + * Generic Picture Types: I, P and B + * H.263 Specific Picture Types: SI and SP + * H.264 Specific Picture Types: EI and EP + * MPEG-4 Specific Picture Types: S + */ +typedef enum OMX_VIDEO_PICTURETYPE { + OMX_VIDEO_PictureTypeI = 0x01, + OMX_VIDEO_PictureTypeP = 0x02, + OMX_VIDEO_PictureTypeB = 0x04, + OMX_VIDEO_PictureTypeSI = 0x08, + OMX_VIDEO_PictureTypeSP = 0x10, + OMX_VIDEO_PictureTypeEI = 0x11, + OMX_VIDEO_PictureTypeEP = 0x12, + OMX_VIDEO_PictureTypeS = 0x14, + OMX_VIDEO_PictureTypeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_PictureTypeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_PictureTypeMax = 0x7FFFFFFF +} OMX_VIDEO_PICTURETYPE; + + +/** + * H.263 Params + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nPFrames : Number of P frames between each I frame + * nBFrames : Number of B frames between each I frame + * eProfile : H.263 profile(s) to use + * eLevel : H.263 level(s) to use + * bPLUSPTYPEAllowed : Indicating that it is allowed to use PLUSPTYPE + * (specified in the 1998 version of H.263) to + * indicate custom picture sizes or clock + * frequencies + * nAllowedPictureTypes : Specifies the picture types allowed in the + * bitstream + * bForceRoundingTypeToZero : value of the RTYPE bit (bit 6 of MPPTYPE) is + * not constrained. It is recommended to change + * the value of the RTYPE bit for each reference + * picture in error-free communication + * nPictureHeaderRepetition : Specifies the frequency of picture header + * repetition + * nGOBHeaderInterval : Specifies the interval of non-empty GOB + * headers in units of GOBs + */ +typedef struct OMX_VIDEO_PARAM_H263TYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nPFrames; + OMX_U32 nBFrames; + OMX_VIDEO_H263PROFILETYPE eProfile; + OMX_VIDEO_H263LEVELTYPE eLevel; + OMX_BOOL bPLUSPTYPEAllowed; + OMX_U32 nAllowedPictureTypes; + OMX_BOOL bForceRoundingTypeToZero; + OMX_U32 nPictureHeaderRepetition; + OMX_U32 nGOBHeaderInterval; +} OMX_VIDEO_PARAM_H263TYPE; + + +/** + * MPEG-2 profile types, each profile indicates support for various + * performance bounds and different annexes. + */ +typedef enum OMX_VIDEO_MPEG2PROFILETYPE { + OMX_VIDEO_MPEG2ProfileSimple = 0, /**< Simple Profile */ + OMX_VIDEO_MPEG2ProfileMain, /**< Main Profile */ + OMX_VIDEO_MPEG2Profile422, /**< 4:2:2 Profile */ + OMX_VIDEO_MPEG2ProfileSNR, /**< SNR Profile */ + OMX_VIDEO_MPEG2ProfileSpatial, /**< Spatial Profile */ + OMX_VIDEO_MPEG2ProfileHigh, /**< High Profile */ + OMX_VIDEO_MPEG2ProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_MPEG2ProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_MPEG2ProfileMax = 0x7FFFFFFF +} OMX_VIDEO_MPEG2PROFILETYPE; + + +/** + * MPEG-2 level types, each level indicates support for various frame + * sizes, bit rates, decoder frame rates. No need + */ +typedef enum OMX_VIDEO_MPEG2LEVELTYPE { + OMX_VIDEO_MPEG2LevelLL = 0, /**< Low Level */ + OMX_VIDEO_MPEG2LevelML, /**< Main Level */ + OMX_VIDEO_MPEG2LevelH14, /**< High 1440 */ + OMX_VIDEO_MPEG2LevelHL, /**< High Level */ + OMX_VIDEO_MPEG2LevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_MPEG2LevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_MPEG2LevelMax = 0x7FFFFFFF +} OMX_VIDEO_MPEG2LEVELTYPE; + + +/** + * MPEG-2 params + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nPFrames : Number of P frames between each I frame + * nBFrames : Number of B frames between each I frame + * eProfile : MPEG-2 profile(s) to use + * eLevel : MPEG-2 levels(s) to use + */ +typedef struct OMX_VIDEO_PARAM_MPEG2TYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nPFrames; + OMX_U32 nBFrames; + OMX_VIDEO_MPEG2PROFILETYPE eProfile; + OMX_VIDEO_MPEG2LEVELTYPE eLevel; +} OMX_VIDEO_PARAM_MPEG2TYPE; + + +/** + * MPEG-4 profile types, each profile indicates support for various + * performance bounds and different annexes. + * + * ENUMS: + * - Simple Profile, Levels 1-3 + * - Simple Scalable Profile, Levels 1-2 + * - Core Profile, Levels 1-2 + * - Main Profile, Levels 2-4 + * - N-bit Profile, Level 2 + * - Scalable Texture Profile, Level 1 + * - Simple Face Animation Profile, Levels 1-2 + * - Simple Face and Body Animation (FBA) Profile, Levels 1-2 + * - Basic Animated Texture Profile, Levels 1-2 + * - Hybrid Profile, Levels 1-2 + * - Advanced Real Time Simple Profiles, Levels 1-4 + * - Core Scalable Profile, Levels 1-3 + * - Advanced Coding Efficiency Profile, Levels 1-4 + * - Advanced Core Profile, Levels 1-2 + * - Advanced Scalable Texture, Levels 2-3 + */ +typedef enum OMX_VIDEO_MPEG4PROFILETYPE { + OMX_VIDEO_MPEG4ProfileSimple = 0x01, + OMX_VIDEO_MPEG4ProfileSimpleScalable = 0x02, + OMX_VIDEO_MPEG4ProfileCore = 0x04, + OMX_VIDEO_MPEG4ProfileMain = 0x08, + OMX_VIDEO_MPEG4ProfileNbit = 0x10, + OMX_VIDEO_MPEG4ProfileScalableTexture = 0x20, + OMX_VIDEO_MPEG4ProfileSimpleFace = 0x40, + OMX_VIDEO_MPEG4ProfileSimpleFBA = 0x80, + OMX_VIDEO_MPEG4ProfileBasicAnimated = 0x100, + OMX_VIDEO_MPEG4ProfileHybrid = 0x200, + OMX_VIDEO_MPEG4ProfileAdvancedRealTime = 0x400, + OMX_VIDEO_MPEG4ProfileCoreScalable = 0x800, + OMX_VIDEO_MPEG4ProfileAdvancedCoding = 0x1000, + OMX_VIDEO_MPEG4ProfileAdvancedCore = 0x2000, + OMX_VIDEO_MPEG4ProfileAdvancedScalable = 0x4000, + OMX_VIDEO_MPEG4ProfileAdvancedSimple = 0x8000, + OMX_VIDEO_MPEG4ProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_MPEG4ProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_MPEG4ProfileMax = 0x7FFFFFFF +} OMX_VIDEO_MPEG4PROFILETYPE; + + +/** + * MPEG-4 level types, each level indicates support for various frame + * sizes, bit rates, decoder frame rates. No need + */ +typedef enum OMX_VIDEO_MPEG4LEVELTYPE { + OMX_VIDEO_MPEG4Level0 = 0x01, /**< Level 0 */ + OMX_VIDEO_MPEG4Level0b = 0x02, /**< Level 0b */ + OMX_VIDEO_MPEG4Level1 = 0x04, /**< Level 1 */ + OMX_VIDEO_MPEG4Level2 = 0x08, /**< Level 2 */ + OMX_VIDEO_MPEG4Level3 = 0x10, /**< Level 3 */ + OMX_VIDEO_MPEG4Level4 = 0x20, /**< Level 4 */ + OMX_VIDEO_MPEG4Level4a = 0x40, /**< Level 4a */ + OMX_VIDEO_MPEG4Level5 = 0x80, /**< Level 5 */ + OMX_VIDEO_MPEG4Level6 = 0x100, /**< Level 5 */ + OMX_VIDEO_MPEG4LevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_MPEG4LevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_MPEG4LevelMax = 0x7FFFFFFF +} OMX_VIDEO_MPEG4LEVELTYPE; + + +/** + * MPEG-4 configuration. This structure handles configuration options + * which are specific to MPEG4 algorithms + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nSliceHeaderSpacing : Number of macroblocks between slice header (H263+ + * Annex K). Put zero if not used + * bSVH : Enable Short Video Header mode + * bGov : Flag to enable GOV + * nPFrames : Number of P frames between each I frame (also called + * GOV period) + * nBFrames : Number of B frames between each I frame + * nIDCVLCThreshold : Value of intra DC VLC threshold + * bACPred : Flag to use ac prediction + * nMaxPacketSize : Maximum size of packet in bytes. + * nTimeIncRes : Used to pass VOP time increment resolution for MPEG4. + * Interpreted as described in MPEG4 standard. + * eProfile : MPEG-4 profile(s) to use. + * eLevel : MPEG-4 level(s) to use. + * nAllowedPictureTypes : Specifies the picture types allowed in the bitstream + * nHeaderExtension : Specifies the number of consecutive video packet + * headers within a VOP + * bReversibleVLC : Specifies whether reversible variable length coding + * is in use + */ +typedef struct OMX_VIDEO_PARAM_MPEG4TYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nSliceHeaderSpacing; + OMX_BOOL bSVH; + OMX_BOOL bGov; + OMX_U32 nPFrames; + OMX_U32 nBFrames; + OMX_U32 nIDCVLCThreshold; + OMX_BOOL bACPred; + OMX_U32 nMaxPacketSize; + OMX_U32 nTimeIncRes; + OMX_VIDEO_MPEG4PROFILETYPE eProfile; + OMX_VIDEO_MPEG4LEVELTYPE eLevel; + OMX_U32 nAllowedPictureTypes; + OMX_U32 nHeaderExtension; + OMX_BOOL bReversibleVLC; +} OMX_VIDEO_PARAM_MPEG4TYPE; + + +/** + * WMV Versions + */ +typedef enum OMX_VIDEO_WMVFORMATTYPE { + OMX_VIDEO_WMVFormatUnused = 0x01, /**< Format unused or unknown */ + OMX_VIDEO_WMVFormat7 = 0x02, /**< Windows Media Video format 7 */ + OMX_VIDEO_WMVFormat8 = 0x04, /**< Windows Media Video format 8 */ + OMX_VIDEO_WMVFormat9 = 0x08, /**< Windows Media Video format 9 */ + OMX_VIDEO_WMFFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_WMFFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_WMVFormatMax = 0x7FFFFFFF +} OMX_VIDEO_WMVFORMATTYPE; + + +/** + * WMV Params + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eFormat : Version of WMV stream / data + */ +typedef struct OMX_VIDEO_PARAM_WMVTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_WMVFORMATTYPE eFormat; +} OMX_VIDEO_PARAM_WMVTYPE; + + +/** + * Real Video Version + */ +typedef enum OMX_VIDEO_RVFORMATTYPE { + OMX_VIDEO_RVFormatUnused = 0, /**< Format unused or unknown */ + OMX_VIDEO_RVFormat8, /**< Real Video format 8 */ + OMX_VIDEO_RVFormat9, /**< Real Video format 9 */ + OMX_VIDEO_RVFormatG2, /**< Real Video Format G2 */ + OMX_VIDEO_RVFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_RVFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_RVFormatMax = 0x7FFFFFFF +} OMX_VIDEO_RVFORMATTYPE; + + +/** + * Real Video Params + * + * STUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * eFormat : Version of RV stream / data + * nBitsPerPixel : Bits per pixel coded in the frame + * nPaddedWidth : Padded width in pixel of a video frame + * nPaddedHeight : Padded Height in pixels of a video frame + * nFrameRate : Rate of video in frames per second + * nBitstreamFlags : Flags which internal information about the bitstream + * nBitstreamVersion : Bitstream version + * nMaxEncodeFrameSize: Max encoded frame size + * bEnablePostFilter : Turn on/off post filter + * bEnableTemporalInterpolation : Turn on/off temporal interpolation + * bEnableLatencyMode : When enabled, the decoder does not display a decoded + * frame until it has detected that no enhancement layer + * frames or dependent B frames will be coming. This + * detection usually occurs when a subsequent non-B + * frame is encountered + */ +typedef struct OMX_VIDEO_PARAM_RVTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_RVFORMATTYPE eFormat; + OMX_U16 nBitsPerPixel; + OMX_U16 nPaddedWidth; + OMX_U16 nPaddedHeight; + OMX_U32 nFrameRate; + OMX_U32 nBitstreamFlags; + OMX_U32 nBitstreamVersion; + OMX_U32 nMaxEncodeFrameSize; + OMX_BOOL bEnablePostFilter; + OMX_BOOL bEnableTemporalInterpolation; + OMX_BOOL bEnableLatencyMode; +} OMX_VIDEO_PARAM_RVTYPE; + + +/** + * AVC profile types, each profile indicates support for various + * performance bounds and different annexes. + */ +typedef enum OMX_VIDEO_AVCPROFILETYPE { + OMX_VIDEO_AVCProfileBaseline = 0x01, /**< Baseline profile */ + OMX_VIDEO_AVCProfileMain = 0x02, /**< Main profile */ + OMX_VIDEO_AVCProfileExtended = 0x04, /**< Extended profile */ + OMX_VIDEO_AVCProfileHigh = 0x08, /**< High profile */ + OMX_VIDEO_AVCProfileHigh10 = 0x10, /**< High 10 profile */ + OMX_VIDEO_AVCProfileHigh422 = 0x20, /**< High 4:2:2 profile */ + OMX_VIDEO_AVCProfileHigh444 = 0x40, /**< High 4:4:4 profile */ + OMX_VIDEO_AVCProfileConstrainedBaseline = 0x80, /**< Constrained Baseline Profile */ + OMX_VIDEO_AVCProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_AVCProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_AVCProfileMax = 0x7FFFFFFF +} OMX_VIDEO_AVCPROFILETYPE; + + +/** + * AVC level types, each level indicates support for various frame sizes, + * bit rates, decoder frame rates. No need + */ +typedef enum OMX_VIDEO_AVCLEVELTYPE { + OMX_VIDEO_AVCLevel1 = 0x01, /**< Level 1 */ + OMX_VIDEO_AVCLevel1b = 0x02, /**< Level 1b */ + OMX_VIDEO_AVCLevel11 = 0x04, /**< Level 1.1 */ + OMX_VIDEO_AVCLevel12 = 0x08, /**< Level 1.2 */ + OMX_VIDEO_AVCLevel13 = 0x10, /**< Level 1.3 */ + OMX_VIDEO_AVCLevel2 = 0x20, /**< Level 2 */ + OMX_VIDEO_AVCLevel21 = 0x40, /**< Level 2.1 */ + OMX_VIDEO_AVCLevel22 = 0x80, /**< Level 2.2 */ + OMX_VIDEO_AVCLevel3 = 0x100, /**< Level 3 */ + OMX_VIDEO_AVCLevel31 = 0x200, /**< Level 3.1 */ + OMX_VIDEO_AVCLevel32 = 0x400, /**< Level 3.2 */ + OMX_VIDEO_AVCLevel4 = 0x800, /**< Level 4 */ + OMX_VIDEO_AVCLevel41 = 0x1000, /**< Level 4.1 */ + OMX_VIDEO_AVCLevel42 = 0x2000, /**< Level 4.2 */ + OMX_VIDEO_AVCLevel5 = 0x4000, /**< Level 5 */ + OMX_VIDEO_AVCLevel51 = 0x8000, /**< Level 5.1 */ + OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF +} OMX_VIDEO_AVCLEVELTYPE; + + +/** + * AVC loop filter modes + * + * OMX_VIDEO_AVCLoopFilterEnable : Enable + * OMX_VIDEO_AVCLoopFilterDisable : Disable + * OMX_VIDEO_AVCLoopFilterDisableSliceBoundary : Disabled on slice boundaries + */ +typedef enum OMX_VIDEO_AVCLOOPFILTERTYPE { + OMX_VIDEO_AVCLoopFilterEnable = 0, + OMX_VIDEO_AVCLoopFilterDisable, + OMX_VIDEO_AVCLoopFilterDisableSliceBoundary, + OMX_VIDEO_AVCLoopFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_AVCLoopFilterVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_AVCLoopFilterMax = 0x7FFFFFFF +} OMX_VIDEO_AVCLOOPFILTERTYPE; + + +/** + * AVC params + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nSliceHeaderSpacing : Number of macroblocks between slice header, put + * zero if not used + * nPFrames : Number of P frames between each I frame + * nBFrames : Number of B frames between each I frame + * bUseHadamard : Enable/disable Hadamard transform + * nRefFrames : Max number of reference frames to use for inter + * motion search (1-16) + * nRefIdxTrailing : Pic param set ref frame index (index into ref + * frame buffer of trailing frames list), B frame + * support + * nRefIdxForward : Pic param set ref frame index (index into ref + * frame buffer of forward frames list), B frame + * support + * bEnableUEP : Enable/disable unequal error protection. This + * is only valid of data partitioning is enabled. + * bEnableFMO : Enable/disable flexible macroblock ordering + * bEnableASO : Enable/disable arbitrary slice ordering + * bEnableRS : Enable/disable sending of redundant slices + * eProfile : AVC profile(s) to use + * eLevel : AVC level(s) to use + * nAllowedPictureTypes : Specifies the picture types allowed in the + * bitstream + * bFrameMBsOnly : specifies that every coded picture of the + * coded video sequence is a coded frame + * containing only frame macroblocks + * bMBAFF : Enable/disable switching between frame and + * field macroblocks within a picture + * bEntropyCodingCABAC : Entropy decoding method to be applied for the + * syntax elements for which two descriptors appear + * in the syntax tables + * bWeightedPPrediction : Enable/disable weighted prediction shall not + * be applied to P and SP slices + * nWeightedBipredicitonMode : Default weighted prediction is applied to B + * slices + * bconstIpred : Enable/disable intra prediction + * bDirect8x8Inference : Specifies the method used in the derivation + * process for luma motion vectors for B_Skip, + * B_Direct_16x16 and B_Direct_8x8 as specified + * in subclause 8.4.1.2 of the AVC spec + * bDirectSpatialTemporal : Flag indicating spatial or temporal direct + * mode used in B slice coding (related to + * bDirect8x8Inference) . Spatial direct mode is + * more common and should be the default. + * nCabacInitIdx : Index used to init CABAC contexts + * eLoopFilterMode : Enable/disable loop filter + */ +typedef struct OMX_VIDEO_PARAM_AVCTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nSliceHeaderSpacing; + OMX_U32 nPFrames; + OMX_U32 nBFrames; + OMX_BOOL bUseHadamard; + OMX_U32 nRefFrames; + OMX_U32 nRefIdx10ActiveMinus1; + OMX_U32 nRefIdx11ActiveMinus1; + OMX_BOOL bEnableUEP; + OMX_BOOL bEnableFMO; + OMX_BOOL bEnableASO; + OMX_BOOL bEnableRS; + OMX_VIDEO_AVCPROFILETYPE eProfile; + OMX_VIDEO_AVCLEVELTYPE eLevel; + OMX_U32 nAllowedPictureTypes; + OMX_BOOL bFrameMBsOnly; + OMX_BOOL bMBAFF; + OMX_BOOL bEntropyCodingCABAC; + OMX_BOOL bWeightedPPrediction; + OMX_U32 nWeightedBipredicitonMode; + OMX_BOOL bconstIpred ; + OMX_BOOL bDirect8x8Inference; + OMX_BOOL bDirectSpatialTemporal; + OMX_U32 nCabacInitIdc; + OMX_VIDEO_AVCLOOPFILTERTYPE eLoopFilterMode; +} OMX_VIDEO_PARAM_AVCTYPE; + +typedef struct OMX_VIDEO_PARAM_PROFILELEVELTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 eProfile; /**< type is OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE, + or OMX_VIDEO_MPEG4PROFILETYPE depending on context */ + OMX_U32 eLevel; /**< type is OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE, + or OMX_VIDEO_MPEG4PROFILETYPE depending on context */ + OMX_U32 nProfileIndex; /**< Used to query for individual profile support information, + This parameter is valid only for + OMX_IndexParamVideoProfileLevelQuerySupported index, + For all other indices this parameter is to be ignored. */ +} OMX_VIDEO_PARAM_PROFILELEVELTYPE; + +/** + * Structure for dynamically configuring bitrate mode of a codec. + * + * STRUCT MEMBERS: + * nSize : Size of the struct in bytes + * nVersion : OMX spec version info + * nPortIndex : Port that this struct applies to + * nEncodeBitrate : Target average bitrate to be generated in bps + */ +typedef struct OMX_VIDEO_CONFIG_BITRATETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nEncodeBitrate; +} OMX_VIDEO_CONFIG_BITRATETYPE; + +/** + * Defines Encoder Frame Rate setting + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * xEncodeFramerate : Encoding framerate represented in Q16 format + */ +typedef struct OMX_CONFIG_FRAMERATETYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 xEncodeFramerate; /* Q16 format */ +} OMX_CONFIG_FRAMERATETYPE; + +typedef struct OMX_CONFIG_INTRAREFRESHVOPTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL IntraRefreshVOP; +} OMX_CONFIG_INTRAREFRESHVOPTYPE; + +typedef struct OMX_CONFIG_MACROBLOCKERRORMAPTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nErrMapSize; /* Size of the Error Map in bytes */ + OMX_U8 ErrMap[1]; /* Error map hint */ +} OMX_CONFIG_MACROBLOCKERRORMAPTYPE; + +typedef struct OMX_CONFIG_MBERRORREPORTINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnabled; +} OMX_CONFIG_MBERRORREPORTINGTYPE; + +typedef struct OMX_PARAM_MACROBLOCKSTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nMacroblocks; +} OMX_PARAM_MACROBLOCKSTYPE; + +/** + * AVC Slice Mode modes + * + * OMX_VIDEO_SLICEMODE_AVCDefault : Normal frame encoding, one slice per frame + * OMX_VIDEO_SLICEMODE_AVCMBSlice : NAL mode, number of MBs per frame + * OMX_VIDEO_SLICEMODE_AVCByteSlice : NAL mode, number of bytes per frame + */ +typedef enum OMX_VIDEO_AVCSLICEMODETYPE { + OMX_VIDEO_SLICEMODE_AVCDefault = 0, + OMX_VIDEO_SLICEMODE_AVCMBSlice, + OMX_VIDEO_SLICEMODE_AVCByteSlice, + OMX_VIDEO_SLICEMODE_AVCKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ + OMX_VIDEO_SLICEMODE_AVCVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ + OMX_VIDEO_SLICEMODE_AVCLevelMax = 0x7FFFFFFF +} OMX_VIDEO_AVCSLICEMODETYPE; + +/** + * AVC FMO Slice Mode Params + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nNumSliceGroups : Specifies the number of slice groups + * nSliceGroupMapType : Specifies the type of slice groups + * eSliceMode : Specifies the type of slice + */ +typedef struct OMX_VIDEO_PARAM_AVCSLICEFMO { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U8 nNumSliceGroups; + OMX_U8 nSliceGroupMapType; + OMX_VIDEO_AVCSLICEMODETYPE eSliceMode; +} OMX_VIDEO_PARAM_AVCSLICEFMO; + +/** + * AVC IDR Period Configs + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nIDRPeriod : Specifies periodicity of IDR frames + * nPFrames : Specifies internal of coding Intra frames + */ +typedef struct OMX_VIDEO_CONFIG_AVCINTRAPERIOD { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nIDRPeriod; + OMX_U32 nPFrames; +} OMX_VIDEO_CONFIG_AVCINTRAPERIOD; + +/** + * AVC NAL Size Configs + * + * STRUCT MEMBERS: + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to + * nNaluBytes : Specifies the NAL unit size + */ +typedef struct OMX_VIDEO_CONFIG_NALSIZE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_U32 nNaluBytes; +} OMX_VIDEO_CONFIG_NALSIZE; + + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif +/* File EOF */ + diff --git a/interface/vmcs_host/linux/vcfiled/CMakeLists.txt b/interface/vmcs_host/linux/vcfiled/CMakeLists.txt new file mode 100755 index 0000000..aed0e83 --- /dev/null +++ b/interface/vmcs_host/linux/vcfiled/CMakeLists.txt @@ -0,0 +1,33 @@ + +add_definitions(-Werror) + +# vcfiled - serves files to videocore. used for media handlers from +# OpenMAX/IL and loading VLLs. +add_executable(vcfiled vcfiled.c) + +# library to check if vcfiled is running or not +add_library(vcfiled_check vcfiled_check.c) + +target_link_libraries(vcfiled + vcfiled_check + vchostif + vchiq_arm + vcos) + +install(TARGETS vcfiled + RUNTIME DESTINATION sbin) + +configure_file (etc/init.d/vcfiled ${PROJECT_BINARY_DIR}/etc/init.d/vcfiled) + +# script to start up vcfiled at start of day +install(PROGRAMS ${PROJECT_BINARY_DIR}/etc/init.d/vcfiled + DESTINATION /etc/init.d) +# install locally to the installation directory too +install(PROGRAMS ${PROJECT_BINARY_DIR}/etc/init.d/vcfiled + DESTINATION ${VMCS_INSTALL_PREFIX}/share/install) + +# test program for vcfiled_check library +add_executable(vcfiled_lock_test vcfiled_lock_test.c) +target_link_libraries(vcfiled_lock_test vcfiled_check) + +install(TARGETS vcfiled_check DESTINATION lib) diff --git a/interface/vmcs_host/linux/vcfiled/etc/init.d/vcfiled b/interface/vmcs_host/linux/vcfiled/etc/init.d/vcfiled new file mode 100755 index 0000000..afe7cbc --- /dev/null +++ b/interface/vmcs_host/linux/vcfiled/etc/init.d/vcfiled @@ -0,0 +1,132 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: vcfiled +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Required-Start: udev +# Required-Stop: udev +# Short-Description: VideoCore file server daemon +### END INIT INFO + +# Author: Luke Diamand +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="VideoCore file server daemon" +NAME=vcfiled +VCROOT=${CMAKE_INSTALL_PREFIX} +DAEMON=$VCROOT/sbin/$NAME +DAEMON_ARGS="" +PIDFILE=/var/run/$NAME/$NAME +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/interface/vmcs_host/linux/vcfiled/vcfiled.c b/interface/vmcs_host/linux/vcfiled/vcfiled.c new file mode 100755 index 0000000..c856285 --- /dev/null +++ b/interface/vmcs_host/linux/vcfiled/vcfiled.c @@ -0,0 +1,220 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * @file + * + * Daemon serving files to VideoCore from the host filing system. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ANDROID +#include +#endif + +#include "vchiq.h" +#include "interface/vchi/vchi.h" +#include "interface/vcos/vcos.h" +#include "interface/vmcs_host/vc_vchi_filesys.h" +#include "vchost.h" +#include "vcfiled_check.h" + +static int log_stderr; /** Debug output to stderr? */ +static int foreground; /** Don't fork - run in foreground? */ +static const char *work_dir = "/"; /** Working directory */ +static const char *lock_prefix = ""; /** Lock file prefix */ +static int lock_prefix_set = 0; +static const char *progname; + +VCHI_INSTANCE_T global_initialise_instance; +VCHI_CONNECTION_T *global_connection; + +void vc_host_get_vchi_state(VCHI_INSTANCE_T *initialise_instance, VCHI_CONNECTION_T **connection) +{ + *initialise_instance = global_initialise_instance; + *connection = global_connection; +} + +static void logmsg(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if (log_stderr) + { + vfprintf(stderr, fmt, ap); + } + else + { +#ifdef ANDROID + __android_log_vprint(level, "vcfiled", fmt, ap); +#else + vsyslog(level | LOG_DAEMON, fmt, ap); +#endif + } + va_end(ap); +} + +static void usage(const char *progname) +{ + + fprintf(stderr,"usage: %s [-debug] [-foreground] [-root dir] [-lock prefix]\n", + progname); + fprintf(stderr," --debug - debug to stderr rather than syslog\n"); + fprintf(stderr," --foreground - do not fork, stay in foreground\n"); + fprintf(stderr," --root dir - chdir to this directory first\n"); + fprintf(stderr," --lock prefix - prefix to append to VCFILED_LOCKFILE\n"); +} + +enum { OPT_DEBUG, OPT_FG, OPT_ROOT, OPT_LOCK }; + +static void parse_args(int argc, char *const *argv) +{ + int finished = 0; + static struct option long_options[] = + { + {"debug", no_argument, &log_stderr, 1}, + {"foreground", no_argument, &foreground, 1}, + {"root", required_argument, NULL, OPT_ROOT}, + {"lock", required_argument, NULL, OPT_LOCK}, + {0, 0, 0, 0}, + }; + while (!finished) + { + int option_index = 0; + int c = getopt_long_only(argc, argv, "", long_options, &option_index); + + switch (c) + { + case 0: + // debug or foreground options, just sets flags directly + break; + case OPT_ROOT: + work_dir = optarg; + // sanity check directory + if (chdir(work_dir) < 0) + { + fprintf(stderr,"cannot chdir to %s: %s\n", work_dir, strerror(errno)); + exit(-1); + } + break; + case OPT_LOCK: + lock_prefix = optarg; + lock_prefix_set = 1; + break; + + default: + case '?': + usage(argv[0]); + exit(-1); + break; + + case -1: + finished = 1; + break; + } + + } +} + +int main(int argc, char *const *argv) +{ + VCHIQ_INSTANCE_T vchiq_instance; + int success; + char lockfile[128]; + + progname = argv[0]; + const char *sep = strrchr(progname, '/'); + if (sep) + progname = sep+1; + + parse_args(argc, argv); + + if (!foreground) + { + if ( daemon( 0, 1 ) != 0 ) + { + fprintf( stderr, "Failed to daemonize: errno = %d", errno ); + return -1; + } + log_stderr = 0; + } + if ( lock_prefix_set ) + { + vcos_safe_sprintf( lockfile, sizeof(lockfile), 0, "%s/%s", lock_prefix, VCFILED_LOCKFILE ); + } + else + { + vcos_safe_sprintf( lockfile, sizeof(lockfile), 0, "%s", VCFILED_LOCKFILE ); + } + success = vcfiled_lock(lockfile, logmsg); + if (success != 0) + { + return -1; + } + + logmsg( LOG_INFO, "vcfiled started" ); + + vcos_init(); + + if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) + { + logmsg(LOG_ERR, "%s: failed to open vchiq instance\n", argv[0]); + return -1; + } + + success = vchi_initialise( &global_initialise_instance); + vcos_assert(success == 0); + vchiq_instance = (VCHIQ_INSTANCE_T)global_initialise_instance; + + global_connection = vchi_create_connection(single_get_func_table(), + vchi_mphi_message_driver_func_table()); + + vchi_connect(&global_connection, 1, global_initialise_instance); + + vc_vchi_filesys_init (global_initialise_instance, &global_connection, 1); + + for (;;) + { + sleep(INT_MAX); + } + + vcos_deinit (); + + return 0; +} + diff --git a/interface/vmcs_host/linux/vcfiled/vcfiled_check.c b/interface/vmcs_host/linux/vcfiled/vcfiled_check.c new file mode 100755 index 0000000..35e0899 --- /dev/null +++ b/interface/vmcs_host/linux/vcfiled/vcfiled_check.c @@ -0,0 +1,169 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "vcfiled_check.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int vcfiled_lock(const char *lockfile, VCFILED_LOGMSG_T logmsg) +{ + int rc, fd; + char pidbuf[32]; + char *lockdir = strdup(lockfile); + char *sep = strrchr(lockdir, '/'); + int ret = -1; + if (!sep) + { + free(lockdir); + return -1; + } + *sep = '\0'; + + if (mkdir(lockdir, S_IRWXU | S_IRGRP|S_IXGRP) < 0) + { + if (errno != EEXIST) + { + logmsg(LOG_CRIT, "could not create %s:%s\n", lockdir,strerror(errno)); + goto finish; + } + } + fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, 0640); + if (fd<0) + { + if (errno != EEXIST) + { + logmsg(LOG_CRIT, "could not create lockfile %s:%s\n", lockfile, strerror(errno)); + goto finish; + } + else + { + // it already exists - reopen it and try to lock it + fd = open(lockfile, O_RDWR); + if (fd<0) + { + logmsg(LOG_CRIT, "could not re-open lockfile %s:%s\n", lockfile, strerror(errno)); + goto finish; + } + } + } + // at this point, we have opened the file, and can use discretionary locking, + // which should work even over NFS + + struct flock flock; + memset(&flock, 0, sizeof(flock)); + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + if (fcntl(fd, F_SETLK, &flock) < 0) + { + // if we failed to lock, then it might mean we're already running, or + // something else bad. + if (errno == EACCES || errno == EAGAIN) + { + // already running + int pid = 0, rc = read(fd, pidbuf, sizeof(pidbuf)); + if (rc) + pid = atoi(pidbuf); + logmsg(LOG_CRIT, "already running at pid %d\n", pid); + close(fd); + goto finish; + } + else + { + logmsg(LOG_CRIT, "could not lock %s:%s\n", lockfile, strerror(errno)); + close(fd); + goto finish; + } + } + snprintf(pidbuf,sizeof(pidbuf),"%d", getpid()); + rc = write(fd, pidbuf, strlen(pidbuf)+1); + if (rc<0) + { + logmsg(LOG_CRIT, "could not write pid:%s\n", strerror(errno)); + goto finish; + } + /* do not close the file, as that will release the lock - it will + * will close automatically when the program exits. + */ + ret = 0; +finish: + free(lockdir); + /* coverity[leaked_handle] - fd left open on purpose */ + return ret; +} + +int vcfiled_is_running(const char *filename) +{ + int ret; + + int fd = open(filename, O_RDONLY); + if (fd < 0) + { + // file not there, so filed not running + ret = 0; + } + + else + { + struct flock flock; + memset(&flock, 0, sizeof(flock)); + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + int rc = fcntl(fd, F_GETLK, &flock); + if (rc != 0) + { + /* could not access lock info */ + printf("%s: Could not access lockfile %s: %s\n", + "vmcs_main", filename, strerror(errno)); + ret = 0; + } + else if (flock.l_pid == 0) + { + /* file is unlocked, so filed not running */ + ret = 0; + } + else + { + /* file is locked, so filed is running */ + ret = 1; + } + } + /* coverity[leaked_handle] - fd left open on purpose */ + return ret; +} + + + diff --git a/interface/vmcs_host/linux/vcfiled/vcfiled_check.h b/interface/vmcs_host/linux/vcfiled/vcfiled_check.h new file mode 100755 index 0000000..360f50a --- /dev/null +++ b/interface/vmcs_host/linux/vcfiled/vcfiled_check.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCFILED_CHECK_H +#define VCFILED_CHECK_H + +#ifdef ANDROID +#define VCFILED_LOCKDIR "/tmp/vcfiled/vcfiled.pid" +#define VCFILED_LOCKFILE "/tmp/vcfiled" +#endif + +#ifndef VCFILED_LOCKFILE +#define VCFILED_LOCKDIR "/var/run/vcfiled" +#define VCFILED_LOCKFILE VCFILED_LOCKDIR "/vcfiled" +#endif + +typedef void (*VCFILED_LOGMSG_T)(int level, const char *fmt, ...); +int vcfiled_lock(const char *filename, VCFILED_LOGMSG_T logmsg); +extern int vcfiled_is_running(const char *lockfile); + + +#endif + + diff --git a/interface/vmcs_host/linux/vcfiled/vcfiled_lock_test.c b/interface/vmcs_host/linux/vcfiled/vcfiled_lock_test.c new file mode 100755 index 0000000..28e83ca --- /dev/null +++ b/interface/vmcs_host/linux/vcfiled/vcfiled_lock_test.c @@ -0,0 +1,82 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** Very simple test code for the vcfiled locking + */ +#include "vcfiled_check.h" +#include +#include +#include +#include +#include + +static void usage(const char *prog) +{ + fprintf(stderr, "usage: %s lock|check \n", prog); + exit(1); +} + +static void logmsg(int level, const char *fmt, ...) +{ + (void)level; + + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +int main(int argc, const char **argv) +{ + if (argc != 3) + { + usage(argv[0]); + } + const char *lockfile = argv[2]; + if (strcmp(argv[1], "lock") == 0) + { + int rc = vcfiled_lock(lockfile, logmsg); + if (rc) + { + printf("failed to lock %s\n", lockfile); + exit(1); + } + sleep(300); + } + else if (strcmp(argv[1], "check") == 0) + { + printf("%s\n", + vcfiled_is_running(lockfile) ? + "running" : "not running"); + } + else + { + usage(argv[0]); + } + return 0; +} + diff --git a/interface/vmcs_host/linux/vcfilesys.c b/interface/vmcs_host/linux/vcfilesys.c new file mode 100755 index 0000000..9f3ebf1 --- /dev/null +++ b/interface/vmcs_host/linux/vcfilesys.c @@ -0,0 +1,1131 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define VCOS_LOG_CATEGORY (&hostfs_log_cat) + +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#define _FILE_OFFSET_BITS 64 /* So we get lseek and lseek64 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GLIBC__) && !defined( __USE_FILE_OFFSET64 ) +#error "__USE_FILE_OFFSET64 isn't defined" +#endif + +#include "interface/vcos/vcos.h" + +/* Some hackery to prevent a clash with the Linux type of the same name */ +#define dirent fs_dirent +#include "vcfilesys_defs.h" +#include "vchost.h" +#undef dirent + +#include + +#include "vc_fileservice_defs.h" + +VCOS_LOG_CAT_T hostfs_log_cat; + +/****************************************************************************** +Global data. +******************************************************************************/ + +/****************************************************************************** +Local types and defines. +******************************************************************************/ + +//#define DEBUG_LEVEL 1 +#define DEBUG_MINOR(...) vcos_log_info(__VA_ARGS__) +#define DEBUG_MAJOR(...) vcos_log_warn(__VA_ARGS__) + +/* Define a wrapper for the native directory handle which includes the path + * to that directory (needed to retrieve size and attributes via stat()). + */ + +struct fs_dir +{ + DIR *dhandle; + int pathlen; + char pathbuf[PATH_MAX]; +}; + +/* + * The media player on the Videocore may be asked to open a file on the Host that + * is in fact a FIFO. We need to note when a FIFO has been opened so that we + * can fake out some FIFO seeks that the Videocore may perform, hence the following + * types and variables. + */ + +typedef struct +{ + int is_fifo; // non-zero if file is a FIFO + uint64_t read_offset; // read offset into file +} file_info_t; + +#define FILE_INFO_TABLE_CHUNK_LEN 20 + +/****************************************************************************** +Static data. +******************************************************************************/ + +static file_info_t *p_file_info_table = NULL; +static int file_info_table_len = 0; + +/****************************************************************************** +Static functions. +******************************************************************************/ + +static void backslash_to_slash( char *s ); + +/****************************************************************************** +Global functions. +******************************************************************************/ + +/****************************************************************************** +NAME + vc_hostfs_init + +SYNOPSIS + void vc_hostfs_init(void) + +FUNCTION + Initialises the host to accept requests from Videocore + +RETURNS + void +******************************************************************************/ + +void vc_hostfs_init(void) +{ + // This hostfs module is not thread safe - it allocaes a block + // of memory and uses it without any kind of locking. + // + // It offers no advantage of stdio, and so most clients should + // not use it. Arguably FILESYS should use it in order to get + // the FIFO support. + + const char *thread_name = vcos_thread_get_name(vcos_thread_current()); + if (strcmp(thread_name, "FILESYS") != 0 && strcmp(thread_name, "HFilesys") != 0) + { + fprintf(stderr,"%s: vc_hostfs is deprecated. Please use stdio\n", + vcos_thread_get_name(vcos_thread_current())); + } + + vcos_log_register("hostfs", &hostfs_log_cat); + DEBUG_MINOR("init"); + // Allocate memory for the file info table + p_file_info_table = (file_info_t *)calloc( FILE_INFO_TABLE_CHUNK_LEN, sizeof( file_info_t ) ); + assert( p_file_info_table != NULL ); + if (p_file_info_table) + { + file_info_table_len = FILE_INFO_TABLE_CHUNK_LEN; + } +} + +/** Terminate this library. Clean up resources. + */ + +void vc_hostfs_exit(void) +{ + vcos_log_unregister(&hostfs_log_cat); + if (p_file_info_table) + { + free(p_file_info_table); + p_file_info_table = NULL; + } +} + +/****************************************************************************** +NAME + vc_hostfs_close + +SYNOPSIS + int vc_hostfs_close(int fildes) + +FUNCTION + Deallocates the file descriptor to a file. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_close(int fildes) +{ + DEBUG_MINOR("vc_hostfs_close(%d)", fildes); + return close(fildes); +} + +/****************************************************************************** +NAME + vc_hostfs_lseek + +SYNOPSIS + long vc_hostfs_lseek(int fildes, long offset, int whence) + +FUNCTION + Sets the file pointer associated with the open file specified by fildes. If + the file is a FIFO (Linux does not support seeking on a FIFO) then, for the + benefit of the Videocore streaming file handlers which do a number of null seeks, + that is, seeks to the current position, the return value is faked without an + actual seek being done. + +RETURNS + Successful completion: offset + Otherwise: -1 +******************************************************************************/ + +long vc_hostfs_lseek(int fildes, long offset, int whence) +{ + return (long) vc_hostfs_lseek64( fildes, (int64_t) offset, whence); +} + + +/****************************************************************************** +NAME + vc_hostfs_lseek64 + +SYNOPSIS + int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence) + +FUNCTION + Sets the file pointer associated with the open file specified by fildes. If + the file is a FIFO (Linux does not support seeking on a FIFO) then, for the + benefit of the Videocore streaming file handlers which do a number of null seeks, + that is, seeks to the current position, the return value is faked without an + actual seek being done. + +RETURNS + Successful completion: offset + Otherwise: -1 +******************************************************************************/ + +int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence) +{ + DEBUG_MINOR("vc_hostfs_lseek(%d,%" PRId64 ",%d)", fildes, offset, whence); + if (fildes >= file_info_table_len) + { + // File descriptor not in table, so this is an error + DEBUG_MAJOR("vc_hostfs_lseek: invalid fildes %d", fildes); + return -1; + } + else + { + // There is entry in the file info table for this file descriptor, so go + // ahead and handle the seek + int64_t read_offset = p_file_info_table[fildes].read_offset; + + if (p_file_info_table[fildes].is_fifo) + { + // The Videocore is attempting to seek on a FIFO. FIFOs don't support seeking + // but, for the benefit of certain Videocore "streaming" file handlers, we + // will fake limited FIFO seek functionality by computing where a seek + // would take us to + if (whence == SEEK_SET) + { + read_offset = offset; + } + else if (whence == SEEK_CUR) + { + read_offset += offset; + } + else + { + // seeking to the end of FIFO makes no sense, so this is an error + DEBUG_MAJOR("vc_hostfs_lseek(%d,%lld,%d): SEEK_END not supported on FIFO", fildes, (long long)offset, whence); + return -1; + } + } + else + { + // File is not a FIFO, so do the seek + read_offset = lseek64(fildes, offset, whence); + } + p_file_info_table[fildes].read_offset = read_offset; + DEBUG_MINOR("vc_hostfs_lseek returning %" PRId64 ")", read_offset); + return read_offset; + } +} + + + + +/****************************************************************************** +NAME + vc_hostfs_open + +SYNOPSIS + int vc_hostfs_open(const char *path, int vc_oflag) + +FUNCTION + Establishes a connection between a file and a file descriptor. For the benefit + of faking out seeks on a FIFO, we will need to keep track of the read offset for + all reads, and to facilitate this each opened file is given an entry in a local + file info table. + +RETURNS + Successful completion: file descriptor + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_open(const char *inPath, int vc_oflag) +{ + char *path = strdup( inPath ); + //char *s; + int flags = 0, ret=errno; + struct stat fileStat; + + // Replace all '\' with '/' + backslash_to_slash( path ); + +#if 0 + s = path + strlen( path ); + if (( s - path ) >= 4 ) + { + if ( strcasecmp( &s[ -4 ], ".vll" ) == 0 ) + { + // The Videocore is asking for a .vll file. Since it isn't consistent with + // the case, we convert .vll files to all lowercase. + "vc_hostfs_open: '%s'", path ; + + s--; // backup to the last character (*s is on the '\0') + while (( s >= path ) && ( *s != '/' )) + { + *s = tolower( *s ); + s--; + } + } + } +#endif + DEBUG_MINOR("vc_hostfs_open: '%s'", path); + + flags = O_RDONLY; + if (vc_oflag & VC_O_WRONLY) flags = O_WRONLY; + if (vc_oflag & VC_O_RDWR) flags = O_RDWR; + if (vc_oflag & VC_O_APPEND) flags |= O_APPEND; + if (vc_oflag & VC_O_CREAT) flags |= O_CREAT; + if (vc_oflag & VC_O_TRUNC) flags |= O_TRUNC; + if (vc_oflag & VC_O_EXCL) flags |= O_EXCL; + + //while (*path == '\\') path++; // do not want initial '\' + if (flags & O_CREAT) + ret = open(path, flags, S_IRUSR | S_IWUSR ); + else + ret = open(path, flags ); + + if (ret < 0 ) + { + DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret); + } + else + { + DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret); + } + + // If the file was successfully open then initialize its entry in + // the file info table. If necessary, we expand the size of the table + if (ret >= 0) + { + // File was successfully opened + if (ret >= file_info_table_len) + { + file_info_t *p_new_file_info_table = p_file_info_table; + int new_file_info_table_len = file_info_table_len; + + // try and allocate a bigger buffer for the file info table + new_file_info_table_len += FILE_INFO_TABLE_CHUNK_LEN; + p_new_file_info_table = calloc( (size_t)new_file_info_table_len, sizeof( file_info_t ) ); + if (p_new_file_info_table == NULL) + { + // calloc failed + DEBUG_MAJOR("vc_hostfs_open: file_info_table calloc failed"); + assert( 0 ); + } + else + { + // calloc successful, so copy data from previous buffer to new buffer, + // free previous buffer and update ptr and len info + memcpy( p_new_file_info_table, p_file_info_table, sizeof( file_info_t ) * file_info_table_len ); + free( p_file_info_table ); + p_file_info_table = p_new_file_info_table; + file_info_table_len = new_file_info_table_len; + } + } + assert( ret < file_info_table_len ); + { + // initialize this file's entry in the file info table + p_file_info_table[ret].is_fifo = 0; + p_file_info_table[ret].read_offset = 0; + } + + // Check whether the file is a FIFO. A FIFO does not support seeking + // but we will fake, to the extent supported by the buffered file system + // on the Videocore, limited FIFO seek functionality. This is for the benefit + // of certain Videocore "streaming" file handlers. + if (fstat( ret, &fileStat ) != 0) + { + DEBUG_MINOR("vc_hostfs_open: fstat failed: %s", strerror(errno)); + } + else if (S_ISFIFO( fileStat.st_mode )) + { + // file is a FIFO, so note its fildes for future reference + p_file_info_table[ret].is_fifo = 1; + DEBUG_MINOR("vc_hostfs_open: file with fildes %d is a FIFO", ret); + } + } + + free( path ); + + return ret; +} + +/****************************************************************************** +NAME + vc_hostfs_read + +SYNOPSIS + int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte) + +FUNCTION + Attempts to read nbyte bytes from the file associated with the file + descriptor, fildes, into the buffer pointed to by buf. For the benefit + of faking out seeks on a FIFO, we keep track of the read offset for all + reads. + +RETURNS + Successful completion: number of bytes read + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte) +{ + if (fildes >= file_info_table_len) + { + // File descriptor not in table, so this is an error + DEBUG_MAJOR("vc_hostfs_read(%d,%p,%u): invalid fildes", fildes, buf, nbyte); + return -1; + } + else + { + // There is entry in the file info table for this file descriptor, so go + // ahead and handle the read + int ret = (int) read(fildes, buf, nbyte); + DEBUG_MINOR("vc_hostfs_read(%d,%p,%u) = %d", fildes, buf, nbyte, ret); + if (ret > 0) + { + p_file_info_table[fildes].read_offset += (long) ret; + } + return ret; + } +} + +/****************************************************************************** +NAME + vc_hostfs_write + +SYNOPSIS + int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte) + +FUNCTION + Attempts to write nbyte bytes from the buffer pointed to by buf to file + associated with the file descriptor, fildes. + +RETURNS + Successful completion: number of bytes written + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte) +{ + int ret = (int) write(fildes, buf, nbyte); + DEBUG_MINOR("vc_hostfs_write(%d,%p,%u) = %d", fildes, buf, nbyte, ret); + return ret; +} + +/****************************************************************************** +NAME + vc_hostfs_closedir + +SYNOPSIS + int vc_hostfs_closedir(void *dhandle) + +FUNCTION + Ends a directory list iteration. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_closedir(void *dhandle) +{ + struct fs_dir *fsdir = (struct fs_dir *)dhandle; + int ret = -1; + + DEBUG_MINOR( "vc_hostfs_closedir(%p)", dhandle ); + + if (dhandle && fsdir->dhandle) + { + (void)closedir(fsdir->dhandle); + fsdir->dhandle = NULL; + free(fsdir); + ret = 0; + } + + return ret; +} + +/****************************************************************************** +NAME + vc_hostfs_format + +SYNOPSIS + int vc_hostfs_format(const char *path) + +FUNCTION + Formats the physical file system that contains path. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_format(const char *path) +{ + DEBUG_MINOR("vc_hostfs_format: '%s' not implemented", path); + return -1; +} + +/****************************************************************************** +NAME + vc_hostfs_freespace + +SYNOPSIS + int vc_hostfs_freespace(const char *path) + +FUNCTION + Returns the amount of free space on the physical file system that contains + path. + +RETURNS + Successful completion: free space + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_freespace(const char *inPath) +{ + int ret; + + int64_t freeSpace = vc_hostfs_freespace64( inPath ); + + // Saturate return value (need this in case we have a large file system) + if (freeSpace > (int64_t) INT_MAX) + { + ret = INT_MAX; + } + else + { + ret = (int) freeSpace; + } + + return ret; +} + + + + + +/****************************************************************************** +NAME + vc_hostfs_freespace + +SYNOPSIS + int vc_hostfs_freespace(const char *path) + +FUNCTION + Returns the amount of free space on the physical file system that contains + path. + +RETURNS + Successful completion: free space + Otherwise: -1 +******************************************************************************/ +int64_t vc_hostfs_freespace64(const char *inPath) +{ + char *path = strdup( inPath ); + int64_t ret; + struct statfs fsStat; + + // Replace all '\' with '/' + backslash_to_slash( path ); + + ret = (int64_t) statfs( path, &fsStat ); + + if (ret == 0) + { + ret = fsStat.f_bsize * fsStat.f_bavail; + } + else + { + ret = -1; + } + + DEBUG_MINOR( "vc_hostfs_freespace64 for '%s' returning %" PRId64 "", path, ret ); + + free( path ); + return ret; +} + + +/****************************************************************************** +NAME + vc_hostfs_get_attr + +SYNOPSIS + int vc_hostfs_get_attr(const char *path, fattributes_t *attr) + +FUNCTION + Gets the file/directory attributes. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_get_attr(const char *path, fattributes_t *attr) +{ + struct stat sb; + + DEBUG_MINOR("vc_hostfs_get_attr: '%s'", path ); + + + *attr = 0; + + if ( stat( path, &sb ) == 0 ) + { + if ( S_ISDIR( sb.st_mode )) + { + *attr |= ATTR_DIRENT; + } + + if (( sb.st_mode & S_IWUSR ) == 0 ) + { + *attr |= ATTR_RDONLY; + } + + return 0; + } + return -1; +} + +/****************************************************************************** +NAME + vc_hostfs_mkdir + +SYNOPSIS + int vc_hostfs_mkdir(const char *path) + +FUNCTION + Creates a new directory named by the pathname pointed to by path. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_mkdir(const char *path) +{ + DEBUG_MINOR( "vc_hostfs_mkdir: '%s'", path ); + if ( mkdir( path, 0777 ) == 0 ) + { + return 0; + } + return -1; +} + +/****************************************************************************** +NAME + vc_hostfs_opendir + +SYNOPSIS + void *vc_hostfs_opendir(const char *dirname) + +FUNCTION + Starts a directory list iteration of sub-directories. + +RETURNS + Successful completion: dhandle (pointer) + Otherwise: NULL +******************************************************************************/ + +void *vc_hostfs_opendir(const char *dirname) +{ + struct fs_dir *fsdir = NULL; + + DEBUG_MINOR( "vc_hostfs_opendir: '%s'", dirname ); + + if (dirname && dirname[0]) + { + fsdir = (struct fs_dir *)malloc(sizeof(struct fs_dir)); + + if (fsdir) + { + DIR *dhandle; + int len = strlen(dirname); + + memcpy(fsdir->pathbuf, dirname, len); + + backslash_to_slash(fsdir->pathbuf); + + /* Remove any trailing slashes */ + while (fsdir->pathbuf[len - 1] == '/') + len--; + + fsdir->pathbuf[len] = '\0'; + + dhandle = opendir(fsdir->pathbuf); + DEBUG_MINOR( "opendir: '%s' = %p", fsdir->pathbuf, dhandle ); + + if (dhandle) + { + fsdir->pathlen = len; + fsdir->dhandle = dhandle; + } + else + { + free(fsdir); + fsdir = NULL; + } + } + } + + return fsdir; +} + +/****************************************************************************** +NAME + vc_hostfs_readdir_r + +SYNOPSIS + struct dirent *vc_hostfs_readdir_r(void *dhandle, struct dirent *result) + +FUNCTION + Fills in the passed result structure with details of the directory entry + at the current psition in the directory stream specified by the argument + dhandle, and positions the directory stream at the next entry. If the last + sub-directory has been reached it ends the iteration and begins a new one + for files in the directory. + +RETURNS + Successful completion: result + End of directory stream: NULL +******************************************************************************/ + +struct fs_dirent *vc_hostfs_readdir_r(void *dhandle, struct fs_dirent *result) +{ + struct fs_dir *fsdir = (struct fs_dir *)dhandle; + + DEBUG_MINOR( "vc_hostfs_readdir_r(%p)", fsdir ); + + if (fsdir && result) + { + struct dirent *dent; + + while ((dent = readdir(fsdir->dhandle)) != NULL) + { + struct stat statbuf; + int ret; + + /* Append the filename, and stat the resulting path */ + fsdir->pathbuf[fsdir->pathlen] = '/'; + vcos_safe_strcpy(fsdir->pathbuf, dent->d_name, sizeof(fsdir->pathbuf), fsdir->pathlen + 1); + ret = stat(fsdir->pathbuf, &statbuf); + fsdir->pathbuf[fsdir->pathlen] = '\0'; + + if (ret == 0) + { + vcos_safe_strcpy(result->d_name, dent->d_name, sizeof(result->d_name), 0); + result->d_size = (statbuf.st_size <= 0xffffffff) ? (unsigned int)statbuf.st_size : 0xffffffff; + result->d_attrib = ATTR_NORMAL; + if ((statbuf.st_mode & S_IWUSR) == 0) + result->d_attrib |= ATTR_RDONLY; + if (statbuf.st_mode & S_IFDIR) + result->d_attrib |= ATTR_DIRENT; + result->d_creatime = statbuf.st_ctime; + result->d_modtime = statbuf.st_mtime; + DEBUG_MINOR( "vc_hostfs_readdir_r() = '%s', %x, %x", result->d_name, result->d_size, result->d_attrib ); + break; + } + } + + if (!dent) + { + DEBUG_MINOR( "vc_hostfs_readdir_r() = NULL" ); + rewinddir(fsdir->dhandle); + result = NULL; + } + } + else + { + result = NULL; + } + + return result; +} + +/****************************************************************************** +NAME + vc_hostfs_remove + +SYNOPSIS + int vc_hostfs_remove(const char *path) + +FUNCTION + Removes a file or a directory. A directory must be empty before it can be + deleted. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_remove(const char *path) +{ + char *pathbuf = strdup(path); + int ret = -1; + + DEBUG_MINOR( "vc_hostfs_remove: '%s'", path ); + + if (pathbuf) + { + backslash_to_slash(pathbuf); + + if ( unlink( pathbuf ) == 0 ) + ret = 0; + } + + free(pathbuf); + + return ret; +} + +/****************************************************************************** +NAME + vc_hostfs_rename + +SYNOPSIS + int vc_hostfs_rename(const char *old, const char *new) + +FUNCTION + Changes the name of a file. The old and new pathnames must be on the same + physical file system. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_rename(const char *old, const char *new) +{ + char *oldbuf = strdup(old); + char *newbuf = strdup(new); + int ret = -1; + + DEBUG_MINOR( "vc_hostfs_rename: '%s' to '%s'", old, new ); + + if (oldbuf && newbuf) + { + backslash_to_slash(oldbuf); + backslash_to_slash(newbuf); + + if ( rename( oldbuf, newbuf ) == 0 ) + ret = 0; + } + + if (oldbuf) + free(oldbuf); + + if (newbuf) + free(newbuf); + + return ret; +} + +/****************************************************************************** +NAME + vc_hostfs_set_attr + +SYNOPSIS + int vc_hostfs_set_attr(const char *path, fattributes_t attr) + +FUNCTION + Sets file/directory attributes. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_set_attr(const char *path, fattributes_t attr) +{ + char *pathbuf = strdup(path); + int ret = -1; + + DEBUG_MINOR( "vc_hostfs_set_attr: '%s', %x", path, attr ); + + if (pathbuf) + { + mode_t mode = 0; + struct stat sb; + + backslash_to_slash(pathbuf); + + if ( stat( path, &sb ) == 0 ) + { + mode = sb.st_mode; + + if ( attr & ATTR_RDONLY ) + { + mode &= ~S_IWUSR; + } + else + { + mode |= S_IWUSR; + } + + /* coverity[toctou] Not doing anything security-relevant here, + * so the race condition is harmless */ + if ( chmod( path, mode ) == 0 ) + ret = 0; + } + } + + if (pathbuf) + free(pathbuf); + + return ret; +} + +/****************************************************************************** +NAME + vc_hostfs_setend + +SYNOPSIS + int vc_hostfs_setend(int fildes) + +FUNCTION + Truncates file at current position. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_setend(int filedes) +{ + off_t currPosn; + + if (( currPosn = lseek( filedes, 0, SEEK_CUR )) != (off_t)-1 ) + { + if ( ftruncate( filedes, currPosn ) == 0 ) + { + return 0; + } + } + return -1; +} + + +/****************************************************************************** +NAME + vc_hostfs_totalspace64 + +SYNOPSIS + int64_t vc_hostfs_totalspace64(const char *path) + +FUNCTION + Returns the total amount of space on the physical file system that contains + path. + +RETURNS + Successful completion: total space + Otherwise: -1 +******************************************************************************/ + +int64_t vc_hostfs_totalspace64(const char *inPath) +{ + char *path = strdup( inPath ); + int64_t ret = -1; + struct statfs fsStat; + + // Replace all '\' with '/' + if (path) + { + backslash_to_slash( path ); + + ret = statfs( path, &fsStat ); + + if (ret == 0) + { + ret = fsStat.f_bsize * fsStat.f_blocks; + } + else + { + ret = -1; + } + } + + DEBUG_MINOR( "vc_hostfs_totalspace for '%s' returning %" PRId64 "", path, ret ); + + if (path) + free( path ); + return ret; +} + + +/****************************************************************************** +NAME + vc_hostfs_totalspace + +SYNOPSIS + int vc_hostfs_totalspace(const char *path) + +FUNCTION + Returns the total amount of space on the physical file system that contains + path. + +RETURNS + Successful completion: total space + Otherwise: -1 +******************************************************************************/ + +int vc_hostfs_totalspace(const char *inPath) +{ + int ret; + int64_t totalSpace = vc_hostfs_totalspace64(inPath); + + // Saturate return value (need this in case we have a large file system) + if (totalSpace > (int64_t) INT_MAX) + { + ret = INT_MAX; + } + else + { + ret = (int) totalSpace; + } + return ret; +} + +/****************************************************************************** +NAME + backslash_to_slash + +SYNOPSIS + void backslash_to_slash( char *s ) + +FUNCTION + Convert all '\' in a string to '/'. + +RETURNS + None. +******************************************************************************/ + +static void backslash_to_slash( char *s ) +{ + while ( *s != '\0' ) + { + if ( *s == '\\' ) + { + *s = '/'; + } + s++; + } +} + +/****************************************************************************** +NAME + vc_hostfs_scandisk + +SYNOPSIS + void vc_hostfs_scandisk(const char *path) + +FUNCTION + Invalidates any cluster chains in the FAT that are not referenced + in any directory structures + +RETURNS + Void +******************************************************************************/ + +void vc_hostfs_scandisk(const char *path) +{ + (void)path; + + // not yet implemented +} + + +/****************************************************************************** +NAME + vc_hostfs_chkdsk + +SYNOPSIS + int vc_hostfs_chkdsk(const char *path, int fix_errors) + +FUNCTION + Checks whether or not a FAT filesystem is corrupt or not. If fix_errors + is TRUE behaves exactly as vc_filesys_scandisk. + +RETURNS + Successful completion: 0 + Otherwise: indicates failure +******************************************************************************/ + +int vc_hostfs_chkdsk(const char *path, int fix_errors) +{ + (void)path; + (void)fix_errors; + return 0; +} + diff --git a/interface/vmcs_host/linux/vchost_config.h b/interface/vmcs_host/linux/vchost_config.h new file mode 100755 index 0000000..4207ec8 --- /dev/null +++ b/interface/vmcs_host/linux/vchost_config.h @@ -0,0 +1,61 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCHOST_CONFIG_H +#define VCHOST_CONFIG_H + +#include "interface/vcos/vcos.h" + +#if 0 +/* Types that map onto VideoCore's types of the same name. */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef long int32_t; +typedef unsigned long uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#ifndef vc_assert +#define vc_assert(cond) vcos_assert(cond) +#endif +#endif + +/* On this platform we need to be able to release the host-side software resources. */ +extern void vc_os_close(void); + +#ifndef VCHPRE_ +#define VCHPRE_ extern +#endif +#ifndef VCHPOST_ +#define VCHPOST_ +#endif +#ifndef VCCPRE_ +#define VCCPRE_ +#endif + +#endif diff --git a/interface/vmcs_host/linux/vcmisc.c b/interface/vmcs_host/linux/vcmisc.c new file mode 100755 index 0000000..3253669 --- /dev/null +++ b/interface/vmcs_host/linux/vcmisc.c @@ -0,0 +1,33 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +void vc_sleep(int ms) +{ + usleep(1000*ms); +} diff --git a/interface/vmcs_host/vc_cec.h b/interface/vmcs_host/vc_cec.h new file mode 100755 index 0000000..f7fe24e --- /dev/null +++ b/interface/vmcs_host/vc_cec.h @@ -0,0 +1,513 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * CEC related constants - shared by both host and vc. + */ + +#ifndef _VC_CEC_H_ +#define _VC_CEC_H_ + +#ifndef STRINGIFY +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#endif + +//Broadcast address and TV logical address +#define CEC_BROADCAST_ADDR 0x0F +#define CEC_TV_ADDRESS 0x00 + +//Maximum transmit length excluding the header byte */ +#define CEC_MAX_XMIT_LENGTH 15 /* +1 for CEC Header Length */ +//Invalid physical address +#define CEC_CLEAR_ADDR 0xFFFF /* packed 16 bits of F.F.F.F */ + +/* ---------------------------------------------------------------------- + * general CEC defines + * -------------------------------------------------------------------- */ +//Maximum transmission length and invalid physical address are now in vc_cec.h +#define CEC_VERSION 0x04 /* HDMI 1.3a */ +//This OUI ID is registered at the current HQ address in Irvine +#define CEC_VENDOR_ID_BROADCOM (0x18C086L) // 24 bit OUI company id from IEEE. = Broadcom +//These three OUI IDs are registered with the old address of Irvine office in case you need them +//#define CEC_VENDOR_ID_BROADCOM (0x000AF7L) +//#define CEC_VENDOR_ID_BROADCOM (0x001018L) +//#define CEC_VENDOR_ID_BROADCOM (0x001BE9L) +#define CEC_VENDOR_ID_ONKYO (0x0009B0L) +#define CEC_VENDOR_ID_PANASONIC_EUROPE (0x000F12L) + +//If we want to "pretend" to be somebody else use a different company id +#define CEC_VENDOR_ID (0x000000L) //We should set the vendor id + +#define CEC_BLOCKING 1 +#define CEC_NONBLOCKING 0 + +/** + * These are the logical addresses for all possible attached devices + */ +typedef enum CEC_AllDevices { + CEC_AllDevices_eTV = 0, /** + */ +typedef enum CEC_DeviceTypes{ + CEC_DeviceType_TV = 0, /** + */ +typedef enum { + CEC_Abort_Reason_Unrecognised_Opcode = 0, + CEC_Abort_Reason_Wrong_Mode = 1, + CEC_Abort_Reason_Cannot_Provide_Source = 2, + CEC_Abort_Reason_Invalid_Operand = 3, + CEC_Abort_Reason_Refused = 4, + CEC_Abort_Reason_Undetermined = 5 +} CEC_ABORT_REASON_T; + +/** + * Display control parameter for + */ +typedef enum { + CEC_DISPLAY_CONTROL_DEFAULT_TIME = 0, + CEC_DISPLAY_CONTROL_UNTIL_CLEARED = (1<<6), + CEC_DISPLAY_CONTROL_CLEAR_PREV_MSG = (1<<7) +} CEC_DISPLAY_CONTROL_T; + +/** + * Power status parameter for + */ +typedef enum { + CEC_POWER_STATUS_ON = 0, + CEC_POWER_STATUS_STANDBY = 1, + CEC_POWER_STATUS_ON_PENDING = 2, + CEC_POWER_STATUS_STANDBY_PENDING = 3 +} CEC_POWER_STATUS_T; + +/** + * Menu state parameter for + */ +typedef enum { + CEC_MENU_STATE_ACTIVATED = 0, + CEC_MENU_STATE_DEACTIVATED = 1, + CEC_MENU_STATE_QUERY = 2 +} CEC_MENU_STATE_T; + +/** + * Deck status parameter for + */ +typedef enum { + CEC_DECK_INFO_PLAY = 0x11, + CEC_DECK_INFO_RECORD = 0x12, + CEC_DECK_INFO_PLAY_REVERSE = 0x13, + CEC_DECK_INFO_STILL = 0x14, + CEC_DECK_INFO_SLOW = 0x15, + CEC_DECK_INFO_SLOW_REVERSE = 0x16, + CEC_DECK_INFO_SEARCH_FORWARD = 0x17, + CEC_DECK_INFO_SEARCH_REVERSE = 0x18, + CEC_DECK_INFO_NO_MEDIA = 0x19, + CEC_DECK_INFO_STOP = 0x1A, + CEC_DECK_INFO_WIND = 0x1B, + CEC_DECK_INFO_REWIND = 0x1C, + CEC_DECK_IDX_SEARCH_FORWARD = 0x1D, + CEC_DECK_IDX_SEARCH_REVERSE = 0x1E, + CEC_DECK_OTHER_STATUS = 0x1F +} CEC_DECK_INFO_T; + +/** + * Deck control mode for + */ +typedef enum { + CEC_DECK_CTRL_FORWARD = 1, + CEC_DECK_CTRL_BACKWARD = 2, + CEC_DECK_CTRL_STOP = 3, + CEC_DECK_CTRL_EJECT = 4 +} CEC_DECK_CTRL_MODE_T; + +/** + * Play mode for + */ +typedef enum { + CEC_PLAY_FORWARD = 0x24, + CEC_PLAY_REVERSE = 0x20, + CEC_PLAY_STILL = 0x25, + CEC_PLAY_SCAN_FORWARD_MIN_SPEED = 0x05, + CEC_PLAY_SCAN_FORWARD_MED_SPEED = 0x06, + CEC_PLAY_SCAN_FORWARD_MAX_SPEED = 0x07, + CEC_PLAY_SCAN_REVERSE_MIN_SPEED = 0x09, + CEC_PLAY_SCAN_REVERSE_MED_SPEED = 0x0A, + CEC_PLAY_SCAN_REVERSE_MAX_SPEED = 0x0B, + CEC_PLAY_SLOW_FORWARD_MIN_SPEED = 0x15, + CEC_PLAY_SLOW_FORWARD_MED_SPEED = 0x16, + CEC_PLAY_SLOW_FORWARD_MAX_SPEED = 0x17, + CEC_PLAY_SLOW_REVERSE_MIN_SPEED = 0x19, + CEC_PLAY_SLOW_REVERSE_MED_SPEED = 0x1A, + CEC_PLAY_SLOW_REVERSE_MAX_SPEED = 0x1B +} CEC_PLAY_MODE_T; + +/** + * Status request for + */ +typedef enum { + CEC_DECK_STATUS_ON = 1, + CEC_DECK_STATUS_OFF = 2, + CEC_DECK_STATUS_ONCE = 3 +} CEC_DECK_STATUS_REQUEST_T; + +/** + * Button code for + */ +typedef enum { + CEC_User_Control_Select = 0x00, + CEC_User_Control_Up = 0x01, + CEC_User_Control_Down = 0x02, + CEC_User_Control_Left = 0x03, + CEC_User_Control_Right = 0x04, + CEC_User_Control_RightUp = 0x05, + CEC_User_Control_RightDown = 0x06, + CEC_User_Control_LeftUp = 0x07, + CEC_User_Control_LeftDown = 0x08, + CEC_User_Control_RootMenu = 0x09, + CEC_User_Control_SetupMenu = 0x0A, + CEC_User_Control_ContentsMenu = 0x0B, + CEC_User_Control_FavoriteMenu = 0x0C, + CEC_User_Control_Exit = 0x0D, + CEC_User_Control_Number0 = 0x20, + CEC_User_Control_Number1 = 0x21, + CEC_User_Control_Number2 = 0x22, + CEC_User_Control_Number3 = 0x23, + CEC_User_Control_Number4 = 0x24, + CEC_User_Control_Number5 = 0x25, + CEC_User_Control_Number6 = 0x26, + CEC_User_Control_Number7 = 0x27, + CEC_User_Control_Number8 = 0x28, + CEC_User_Control_Number9 = 0x29, + CEC_User_Control_Dot = 0x2A, + CEC_User_Control_Enter = 0x2B, + CEC_User_Control_Clear = 0x2C, + CEC_User_Control_ChannelUp = 0x30, + CEC_User_Control_ChannelDown = 0x31, + CEC_User_Control_PreviousChannel = 0x32, + CEC_User_Control_SoundSelect = 0x33, + CEC_User_Control_InputSelect = 0x34, + CEC_User_Control_DisplayInformation = 0x35, + CEC_User_Control_Help = 0x36, + CEC_User_Control_PageUp = 0x37, + CEC_User_Control_PageDown = 0x38, + CEC_User_Control_Power = 0x40, + CEC_User_Control_VolumeUp = 0x41, + CEC_User_Control_VolumeDown = 0x42, + CEC_User_Control_Mute = 0x43, + CEC_User_Control_Play = 0x44, + CEC_User_Control_Stop = 0x45, + CEC_User_Control_Pause = 0x46, + CEC_User_Control_Record = 0x47, + CEC_User_Control_Rewind = 0x48, + CEC_User_Control_FastForward = 0x49, + CEC_User_Control_Eject = 0x4A, + CEC_User_Control_Forward = 0x4B, + CEC_User_Control_Backward = 0x4C, + CEC_User_Control_Angle = 0x50, + CEC_User_Control_Subpicture = 0x51, + CEC_User_Control_VideoOnDemand = 0x52, + CEC_User_Control_EPG = 0x53, + CEC_User_Control_TimerProgramming = 0x54, + CEC_User_Control_InitialConfig = 0x55, + CEC_User_Control_PlayFunction = 0x60, + CEC_User_Control_PausePlayFunction = 0x61, + CEC_User_Control_RecordFunction = 0x62, + CEC_User_Control_PauseRecordFunction = 0x63, + CEC_User_Control_StopFunction = 0x64, + CEC_User_Control_MuteFunction = 0x65, + CEC_User_Control_RestoreVolumeFunction = 0x66, + CEC_User_Control_TuneFunction = 0x67, + CEC_User_Control_SelectDiskFunction = 0x68, + CEC_User_Control_SelectAVInputFunction = 0x69, + CEC_User_Control_SelectAudioInputFunction = 0x6A, + CEC_User_Control_F1Blue = 0x71, + CEC_User_Control_F2Red = 0x72, + CEC_User_Control_F3Green = 0x73, + CEC_User_Control_F4Yellow = 0x74, + CEC_User_Control_F5 = 0x75 +} CEC_USER_CONTROL_T; + +/** + *CEC topology struct + * + * Meaning of device_attr is as follows (one per active logical device) + * bit 3-0 logical address (see CEC_AllDevices_T above) + * bit 7-4 device type (see CEC_DEVICE_TYPE_T above) + * bit 11-8 index to upstream device + * bit 15-12 number of downstream device + * bit 31-16 index of first 4 downstream devices + * + * To keep life simple we only show the first 4 connected downstream devices + * + */ +typedef struct { + uint16_t active_mask; /** */ + VC_CEC_BUTTON_RELEASE = (1 << 3), /**< */ + VC_CEC_REMOTE_PRESSED = (1 << 4), /**< */ + VC_CEC_REMOTE_RELEASE = (1 << 5), /**< */ + VC_CEC_LOGICAL_ADDR = (1 << 6), /** and messages respectively) + * returns the code from the most recent respectively. + * The host application will need to find out the vendor ID of the initiator + * separately in the case if / commands were received. + * will not be longer than 6 bytes (including header) + * + * VC_CEC_LOGICAL_ADDR returns 0xF in param1 whenever no logical address is in used. If physical address is 0xFFFF, + * this means CEC is being disabled. Otherwise physical address is the one read from EDID (and no suitable logical address + * is avaiable to be allocated). Host application should only attempt to send message if both param1 is not 0xF AND param2 + * is not 0xFFFF. + * + * VC_CEC_TOPOLOGY returns a 16-bit mask in param1 where bit n is set if logical address n is present. Host application + * must explicitly retrieve the entire topology if it wants to know how devices are connected. The bit mask includes our + * own logical address. + * + * If CEC is running in passive mode, the host will get a VC_CEC_LOGICAL_ADDR_LOST callback if the logical address is + * lost (e.g. HDMI mode change). In this case the host should try a new logical address. The physical address returned may + * also change, so the host should check this. + */ + +/** + * General callback function for notifications from CEC middleware (and CEC service) + * + * @param client_p is the callback context passed in by user + * + * @param reason is the notification nature (plus message lengths, return code, etc.) + * + * @param param1 is the first parameter of notification (see above) + * + * @param param2 is the second parameter of notification (see above) + * + * @param param3 is the third parameter of notification (see above) + * + * @param param4 is the fourth parameter of notification (see above) + * + * @return void + */ +typedef void (*CEC_CALLBACK_T)(void *client_p, uint32_t reason, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4); + +/** + * Some macros to get some fields from the callback parameters in CEC callback + */ +//Marcos operating on reason +#define CEC_CB_REASON(x) ((x) & 0xFFFF) /** Get callback reason */ +#define CEC_CB_MSG_LENGTH(x) (((x) >> 16) & 0xFF) /** Get callback parameter length (this includes the header byte) */ +#define CEC_CB_RC(x) (((x) >> 24) & 0xFF) /** Get return value (only for TX callbacks for the moment) */ + +//Macros operating on param1 +#define CEC_CB_INITIATOR(x) (((x) >> 4) & 0xF) /** Get the initiator from first parameter */ +#define CEC_CB_FOLLOWER(x) ((x) & 0xF) /** Get the follower from first parameter */ +#define CEC_CB_OPCODE(x) (((x) >> 8) & 0xFF) /** Get the opcode from first parameter */ +#define CEC_CB_OPERAND1(x) (((x) >> 16) & 0xFF) /** Get the button code from or the first operand of the opcode */ +#define CEC_CB_OPERAND2(x) (((x) >> 24) & 0xFF) /** Get the second operand of opcode */ + +//CEC service return code +typedef enum { + VC_CEC_SUCCESS = 0, /** OK */ + VC_CEC_ERROR_NO_ACK = 1, /** No acknowledgement */ + VC_CEC_ERROR_SHUTDOWN = 2, /** In the process of shutting down */ + VC_CEC_ERROR_BUSY = 3, /** block is busy */ + VC_CEC_ERROR_NO_LA = 4, /** No logical address */ + VC_CEC_ERROR_NO_PA = 5, /** No physical address */ + VC_CEC_ERROR_NO_TOPO = 6, /** No topology */ + VC_CEC_ERROR_INVALID_FOLLOWER = 7, /** Invalid follower */ + VC_CEC_ERROR_INVALID_ARGUMENT = 8 /** Invalid arguments */ +} VC_CEC_ERROR_T; + +#endif diff --git a/interface/vmcs_host/vc_cecservice.h b/interface/vmcs_host/vc_cecservice.h new file mode 100755 index 0000000..1b24221 --- /dev/null +++ b/interface/vmcs_host/vc_cecservice.h @@ -0,0 +1,515 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * CEC service host API, + * See vc_cec.h and vc_cecservice_defs.h for related constants + */ + +#ifndef _VC_CECSERVICE_H_ +#define _VC_CECSERVICE_H_ + +#include "vcinclude/common.h" +#include "interface/vcos/vcos.h" +#include "interface/vchi/vchi.h" +#include "interface/vmcs_host/vc_cecservice_defs.h" +#include "interface/vmcs_host/vc_cec.h" + +/** + * \file + * This API defines the controls for CEC. HDMI must be powered on before + * CEC is available (subject to CEC support in TV). + * + * In general, a zero return value indicates success; a negative return + * value indicates error in VCHI layer; a positive return value indicates + * alternative return value from the server + */ + +/** + * Callback function for host side notification + * This is the SAME as the callback function type defined in vc_cec.h + * Host applications register a single callback for all CEC related notifications. + * See vc_cec.h for meanings of all parameters + * + * @param callback_data is the context passed in by user in vc_cec_register_callback + * + * @param reason bits 15-0 is VC_CEC_NOTIFY_T in vc_cec.h; + * bits 23-16 is the valid length of message in param1 to param4 (LSB of param1 is the byte0, MSB of param4 is byte15), little endian + * bits 31-24 is the return code (if any) + * + * @param param1 is the first parameter + * + * @param param2 is the second parameter + * + * @param param3 is the third parameter + * + * @param param4 is the fourth parameter + * + * @return void + */ +typedef void (*CECSERVICE_CALLBACK_T)(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4); + +//API at application start time +/** + * Call vc_vchi_cec_init to initialise the CEC service for use. + * + * @param initialise_instance is the VCHI instance + * @param connections are array of pointers to VCHI connections + * @param num_connections is the number of connections in array + * @return void + **********************************************************/ +VCHPRE_ void vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ); + +/** + * Call vc_vchi_cec_stop to stop host side CEC service. + * + * @param none + * @return void + **********************************************************/ +VCHPRE_ void vc_vchi_cec_stop( void ); + +/** + * Host applications use vc_cec_register_callaback to register + * callback to handle all CEC notifications. If more than one applications + * need to use CEC, there should be ONE central application which acts on + * behalf of all clients and handles all communications with CEC services. + * + * @param callback function + * @param context to be passed when function is called + * @return void + ***********************************************************/ +VCHPRE_ void vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data); + +//Service API +/** + * Use vc_cec_register_command to register an opcode to + * to forwarded to the host application. By default + * is always forwarded. Once an opcode is registered, it is left to + * the host application to reply to a CEC message (where appropriate). + * It is recommended NOT to register the following commands as they + * are replied to automatically by CEC middleware: + * , , , + * , , , + * and + * In addition, the following opcodes cannot be registered: + * , , + * , , + * and . + * is always forwarded if it is the reply + * of a command the host sent. + * + * @param opcode to be registered. + * + * @return zero if the command is successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_register_command(CEC_OPCODE_T opcode); + +/** + * vc_cec_register_all registers all opcodes except + * to be forwarded as CEC_RX notification. + * Button presses , etc. will still be forwarded + * separately as VC_CEC_BUTTON_PRESSED etc. notification. + * + * @param None + * + * @return zero if the command is successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_register_all( void ); + +/** + * Use vc_cec_deregister_command to remove an opcode from + * the filter for forwarding. By default is always forwarded. + * The following opcode cannot be deregistered: + * , , + * , , + * and . + * + * @param opcode to be deregistered + * + * @return zero if the command is successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_deregister_command(CEC_OPCODE_T opcode); + +/** + * vc_cec_deregister_all removes all registered opcodes, + * except the ones (e.g. button presses) which are always forwarded. + * + * @param None + * + * @return zero if the command is successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_deregister_all( void ); + +/** + * vc_cec_send_message allows a host application to + * send a CEC message to another device. There are several + * built-in functions for sending command messages. The host + * application MUST have a valid logical address (between 1 and + * 14 inclusive) before it can send a message. + * (For poll message set payload to NULL and length to zero). + * + * @param Follower's logical address + * + * @param Message payload WITHOUT the header byte (can be NULL) + * + * @param Payload length WITHOUT the header byte (can be zero) + * + * @param VC_TRUE if the message is a reply to an incoming message + * + * @return zero if the command is successful, non-zero otherwise + * If the command is successful, there will be a Tx callback + * in due course to indicate whether the message has been + * acknowledged by the recipient or not + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_message(const uint32_t follower, + const uint8_t *payload, + uint32_t length, + vcos_bool_t is_reply); +/** + * vc_cec_get_logical_address gets the logical address, + * If one is being allocated 0xF (unregistered) will be set. + * A address value of 0xF also means CEC system is not yet ready + * to send or receive any messages. + * + * @param pointer to logical address (set to allocated address) + * + * @return zero if the command is successful, non-zero otherwise + * logical_address is not modified if command failed + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_logical_address(CEC_AllDevices_T *logical_address); + +/** + * vc_cec_alloc_logical_address starts the allocation + * of a logical address. Logical address is automatically allocated + * after HDMI power on is complete and AV mute is deassert. + * The host only needs to call this if the + * initial allocation failed (logical address being 0xF and + * physical address is NOT 0xFFFF from VC_CEC_LOGICAL_ADDR + * notification), or if the host explicitly released its logical + * address. + * + * @param none + * + * @return zero if the command is successful, non-zero otherwise + * If successful, there will be a callback notification + * VC_CEC_LOGICAL_ADDR. + * The host should wait for this before calling this + * function again. + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_alloc_logical_address( void ); + +/** + * Normally vc_cec_release_logical_address will not + * be called by the host application. It is used to release + * our logical address. This effectively disables CEC. + * The host will need to allocate a new logical address before + * doing any CEC calls (send/receive message, get topology, etc.). + * + * @param none + * + * @return zero if the command is successful, non-zero otherwise + * The host should get a callback VC_CEC_LOGICAL_ADDR + * with 0xF being the logical address and 0xFFFF + * being the physical address. + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_release_logical_address( void ); + +/** + * Use vc_cec_get_topology to get the topology. + * + * @param pointer to VC_CEC_TOPOLOGY_T + * + * @return zero if the command is successful, non-zero otherwise + * If successful, the topology will be set, otherwise it is unchanged + * A topology with only 1 device (us) means CEC is not supported. + * If there is no topology available, this also returns a failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_topology( VC_CEC_TOPOLOGY_T* topology); + +/** + * Use vc_cec_set_vendor_id to + * set the response to + * + * @param 24-bit IEEE vendor id + * + * @return zero if the command is successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_set_vendor_id( const uint32_t id ); + +/** + * Use vc_cec_set_osd_name to + * set the response to + * + * @param OSD name (14 byte char array) + * + * @return zero if the command is successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_set_osd_name( const char* name ); + +/** + * vc_cec_get_physical_address gets our physical address + * + * @param pointer to physical address (returned as 16-bit packed value) + * + * @return zero if the command is successful, non-zero otherwise + * If failed, physical address argument will not be changed + * A physical address of 0xFFFF means CEC is not supported + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_physical_address(uint16_t *physical_address); + +/** + * vc_cec_get_vendor_id( gets the vendor id of a particular logical address + * + * @param logical_address is the logical address of the device [in] + * + * @param vendorid is the pointer to vendor ID (24-bit IEEE OUI value) [out] + * + * @return zero if the command is successful, non-zero otherwise + * If failed, vendor id argument will not be changed + * A vendor ID of 0xFFFFFF means the device does not exist + * A vendor ID of 0x0 means vendor ID is not known and + * the application can send to that device + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_vendor_id(const CEC_AllDevices_T logical_address, uint32_t *vendor_id); + +/** + * vc_cec_device_type( returns the default device type of a particular + * logical address, which can be used as the argument to vc_cec_send_ReportPhysicalAddress. + * + * @param logical address + * + * @return the default device type, if there is any error, the return device + * type will be CEC_DeviceType_Invalid + * + ************************************************************/ +VCHPRE_ CEC_DEVICE_TYPE_T VCHPOST_ vc_cec_device_type(const CEC_AllDevices_T logical_address); + +/** + * These couple of functions are provided for host application's convenience: + * If the xmit message is encapsulate in a VC_CEC_MESSAGE_T + * then it can be sent as a normal message (not as a reply) + * and the initiator field is ignored with vc_cec_send_message2 + * and return zero for success + * + * Applications can call vc_cec_param2message to turn the callback parameters + * into a VC_CEC_MESSAGE_T (not for LOGICAL_ADDR and TOPOLOGY callbacks). + * It also returns zero for success. + */ +VCHPRE_ int VCHPOST_ vc_cec_send_message2(const VC_CEC_MESSAGE_T *message); + +VCHPRE_ int VCHPOST_ vc_cec_param2message( const uint32_t reason, const uint32_t param1, + const uint32_t param2, const uint32_t param3, + const uint32_t param4, VC_CEC_MESSAGE_T *message); + +//Extra API if CEC is running in passive mode +//If CEC is not in passive mode the following 3 functions always +//return failure +/** + * vc_cec_poll_address sets and polls a particular address to find out + * its availability in the CEC network. Only available when CEC is running in passive + * mode. The host can only call this function during logical address allocation stage. + * + * @param logical address to try + * + * @return 0 if poll is successful (address is occupied) + * >0 if poll is unsuccessful (Error code is in VC_CEC_ERROR_T in vc_cec.h) + * <0 VCHI errors + */ +VCHPRE_ int VCHPOST_ vc_cec_poll_address(const CEC_AllDevices_T logical_address); + +/** + * vc_cec_set_logical_address sets the logical address, device type + * and vendor ID to be in use. Only available when CEC is running in passive + * mode. It is the responsibility of the host to make sure the logical address + * is actually free to be used. Physical address will be what is read from EDID. + * + * @param logical address + * + * @param device type + * + * @param vendor ID + * + * @return 0 if successful, non-zero otherwise + */ +VCHPRE_ int VCHPOST_ vc_cec_set_logical_address(const CEC_AllDevices_T logical_address, + const CEC_DEVICE_TYPE_T device_type, + const uint32_t vendor_id); + +/** + * vc_cec_add_device adds a new device to topology. + * Only available when CEC is running in passive mode. Device will be + * automatically removed from topology if a failed xmit is detected. + * If last_device is true, it will trigger a topology computation + * (and may trigger a topology callback). + * + * @param logical address + * + * @param physical address + * + * @param device type + * + * @param true if this is the last device, false otherwise + * + * @return 0 if successful, non-zero otherwise + */ +VCHPRE_ int VCHPOST_ vc_cec_add_device(const CEC_AllDevices_T logical_address, + const uint16_t physical_address, + const CEC_DEVICE_TYPE_T device_type, + vcos_bool_t last_device); + +/** + * vc_cec_set_passive enables and disables passive mode. + * Call this function first (with VC_TRUE as the argument) to enable + * passive mode before calling any of the above passive API functions + * + * @param TRUE to enable passive mode, FALSE to disable + * + * @return 0 if successful, non-zero otherwise + */ +VCHPRE_ int VCHPOST_ vc_cec_set_passive(vcos_bool_t enabled); + + +//API for some common CEC messages +/** + * Functions beginning with vc_cec_send_xxx make it easier for the + * host application to send CEC message xxx to other devices + */ +/** + * vc_cec_send_FeatureAbort sends + * for a received command. + * + * @param follower (cannot be 0xF) + * + * @param rejected opcode + * + * @param reject reason CEC_ABORT_REASON_T + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_FeatureAbort(uint32_t follower, + CEC_OPCODE_T opcode, + CEC_ABORT_REASON_T reason); + +/** + * vc_cec_send_ActiveSource broadcasts + * to all devices + * + * @param physical address (16-bit packed) + * + * @param reply or not (normally not) + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_ActiveSource(uint16_t physical_address, vcos_bool_t is_reply); + +/** + * vc_cec_send_ImageViewOn sends + * + * @param follower + * + * @param reply or not (normally not) + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_ImageViewOn(uint32_t follower, vcos_bool_t is_reply); + +/** + * vc_cec_send_SetOSDString sends + * + * @param follower + * + * @param string (char[13]) + * + * @param display control CEC_DISPLAY_CONTROL_T + * + * @param reply or not (normally not) + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_SetOSDString(uint32_t follower, + CEC_DISPLAY_CONTROL_T disp_ctrl, + const char* string, + vcos_bool_t is_reply); + +/** + * vc_cec_send_Standby sends . + * This will put any/all devices to standby if they support + * this CEC message. + * + * @param follower (can be 0xF) + * + * @param reply or not (normally not) + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_Standby(uint32_t follower, vcos_bool_t is_reply); + +/** + * vc_cec_send_MenuStatus sends + * (response to ) + * + * @param follower + * + * @param menu state CEC_MENU_STATE_T but NOT CEC_MENU_STATE_QUERY + * + * @param reply or not (should always be yes) + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_MenuStatus(uint32_t follower, + CEC_MENU_STATE_T menu_state, + vcos_bool_t is_reply); + +/** + * vc_cec_send_ReportPhysicalAddress broadcasts + * to all devices. Note although + * the passed in device type can be override the default one + * associated the allocated logical address, it is not + * recommended to do so. One can use vc_cec_device_type + * to get the default device type associated with the logical + * address returned via VC_CEC_LOGICAL_ADDR callback. + * + * @param physical address (16-bit packed) + * + * @param device type to be broadcasted + * + * @param reply or not (normally not) + * + * @return zero if the command is successful, non-zero otherwise + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_ReportPhysicalAddress(uint16_t physical_address, + CEC_DEVICE_TYPE_T device_type, + vcos_bool_t is_reply); + +#endif //_VC_CECSERVICE_H_ diff --git a/interface/vmcs_host/vc_cecservice_defs.h b/interface/vmcs_host/vc_cecservice_defs.h new file mode 100755 index 0000000..01045e3 --- /dev/null +++ b/interface/vmcs_host/vc_cecservice_defs.h @@ -0,0 +1,182 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * CEC service command enumeration and parameter types. + */ + +/** + * \file + * This file contains definition shared by host side and + * Videocore side CEC service: + * + * In general, a zero return value indicates success of the function + * A non-zero value indicates VCHI error + * A positive value indicates alternative return value (for some functions). + * + */ + + +#ifndef _VC_CECSERVICE_DEFS_H_ +#define _VC_CECSERVICE_DEFS_H_ +#include "vcinclude/common.h" +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_logging.h" +#include "interface/vchi/message_drivers/message.h" + +//CEC VCOS logging stuff +#define CECHOST_LOG_CATEGORY (&cechost_log_category) +#define vc_cec_log_trace(...) _VCOS_LOG_X(CECHOST_LOG_CATEGORY, VCOS_LOG_TRACE, __VA_ARGS__) +#define vc_cec_log_warn(...) _VCOS_LOG_X(CECHOST_LOG_CATEGORY, VCOS_LOG_WARN, __VA_ARGS__) +#define vc_cec_log_error(...) _VCOS_LOG_X(CECHOST_LOG_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__) +#define vc_cec_log_info(...) _VCOS_LOG_X(CECHOST_LOG_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) +extern VCOS_LOG_CAT_T cechost_log_category; //The actual object lives in CEC host side service code + +#define VC_CECSERVICE_VER 1 +#define CECSERVICE_MSGFIFO_SIZE 1024 +#define CECSERVICE_CLIENT_NAME MAKE_FOURCC("CECS") +#define CECSERVICE_NOTIFY_NAME MAKE_FOURCC("CECN") + +//CEC service commands +typedef enum { + VC_CEC_REGISTER_CMD = 0, + VC_CEC_REGISTER_ALL, + VC_CEC_DEREGISTER_CMD, + VC_CEC_DEREGISTER_ALL, + VC_CEC_SEND_MSG, + VC_CEC_GET_LOGICAL_ADDR, + VC_CEC_ALLOC_LOGICAL_ADDR, + VC_CEC_RELEASE_LOGICAL_ADDR, + VC_CEC_GET_TOPOLOGY, + VC_CEC_SET_VENDOR_ID, + VC_CEC_SET_OSD_NAME, + VC_CEC_GET_PHYSICAL_ADDR, + VC_CEC_GET_VENDOR_ID, + + //The following 3 commands are used when CEC middleware is + //running in passive mode (i.e. it does not allocate + //logical address automatically) + VC_CEC_POLL_ADDR, + VC_CEC_SET_LOGICAL_ADDR, + VC_CEC_ADD_DEVICE, + VC_CEC_SET_PASSIVE, + //Add more commands here + VC_CEC_END_OF_LIST +} VC_CEC_CMD_CODE_T; + +//See vc_cec.h for details +//REGISTER_CMD +//Parameters: opcode to register (CEC_OPCODE_T sent as uint32) +//Reply: none + +//REGISTER_ALL +//Parameters: none +//Reply: none + +//DEREGISTER_CMD +//Parameters: opcode to deregister (CEC_OPCODE_T sent as uint32) +//Reply: none + +//DEREGISTER_ALL +//Parameters: none +//Reply: none + +//SEND_MSG +//Parameters: destination, length, 16 bytes buffer (message can only be at most 15 bytes however) +//Reply: none (callback) +typedef struct { + uint32_t follower; + uint32_t length; + uint8_t payload[16]; //max. 15 bytes padded to 16 + uint32_t is_reply; //non-zero if this is a reply, zero otherwise +} CEC_SEND_MSG_PARAM_T; + +//GET_LOGICAL_ADDR +//Parameters: none +//Reply: logical address (uint8 returned as uint32) + +//ALLOC_LOGICAL_ADDR +//Parameters: none +//Reply: none (callback) + +//GET_TOPOLOGY +//Parameters: none +//Reply: topology (see VC_TOPOLOGY_T) + +//SET_VENDOR_ID +//Parameters: vendor id (uint32) +//Reply: none + +//Set OSD name +//Parameters: 14 byte char +//Reply: none +#define OSD_NAME_LENGTH 14 + +//GET_PHYSICAL_ADDR +//Parameter: none +//Reply: packed physical address returned as uint16 + +//GET_VENDOR_ID +//Parameter: logical address (CEC_AllDevices_T sent as uint32_t) +//Reply: (uint32_t vendor id) + +//POLL_LOGICAL_ADDR (only for passive mode) +//Used by host to test a logical address to see if it is available for use +//Only available if CEC is compiled in passive mode and while the host +//is testing the availability of a logical address +//Parameter: logical address +//Reply: + +//SET_LOGICAL_ADDR [(only for passive mode) This will be changed when we support multiple logical addresses] +//Set the logical address used +//Only available if CEC is compiled in passive mode +//Parameter: logical address, device type, vendor ID +//Reply: (int32_t - zero means success, non-zero otherwise) +//This function will result in a VC_CEC_LOGICAL_ADDR callback +typedef struct { + uint32_t logical_address; + uint32_t device_type; + uint32_t vendor_id; +} CEC_SET_LOGICAL_ADDR_PARAM_T; + +//ADD_DEVICE (only for passive mode) +//Only available if CEC is compiled in passive mode +//Parameter: logical address, physical address, device type, last device? +//Reply: (int32_t - zero means success, non-zero otherwise) +typedef struct { + uint32_t logical_address; /** + +#define VC_CMA_IOC_MAGIC 0xc5 + +#define VC_CMA_IOC_RESERVE _IO(VC_CMA_IOC_MAGIC, 0) + +#endif + +#define VC_CMA_FOURCC VCHIQ_MAKE_FOURCC('C','M','A',' ') +#define VC_CMA_VERSION 2 + +#define VC_CMA_CHUNK_ORDER 6 /* 256K */ +#define VC_CMA_CHUNK_SIZE (4096 << VC_CMA_CHUNK_ORDER) +#define VC_CMA_MAX_PARAMS_PER_MSG ((VCHIQ_MAX_MSG_SIZE - sizeof(unsigned short)) / sizeof(unsigned short)) + +enum +{ + VC_CMA_MSG_QUIT, + VC_CMA_MSG_OPEN, + VC_CMA_MSG_TICK, + VC_CMA_MSG_ALLOC, /* chunk count */ + VC_CMA_MSG_FREE, /* chunk, chunk, ... */ + VC_CMA_MSG_ALLOCATED, /* chunk, chunk, ... */ + VC_CMA_MSG_REQUEST_ALLOC, /* chunk count */ + VC_CMA_MSG_REQUEST_FREE, /* chunk count */ + VC_CMA_MSG_RESERVE, /* bytes lo, bytes hi */ + VC_CMA_MSG_MAX +}; + +typedef struct vc_cma_msg_struct +{ + unsigned short type; + unsigned short params[VC_CMA_MAX_PARAMS_PER_MSG]; +} VC_CMA_MSG_T; + +#endif diff --git a/interface/vmcs_host/vc_dispmanx.h b/interface/vmcs_host/vc_dispmanx.h new file mode 100755 index 0000000..37fdae1 --- /dev/null +++ b/interface/vmcs_host/vc_dispmanx.h @@ -0,0 +1,142 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Display manager service API + +#ifndef _VC_DISPMANX_H_ +#define _VC_DISPMANX_H_ + +#include "interface/vcos/vcos.h" +#include "interface/vctypes/vc_image_types.h" +#include "vc_dispservice_x_defs.h" +#include "interface/vmcs_host/vc_dispmanx_types.h" +#include "interface/vchi/vchi.h" + +#ifdef __cplusplus +extern "C" { +#endif +// Same function as above, to aid migration of code. +VCHPRE_ int VCHPOST_ vc_dispman_init( void ); +// Stop the service from being used +VCHPRE_ void VCHPOST_ vc_dispmanx_stop( void ); +// Set the entries in the rect structure +VCHPRE_ int VCHPOST_ vc_dispmanx_rect_set( VC_RECT_T *rect, uint32_t x_offset, uint32_t y_offset, uint32_t width, uint32_t height ); +// Resources +// Create a new resource +VCHPRE_ DISPMANX_RESOURCE_HANDLE_T VCHPOST_ vc_dispmanx_resource_create( VC_IMAGE_TYPE_T type, uint32_t width, uint32_t height, uint32_t *native_image_handle ); +// Write the bitmap data to VideoCore memory +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data( DISPMANX_RESOURCE_HANDLE_T res, VC_IMAGE_TYPE_T src_type, int src_pitch, void * src_address, const VC_RECT_T * rect ); +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data_handle( DISPMANX_RESOURCE_HANDLE_T res, VC_IMAGE_TYPE_T src_type, int src_pitch, VCHI_MEM_HANDLE_T handle, uint32_t offset, const VC_RECT_T * rect ); +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_read_data( + DISPMANX_RESOURCE_HANDLE_T handle, + const VC_RECT_T* p_rect, + void * dst_address, + uint32_t dst_pitch ); +// Delete a resource +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_delete( DISPMANX_RESOURCE_HANDLE_T res ); + +// Displays +// Opens a display on the given device +VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open( uint32_t device ); +// Opens a display on the given device in the request mode +VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open_mode( uint32_t device, uint32_t mode ); +// Open an offscreen display +VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open_offscreen( DISPMANX_RESOURCE_HANDLE_T dest, DISPMANX_TRANSFORM_T orientation ); +// Change the mode of a display +VCHPRE_ int VCHPOST_ vc_dispmanx_display_reconfigure( DISPMANX_DISPLAY_HANDLE_T display, uint32_t mode ); +// Sets the desstination of the display to be the given resource +VCHPRE_ int VCHPOST_ vc_dispmanx_display_set_destination( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_RESOURCE_HANDLE_T dest ); +// Set the background colour of the display +VCHPRE_ int VCHPOST_ vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + uint8_t red, uint8_t green, uint8_t blue ); +// get the width, height, frame rate and aspect ratio of the display +VCHPRE_ int VCHPOST_ vc_dispmanx_display_get_info( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_MODEINFO_T * pinfo ); +// Closes a display +VCHPRE_ int VCHPOST_ vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ); + +// Updates +// Start a new update, DISPMANX_NO_HANDLE on error +VCHPRE_ DISPMANX_UPDATE_HANDLE_T VCHPOST_ vc_dispmanx_update_start( int32_t priority ); +// Add an elment to a display as part of an update +VCHPRE_ DISPMANX_ELEMENT_HANDLE_T VCHPOST_ vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + int32_t layer, const VC_RECT_T *dest_rect, DISPMANX_RESOURCE_HANDLE_T src, + const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, + VC_DISPMANX_ALPHA_T *alpha, + DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform ); +// Change the source image of a display element +VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_source( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element, + DISPMANX_RESOURCE_HANDLE_T src ); +// Change the layer number of a display element +VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_layer ( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element, + int32_t layer ); +// Signal that a region of the bitmap has been modified +VCHPRE_ int VCHPOST_ vc_dispmanx_element_modified( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element, const VC_RECT_T * rect ); +// Remove a display element from its display +VCHPRE_ int VCHPOST_ vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ); +// Ends an update +VCHPRE_ int VCHPOST_ vc_dispmanx_update_submit( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg ); +// End an update and wait for it to complete +VCHPRE_ int VCHPOST_ vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ); +// Query the image formats supported in the VMCS build +VCHPRE_ int VCHPOST_ vc_dispmanx_query_image_formats( uint32_t *supported_formats ); + +//New function added to VCHI to change attributes, set_opacity does not work there. +VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_attributes( DISPMANX_UPDATE_HANDLE_T update, + DISPMANX_ELEMENT_HANDLE_T element, + uint32_t change_flags, + int32_t layer, + uint8_t opacity, + const VC_RECT_T *dest_rect, + const VC_RECT_T *src_rect, + DISPMANX_RESOURCE_HANDLE_T mask, + DISPMANX_TRANSFORM_T transform ); + +//xxx hack to get the image pointer from a resource handle, will be obsolete real soon +VCHPRE_ uint32_t VCHPOST_ vc_dispmanx_resource_get_image_handle( DISPMANX_RESOURCE_HANDLE_T res); + +//Call this instead of vc_dispman_init +VCHPRE_ void VCHPOST_ vc_vchi_dispmanx_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ); + +// Take a snapshot of a display in its current state. +// This call may block for a time; when it completes, the snapshot is ready. +// only transform=0 is supported +VCHPRE_ int VCHPOST_ vc_dispmanx_snapshot( DISPMANX_DISPLAY_HANDLE_T display, + DISPMANX_RESOURCE_HANDLE_T snapshot_resource, + DISPMANX_TRANSFORM_T transform ); + +// Set the resource palette (for VC_IMAGE_4BPP and VC_IMAGE_8BPP) +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_set_palette( DISPMANX_RESOURCE_HANDLE_T handle, + void * src_address, int offset, int size); + +// Start triggering callbacks synced to vsync +VCHPRE_ int VCHPOST_ vc_dispmanx_vsync_callback( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg ); + +#ifdef __cplusplus +} +#endif + +#endif // _VC_DISPMANX_H_ diff --git a/interface/vmcs_host/vc_dispmanx_types.h b/interface/vmcs_host/vc_dispmanx_types.h new file mode 100755 index 0000000..9ae9f26 --- /dev/null +++ b/interface/vmcs_host/vc_dispmanx_types.h @@ -0,0 +1,228 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Typedefs and enums for the VideoCore III Display Manager + +#ifndef _DISPMANX_TYPES_H +#define _DISPMANX_TYPES_H + +#include "interface/vctypes/vc_image_types.h" +#include "interface/vctypes/vc_display_types.h" + +#define VC_DISPMANX_VERSION 1 + +/* Opaque handles */ +typedef uint32_t DISPMANX_DISPLAY_HANDLE_T; +typedef uint32_t DISPMANX_UPDATE_HANDLE_T; +typedef uint32_t DISPMANX_ELEMENT_HANDLE_T; +typedef uint32_t DISPMANX_RESOURCE_HANDLE_T; + +typedef uint32_t DISPMANX_PROTECTION_T; + +#define DISPMANX_NO_HANDLE 0 + +#define DISPMANX_PROTECTION_MAX 0x0f +#define DISPMANX_PROTECTION_NONE 0 +#define DISPMANX_PROTECTION_HDCP 11 // Derived from the WM DRM levels, 101-300 + + + +/* Default display IDs. + Note: if you overwrite with your own dispmanx_platform_init function, you + should use IDs you provided during dispmanx_display_attach. +*/ +#define DISPMANX_ID_MAIN_LCD 0 +#define DISPMANX_ID_AUX_LCD 1 +#define DISPMANX_ID_HDMI 2 +#define DISPMANX_ID_SDTV 3 +#define DISPMANX_ID_FORCE_LCD 4 +#define DISPMANX_ID_FORCE_TV 5 +#define DISPMANX_ID_FORCE_OTHER 6 /* non-default display */ + +/* Return codes. Nonzero ones indicate failure. */ +typedef enum { + DISPMANX_SUCCESS = 0, + DISPMANX_INVALID = -1 + /* XXX others TBA */ +} DISPMANX_STATUS_T; + +typedef enum { + /* Bottom 2 bits sets the orientation */ + DISPMANX_NO_ROTATE = 0, + DISPMANX_ROTATE_90 = 1, + DISPMANX_ROTATE_180 = 2, + DISPMANX_ROTATE_270 = 3, + + DISPMANX_FLIP_HRIZ = 1 << 16, + DISPMANX_FLIP_VERT = 1 << 17, + + /* invert left/right images */ + DISPMANX_STEREOSCOPIC_INVERT = 1 << 19, + /* extra flags for controlling 3d duplication behaviour */ + DISPMANX_STEREOSCOPIC_NONE = 0 << 20, + DISPMANX_STEREOSCOPIC_MONO = 1 << 20, + DISPMANX_STEREOSCOPIC_SBS = 2 << 20, + DISPMANX_STEREOSCOPIC_TB = 3 << 20, + DISPMANX_STEREOSCOPIC_MASK = 15 << 20, + + /* extra flags for controlling snapshot behaviour */ + DISPMANX_SNAPSHOT_NO_YUV = 1 << 24, + DISPMANX_SNAPSHOT_NO_RGB = 1 << 25, + DISPMANX_SNAPSHOT_FILL = 1 << 26, + DISPMANX_SNAPSHOT_SWAP_RED_BLUE = 1 << 27, + DISPMANX_SNAPSHOT_PACK = 1 << 28 +} DISPMANX_TRANSFORM_T; + +typedef enum { + /* Bottom 2 bits sets the alpha mode */ + DISPMANX_FLAGS_ALPHA_FROM_SOURCE = 0, + DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS = 1, + DISPMANX_FLAGS_ALPHA_FIXED_NON_ZERO = 2, + DISPMANX_FLAGS_ALPHA_FIXED_EXCEED_0X07 = 3, + + DISPMANX_FLAGS_ALPHA_PREMULT = 1 << 16, + DISPMANX_FLAGS_ALPHA_MIX = 1 << 17 +} DISPMANX_FLAGS_ALPHA_T; + +typedef struct { + DISPMANX_FLAGS_ALPHA_T flags; + uint32_t opacity; + VC_IMAGE_T *mask; +} DISPMANX_ALPHA_T; + +typedef struct { + DISPMANX_FLAGS_ALPHA_T flags; + uint32_t opacity; + DISPMANX_RESOURCE_HANDLE_T mask; +} VC_DISPMANX_ALPHA_T; /* for use with vmcs_host */ + +typedef enum { + DISPMANX_FLAGS_CLAMP_NONE = 0, + DISPMANX_FLAGS_CLAMP_LUMA_TRANSPARENT = 1, +#if __VCCOREVER__ >= 0x04000000 + DISPMANX_FLAGS_CLAMP_TRANSPARENT = 2, + DISPMANX_FLAGS_CLAMP_REPLACE = 3 +#else + DISPMANX_FLAGS_CLAMP_CHROMA_TRANSPARENT = 2, + DISPMANX_FLAGS_CLAMP_TRANSPARENT = 3 +#endif +} DISPMANX_FLAGS_CLAMP_T; + +typedef enum { + DISPMANX_FLAGS_KEYMASK_OVERRIDE = 1, + DISPMANX_FLAGS_KEYMASK_SMOOTH = 1 << 1, + DISPMANX_FLAGS_KEYMASK_CR_INV = 1 << 2, + DISPMANX_FLAGS_KEYMASK_CB_INV = 1 << 3, + DISPMANX_FLAGS_KEYMASK_YY_INV = 1 << 4 +} DISPMANX_FLAGS_KEYMASK_T; + +typedef union { + struct { + uint8_t yy_upper; + uint8_t yy_lower; + uint8_t cr_upper; + uint8_t cr_lower; + uint8_t cb_upper; + uint8_t cb_lower; + } yuv; + struct { + uint8_t red_upper; + uint8_t red_lower; + uint8_t blue_upper; + uint8_t blue_lower; + uint8_t green_upper; + uint8_t green_lower; + } rgb; +} DISPMANX_CLAMP_KEYS_T; + +typedef struct { + DISPMANX_FLAGS_CLAMP_T mode; + DISPMANX_FLAGS_KEYMASK_T key_mask; + DISPMANX_CLAMP_KEYS_T key_value; + uint32_t replace_value; +} DISPMANX_CLAMP_T; + +typedef struct { + int32_t width; + int32_t height; + DISPMANX_TRANSFORM_T transform; + DISPLAY_INPUT_FORMAT_T input_format; + uint32_t display_num; +} DISPMANX_MODEINFO_T; + +/* Update callback. */ +typedef void (*DISPMANX_CALLBACK_FUNC_T)(DISPMANX_UPDATE_HANDLE_T u, void * arg); + +/* Progress callback */ +typedef void (*DISPMANX_PROGRESS_CALLBACK_FUNC_T)(DISPMANX_UPDATE_HANDLE_T u, + uint32_t line, + void * arg); + +/* Pluggable display interface */ + +typedef struct tag_DISPMANX_DISPLAY_FUNCS_T { + // Get essential HVS configuration to be passed to the HVS driver. Options + // is any combination of the following flags: HVS_ONESHOT, HVS_FIFOREG, + // HVS_FIFO32, HVS_AUTOHSTART, HVS_INTLACE; and if HVS_FIFOREG, one of; + // { HVS_FMT_RGB888, HVS_FMT_RGB565, HVS_FMT_RGB666, HVS_FMT_YUV }. + int32_t (*get_hvs_config)(void *instance, uint32_t *pchan, + uint32_t *poptions, DISPLAY_INFO_T *info, + uint32_t *bg_colour, uint32_t *test_mode); + + // Get optional HVS configuration for gamma tables, OLED matrix and dither controls. + // Set these function pointers to NULL if the relevant features are not required. + int32_t (*get_gamma_params)(void * instance, + int32_t gain[3], int32_t offset[3], int32_t gamma[3]); + int32_t (*get_oled_params)(void * instance, uint32_t * poffsets, + uint32_t coeffs[3]); + int32_t (*get_dither)(void * instance, uint32_t * dither_depth, uint32_t * dither_type); + + // Get mode information, which may be returned to the applications as a courtesy. + // Transform should be set to 0, and {width,height} should be final dimensions. + int32_t (*get_info)(void * instance, DISPMANX_MODEINFO_T * info); + + // Inform driver that the application refcount has become nonzero / zero + // These callbacks might perhaps be used for backlight and power management. + int32_t (*open)(void * instance); + int32_t (*close)(void * instance); + + // Display list updated callback. Primarily of use to a "one-shot" display. + // For convenience of the driver, we pass the register address of the HVS FIFO. + void (*dlist_updated)(void * instance, volatile uint32_t * fifo_reg); + + // End-of-field callback. This may occur in an interrupt context. + void (*eof_callback)(void * instance); + + // Return screen resolution format + DISPLAY_INPUT_FORMAT_T (*get_input_format)(void * instance); + + int32_t (*suspend_resume)(void *instance, int up); + + DISPLAY_3D_FORMAT_T (*get_3d_format)(void * instance); +} DISPMANX_DISPLAY_FUNCS_T; + +#endif /* ifndef _DISPMANX_TYPES_H */ diff --git a/interface/vmcs_host/vc_dispservice_defs.h b/interface/vmcs_host/vc_dispservice_defs.h new file mode 100755 index 0000000..1240f0e --- /dev/null +++ b/interface/vmcs_host/vc_dispservice_defs.h @@ -0,0 +1,250 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_DISPSERVICE_DEFS_H +#define VC_DISPSERVICE_DEFS_H + +#define HOST_PITCH_ALIGNMENT 4 + +//Round up to the nearest multiple of 16 +#define PAD16(x) (((x) + (VC_INTERFACE_BLOCK_SIZE-1)) & ~(VC_INTERFACE_BLOCK_SIZE-1)) + +//The max length for an effect name +#define DISPMAN_MAX_EFFECT_NAME (28) + +typedef enum { + // Values initially chosen to match VC_IMAGE_TYPE_T to aid debugging + // This is now a mandatory constraint + VC_FORMAT_RGB565 = 1, + VC_FORMAT_YUV420 = 3, + VC_FORMAT_RGB888 = 5, + VC_FORMAT_RGBA32 = 15, + VC_FORMAT_RGBA565 = 17, + VC_FORMAT_RGBA16 = 18, + VC_FORMAT_TF_RGBA32 = 20, + VC_FORMAT_TF_RGBA16 = 23, + VC_FORMAT_TF_RGB565 = 25, + VC_FORMAT_BGR888 = 31, + VC_FORMAT_BGR888_NP = 32, + + VC_FORMAT_ARGB8888 = 43, + VC_FORMAT_XRGB8888 = 44, + + /* To force 32-bit storage, enabling use in structures over-the-wire */ + VC_FORMAT_RANGE_MAX = 0x7FFFFFFF +} VC_IMAGE_FORMAT_T; + +// Transforms. +/* Image transformations. These must match the DISPMAN and Media Player versions */ +#define TRANSFORM_HFLIP (1<<0) +#define TRANSFORM_VFLIP (1<<1) +#define TRANSFORM_TRANSPOSE (1<<2) + +typedef enum { + VC_DISPMAN_ROT0 = 0, + VC_DISPMAN_MIRROR_ROT0 = TRANSFORM_HFLIP, + VC_DISPMAN_MIRROR_ROT180 = TRANSFORM_VFLIP, + VC_DISPMAN_ROT180 = TRANSFORM_HFLIP|TRANSFORM_VFLIP, + VC_DISPMAN_MIRROR_ROT90 = TRANSFORM_TRANSPOSE, + VC_DISPMAN_ROT270 = TRANSFORM_TRANSPOSE|TRANSFORM_HFLIP, + VC_DISPMAN_ROT90 = TRANSFORM_TRANSPOSE|TRANSFORM_VFLIP, + VC_DISPMAN_MIRROR_ROT270 = TRANSFORM_TRANSPOSE|TRANSFORM_HFLIP|TRANSFORM_VFLIP, +} VC_DISPMAN_TRANSFORM_T; + +typedef enum { + VC_RESOURCE_TYPE_HOST, + VC_RESOURCE_TYPE_VIDEOCORE, + VC_RESOURCE_TYPE_VIDEOCORE_UNCACHED, +} VC_RESOURCE_TYPE_T; + +typedef struct { + uint8_t type; // VC_IMAGE_FORMAT_T + uint32_t width; // width in pixels + uint32_t height; // height in pixels + uint32_t pitch; // pitch of image_data array in *bytes* + uint32_t size; // number of *bytes* available in the image_data arry + uint32_t pointer; // pointer for image_data - this allows the object to be cast to a VC_IMAGE_T on the VIDEOCORE side +} VC_IMAGE_PARAM_T; + +typedef enum { + VC_DISPMAN_DISPLAY_SET_DESTINATION = 0, + VC_DISPMAN_DISPLAY_UPDATE_START, + VC_DISPMAN_DISPLAY_UPDATE_END, + VC_DISPMAN_DISPLAY_OBJECT_ADD, + VC_DISPMAN_DISPLAY_OBJECT_REMOVE, + VC_DISPMAN_DISPLAY_OBJECT_MODIFY, + VC_DISPMAN_DISPLAY_LOCK, + VC_DISPMAN_DISPLAY_UNLOCK, + VC_DISPMAN_DISPLAY_RESOURCE_CREATE, + VC_DISPMAN_DISPLAY_RESOURCE_DELETE, + VC_DISPMAN_DISPLAY_GET_COMPOSITE, + VC_DISPMAN_DISPLAY_APPLY_EFFECT_INSTANCE, + VC_DISPMAN_DISPLAY_RECONFIGURE, + VC_DISPMAN_DISPLAY_CREATE_EFFECTS_INSTANCE, + VC_DISPMAN_DISPLAY_DELETE_EFFECTS_INSTANCE, + VC_DISPMAN_DISPLAY_SET_EFFECT, + VC_DISPMAN_DISPLAY_RESOURCE_SET_ALPHA, + VC_DISPMAN_DISPLAY_SNAPSHOT, + VC_DISPMAN_DISPLAY_QUERY_IMAGE_FORMATS, + VC_DISPMAN_DISPLAY_GET_DISPLAY_DETAILS, + // new features - add to end of list + VC_DISPMAN_DISPLAY_RESOURCE_CREATE_FROM_IMAGE, + VC_CMD_END_OF_LIST +} VC_CMD_CODE_T; + +/* The table of functions executed for each command. */ + +typedef void (*INTERFACE_EXECUTE_FN_T)(int, int); + +extern INTERFACE_EXECUTE_FN_T interface_execute_fn[]; + +//Parameter sets for dispservice commands +typedef struct { + uint32_t state; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_LOCK_PARAM_T; + +typedef struct { + uint32_t display; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_GET_DISPLAY_DETAILS_PARAM_T; + +typedef struct { + uint32_t display; + uint32_t resource; + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMAN_SET_DEST_PARAM_T; + +typedef struct { + uint32_t display; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_GET_COMPOSITE_PARAM_T; + +typedef struct +{ + uint32_t display; + uint32_t effects_instance; + + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMAN_APPLY_EFFECTS_INSTANCE_PARAM_T; + +typedef struct +{ + uint32_t read_response; + uint32_t effects_instance; +} DISPMAN_CREATE_EFFECTS_INSTANCE_RESPONSE_T; + +typedef struct +{ + uint32_t effects_instance; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_DELETE_EFFECTS_INSTANCE_PARAM_T; + +typedef struct +{ + uint32_t effects_instance; + char effect_name[ DISPMAN_MAX_EFFECT_NAME ]; + //no need to pad as long as DISPMAN_MAX_EFFECT_NAME +sizeof(uint32) = 32 +} DISPMAN_SET_EFFECT_PARAM_T; + +typedef struct { + uint32_t display; + uint16_t width; + uint16_t height; + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMAN_RECONFIGURE_PARAM_T; + +typedef struct { + uint32_t display; + uint32_t transparent_colour; + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMAN_SET_TRANSPARENT_COLOUR_PARAM_T; + +typedef struct { + //uint32_t object; + uint32_t display; + int32_t layer; + uint32_t transform; + uint32_t resource; + uint16_t dest_x; + uint16_t dest_y; + uint16_t dest_width; + uint16_t dest_height; + uint16_t src_x; + uint16_t src_y; + uint16_t src_width; + uint16_t src_height; +} DISPMAN_OBJECT_ADD_PARAM_T; + +typedef struct { + uint32_t object; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_OBJECT_REMOVE_PARAM_T; + +typedef struct { + uint32_t object; + uint16_t src_x; + uint16_t src_y; + uint16_t width; + uint16_t height; + uint32_t dummy[1]; // Pad to multiple of 16 bytes +} DISPMAN_OBJECT_MODIFY_PARAM_T; + +typedef struct +{ + uint32_t *resource; + VC_IMAGE_PARAM_T image; + uint8_t type; // VC_RESOURCE_TYPE_T + //Removed padding. VC_IMAGE_T may change in size, so handle the size in the code that sends and receives the commands + //uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_RESOURCE_CREATE_PARAM_T; + +typedef struct +{ + uint32_t native_image_ptr; + uint32_t type; // VC_RESOURCE_TYPE_T + uint32_t dummy[2]; // Pad to multiple of 16 bytes +} DISPMAN_RESOURCE_CREATE_FROM_IMAGE_PARAM_T; + +typedef struct { + uint32_t resource; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMAN_RESOURCE_DELETE_PARAM_T; + +typedef struct { + uint32_t resource; + uint32_t alpha; + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMAN_RESOURCE_SET_ALPHA_PARAM_T; + +typedef struct { + uint32_t display; + uint32_t resource; + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMAN_DISPLAY_SNAPSHOT_PARAM_T; + +#endif //VC_DISPSERVICE_DEFS_H diff --git a/interface/vmcs_host/vc_dispservice_x_defs.h b/interface/vmcs_host/vc_dispservice_x_defs.h new file mode 100755 index 0000000..9291eef --- /dev/null +++ b/interface/vmcs_host/vc_dispservice_x_defs.h @@ -0,0 +1,276 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Display service command enumeration. + +#ifndef VC_DISPSERVICEX_DEFS_H +#define VC_DISPSERVICEX_DEFS_H + +#include "interface/vctypes/vc_image_types.h" + +#define HOST_PITCH_ALIGNMENT 4 + +//Round up to the nearest multiple of 16 +#define PAD16(x) (((x) + (VC_INTERFACE_BLOCK_SIZE-1)) & ~(VC_INTERFACE_BLOCK_SIZE-1)) + +//The max length for an effect name +#define DISPMANX_MAX_EFFECT_NAME (28) + +// Should really use the VC_IMAGE_TYPE_T, but this one has been extended +// to force it up to 32-bits... +typedef enum { + // Values initially chosen to match VC_IMAGE_TYPE_T to aid debugging + // This is now a mandatory constraint + VC_FORMAT_RGB565 = VC_IMAGE_RGB565, + VC_FORMAT_YUV420 = VC_IMAGE_YUV420, + VC_FORMAT_RGB888 = VC_IMAGE_RGB888, + VC_FORMAT_RGBA32 = VC_IMAGE_RGBA32, + VC_FORMAT_RGBA565 = VC_IMAGE_RGBA565, + VC_FORMAT_RGBA16 = VC_IMAGE_RGBA16, + VC_FORMAT_TF_RGBA32 = VC_IMAGE_TF_RGBA32, + VC_FORMAT_TF_RGBA16 = VC_IMAGE_TF_RGBA16, + VC_FORMAT_TF_RGB565 = VC_IMAGE_TF_RGB565, + VC_FORMAT_BGR888 = VC_IMAGE_BGR888, + VC_FORMAT_BGR888_NP = VC_IMAGE_BGR888_NP, + + VC_FORMAT_ARGB8888 = VC_IMAGE_ARGB8888, + VC_FORMAT_XRGB8888 = VC_IMAGE_XRGB8888, + + /* To force 32-bit storage, enabling use in structures over-the-wire */ + VC_FORMAT_RANGE_MAX = 0x7FFFFFFF +} VC_IMAGE_FORMAT_T; + +// Transforms. +/* Image transformations. These must match the DISPMAN and Media Player versions */ +#define TRANSFORM_HFLIP (1<<0) +#define TRANSFORM_VFLIP (1<<1) +#define TRANSFORM_TRANSPOSE (1<<2) + +#define VC_DISPMAN_ROT0 VC_IMAGE_ROT0 +#define VC_DISPMAN_ROT90 VC_IMAGE_ROT90 +#define VC_DISPMAN_ROT180 VC_IMAGE_ROT180 +#define VC_DISPMAN_ROT270 VC_IMAGE_ROT270 +#define VC_DISPMAN_MIRROR_ROT0 VC_IMAGE_MIRROR_ROT0 +#define VC_DISPMAN_MIRROR_ROT90 VC_IMAGE_MIRROR_ROT90 +#define VC_DISPMAN_MIRROR_ROT180 VC_IMAGE_MIRROR_ROT180 +#define VC_DISPMAN_MIRROR_ROT270 VC_IMAGE_MIRROR_ROT270 +#define VC_DISPMAN_TRANSFORM_T VC_IMAGE_TRANSFORM_T + +typedef enum { + VC_RESOURCE_TYPE_HOST, + VC_RESOURCE_TYPE_VIDEOCORE, + VC_RESOURCE_TYPE_VIDEOCORE_UNCACHED, +} VC_RESOURCE_TYPE_T; + +typedef struct { + uint8_t type; // VC_IMAGE_FORMAT_T + uint32_t width; // width in pixels + uint32_t height; // height in pixels + uint32_t pitch; // pitch of image_data array in *bytes* + uint32_t size; // number of *bytes* available in the image_data arry + uint32_t pointer; // pointer for image_data - this allows the object to be cast to a VC_IMAGE_T on the VIDEOCORE side +} VC_IMAGE_PARAM_T; + +typedef enum { + VC_DISPMANX_GET_DEVICES = 0, + VC_DISPMANX_GET_DEVICE_NAME, + VC_DISPMANX_GET_MODES, + VC_DISPMANX_GET_MODE_INFO, + VC_DISPMANX_DISPLAY_QUERY_IMAGE_FORMATS, + // Resources + VC_DISPMANX_RESOURCE_CREATE, + VC_DISPMANX_RESOURCE_WRITE_DATA, + VC_DISPMANX_RESOURCE_DELETE, + // Displays + VC_DISPMANX_DISPLAY_OPEN, + VC_DISPMANX_DISPLAY_OPEN_MODE, + VC_DISPMANX_DISPLAY_OPEN_OFFSCREEN, + VC_DISPMANX_DISPLAY_RECONFIGURE, + VC_DISPMANX_DISPLAY_SET_DESTINATION, + VC_DISPMANX_DISPLAY_SET_BACKGROUND, + VC_DISPMANX_DISPLAY_GET_INFO, + VC_DISPMANX_DISPLAY_CLOSE, + // Updates + VC_DISPMANX_UPDATE_START, + VC_DISPMANX_ELEMENT_ADD, + VC_DISPMANX_ELEMENT_CHANGE_SOURCE, + VC_DISPMANX_ELEMENT_MODIFIED, + VC_DISPMANX_ELEMENT_REMOVE, + VC_DISPMANX_UPDATE_SUBMIT, + VC_DISPMANX_UPDATE_SUBMIT_SYNC, + // Miscellaneous + VC_DISPMANX_SNAPSHOT, + // new features - add to end of list + VC_CMD_END_OF_LIST +} VC_CMD_CODE_T; + +/* The table of functions executed for each command. */ + +typedef void (*INTERFACE_EXECUTE_FN_T)(int, int); + +extern INTERFACE_EXECUTE_FN_T interface_execute_fn[]; + +#define DISPMANX_MAX_HOST_DEVICES 8 +#define DISPMANX_MAX_DEVICE_NAME_LEN 16 + +//Parameter sets for dispservice commands + +typedef struct { + int32_t response; + uint32_t ndevices; + uint32_t dummy[2]; + uint8_t names[DISPMANX_MAX_HOST_DEVICES][DISPMANX_MAX_DEVICE_NAME_LEN]; +} DISPMANX_GET_DEVICES_RESP_T; +typedef struct { + uint32_t device; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMANX_GET_MODES_PARAM_T; +typedef struct { + uint32_t display; + uint32_t mode; + uint32_t dummy[2]; //Pad to multiple of 16 bytes +} DISPMANX_GET_MODE_INFO_PARAM_T; +typedef struct { + uint32_t type; + uint32_t width; + uint32_t height; + uint32_t dummy[1]; // Pad to multiple of 16 bytes +} DISPMANX_RESOURCE_CREATE_PARAM_T; +typedef struct { + // This will be needed when we change to vchi. + int junk; // empty structure not allowed +} DISPMANX_RESOURCE_WRITE_DATA_PARAM_T; +typedef struct { + uint32_t handle; + uint32_t dummy[3]; //Pad to multiple of 16 bytes +} DISPMANX_RESOURCE_DELETE_PARAM_T; +typedef struct { + uint32_t device; + uint32_t dummy[3]; +} DISPMANX_DISPLAY_OPEN_PARAM_T; +typedef struct { + uint32_t device; + uint32_t mode; + uint32_t dummy[2]; +} DISPMANX_DISPLAY_OPEN_MODE_PARAM_T; +typedef struct { + uint32_t dest; + uint32_t orientation; + uint32_t dummy[2]; +} DISPMANX_DISPLAY_OPEN_OFFSCREEN_PARAM_T; +typedef struct { + uint32_t display; + uint32_t dest; + uint32_t dummy[2]; +} DISPMANX_DISPLAY_SET_DESTINATION_PARAM_T; +typedef struct { + uint32_t display; + uint32_t update; + uint32_t colour; + uint32_t dummy; +} DISPMANX_DISPLAY_SET_BACKGROUND_PARAM_T; +typedef struct { + uint32_t display; + uint32_t dummy[3]; +} DISPMANX_DISPLAY_GET_INFO_PARAM_T; +typedef struct { + uint32_t read_response; + int32_t width; + int32_t height; + int32_t aspect_pixwidth; + int32_t aspect_pixheight; + int32_t fieldrate_num; + int32_t fieldrate_denom; + int32_t fields_per_frame; + uint32_t transform; + uint32_t dummy[3]; +} DISPMANX_DISPLAY_GET_INFO_RESP_T; +typedef struct { + int32_t priority; + uint32_t dummy[3]; +} DISPMANX_UPDATE_START_PARAM_T; +typedef struct { + uint32_t update; + uint32_t display; + int32_t layer; + uint32_t transform; + uint32_t src_resource; + uint16_t dest_x; + uint16_t dest_y; + uint16_t dest_width; + uint16_t dest_height; + uint16_t src_x; + uint16_t src_y; + uint16_t src_width; + uint16_t src_height; + uint32_t flags; + uint32_t opacity; + uint32_t mask_resource; + // already 16 byte aligned +} DISPMANX_ELEMENT_ADD_PARAM_T; +typedef struct { + uint32_t update; + uint32_t element; + uint32_t src_resource; + uint32_t dummy; // pad to 16 bytes +} DISPMANX_ELEMENT_CHANGE_SOURCE_PARAM_T; +typedef struct { + uint32_t update; + uint32_t element; + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; +} DISPMANX_ELEMENT_MODIFIED_PARAM_T; +typedef struct { + uint32_t update; + uint32_t element; + uint32_t dummy[2]; +} DISPMANX_ELEMENT_REMOVE_PARAM_T; +typedef struct { + uint32_t update; + uint32_t dummy[3]; +} DISPMANX_UPDATE_SUBMIT_PARAM_T; +typedef struct { + uint32_t update; + uint32_t dummy[3]; +} DISPMANX_UPDATE_SUBMIT_SYNC_PARAM_T; +typedef struct { + uint32_t display; + uint32_t snapshot_resource; + uint32_t transform; + uint32_t dummy[1]; +} DISPMANX_DISPLAY_SNAPSHOT_PARAM_T; + +// for dispmanx + +#define TRANSFORM_HFLIP (1<<0) +#define TRANSFORM_VFLIP (1<<1) +#define TRANSFORM_TRANSPOSE (1<<2) + + +#endif //VC_DISPSERVICEX_DEFS_H diff --git a/interface/vmcs_host/vc_fileservice_defs.h b/interface/vmcs_host/vc_fileservice_defs.h new file mode 100755 index 0000000..cfcb738 --- /dev/null +++ b/interface/vmcs_host/vc_fileservice_defs.h @@ -0,0 +1,119 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// File service command enumeration. + +#ifndef VC_FILESERVICE_DEFS_H +#define VC_FILESERVICE_DEFS_H + +#define VC_FILESERV_VER 1 +/* Definitions (not used by API) */ +#define FS_MAX_DATA 8192 //4096 + +/* Protocol (not used by API) version 1.2 */ + +enum { + /* Over-the-wire file open flags */ + VC_O_RDONLY = 0x01, + VC_O_WRONLY = 0x02, + VC_O_RDWR = 0x04, + VC_O_APPEND = 0x08, + VC_O_CREAT = 0x10, + VC_O_TRUNC = 0x20, + VC_O_EXCL = 0x40, + + /* Request Commands (VC->Host->VC) */ + + /* These commands don't require a pathname */ + VC_FILESYS_RESET = 64, + VC_FILESYS_CLOSE = 65, + VC_FILESYS_CLOSEDIR = 66, + VC_FILESYS_LSEEK = 67, + VC_FILESYS_READ = 68, + VC_FILESYS_READDIR = 69, + VC_FILESYS_SETEND = 70, + VC_FILESYS_WRITE = 71, + + /* These commands require a pathname */ + VC_FILESYS_FORMAT = 72, + VC_FILESYS_FREESPACE = 73, + VC_FILESYS_GET_ATTR = 74, + VC_FILESYS_MKDIR = 75, + VC_FILESYS_OPEN = 76, + VC_FILESYS_OPENDIR = 77, + VC_FILESYS_REMOVE = 78, + VC_FILESYS_RENAME = 79, + VC_FILESYS_SET_ATTR = 80, + VC_FILESYS_SCANDISK = 81, + VC_FILESYS_TOTALSPACE = 82, + VC_FILESYS_DISKWRITABLE=83, + VC_FILESYS_OPEN_DISK_RAW = 84, + VC_FILESYS_CLOSE_DISK_RAW = 85, + VC_FILESYS_NUMSECTORS = 86, + VC_FILESYS_READ_SECTORS = 87, + VC_FILESYS_WRITE_SECTORS = 88, + + VC_FILESYS_MOUNT = 89, + VC_FILESYS_UMOUNT = 90, + VC_FILESYS_FSTYPE = 91, + + VC_FILESYS_READ_DIRECT = 92, + + VC_FILESYS_LSEEK64 = 93, + VC_FILESYS_FREESPACE64 = 94, + VC_FILESYS_TOTALSPACE64= 95, + VC_FILESYS_OPEN_DISK = 96, + VC_FILESYS_CLOSE_DISK = 97, + + /* extra simple functions for mass storage testing */ + VC_FILESYS_READ_SECTOR = 98, //1sect + VC_FILESYS_STREAM_SECTOR_BEGIN = 99, + VC_FILESYS_STREAM_SECTOR_END = 100, + VC_FILESYS_WRITE_SECTOR = 101, + VC_FILESYS_FSTAT = 102, + VC_FILESYS_DIRSIZE = 103, + VC_FILESYS_LIST_DIRS = 104, + VC_FILESYS_LIST_FILES = 105, + VC_FILESYS_NUM_DIRS = 106, + VC_FILESYS_NUM_FILES = 107, + VC_FILESYS_MAX_FILESIZE = 108, + VC_FILESYS_CHKDSK = 109, +}; + +/* Parameters for lseek */ + +#define VC_FILESYS_SEEK_SET 0 /* Set file pointer to "offset" */ +#define VC_FILESYS_SEEK_CUR 1 /* Set file pointer to current plus "offset" */ +#define VC_FILESYS_SEEK_END 2 /* Set file pointer to EOF plus "offset" */ + +/* Return values of vc_filesys_type */ +#define VC_FILESYS_FS_UNKNOWN 0 +#define VC_FILESYS_FS_FAT12 1 +#define VC_FILESYS_FS_FAT16 2 +#define VC_FILESYS_FS_FAT32 3 + +#endif diff --git a/interface/vmcs_host/vc_gencmd_defs.h b/interface/vmcs_host/vc_gencmd_defs.h new file mode 100755 index 0000000..ef9ea6f --- /dev/null +++ b/interface/vmcs_host/vc_gencmd_defs.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_GENCMD_DEFS_H +#define VC_GENCMD_DEFS_H + +//Format of reply message is error code followed by a string +#define GENCMDSERVICE_MSGFIFO_SIZE 1024 + +#define VC_GENCMD_VER 1 + +#endif diff --git a/interface/vmcs_host/vc_hdmi.h b/interface/vmcs_host/vc_hdmi.h new file mode 100755 index 0000000..9fd844a --- /dev/null +++ b/interface/vmcs_host/vc_hdmi.h @@ -0,0 +1,545 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * HDMI common host header for TV service, defines resolution code which host applications should + * use for power up command for HDMI + */ + +#ifndef _VC_HDMI_H_ +#define _VC_HDMI_H_ + +#include "vc_hdmi_property.h" /**< All HDMI related properties have been moved to here */ + +typedef int VC_HDMI_BOOL_T; + +/** + * HDMI resolution groups. There are two main groups: + * CEA - the conventional HDMI ones like 720p + * DMT - computer monitor resolutions like XGA + */ +typedef enum { + HDMI_RES_GROUP_INVALID = 0, /**< Initialised value */ + HDMI_RES_GROUP_CEA = 1, /**< CEA - HDMI device */ + HDMI_RES_GROUP_DMT = 2, /**< DMT - computer monitors */ + HDMI_RES_GROUP_CEA_3D = 3, /* deprecated */ + +} HDMI_RES_GROUP_T; + +#define HDMI_RES_GROUP_NAME(g) \ + (((g) == HDMI_RES_GROUP_INVALID) ? "Invalid" : \ + (((g) == HDMI_RES_GROUP_CEA) ? "CEA" : \ + (((g) == HDMI_RES_GROUP_DMT) ? "DMT" : \ + "Unknown"))) + +/** + * CEA 861 defined video code and aspect ratios for various HDMI modes + * Not all values are valid for AVI infoframe + */ +typedef enum { + HDMI_ASPECT_UNKNOWN = 0, /**< Unknown aspect ratio, or not one of the values below */ + HDMI_ASPECT_4_3 = 1, /**< 4:3 */ + HDMI_ASPECT_14_9 = 2, /**< 14:9 */ + HDMI_ASPECT_16_9 = 3, /**< 16:9 */ + HDMI_ASPECT_5_4 = 4, /**< 5:4 */ + HDMI_ASPECT_16_10 = 5, /**< 16:10*/ + HDMI_ASPECT_15_9 = 6, /**< 15:9 */ + HDMI_ASPECT_64_27 = 7, /**< 64:27 */ + HDMI_ASPECT_21_9 = HDMI_ASPECT_64_27 /**< 21:9 is jargon, 64:27 is the actual aspect ratio */ + /* More aspect ratio values may be added here if defined by CEA in future */ +} HDMI_ASPECT_T; + +/** + * Display options set the bounding box (only used in CEA mode) + */ +typedef struct { + uint16_t aspect; /** IL_BUFFER_BULK_UNALIGNED_MAX ) + #error "VCHI_BULK_ALIGN > IL_BUFFER_BULK_UNALIGNED_MAX. Just set max higher on both VC and HOST so there's space to put the unaligned bytes." +#endif +typedef struct { + OMX_U8 header[IL_BUFFER_BULK_UNALIGNED_MAX-1]; + OMX_U8 headerlen; + OMX_U8 trailer[IL_BUFFER_BULK_UNALIGNED_MAX-1]; + OMX_U8 trailerlen; +} IL_BUFFER_BULK_T; + +typedef struct { + OMX_U8 buffer[1]; +} IL_BUFFER_INLINE_T; + +typedef struct { + void *reference; + OMX_BUFFERHEADERTYPE bufferHeader; + IL_BUFFER_METHOD_T method; + OMX_U32 bufferLen; +} IL_PASS_BUFFER_EXECUTE_T; + +// get component version +typedef struct { + IL_FUNCTION_T func; + OMX_ERRORTYPE err; + char name[128]; + OMX_VERSIONTYPE component_version; + OMX_VERSIONTYPE spec_version; + OMX_UUIDTYPE uuid; +} IL_GET_VERSION_RESPONSE_T; + +// get extension index +typedef struct { + void *reference; + char name[128]; +} IL_GET_EXTENSION_EXECUTE_T; + +typedef struct { + IL_FUNCTION_T func; + OMX_ERRORTYPE err; + OMX_INDEXTYPE index; +} IL_GET_EXTENSION_RESPONSE_T; + +// component role enum +typedef struct { + void *reference; + OMX_U32 index; +} IL_COMPONENT_ROLE_ENUM_EXECUTE_T; + +typedef struct { + IL_FUNCTION_T func; + OMX_ERRORTYPE err; + OMX_U8 role[128]; +} IL_COMPONENT_ROLE_ENUM_RESPONSE_T; + +typedef struct { + void *reference; + OMX_U32 port; + OMX_PTR tunnel_ref; // reference to use in requests - address of host/vc component + OMX_BOOL tunnel_host; // whether tunnel_ref is a host component + OMX_U32 tunnel_port; + OMX_TUNNELSETUPTYPE setup; +} IL_TUNNEL_REQUEST_EXECUTE_T; + +typedef struct { + IL_FUNCTION_T func; + OMX_ERRORTYPE err; + OMX_TUNNELSETUPTYPE setup; +} IL_TUNNEL_REQUEST_RESPONSE_T; + +typedef struct { + int index; +} IL_COMPONENT_NAME_ENUM_EXECUTE_T; + +typedef struct { + IL_FUNCTION_T func; + OMX_ERRORTYPE err; + OMX_U8 name[128]; +} IL_COMPONENT_NAME_ENUM_RESPONSE_T; + +typedef struct { + OMX_S32 len; +} IL_GET_DEBUG_INFORMATION_EXECUTE_T; + +#endif // VC_ILCS_DEFS_H diff --git a/interface/vmcs_host/vc_imageconv_defs.h b/interface/vmcs_host/vc_imageconv_defs.h new file mode 100755 index 0000000..418d758 --- /dev/null +++ b/interface/vmcs_host/vc_imageconv_defs.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef IMAGECONV_DEFS_H +#define IMAGECONV_DEFS_H + +/** Statistics for image conversion to foreign image types + */ +typedef struct +{ + uint32_t magic; + uint32_t size; /**< Size of this structure, in bytes */ + uint32_t conversions; /**< Total conversions so far */ + uint32_t duplicate_conversions; /**< Duplicate conversions (same image twice) */ + uint32_t size_requests; /**< Num calls to get_converted_size */ + uint32_t consumed_count; /**< How many converted images were consumed */ + uint32_t failures; /**< Failed conversions */ + uint32_t time_spent; /**< Time spent converting, us */ + uint32_t max_vrf_delay; /**< The max time waiting for the VRF */ + uint32_t vrf_wait_time; /**< Total time waiting for the VRF */ + uint32_t last_mem_handle; /**< Last mem handle converted */ + uint32_t first_image_ts; /**< Timestamp of first image */ + uint32_t last_image_ts; /**< Timestamp of first image */ + uint32_t max_delay; /**< Jitter */ +} IMAGECONV_STATS_T; + +#define IMAGECONV_STATS_MAGIC 0x494D454C +#endif diff --git a/interface/vmcs_host/vc_sdtv.h b/interface/vmcs_host/vc_sdtv.h new file mode 100755 index 0000000..5a57528 --- /dev/null +++ b/interface/vmcs_host/vc_sdtv.h @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * SDTV common host header for TV service + */ + +#ifndef _VC_SDTV_H_ +#define _VC_SDTV_H_ + +/** Different SDTV modes */ +/** colour */ +typedef enum SDTV_COLOUR_ +{ + SDTV_COLOUR_UNKNOWN = 0x0, + SDTV_COLOUR_RGB = 0x4, + SDTV_COLOUR_YPRPB = 0x8, + SDTV_COLOUR_FORCE_32BIT = 0x80000000 +} SDTV_COLOUR_T; +/** operation mode */ +typedef enum SDTV_MODE_T_ +{ + SDTV_MODE_NTSC = 0, /**VC_HDMI_NOTIFY_T and VC_SDTV_NOTIFY_T in vc_hdmi.h and vc_sdtv.h + * respectively for list of reasons and respective param1 and param2 + * + * @param callback_data is the context passed in during the call to vc_tv_register_callback + * + * @param reason is the notification reason + * + * @param param1 is the first optional parameter + * + * @param param2 is the second optional parameter + * + * @return void + */ +typedef void (*TVSERVICE_CALLBACK_T)(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2); + +/* API at application start time */ +/** + * vc_vchi_tv_init is called at the beginning of the application + * to initialise the client to TV service + * + * @param initialise_instance is the VCHI instance + * + * @param array of pointers of connections + * + * @param number of connections (currently this is always 1 + * + * @return Zero is successful A negative return value indicates failure (which may mean it has not been started on VideoCore). + */ +VCHPRE_ int vc_vchi_tv_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ); + +/** + * vc_vchi_tv_stop is called to stop the host side of TV service. + * + * @param none + * + * @return void + */ +VCHPRE_ void vc_vchi_tv_stop( void ); + +/** + * Host applications should call vc_tv_register_callback at + * the beginning to register a callback function to handle all notifications. + * See TVSERVICE_CALLBACK_T + * + * @param callback function + * + * @param callback_data is the context to be passed when function is called + * + * @return void + */ +VCHPRE_ void vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data); + +/** + * vc_tv_unregister_callback removes a function registered with + * vc_tv_register_callback from the list of callbacks. + * + * @param callback function + * + * @return void + */ +VCHPRE_ void vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback); + +/** + * vc_tv_unregister_callback removes a function registered with + * vc_tv_register_callback from the list of callbacks. + * In contrast to vc_tv_unregister_callback this one matches not only the + * function pointer but also the data pointer before removal. + * + * @param callback function + * + * @return void + */ +VCHPRE_ void vc_tv_unregister_callback_full(TVSERVICE_CALLBACK_T callback, void *callback_data); + +/** + * In the following API any functions applying to HDMI only will have hdmi_ + * in the name, ditto for SDTV only will have sdtv_ in the name, + * Otherwise the function applies to both SDTV and HDMI (e.g. power off) + */ + +/** + * vc_tv_get_state is used to obtain the current TV state. + * Host applications should call this function right after registering + * a callback in case any notifications are missed. + * + * Now deprecated - use vc_tv_get_display_state instead + * + * @param pointer to TV_GET_STATE_RESP_T + * + * @return zero if the command is sent successfully, non zero otherwise + * If the command fails to be sent, passed in state is unchanged + * + */ +VCHPRE_ int VCHPOST_ vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate); + +/** + * vc_tv_get_display_state is used to obtain the current TV display + * state. This function supersedes vc_tv_get_state (which is only kept for + * backward compatibility. + * Host applications should call this function right after registering + * a callback in case any notifications are missed. + * + * @param pointer to TV_DISPLAY_STATE_T + * + * @return zero if the command is sent successfully, non zero otherwise + * If the command fails to be sent, passed in state is unchanged + * + */ +VCHPRE_ int VCHPOST_ vc_tv_get_display_state(TV_DISPLAY_STATE_T *tvstate); + +/** + * Use vc_tv_hdmi_power_on_preferred if you don't care what resolutions + * a TV supports and just want to turn on its native resolution. Analogue TV will + * be powered down if on (same for the following two HDMI power on functions.) + * If power on is successful, a host application must wait for the power on complete + * callback before attempting to open the display. + * + * @param none + * + * @return single value interpreted as HDMI_RESULT_T (zero means success) + * if successful, there will be a callback when the power on is complete + * + **/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred( void ); + +/** + * Same as above, but tell the TV to enter 3D mode. The TV will go to 2D mode + * if the preferred mode doesn't support 3D. + **/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred_3d( void ); + +/** + * Use vc_tv_hdmi_power_on_best to power on HDMI at best matched resolution + * based on passed in parameters. Use HDMI_MODE_MATCH_FRAMERATE if you want to + * match the frame rate; use HDMI_MODE_MATCH_RESOLUTION if you want to match on + * screen size; add HDMI_MODE_MATCH_SCANMODE if you want to force + * interlaced/progressive mode. If no matching mode is found, the native resolution + * will be used instead. + * + * @param width is the desired minimum screen width + * + * @param height is the desired minimum screen height + * + * @param rate is the desired frame rate + * + * @param scan_mode (HDMI_NONINTERLACED / HDMI_INTERLACED) is the desired scan mode + * + * @param match flags is the matching flag EDID_MODE_MATCH_FLAG_T + * + * @return same as vc_tv_hdmi_power_on_preferred + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags); + +/** + * Same as above, but tell the TV to enter 3D mode. The TV will go to 2D mode + * if no suitable 3D mode can be found. + **/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags); + +/** + * Use vc_tv_hdmi_power_on_explicit if you want full control over what mode + * the TV is driven. This function is used, for example, when the host has the EDID + * and HDMI middleware does not know. If HDMI middleware has knowledge of EDID, the + * passed in mode is still subject to TV's supported modes + * + * @param mode (HDMI_MODE_HDMI/HDMI_MODE_DVI/HDMI_MODE_3D) + * + * @param group (HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT) + * + * @param code either HDMI_CEA_RES_CODE_T or HDMI_DMT_RES_CODE_T + * + * @return same as vc_tv_hdmi_power_on_preferred + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code); + +/** + * vc_tv_sdtv_power_on is used to turn on analogue TV. HDMI will + * automatically be powered off if on. + * + * @param SDTV mode SDTV_MODE_T + * + * @param options SDTV_OPTIONS_T + * + * @return single value (zero means success) if successful, there will be a callback when the power on is complete + * + */ +VCHPRE_ int VCHPOST_ vc_tv_sdtv_power_on(SDTV_MODE_T mode, SDTV_OPTIONS_T *options); + +/** + * vc_tv_power_off is used to turn off either analogue or HDMI output. + * If HDMI is powered down, there will be a callback with reason UNPLUGGED (if no + * cable is attached) or STANDBY (if a cable is attached) + * + * @param none + * + * @return whether command is succcessfully sent + * + */ +VCHPRE_ int VCHPOST_ vc_tv_power_off( void ); + +/** + * vc_tv_hdmi_get_supported_modes is used to get a list of supported + * modes for a particular standard (CEA/DMT/CEA3D). Prefer resolution (group and mode) + * is also returned, if needed. If there are more modes supported than the size of the array + * supply, only the array will be filled. + * + * @param group(HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT) + * + * @param array of TV_SUPPORT_MODE_T struct + * + * @param length of array above (in elements, not bytes) + * + * @pointer to preferred group (can be NULL) + * + * @pointer to prefer mode code (can be NULL) + * + * @return the number of modes actually written in the array, + * zero means no modes (no EDID or cable unplugged) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_T group, + TV_SUPPORTED_MODE_NEW_T *supported_modes, + uint32_t max_supported_modes, + HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode); +/** + * vc_tv_hdmi_mode_supported is used to query whether a particular mode + * is supported or not. + * + * @param resolution standard (HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT) + * + * @param mode code + * + * @return > 0 means supported, 0 means unsupported, < 0 means error + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_mode_supported(HDMI_RES_GROUP_T group, + uint32_t mode); + +/** + * vc_tv_hdmi_audio_supported is used to query whether a + * particular audio format is supported. By default a device has to support + * 16-bit stereo PCM at 32/44.1/48 kHz if audio is supported at all. + * Support of other audio formats allow SPDIF to be used. + * A return value of zero means the audio format is completely supported. + * Any non-zero values are interpreted as bit mask (EDID_AUDIO_SUPPORT_FLAG_T). + * For example, if EDID_AUDIO_NO_SUPPORT is set, the audio format is not supported. + * If EDID_AUDIO_CHAN_UNSUPPORTED is set, the max no. of channels has exceeded. + * + * @param audio format supplied as (EDID_AudioFormat + EDID_AudioCodingExtension) + * + * @param no. of channels (1-8) + * + * @param sample rate EDID_AudioSampleRate but NOT "refer to header" + * + * @param bit rate (or sample size if pcm) use EDID_AudioSampleSize as sample size argument + * + * @return: single value return interpreted as flags in EDID_AUDIO_SUPPORT_FLAG_T + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_audio_supported(uint32_t audio_format, uint32_t num_channels, + EDID_AudioSampleRate fs, uint32_t bitrate); + +/** + * Use vc_tv_enable_copyprotect to turn on copy protection. + * For HDMI, only HDMI_CP_HDCP is recognised. + * For SDTV, use one of the values in SDTV_CP_MODE_T + * + * @param copy protect mode + * + * @param time out in milliseconds (only applicable to HDMI) + * + * @return 0 means success, additional result via callback + * + */ +VCHPRE_ int VCHPOST_ vc_tv_enable_copyprotect(uint32_t cp_mode, uint32_t timeout); + +/** + * Use vc_tv_disable_copyprotect to turn off copy protection + * + * @param none + * + * @rturn 0 means success, additional result via callback + * + */ +VCHPRE_ int VCHPOST_ vc_tv_disable_copyprotect( void ); + +/** + * Use vc_tv_show_info to show or hide info screen. + * Only usable in HDMI at the moment. + * + * @param show (1) or hide (0) info screen + * + * @return zero if command is successfully sent + * + */ +VCHPRE_ int VCHPOST_ vc_tv_show_info(uint32_t show); + +/** + * vc_tv_hdmi_get_av_latency is used to get the AV latency + * (in ms) for HDMI (lipsync), only valid if HDMI is currently powered on, + * otherwise you get zero. The latency is defined as the relative delay + * of the video stream to the audio stream + * + * @param none + * + * @return latency (zero if error or latency is not defined), + * < 0 if failed to send command) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_av_latency( void ); + +/** + * Use vc_tv_hdmi_set_hdcp_key to download HDCP key to HDCP middleware + * + * @param AES encrypted key block (328 bytes) + * + * @return single value return indicating queued status + * Callback indicates the validity of key + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_key(const uint8_t *key); + +/** + * Use vc_tv_hdmi_set_hdcp_revoked_list to download SRM + * revoked list + * + * @param list + * + * @param size of list (no. of keys) + * + * @return single value return indicating queued status + * Callback indicates the number of keys set (zero if failed, unless you are clearing the list) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_revoked_list(const uint8_t *list, uint32_t num_keys); + +/** + * vc_tv_hdmi_set_spd is used to set the SPD infoframe. + * + * @param manufacturer (max. 8 characters) + * + * @param description (max. 16 characters) + * + * @param product type HDMI_SPD_TYPE_CODE_T + * + * @return whether command was sent successfully (zero means success) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_spd(const char *manufacturer, const char *description, HDMI_SPD_TYPE_CODE_T type); + +/** + * vc_tv_hdmi_set_display_options is used to set the + * active area for HDMI (bar width/height should be set to zero if absent) + * This information is conveyed in AVI infoframe. + * + * @param aspect ratio HDMI_ASPECT_T + * + * @param left bar width + * + * @param right bar width + * + * @param top bar height + * + * @param bottom bar height + * + * @return whether command was sent successfully (zero means success) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_display_options(HDMI_ASPECT_T aspect, uint32_t left_bar_width, uint32_t right_bar_width, uint32_t top_bar_height, uint32_t bottom_bar_height, uint32_t overscan_flags); + +/** + * Use vc_tv_test_mode_start to generate test signal. + * At the moment only DVI test signal is supported. + * HDMI must be powered off before this function is called. + * + * @param 24-bit background colour (if applicable) + * + * @param test mode TV_TEST_MODE_T + * + * @return whether command was sent successfully (zero means success) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_test_mode_start(uint32_t colour, TV_TEST_MODE_T test_mode); + +/** + * Use vc_tv_test_mode_stop to stop the test signal and power down + * HDMI. + * + * @param none + * + * @return whether command was sent successfully (zero means success) + * + */ +VCHPRE_ int VCHPOST_ vc_tv_test_mode_stop( void ); + +/** + * vc_tv_hdmi_ddc_read allows an host application to read EDID + * with DDC protocol. + * + * @param offset + * + * @param length to read (this is typically 128 bytes to coincide with EDID block size) + * + * @param pointer to buffer, must be 16 byte aligned + * + * @returns length of data read (so zero means error) and the buffer will be filled + * only if there is no error + * + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_ddc_read(uint32_t offset, uint32_t length, uint8_t *buffer); + +/** + * Sets the TV state to attached. + * Required when hotplug interrupt is not handled by VideoCore. + * + * @param attached non-zero if the TV is attached or zero for unplugged. + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_attached(uint32_t attached); + +/** + * Sets one of the HDMI properties. HDMI properties persist + * between HDMI power on/off + * + * @param property [in] + * + * @return zero if successful, non-zero otherwise + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_property(const HDMI_PROPERTY_PARAM_T *property); + +/** + * Gets the current value of an HDMI property. + * + * @param property [in/out] + * + * @return zero if success (param1/param2 will be set), non-zero otherwise (param1/param2 will not be set) + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_property(HDMI_PROPERTY_PARAM_T *property); + +/** + * Converts the notification reason to a string. + * + * @param reason is the notification reason + * @return The notification reason as a string. + */ +VCHPRE_ const char* vc_tv_notification_name(VC_HDMI_NOTIFY_T reason); + +/** + * Get the unique device ID from the EDID + * @param pointer to device ID struct + * @return zero if successful, non-zero if failed. + */ +VCHPRE_ int VCHPOST_ vc_tv_get_device_id(TV_DEVICE_ID_T *id); + +// temporary: maintain backwards compatibility +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, + TV_SUPPORTED_MODE_T *supported_modes, + uint32_t max_supported_modes, + HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode); +// temporary: maintain backwards compatibility +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code); + +#endif diff --git a/interface/vmcs_host/vc_tvservice_defs.h b/interface/vmcs_host/vc_tvservice_defs.h new file mode 100755 index 0000000..a792929 --- /dev/null +++ b/interface/vmcs_host/vc_tvservice_defs.h @@ -0,0 +1,357 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * TV service command enumeration and parameter types. + */ + +#ifndef _VC_TVSERVICE_DEFS_H_ +#define _VC_TVSERVICE_DEFS_H_ +#include "vcinclude/common.h" +#include "interface/vchi/message_drivers/message.h" +#include "vc_hdmi.h" +#include "vc_sdtv.h" + +#define VC_TVSERVICE_VER 1 + +#define TVSERVICE_MSGFIFO_SIZE 1024 +#define TVSERVICE_CLIENT_NAME MAKE_FOURCC("TVSV") +#define TVSERVICE_NOTIFY_NAME MAKE_FOURCC("TVNT") + +#define TVSERVICE_MAX_CALLBACKS 5 + +//TV service commands +typedef enum { + VC_TV_GET_STATE = 0, + VC_TV_HDMI_ON_PREFERRED, + VC_TV_HDMI_ON_BEST, + VC_TV_HDMI_ON_EXPLICIT, + VC_TV_SDTV_ON, + VC_TV_OFF, + VC_TV_QUERY_SUPPORTED_MODES, + VC_TV_QUERY_MODE_SUPPORT, + VC_TV_QUERY_AUDIO_SUPPORT, + VC_TV_ENABLE_COPY_PROTECT, + VC_TV_DISABLE_COPY_PROTECT, + VC_TV_SHOW_INFO, + VC_TV_GET_AV_LATENCY, + VC_TV_HDCP_SET_KEY, + VC_TV_HDCP_SET_SRM, + VC_TV_SET_SPD, + VC_TV_SET_DISPLAY_OPTIONS, + VC_TV_TEST_MODE_START, + VC_TV_TEST_MODE_STOP, + VC_TV_DDC_READ, + VC_TV_SET_ATTACHED, + VC_TV_SET_PROP, + VC_TV_GET_PROP, + VC_TV_GET_DISPLAY_STATE, + VC_TV_QUERY_SUPPORTED_MODES_ACTUAL, + VC_TV_GET_DEVICE_ID, + //Add more commands here + VC_TV_END_OF_LIST +} VC_TV_CMD_CODE_T; + +//Parameters for each command (padded to multiple of 16 bytes) +//See vc_hdmi.h and vc_sdtv.h for details + +//GET_STATE +//Parameters: none +//Reply: state (flags of VC_HDMI_NOTIFY_T and VC_SDTV_NOTIFY_T) +// current width +// current height +// current refresh rate +// current scan mode + +typedef struct { + uint32_t state; /**VC direction +typedef enum +{ + VC_AUDIO_MSG_TYPE_RESULT, // Generic result + VC_AUDIO_MSG_TYPE_COMPLETE, // playback of samples complete + VC_AUDIO_MSG_TYPE_CONFIG, // Configure + VC_AUDIO_MSG_TYPE_CONTROL, // control + VC_AUDIO_MSG_TYPE_OPEN, // open + VC_AUDIO_MSG_TYPE_CLOSE, // close/shutdown + VC_AUDIO_MSG_TYPE_START, // start output (i.e. resume) + VC_AUDIO_MSG_TYPE_STOP, // stop output (i.e. pause) + VC_AUDIO_MSG_TYPE_WRITE, // write samples + VC_AUDIO_MSG_TYPE_LATENCY, // request latency in cycles + VC_AUDIO_MSG_TYPE_MAX + +} VC_AUDIO_MSG_TYPE; + +static const char *vc_audio_msg_type_names[] = { + "VC_AUDIO_MSG_TYPE_RESULT", + "VC_AUDIO_MSG_TYPE_COMPLETE", + "VC_AUDIO_MSG_TYPE_CONFIG", + "VC_AUDIO_MSG_TYPE_CONTROL", + "VC_AUDIO_MSG_TYPE_OPEN", + "VC_AUDIO_MSG_TYPE_CLOSE", + "VC_AUDIO_MSG_TYPE_START", + "VC_AUDIO_MSG_TYPE_STOP", + "VC_AUDIO_MSG_TYPE_WRITE", + "VC_AUDIO_MSG_TYPE_MAX" +}; + +// configure the audio +typedef struct +{ + uint32_t channels; + uint32_t samplerate; + uint32_t bps; + uint32_t channelmap; + +} VC_AUDIO_CONFIG_T; + +typedef struct +{ + uint32_t volume; + uint32_t dest; + +} VC_AUDIO_CONTROL_T; + +// audio +typedef struct +{ + uint32_t dummy; + +} VC_AUDIO_OPEN_T; + +// audio +typedef struct +{ + uint32_t dummy; + +} VC_AUDIO_CLOSE_T; +// audio +typedef struct +{ + uint32_t dummy; + +} VC_AUDIO_START_T; +// audio +typedef struct +{ + uint32_t draining; + +} VC_AUDIO_STOP_T; +// audio +typedef struct +{ + uint32_t dummy; + +} VC_AUDIO_LATENCY_T; + +// configure the write audio samples +typedef struct +{ + uint32_t count; // in bytes + void *callback; + void *cookie; + uint16_t silence; + uint16_t max_packet; +} VC_AUDIO_WRITE_T; + +// Generic result for a request (VC->HOST) +typedef struct +{ + int32_t success; // Success value + +} VC_AUDIO_RESULT_T; + +// Generic result for a request (VC->HOST) +typedef struct +{ + int32_t count; // Success value + void *callback; + void *cookie; +} VC_AUDIO_COMPLETE_T; + +// Message header for all messages in HOST->VC direction +typedef struct +{ + int32_t type; // Message type (VC_AUDIO_MSG_TYPE) + union + { + VC_AUDIO_CONFIG_T config; + VC_AUDIO_CONTROL_T control; + VC_AUDIO_OPEN_T open; + VC_AUDIO_CLOSE_T close; + VC_AUDIO_START_T start; + VC_AUDIO_STOP_T stop; + VC_AUDIO_WRITE_T write; + VC_AUDIO_LATENCY_T latency; + VC_AUDIO_RESULT_T result; + VC_AUDIO_COMPLETE_T complete; + } u; +} VC_AUDIO_MSG_T; + + +#endif // _VC_AUDIO_DEFS_H_ diff --git a/interface/vmcs_host/vc_vchi_bufman.h b/interface/vmcs_host/vc_vchi_bufman.h new file mode 100755 index 0000000..67b19f2 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_bufman.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_VCHI_BUFMAN_H +#define VC_VCHI_BUFMAN_H + +#include "interface/vctypes/vc_image_types.h" +#include "interface/vchi/vchi.h" +#ifdef __SYMBIAN32__ +#include "interface/vmcs_host/vc_vchi_bufman_defs.h" +typedef uint32_t DISPMANX_RESOURCE_HANDLE_T; +namespace BufManX { +#else +#include "interface/vmcs_host/vc_dispmanx.h" +#include "interface/vmcs_host/vc_vchi_bufman_defs.h" +#endif + +typedef void (*vc_bufman_callback_t) (void *next_cookie, void *next_cookie2, int32_t success); + +VCHPRE_ void VCHPOST_ vc_vchi_bufman_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); + +typedef struct +{ + buf_frame_type_t type; + int width, height, pitch; + int bpp; // bits per pixel + int size; + void *pixels; +} BUFMANX_IMAGE_T; + +#define BUFMAN_TRANSFORM_HFLIP (1<<0) +#define BUFMAN_TRANSFORM_VFLIP (1<<1) +#define BUFMAN_TRANSFORM_TRANSPOSE (1<<2) + +typedef enum { + BUFMAN_TRANSFORM_ROT0 = 0, + BUFMAN_TRANSFORM_MIRROR_ROT0 = BUFMAN_TRANSFORM_HFLIP, + BUFMAN_TRANSFORM_MIRROR_ROT180 = BUFMAN_TRANSFORM_VFLIP, + BUFMAN_TRANSFORM_ROT180 = BUFMAN_TRANSFORM_HFLIP|BUFMAN_TRANSFORM_VFLIP, + BUFMAN_TRANSFORM_MIRROR_ROT90 = BUFMAN_TRANSFORM_TRANSPOSE, + BUFMAN_TRANSFORM_ROT270 = BUFMAN_TRANSFORM_TRANSPOSE|BUFMAN_TRANSFORM_HFLIP, + BUFMAN_TRANSFORM_ROT90 = BUFMAN_TRANSFORM_TRANSPOSE|BUFMAN_TRANSFORM_VFLIP, + BUFMAN_TRANSFORM_MIRROR_ROT270 = BUFMAN_TRANSFORM_TRANSPOSE|BUFMAN_TRANSFORM_HFLIP|BUFMAN_TRANSFORM_VFLIP, +} BUFMAN_TRANSFORM_T; + + +// we use an opaque type here as the internals shouldn't be used externally, but allocation of the size of the block is required by the caller. +#define BUFMANX_HANDLE_T_SIZE 1024 +typedef struct { + char opaque[BUFMANX_HANDLE_T_SIZE]; +} BUFMANX_HANDLE_T; + +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_convert_init(void); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_set_transform_buffer(void *pixels, int size); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_allocate_image(BUFMANX_IMAGE_T *image); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_release_image(BUFMANX_IMAGE_T *image); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_get_default_pitch( BUFMANX_IMAGE_T *src ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_get_default_size(BUFMANX_IMAGE_T *src); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_push ( BUFMANX_HANDLE_T *h, const BUFMANX_IMAGE_T *src, DISPMANX_RESOURCE_HANDLE_T dst, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_pull ( BUFMANX_HANDLE_T *h, BUFMANX_IMAGE_T *dst, const DISPMANX_RESOURCE_HANDLE_T src, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_pull_blocking ( BUFMANX_HANDLE_T *h, BUFMANX_IMAGE_T *dst, const DISPMANX_RESOURCE_HANDLE_T src, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_push_blocking ( BUFMANX_HANDLE_T *h, const BUFMANX_IMAGE_T *src, DISPMANX_RESOURCE_HANDLE_T dst, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_pull_striped ( BUFMANX_HANDLE_T *xh, BUFMANX_IMAGE_T *dst, const DISPMANX_RESOURCE_HANDLE_T src, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_pull_striped_blocking ( BUFMANX_HANDLE_T *xh, BUFMANX_IMAGE_T *dst, const DISPMANX_RESOURCE_HANDLE_T src, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_push_striped ( BUFMANX_HANDLE_T *xh, const BUFMANX_IMAGE_T *src, DISPMANX_RESOURCE_HANDLE_T dst, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_push_striped_blocking ( BUFMANX_HANDLE_T *xh, const BUFMANX_IMAGE_T *src, DISPMANX_RESOURCE_HANDLE_T dst, const VC_RECT_T *src_rect, const VC_RECT_T *dest_rect, BUFMAN_TRANSFORM_T transform ); + +VCHPRE_ void VCHPOST_ vc_bufmanx_push_multi ( BUFMANX_HANDLE_T *h, const BUFMANX_IMAGE_T *src, DISPMANX_RESOURCE_HANDLE_T dst, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); +VCHPRE_ void VCHPOST_ vc_bufmanx_pull_multi ( BUFMANX_HANDLE_T *h, BUFMANX_IMAGE_T *dst, const DISPMANX_RESOURCE_HANDLE_T src, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_push_multi_blocking ( BUFMANX_HANDLE_T *h, const BUFMANX_IMAGE_T *src, DISPMANX_RESOURCE_HANDLE_T dst, BUFMAN_TRANSFORM_T transform ); +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_pull_multi_blocking ( BUFMANX_HANDLE_T *h, BUFMANX_IMAGE_T *dst, const DISPMANX_RESOURCE_HANDLE_T src, BUFMAN_TRANSFORM_T transform ); + +// Allocate the specified number and type of buffers on the server side, for use with streams +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_allocate_buffers + (uint32_t stream, uint32_t num_of_buffers, + buf_frame_type_t type, uint32_t width, uint32_t height); + +// Free buffers on the server which are associated with the specified stream +#define VC_BUFMANX_FREE_BUFFERS_ALL 0 +VCHPRE_ int32_t VCHPOST_ vc_bufmanx_free_buffers(uint32_t stream, uint32_t num_of_buffers); + +// Like vc_bufmanx_push_multi(), but specifies a stream, rather than a dispmanx resource handle, +// to push the data to. +VCHPRE_ void VCHPOST_ vc_bufmanx_push_multi_stream ( BUFMANX_HANDLE_T *xh, const BUFMANX_IMAGE_T *src, uint32_t stream, BUFMAN_TRANSFORM_T transform, vc_bufman_callback_t callback, void *cookie, void *cookie2 ); + +VCHPRE_ VC_IMAGE_TYPE_T VCHPOST_ vc_bufmanx_get_vc_image_type(buf_frame_type_t bm_type); + +#ifdef __SYMBIAN32__ +} // namespace BufManX +#endif + +#endif /* VC_VCHI_BUFMAN_H */ diff --git a/interface/vmcs_host/vc_vchi_bufman_defs.h b/interface/vmcs_host/vc_vchi_bufman_defs.h new file mode 100755 index 0000000..57b79e0 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_bufman_defs.h @@ -0,0 +1,136 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_VCHI_BUFMAN_DEFS_H +#define VC_VCHI_BUFMAN_DEFS_H + +#ifdef __SYMBIAN32__ +typedef uint32_t DISPMANX_RESOURCE_HANDLE_T; +namespace BufManX { +#else +#include "interface/vmcs_host/vc_dispmanx.h" +#endif + +typedef enum { + // Insert extra frame types here + FRAME_HOST_IMAGE_BASE = 0x20000, // Base for host format images + FRAME_HOST_IMAGE_EFormatYuv420P, + FRAME_HOST_IMAGE_EFormatYuv422P, + FRAME_HOST_IMAGE_EFormatYuv422LE, + FRAME_HOST_IMAGE_EFormatRgb565, + FRAME_HOST_IMAGE_EFormatRgb888, + FRAME_HOST_IMAGE_EFormatRgbU32, + FRAME_HOST_IMAGE_EFormatRgbA32, + FRAME_HOST_IMAGE_EFormatRgbA32LE, + FRAME_HOST_IMAGE_EFormatRgbU32LE, + + FRAME_FORCE_FIELD_WIDTH = 0xFFFFFFFF +} buf_frame_type_t; + +typedef enum { + //host to videocore + VC_BUFMAN_CONVERT_UNUSED = 0, + VC_BUFMAN_PULL_FRAME, + VC_BUFMAN_PUSH_FRAME, + VC_BUFMAN_MESSAGE_RESPONSE, + VC_BUFMAN_SYNC, + VC_BUFMAN_ALLOC_BUF, + VC_BUFMAN_FREE_BUF, + VC_BUFMAN_PULL_MULTI, + VC_BUFMAN_PUSH_MULTI, + VC_BUFMAN_PUSH_MULTI_STREAM, + //vc to host + VC_BUFMAN_FRAME_SENT_CALLBACK, + VC_BUFMAN_FORCE_WIDTH = 0x7fffffff, +} buf_command_t; + +/* A header used for all messages sent and received by bufman. + */ +typedef struct { + buf_command_t command; +} BUF_MSG_HDR_T; + +/* General remotely call this bufman operation commands */ +typedef struct { + uint32_t resource_handle; + buf_frame_type_t type; + int32_t size, width, height, pitch; + VC_RECT_T src_rect; // in 16.16 units + VC_RECT_T dest_rect; // in 32.0 units +} BUF_MSG_REMOTE_FUNCTION_FRAME_T; + +typedef struct { + int32_t status; + int32_t total_stripes; + // normal stipe height and size + int32_t stripe_height, stripe_size; + // last stripe size (if height not a mulitple of stripe height, last stripe nay be smaller) + int32_t last_stripe_height, last_stripe_size; +} BUF_MSG_RESPONSE_T; + +typedef struct +{ + uint32_t stream; + uint32_t num_of_buffers; + buf_frame_type_t type; + uint32_t width; + uint32_t height; +} BUF_MSG_ALLOC_BUF_FRAME_T; + +typedef struct +{ + uint32_t stream; + uint32_t num_of_buffers; +} BUF_MSG_FREE_BUF_FRAME_T; + +typedef struct { + BUF_MSG_HDR_T hdr; + union { + BUF_MSG_REMOTE_FUNCTION_FRAME_T frame; + BUF_MSG_RESPONSE_T message_response; + BUF_MSG_ALLOC_BUF_FRAME_T alloc_buf_frame; + BUF_MSG_FREE_BUF_FRAME_T free_buf_frame; + } u; +} BUF_MSG_T; + +enum { + //host to videocore + VC_BUFMAN_ERROR_NONE = 0, + VC_BUFMAN_ERROR_BAD_GENERALLY = -1, + VC_BUFMAN_ERROR_BAD_RESOURCE = -2, + VC_BUFMAN_ERROR_BAD_TRANSFORM = -3, + VC_BUFMAN_ERROR_BAD_RESIZE = -4, + VC_BUFMAN_ERROR_BAD_HOST_FORMAT = -5, + VC_BUFMAN_ERROR_BAD_VC_FORMAT = -6, + VC_BUFMAN_ERROR_BAD_SIZE = -7, +}; + +#ifdef __SYMBIAN32__ +} // namespace BufManX +#endif + +#endif /* VC_VCHI_BUFMAN_DEFS_H */ diff --git a/interface/vmcs_host/vc_vchi_cecservice.c b/interface/vmcs_host/vc_vchi_cecservice.c new file mode 100755 index 0000000..616b5b4 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_cecservice.c @@ -0,0 +1,1365 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include "vchost_platform_config.h" +#include "vchost.h" + +#include "interface/vcos/vcos.h" +#include "interface/vchi/vchi.h" +#include "interface/vchi/common/endian.h" +#include "interface/vchi/message_drivers/message.h" +#include "vc_cecservice.h" +#include "vc_service_common.h" + +/****************************************************************************** +Local types and defines. +******************************************************************************/ +#ifndef _min +#define _min(x,y) (((x) <= (y))? (x) : (y)) +#endif +#ifndef _max +#define _max(x,y) (((x) >= (y))? (x) : (y)) +#endif + +//TV service host side state (mostly the same as Videocore side - TVSERVICE_STATE_T) +typedef struct { + //Generic service stuff + VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC + VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification + uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS]; + char command_buffer[CECSERVICE_MSGFIFO_SIZE]; + char response_buffer[CECSERVICE_MSGFIFO_SIZE]; + uint32_t response_length; + uint32_t notify_buffer[CECSERVICE_MSGFIFO_SIZE/sizeof(uint32_t)]; + uint32_t notify_length; + uint32_t num_connections; + VCOS_MUTEX_T lock; + CECSERVICE_CALLBACK_T notify_fn; + void *notify_data; + int initialised; + int to_exit; + + //CEC state, not much here + //Most things live on Videocore side + uint16_t physical_address; //16-bit packed physical address + CEC_DEVICE_TYPE_T logical_address; //logical address + VC_CEC_TOPOLOGY_T *topology; //16-byte aligned for the transfer + +} CECSERVICE_HOST_STATE_T; + +/****************************************************************************** +Static data. +******************************************************************************/ +static CECSERVICE_HOST_STATE_T cecservice_client; +static VCOS_EVENT_T cecservice_message_available_event; +static VCOS_EVENT_T cecservice_notify_available_event; +static VCOS_THREAD_T cecservice_notify_task; +static uint32_t cecservice_log_initialised = 0; + +//Command strings - must match what's in vc_cecservice_defs.h +static char* cecservice_command_strings[] = { + "register_cmd", + "register_all", + "deregister_cmd", + "deregister_all", + "send_msg", + "get_logical_addr", + "alloc_logical_addr", + "release_logical_addr", + "get_topology", + "set_vendor_id", + "set_osd_name", + "get_physical_addr", + "get_vendor_id", + "poll_addr", + "set_logical_addr", + "add_device", + "set_passive", + "end_of_list" +}; + +static const uint32_t max_command_strings = sizeof(cecservice_command_strings)/sizeof(char *); + +//Notification strings - must match vc_cec.h VC_CEC_NOTIFY_T +static char* cecservice_notify_strings[] = { + "none", + "TX", + "RX", + "User Ctrl Pressed", + "User Ctrl Released", + "Vendor Remote Down", + "Vendor Remote Up", + "logical address", + "topology", + "logical address lost", + "???" +}; + +static const uint32_t max_notify_strings = sizeof(cecservice_notify_strings)/sizeof(char *); + +//Device type strings +static char* cecservice_devicetype_strings[] = { + "TV", + "Rec", + "Reserved", + "Tuner", + "Playback", + "Audio", + "Switch", + "VidProc", + "8", "9", "10", "11", "12", "13", "14", "invalid" +}; + +static const uint32_t max_devicetype_strings = sizeof(cecservice_devicetype_strings)/sizeof(char *); + +/****************************************************************************** +Static functions. +******************************************************************************/ +//Lock the host state +static __inline int lock_obtain (void) { + VCOS_STATUS_T status = VCOS_EAGAIN; + if(cecservice_client.initialised && (status = vcos_mutex_lock(&cecservice_client.lock)) == VCOS_SUCCESS) { + if(cecservice_client.initialised) { // check service hasn't been closed while we were waiting for the lock. + vchi_service_use(cecservice_client.client_handle[0]); + return status; + } else { + vcos_mutex_unlock(&cecservice_client.lock); + vc_cec_log_error("CEC Service closed while waiting for lock"); + return VCOS_EAGAIN; + } + } + vc_cec_log_error("CEC service failed to obtain lock, initialised:%d, lock status:%d", + cecservice_client.initialised, status); + return status; +} + +//Unlock the host state +static __inline void lock_release (void) { + if(cecservice_client.initialised) { + vchi_service_release(cecservice_client.client_handle[0]); + } + vcos_mutex_unlock(&cecservice_client.lock); +} + +//Forward declarations +static void cecservice_client_callback( void *callback_param, + VCHI_CALLBACK_REASON_T reason, + void *msg_handle ); + +static void cecservice_notify_callback( void *callback_param, + VCHI_CALLBACK_REASON_T reason, + void *msg_handle ); + +static int32_t cecservice_wait_for_reply(void *response, uint32_t max_length); + +static int32_t cecservice_wait_for_bulk_receive(void *buffer, uint32_t max_length); + +static int32_t cecservice_send_command( uint32_t command, const void *buffer, uint32_t length, uint32_t has_reply); + +static int32_t cecservice_send_command_reply( uint32_t command, void *buffer, uint32_t length, + void *response, uint32_t max_length); + +static void *cecservice_notify_func(void *arg); + +static void cecservice_logging_init(void); + +/****************************************************************************** + Global data +*****************************************************************************/ +VCOS_LOG_CAT_T cechost_log_category; + +/****************************************************************************** +CEC service API +******************************************************************************/ +/****************************************************************************** +NAME + vc_vchi_cec_init + +SYNOPSIS + void vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) + +FUNCTION + Initialise the CEC service for use. A negative return value + indicates failure (which may mean it has not been started on VideoCore). + +RETURNS + int +******************************************************************************/ +VCHPRE_ void VCHPOST_ vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) { + int32_t success = -1; + VCOS_STATUS_T status; + VCOS_THREAD_ATTR_T attrs; + uint32_t i; + + if (cecservice_client.initialised) + return; + + vc_cec_log_info("Initialising CEC service"); + // record the number of connections + vcos_memset( &cecservice_client, 0, sizeof(CECSERVICE_HOST_STATE_T) ); + cecservice_client.num_connections = num_connections; + cecservice_client.physical_address = CEC_CLEAR_ADDR; + cecservice_client.logical_address = CEC_AllDevices_eUnRegistered; + + status = vcos_mutex_create(&cecservice_client.lock, "HCEC"); + vcos_assert(status == VCOS_SUCCESS); + status = vcos_event_create(&cecservice_message_available_event, "HCEC"); + vcos_assert(status == VCOS_SUCCESS); + status = vcos_event_create(&cecservice_notify_available_event, "HCEC"); + vcos_assert(status == VCOS_SUCCESS); + + cecservice_client.topology = vcos_malloc_aligned(sizeof(VC_CEC_TOPOLOGY_T), 16, "HCEC topology"); + vcos_assert(cecservice_client.topology); + + for (i=0; i < cecservice_client.num_connections; i++) { + + // Create a 'Client' service on the each of the connections + SERVICE_CREATION_T cecservice_parameters = { VCHI_VERSION(VC_CECSERVICE_VER), + CECSERVICE_CLIENT_NAME, // 4cc service code + connections[i], // passed in fn ptrs + 0, // tx fifo size (unused) + 0, // tx fifo size (unused) + &cecservice_client_callback,// service callback + &cecservice_message_available_event, // callback parameter + VC_FALSE, // want_unaligned_bulk_rx + VC_FALSE, // want_unaligned_bulk_tx + VC_FALSE, // want_crc + }; + + SERVICE_CREATION_T cecservice_parameters2 = { VCHI_VERSION(VC_CECSERVICE_VER), + CECSERVICE_NOTIFY_NAME, // 4cc service code + connections[i], // passed in fn ptrs + 0, // tx fifo size (unused) + 0, // tx fifo size (unused) + &cecservice_notify_callback,// service callback + &cecservice_notify_available_event, // callback parameter + VC_FALSE, // want_unaligned_bulk_rx + VC_FALSE, // want_unaligned_bulk_tx + VC_FALSE, // want_crc + }; + + //Create the client to normal CEC service + success = vchi_service_open( initialise_instance, &cecservice_parameters, &cecservice_client.client_handle[i] ); + vcos_assert( success == 0 ); + if(success) + vc_cec_log_error("Failed to connected to CEC service: %d", success); + + //Create the client to the async CEC service (any CEC related notifications) + success = vchi_service_open( initialise_instance, &cecservice_parameters2, &cecservice_client.notify_handle[i] ); + vcos_assert( success == 0 ); + + if(success) + vc_cec_log_error("Failed to connected to CEC async service: %d", success); + + vchi_service_release(cecservice_client.client_handle[i]); + vchi_service_release(cecservice_client.notify_handle[i]); + } + + //Create the notifier task + vcos_thread_attr_init(&attrs); + vcos_thread_attr_setstacksize(&attrs, 2048); + vcos_thread_attr_settimeslice(&attrs, 1); + + //Initialise logging + cecservice_logging_init(); + + status = vcos_thread_create(&cecservice_notify_task, "HCEC Notify", &attrs, cecservice_notify_func, &cecservice_client); + vcos_assert(status == VCOS_SUCCESS); + + cecservice_client.initialised = 1; + vc_cec_log_info("CEC service initialised"); +} + +/*********************************************************** + * Name: vc_vchi_cec_stop + * + * Arguments: + * - + * + * Description: Stops the Host side part of CEC service + * + * Returns: - + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_vchi_cec_stop( void ) { + // Wait for the current lock-holder to finish before zapping TV service + uint32_t i; + + if (!cecservice_client.initialised) + return; + + if(lock_obtain() == 0) + { + void *dummy; + vchi_service_release(cecservice_client.client_handle[0]); + vc_cec_log_info("Stopping CEC service"); + for (i=0; i < cecservice_client.num_connections; i++) { + int32_t result; + vchi_service_use(cecservice_client.client_handle[i]); + vchi_service_use(cecservice_client.notify_handle[i]); + result = vchi_service_close(cecservice_client.client_handle[i]); + vcos_assert( result == 0 ); + result = vchi_service_close(cecservice_client.notify_handle[i]); + vcos_assert( result == 0 ); + } + cecservice_client.initialised = 0; + + lock_release(); + cecservice_client.to_exit = 1; + vcos_event_signal(&cecservice_notify_available_event); + vcos_thread_join(&cecservice_notify_task, &dummy); + vcos_mutex_delete(&cecservice_client.lock); + vcos_event_delete(&cecservice_message_available_event); + vcos_event_delete(&cecservice_notify_available_event); + vcos_free(cecservice_client.topology); + vc_cec_log_info("CEC service stopped"); + } +} + +/*********************************************************** + * Name: vc_cec_register_callaback + * + * Arguments: + * callback function, context to be passed when function is called + * + * Description: Register a callback function for all CEC notifications + * + * Returns: - + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) { + if(lock_obtain() == 0){ + cecservice_client.notify_fn = callback; + cecservice_client.notify_data = callback_data; + vc_cec_log_info("CEC service registered callback 0x%x", (uint32_t) callback); + lock_release(); + } else { + vc_cec_log_error("CEC service registered callback 0x%x failed", (uint32_t) callback); + } +} + +/********************************************************************************* + * + * Static functions definitions + * + *********************************************************************************/ +//TODO: Might need to handle multiple connections later +/*********************************************************** + * Name: cecservice_client_callback + * + * Arguments: semaphore, callback reason and message handle + * + * Description: Callback when a message is available for CEC service + * + ***********************************************************/ +static void cecservice_client_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) { + + VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; + + if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL) + return; + + vcos_event_signal(event); +} + +/*********************************************************** + * Name: cecservice_notify_callback + * + * Arguments: semaphore, callback reason and message handle + * + * Description: Callback when a message is available for CEC notify service + * + ***********************************************************/ +static void cecservice_notify_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) { + VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; + + if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL) + return; + + vcos_event_signal(event); +} + +/*********************************************************** + * Name: cecservice_wait_for_reply + * + * Arguments: response buffer, buffer length + * + * Description: blocked until something is in the buffer + * + * Returns zero if successful or error code of vchi otherwise (see vc_service_common_defs.h) + * If success, response is updated + * + ***********************************************************/ +static int32_t cecservice_wait_for_reply(void *response, uint32_t max_length) { + int32_t success = 0; + uint32_t length_read = 0; + do { + //TODO : we need to deal with messages coming through on more than one connections properly + //At the moment it will always try to read the first connection if there is something there + //Check if there is something in the queue, if so return immediately + //otherwise wait for the semaphore and read again + success = (int32_t) vchi2service_status(vchi_msg_dequeue( cecservice_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE )); + } while( length_read == 0 && vcos_event_wait(&cecservice_message_available_event) == VCOS_SUCCESS); + if(length_read) { + vc_cec_log_info("CEC service got reply %d bytes", length_read); + } else { + vc_cec_log_warn("CEC service wait for reply failed, error: %s", + vchi2service_status_string(success)); + } + + return success; +} + +/*********************************************************** + * Name: cecservice_wait_for_bulk_receive + * + * Arguments: response buffer, buffer length + * + * Description: blocked until bulk receive + * + * Returns error code of vchi + * + ***********************************************************/ +static int32_t cecservice_wait_for_bulk_receive(void *buffer, uint32_t max_length) { + if(!vcos_verify(buffer)) { + vc_cec_log_error("CEC: NULL buffer passed to wait_for_bulk_receive"); + return -1; + } + return (int32_t) vchi2service_status(vchi_bulk_queue_receive( cecservice_client.client_handle[0], + buffer, + max_length, + VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, + NULL )); +} + +/*********************************************************** + * Name: cecservice_send_command + * + * Arguments: command, parameter buffer, parameter legnth, has reply? (non-zero means yes) + * + * Description: send a command and optionally wait for its single value response (TV_GENERAL_RESP_T) + * + * Returns: < 0 if there is VCHI error, if tranmission is successful, value + * returned is the response from CEC server (which will be VC_CEC_ERROR_T (>= 0)) + * + ***********************************************************/ + +static int32_t cecservice_send_command( uint32_t command, const void *buffer, uint32_t length, uint32_t has_reply) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + int32_t success = 0; + int32_t response = -1; + vc_cec_log_info("CEC sending command %s length %d %s", + cecservice_command_strings[command], length, + (has_reply)? "has reply" : " no reply"); + if(lock_obtain() == 0) + { + success = (int32_t) vchi2service_status(vchi_msg_queuev(cecservice_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL )); + if(success == VC_SERVICE_VCHI_SUCCESS && has_reply) { + //otherwise only wait for a reply if we ask for one + success = cecservice_wait_for_reply(&response, sizeof(response)); + if(success == VC_SERVICE_VCHI_SUCCESS) { + response = VC_VTOH32(response); + } else { + response = success; + } + } else { + if(success != VC_SERVICE_VCHI_SUCCESS) + vc_cec_log_error("CEC failed to send command %s length %d, error: %s", + cecservice_command_strings[command], length, + vchi2service_status_string(success)); + //No reply expected or failed to send, send the success code back instead + response = success; + } + lock_release(); + } + return response; +} + +/*********************************************************** + * Name: cecservice_send_command_reply + * + * Arguments: command, parameter buffer, parameter legnth, reply buffer, buffer length + * + * Description: send a command and wait for its non-single value response (in a buffer) + * + * Returns: error code, host app is responsible to do endian translation + * + ***********************************************************/ +static int32_t cecservice_send_command_reply( uint32_t command, void *buffer, uint32_t length, + void *response, uint32_t max_length) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + int32_t success = VC_SERVICE_VCHI_VCHIQ_ERROR, ret = 0; + + vc_cec_log_info("CEC sending command (with reply) %s length %d", + cecservice_command_strings[command], length); + if(lock_obtain() == 0) + { + if((ret = vchi_msg_queuev( cecservice_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL )) == VC_SERVICE_VCHI_SUCCESS) { + success = cecservice_wait_for_reply(response, max_length); + } else { + vc_cec_log_error("CEC failed to send command %s length %d, error code %d", + cecservice_command_strings[command], length, ret); + } + lock_release(); + } + return success; +} + +/*********************************************************** + * Name: cecservice_notify_func + * + * Arguments: CEC service state + * + * Description: This is the notification task which receives all CEC + * service notifications + * + * Returns: does not return + * + ***********************************************************/ +static void *cecservice_notify_func(void *arg) { + int32_t success; + CECSERVICE_HOST_STATE_T *state = (CECSERVICE_HOST_STATE_T *) arg; + + vc_cec_log_info("CEC service async thread started"); + while(1) { + VCOS_STATUS_T status = vcos_event_wait(&cecservice_notify_available_event); + uint32_t cb_reason_str_idx = max_notify_strings - 1; + if(status != VCOS_SUCCESS || !state->initialised || state->to_exit) + break; + + do { + uint32_t reason, param1, param2, param3, param4; + //Get all notifications in the queue + success = vchi_msg_dequeue( state->notify_handle[0], state->notify_buffer, sizeof(state->notify_buffer), &state->notify_length, VCHI_FLAGS_NONE ); + if(success != 0 || state->notify_length < sizeof(uint32_t)*5 ) { //reason + 4x32-bit parameter + vcos_assert(state->notify_length == sizeof(uint32_t)*5); + break; + } + + //if(lock_obtain() != 0) + // break; + //All notifications are of format: reason, param1, param2, param3, param4 (all 32-bit unsigned int) + reason = VC_VTOH32(state->notify_buffer[0]); + param1 = VC_VTOH32(state->notify_buffer[1]); + param2 = VC_VTOH32(state->notify_buffer[2]); + param3 = VC_VTOH32(state->notify_buffer[3]); + param4 = VC_VTOH32(state->notify_buffer[4]); + //lock_release(); + + //Store away physical/logical addresses + if(CEC_CB_REASON(reason) == VC_CEC_LOGICAL_ADDR) { + state->logical_address = (CEC_DEVICE_TYPE_T) param1; + state->physical_address = (uint16_t) (param2 & 0xFFFF); + } + + switch(CEC_CB_REASON(reason)) { + case VC_CEC_NOTIFY_NONE: + cb_reason_str_idx = 0; break; + case VC_CEC_TX: + cb_reason_str_idx = 1; break; + case VC_CEC_RX: + cb_reason_str_idx = 2; break; + case VC_CEC_BUTTON_PRESSED: + cb_reason_str_idx = 3; break; + case VC_CEC_BUTTON_RELEASE: + cb_reason_str_idx = 4; break; + case VC_CEC_REMOTE_PRESSED: + cb_reason_str_idx = 5; break; + case VC_CEC_REMOTE_RELEASE: + cb_reason_str_idx = 6; break; + case VC_CEC_LOGICAL_ADDR: + cb_reason_str_idx = 7; break; + case VC_CEC_TOPOLOGY: + cb_reason_str_idx = 8; break; + case VC_CEC_LOGICAL_ADDR_LOST: + cb_reason_str_idx = 9; break; + } + + vc_cec_log_info("CEC service callback [%s]: 0x%x, 0x%x, 0x%x, 0x%x", + cecservice_notify_strings[cb_reason_str_idx], param1, param2, param3, param4); + + if(state->notify_fn) { + (*state->notify_fn)(state->notify_data, reason, param1, param2, param3, param4); + } else { + vc_cec_log_info("CEC service: No callback handler specified, callback [%s] swallowed", + cecservice_notify_strings[cb_reason_str_idx]); + } + + } while(success == 0 && state->notify_length >= sizeof(uint32_t)*5); //read the next message if any + } //while (1) + + if(state->to_exit) + vc_cec_log_info("CEC service async thread exiting"); + + return 0; +} + +/*********************************************************** + * Name: cecservice_logging_init + * + * Arguments: None + * + * Description: Initialise VCOS logging + * + * Returns: - + * + ***********************************************************/ +static void cecservice_logging_init() { + if(cecservice_log_initialised == 0) { + vcos_log_set_level(&cechost_log_category, VCOS_LOG_WARN); + vcos_log_register("cecservice-client", &cechost_log_category); + vc_cec_log_info("CEC HOST: log initialised"); + cecservice_log_initialised = 1; + } +} + + +/*********************************************************** + Actual CEC service API starts here +***********************************************************/ +/*********************************************************** + * Name: vc_cec_register_command (deprecated) + * + * Arguments: + * + * Description + * + * Returns: zero + ***********************************************************/ +VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_register_command(CEC_OPCODE_T opcode) { + return 0; +} + +/*********************************************************** + * Name: vc_cec_register_all (deprecated) + * + * Arguments: + * + * Description + * + * Returns: zero + ***********************************************************/ +VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_register_all( void ) { + return 0; +} + +/*********************************************************** + * Name: vc_cec_deregister_command (deprecated) + * + * Arguments: + * + * Description + * + * Returns: zero + ***********************************************************/ +VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_deregister_command(CEC_OPCODE_T opcode) { + return 0; +} + +/*********************************************************** + * Name: vc_cec_deregister_all (deprecated) + * + * Arguments: + * + * Description + * + * Returns: zero + ***********************************************************/ +VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_deregister_all( void ) { + return 0; +} + +/*********************************************************** + * Name: vc_cec_send_message + * + * Arguments: + * Follower's logical address + * Message payload WITHOUT the header byte (can be NULL) + * Payload length WITHOUT the header byte (can be zero) + * VC_TRUE if the message is a reply to an incoming message + * (For poll message set payload to NULL and length to zero) + * + * Description + * Remove all commands to be forwarded. This does not affect + * the button presses which are always forwarded + * + * Returns: if the command is successful (zero) or not (non-zero) + * If the command is successful, there will be a Tx callback + * in due course to indicate whether the message has been + * acknowledged by the recipient or not + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_message(const uint32_t follower, + const uint8_t *payload, + uint32_t length, + vcos_bool_t is_reply) { + int success = -1; + CEC_SEND_MSG_PARAM_T param; + if(!vcos_verify(length <= CEC_MAX_XMIT_LENGTH)) + return -1; + + param.follower = VC_HTOV32(follower); + param.length = VC_HTOV32(length); + param.is_reply = VC_HTOV32(is_reply); + vcos_memset(param.payload, 0, sizeof(param.payload)); + vc_cec_log_info("CEC service sending CEC message (%d->%d) (0x%02X) length %d%s", + cecservice_client.logical_address, follower, + (payload)? payload[0] : 0xFF, length, (is_reply)? " as reply" : ""); + + if(length > 0 && vcos_verify(payload)) { + char s[96] = {0}, *p = &s[0]; + int i; + vcos_memcpy(param.payload, payload, _min(length, CEC_MAX_XMIT_LENGTH)); + p += sprintf(p, "0x%02X", (cecservice_client.logical_address << 4) | (follower & 0xF)); + for(i = 0; i < _min(length, CEC_MAX_XMIT_LENGTH); i++) { + p += sprintf(p, " %02X", payload[i]); + } + vc_cec_log_info("CEC message: %s", s); + } + + success = cecservice_send_command( VC_CEC_SEND_MSG, ¶m, sizeof(param), 1); + return success; +} + +/*********************************************************** + * Name: vc_cec_get_logical_address + * + * Arguments: + * pointer to logical address + * + * Description + * Get the logical address, if one is being allocated + * 0xF (unregistered) will be returned + * + * Returns: if the command is successful (zero) or not (non-zero) + * logical_address is not modified if command failed + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_logical_address(CEC_AllDevices_T *logical_address) { + uint32_t response; + int32_t success = cecservice_send_command_reply( VC_CEC_GET_LOGICAL_ADDR, NULL, 0, + &response, sizeof(response)); + if(success == 0) { + *logical_address = (CEC_AllDevices_T)(VC_VTOH32(response) & 0xF); + vc_cec_log_info("CEC got logical address %d", *logical_address); + } + return success; +} + +/*********************************************************** + * Name: vc_cec_alloc_logical_address + * + * Arguments: + * None + * + * Description + * Start the allocation of a logical address. The host only + * needs to call this if the initial allocation failed + * (logical address being 0xF and physical address is NOT 0xFFFF + * from VC_CEC_LOGICAL_ADDR notification), or if the host explicitly + * released its logical address. + * + * Returns: if the command is successful (zero) or not (non-zero) + * If successful, there will be a callback notification + * VC_CEC_LOGICAL_ADDR. The host should wait for this before + * calling this function again. + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_alloc_logical_address( void ) { + return cecservice_send_command( VC_CEC_ALLOC_LOGICAL_ADDR, NULL, 0, 0); +} + +/*********************************************************** + * Name: vc_cec_release_logical_address + * + * Arguments: + * None + * + * Description + * Release our logical address. This effectively disables CEC. + * The host will need to allocate a new logical address before + * doing any CEC calls (send/receive message, get topology, etc.). + * + * Returns: if the command is successful (zero) or not (non-zero) + * The host should get a callback VC_CEC_LOGICAL_ADDR with + * 0xF being the logical address and the current physical address. + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_release_logical_address( void ) { + return cecservice_send_command( VC_CEC_RELEASE_LOGICAL_ADDR, NULL, 0, 0); +} + +/*********************************************************** + * Name: vc_cec_get_topology (deprecated) + * + * Arguments: + * + * Description + * + * Returns: if the command is successful (zero) or not (non-zero) + * + ***********************************************************/ +VCHPRE_ int VCOS_DEPRECATED("returns invalid result") VCHPOST_ vc_cec_get_topology( VC_CEC_TOPOLOGY_T* topology) { + int32_t success = -1; + vchi_service_use(cecservice_client.client_handle[0]); + success = cecservice_send_command( VC_CEC_GET_TOPOLOGY, NULL, 0, 1); + if(success == 0) { + success = cecservice_wait_for_bulk_receive(cecservice_client.topology, sizeof(VC_CEC_TOPOLOGY_T)); + } + vchi_service_release(cecservice_client.client_handle[0]); + if(success == 0) { + int i; + cecservice_client.topology->active_mask = VC_VTOH16(cecservice_client.topology->active_mask); + cecservice_client.topology->num_devices = VC_VTOH16(cecservice_client.topology->num_devices); + vc_cec_log_info("CEC topology: mask=0x%x; #device=%d", + cecservice_client.topology->active_mask, + cecservice_client.topology->num_devices); + for(i = 0; i < 15; i++) { + cecservice_client.topology->device_attr[i] = VC_VTOH32(cecservice_client.topology->device_attr[i]); + } + vcos_memcpy(topology, cecservice_client.topology, sizeof(VC_CEC_TOPOLOGY_T)); + } + return success; +} + +/*********************************************************** + * Name: vc_cec_set_vendor_id + * + * Arguments: + * 24-bit IEEE vendor id + * + * Description + * Set the response to + * + * Returns: if the command is successful (zero) or not (non-zero) + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_set_vendor_id( uint32_t id ) { + uint32_t vendor_id = VC_HTOV32(id); + vc_cec_log_info("CEC setting vendor id to 0x%x", vendor_id); + return cecservice_send_command( VC_CEC_SET_VENDOR_ID, &vendor_id, sizeof(vendor_id), 0); +} + +/*********************************************************** + * Name: vc_cec_set_osd_name + * + * Arguments: + * OSD name (14 byte array) + * + * Description + * Set the response to + * + * Returns: if the command is successful (zero) or not (non-zero) + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_set_osd_name( const char* name ) { + vc_cec_log_info("CEC setting OSD name to %s", name); + return cecservice_send_command( VC_CEC_SET_OSD_NAME, name, OSD_NAME_LENGTH, 0); +} + +/*********************************************************** + * Name: vc_cec_get_physical_address + * + * Arguments: + * pointer to physical address (returned as 16-bit packed value) + * + * Description + * Get the physical address + * + * Returns: if the command is successful (zero) or not (non-zero) + * If failed, physical address argument will not be changed + * A physical address of 0xFFFF means CEC is not supported + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_physical_address(uint16_t *physical_address) { + uint32_t response; + int32_t success = cecservice_send_command_reply( VC_CEC_GET_PHYSICAL_ADDR, NULL, 0, + &response, sizeof(response)); + if(success == 0) { + *physical_address = (uint16_t)(VC_VTOH32(response) & 0xFFFF); + vc_cec_log_info("CEC got physical address: %d.%d.%d.%d", + (*physical_address >> 12), (*physical_address >> 8) & 0xF, + (*physical_address >> 4) & 0xF, (*physical_address) & 0xF); + } + return success; +} + + +/*********************************************************** + * Name: vc_cec_get_vendor_id + * + * Arguments: + * logical address [in] + * pointer to 24-bit IEEE vendor id [out] + * + * Description + * Get the vendor ID of the device with the said logical address + * Application should send if vendor ID + * is not known (and register opcode ) + * + * Returns: if the command is successful (zero) or not (non-zero) + * vendor ID is set to zero if unknown or 0xFFFFFF if + * device does not exist. + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_get_vendor_id( const CEC_AllDevices_T logical_address, uint32_t *vendor_id) { + uint32_t log_addr = VC_HTOV32(logical_address); + uint32_t response; + int32_t success = cecservice_send_command_reply( VC_CEC_GET_VENDOR_ID, &log_addr, sizeof(log_addr), + &response, sizeof(response)); + if(success == 0) { + vcos_assert(vendor_id); + *vendor_id = VC_VTOH32(response); + vc_cec_log_info("CEC got vendor id 0x%X", *vendor_id); + } + return success; +} +/*********************************************************** + * Name: vc_cec_device_type + * + * Arguments: + * logical address [in] + * + * Description + * Get the default device type of a logical address + * Logical address 12 to 14 cannot be used + * + * Returns: For logical addresses 0-11 the default + * device type of that address will be returned + * logical address 12-14 will return "reserved" type. + * + ***********************************************************/ +VCHPRE_ CEC_DEVICE_TYPE_T VCHPOST_ vc_cec_device_type(const CEC_AllDevices_T logical_address) { + CEC_DEVICE_TYPE_T device_type = CEC_DeviceType_Invalid; + switch(logical_address) { + case CEC_AllDevices_eSTB1: + case CEC_AllDevices_eSTB2: + case CEC_AllDevices_eSTB3: + case CEC_AllDevices_eSTB4: + device_type = CEC_DeviceType_Tuner; + break; + case CEC_AllDevices_eDVD1: + case CEC_AllDevices_eDVD2: + case CEC_AllDevices_eDVD3: + device_type = CEC_DeviceType_Playback; + break; + case CEC_AllDevices_eRec1: + case CEC_AllDevices_eRec2: + case CEC_AllDevices_eRec3: + device_type = CEC_DeviceType_Rec; + break; + case CEC_AllDevices_eAudioSystem: + device_type = CEC_DeviceType_Audio; + break; + case CEC_AllDevices_eTV: + device_type = CEC_DeviceType_TV; + break; + case CEC_AllDevices_eRsvd3: + case CEC_AllDevices_eRsvd4: + case CEC_AllDevices_eFreeUse: + device_type = CEC_DeviceType_Reserved; //XXX: Are we allowed to use this? + break; + default: + vcos_assert(0); //Invalid + break; + } + return device_type; +} + +/*********************************************************** + * Name: vc_cec_send_message2 + * + * Arguments: + * pointer to encapsulated message + * + * Description + * Call vc_cec_send_message above + * messages are always sent as non-reply + * + * Returns: if the command is successful (zero) or not (non-zero) + * If the command is successful, there will be a Tx callback + * in due course to indicate whether the message has been + * acknowledged by the recipient or not + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_message2(const VC_CEC_MESSAGE_T *message) { + if(vcos_verify(message)) { + return vc_cec_send_message(message->follower, + (message->length)? + message->payload : NULL, + message->length, + VC_FALSE); + } else { + return -1; + } +} + +/*********************************************************** + * Name: vc_cec_param2message + * + * Arguments: + * arguments from CEC callback (reason, param1 to param4) + * pointer to VC_CEC_MESSAGE_T + * + * Description + * Turn the CEC_TX/CEC_RX/BUTTON_PRESS/BUTTON_RELEASE + * callback parameters back into an encapsulated form + * + * Returns: zero normally + * non-zero if something has gone wrong + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_param2message( const uint32_t reason, const uint32_t param1, + const uint32_t param2, const uint32_t param3, + const uint32_t param4, VC_CEC_MESSAGE_T *message) { + if(vcos_verify(message && + CEC_CB_REASON(reason) != VC_CEC_LOGICAL_ADDR && + CEC_CB_REASON(reason) != VC_CEC_TOPOLOGY)) { + message->length = CEC_CB_MSG_LENGTH(reason) - 1; //Length is without the header byte + message->initiator = CEC_CB_INITIATOR(param1); + message->follower = CEC_CB_FOLLOWER(param1); + if(message->length) { + uint32_t tmp = param1 >> 8; + vcos_memcpy(message->payload, &tmp, sizeof(uint32_t)-1); + vcos_memcpy(message->payload+sizeof(uint32_t)-1, ¶m2, sizeof(uint32_t)); + vcos_memcpy(message->payload+sizeof(uint32_t)*2-1, ¶m3, sizeof(uint32_t)); + vcos_memcpy(message->payload+sizeof(uint32_t)*3-1, ¶m4, sizeof(uint32_t)); + } else { + vcos_memset(message->payload, 0, sizeof(message->payload)); + } + return 0; + } else { + return -1; + } +} + +//Extra API if CEC is running in passive mode + +/*********************************************************** + * Name: vc_cec_poll_address + * + * Arguments: + * logical address to try + * + * Description + * Sets and polls a particular address to find out + * its availability in the CEC network. Only available + * when CEC is running in passive mode. The host can + * only call this function during logical address allocation stage. + * + * Returns: 0 if poll is successful (address is occupied) + * >0 if poll is unsuccessful (address is free if error code is VC_CEC_ERROR_NO_ACK) + * <0 other (VCHI) errors + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_poll_address(const CEC_AllDevices_T logical_address) { + uint32_t log_addr = VC_HTOV32(logical_address); + int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT; + int32_t success = -1; + vc_cec_log_info("CEC polling address %d", logical_address); + success = cecservice_send_command_reply( VC_CEC_POLL_ADDR, &log_addr, sizeof(log_addr), + &response, sizeof(response)); + return (success == 0)? response : success; +} + +/*********************************************************** + * Name: vc_cec_set_logical_address + * + * Arguments: + * logical address, device type, vendor id + * + * Description + * sets the logical address, device type and vendor ID to be in use. + * Only available when CEC is running in passive mode. It is the + * responsibility of the host to make sure the logical address + * is actually free (see vc_cec_poll_address). Physical address used + * will be what is read from EDID and cannot be set. + * + * Returns: 0 if successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_set_logical_address(const CEC_AllDevices_T logical_address, + const CEC_DEVICE_TYPE_T device_type, + const uint32_t vendor_id) { + CEC_SET_LOGICAL_ADDR_PARAM_T param = {VC_HTOV32(logical_address), + VC_HTOV32(device_type), + VC_HTOV32(vendor_id)}; + int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT; + int32_t success = VC_CEC_ERROR_INVALID_ARGUMENT; + if(vcos_verify(logical_address <= CEC_AllDevices_eUnRegistered && + (device_type <= CEC_DeviceType_VidProc || + device_type == CEC_DeviceType_Invalid))) { + vc_cec_log_info("CEC setting logical address to %d; device type %s; vendor 0x%X", + logical_address, + cecservice_devicetype_strings[device_type], vendor_id ); + success = cecservice_send_command_reply( VC_CEC_SET_LOGICAL_ADDR, ¶m, sizeof(param), + &response, sizeof(response)); + } else { + vc_cec_log_error("CEC invalid arguments for set_logical_address"); + } + return (success == 0)? response : success; +} + +/*********************************************************** + * Name: vc_cec_add_device (deprecated) + * + * Arguments: + * + * Description + * + * Returns: 0 if successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_add_device(const CEC_AllDevices_T logical_address, + const uint16_t physical_address, + const CEC_DEVICE_TYPE_T device_type, + vcos_bool_t last_device) { + CEC_ADD_DEVICE_PARAM_T param = {VC_HTOV32(logical_address), + VC_HTOV32(physical_address), + VC_HTOV32(device_type), + VC_HTOV32(last_device)}; + int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT; + int32_t success = VC_CEC_ERROR_INVALID_ARGUMENT; + if(vcos_verify(logical_address <= CEC_AllDevices_eUnRegistered && + (device_type <= CEC_DeviceType_VidProc || + device_type == CEC_DeviceType_Invalid))) { + vc_cec_log_info("CEC adding device %d (0x%X); device type %s", + logical_address, physical_address, + cecservice_devicetype_strings[device_type]); + success = cecservice_send_command_reply( VC_CEC_ADD_DEVICE, ¶m, sizeof(param), + &response, sizeof(response)); + } else { + vc_cec_log_error("CEC invalid arguments for add_device"); + } + return (success == 0)? response : success; +} + +/*********************************************************** + * Name: vc_cec_set_passive + * + * Arguments: + * Enable/disable (TRUE to enable/ FALSE to disable) + * + * Description + * Enable / disable CEC passive mode + * + * Returns: 0 if successful, non-zero otherwise + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_set_passive(vcos_bool_t enabled) { + uint32_t param = VC_HTOV32(enabled); + int32_t response; + int32_t success = cecservice_send_command_reply( VC_CEC_SET_PASSIVE, ¶m, sizeof(param), + &response, sizeof(response)); + return (success == 0)? response : success; +} + +/*********************************************************** + API for some common CEC messages, uses the API above to + actually send the message +***********************************************************/ + +/*********************************************************** + * Name: vc_cec_send_FeatureAbort + * + * Arguments: + * follower, rejected opcode, reject reason, reply or not + * + * Description + * send for a received command + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_FeatureAbort(uint32_t follower, + CEC_OPCODE_T opcode, + CEC_ABORT_REASON_T reason) { + uint8_t tx_buf[3]; + tx_buf[0] = CEC_Opcode_FeatureAbort; // + tx_buf[1] = opcode; + tx_buf[2] = reason; + return vc_cec_send_message(follower, + tx_buf, + sizeof(tx_buf), + VC_TRUE); +} + +/*********************************************************** + * Name: vc_cec_send_ActiveSource + * + * Arguments: + * physical address, reply or not + * + * Description + * send + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_ActiveSource(uint16_t physical_address, + vcos_bool_t is_reply) { + uint8_t tx_buf[3]; + tx_buf[0] = CEC_Opcode_ActiveSource; // + tx_buf[1] = physical_address >> 8; // physical address msb + tx_buf[2] = physical_address & 0x00FF; // physical address lsb + return vc_cec_send_message(CEC_BROADCAST_ADDR, // This is a broadcast only message + tx_buf, + sizeof(tx_buf), + is_reply); +} + +/*********************************************************** + * Name: vc_cec_send_ImageViewOn + * + * Arguments: + * follower, reply or not + * Description + * send + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_ImageViewOn(uint32_t follower, + vcos_bool_t is_reply) { + uint8_t tx_buf[1]; + tx_buf[0] = CEC_Opcode_ImageViewOn; // no param required + return vc_cec_send_message(follower, + tx_buf, + sizeof(tx_buf), + is_reply); +} + +/*********************************************************** + * Name: vc_cec_send_SetOSDString + * + * Arguments: + * follower, display control, string (char[13]), reply or not + * + * Description + * send + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_SetOSDString(uint32_t follower, + CEC_DISPLAY_CONTROL_T disp_ctrl, + const char* string, + vcos_bool_t is_reply) { + uint8_t tx_buf[CEC_MAX_XMIT_LENGTH]; + tx_buf[0] = CEC_Opcode_SetOSDString; // + tx_buf[1] = disp_ctrl; + vcos_memset(&tx_buf[2], 0, sizeof(tx_buf)-2); + vcos_memcpy(&tx_buf[2], string, _min(strlen(string), CEC_MAX_XMIT_LENGTH-2)); + return vc_cec_send_message(follower, + tx_buf, + sizeof(tx_buf), + is_reply); +} + +/*********************************************************** + * Name: vc_cec_send_Standby + * + * Arguments: + * follower, reply or not + * + * Description + * send . Turn other devices to standby + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_Standby(uint32_t follower, vcos_bool_t is_reply) { + uint8_t tx_buf[1]; + tx_buf[0] = CEC_Opcode_Standby; // + return vc_cec_send_message(follower, + tx_buf, + sizeof(tx_buf), + is_reply); +} + +/*********************************************************** + * Name: vc_cec_send_MenuStatus + * + * Arguments: + * follower, menu state, reply or not + * + * Description + * send (response to ) + * menu state is either CEC_MENU_STATE_ACTIVATED or CEC_MENU_STATE_DEACTIVATED + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_MenuStatus(uint32_t follower, + CEC_MENU_STATE_T menu_state, + vcos_bool_t is_reply) { + uint8_t tx_buf[2]; + if(!vcos_verify(menu_state < CEC_MENU_STATE_QUERY)) + return -1; + + tx_buf[0] = CEC_Opcode_MenuStatus; // + tx_buf[1] = menu_state; + return vc_cec_send_message(follower, + tx_buf, + sizeof(tx_buf), + is_reply); +} + +/*********************************************************** + * Name: vc_cec_send_ReportPhysicalAddress + * + * Arguments: + * physical address, device type, reply or not + * + * Description + * send (first command to be + * sent after a successful logical address allocation + * device type should be the appropriate one for + * the allocated logical address + * + * Returns: if the command is successful (zero) or not (non-zero) + * Tx callback if successful. We also get a failure + * if we do not currently have a valid physical address + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_cec_send_ReportPhysicalAddress(uint16_t physical_address, + CEC_DEVICE_TYPE_T device_type, + vcos_bool_t is_reply) { + uint8_t tx_buf[4]; + if(vcos_verify(physical_address == cecservice_client.physical_address && + cecservice_client.physical_address != CEC_CLEAR_ADDR)) { + tx_buf[0] = CEC_Opcode_ReportPhysicalAddress; + tx_buf[1] = physical_address >> 8; // physical address msb + tx_buf[2] = physical_address & 0x00FF; // physical address lsb + tx_buf[3] = device_type; // 'device type' + return vc_cec_send_message(CEC_BROADCAST_ADDR, // This is a broadcast only message + tx_buf, + sizeof(tx_buf), + is_reply); + } else { + //Current we do not allow sending a random physical address + vc_cec_log_error("CEC cannot send physical address 0x%X, does not match internal 0x%X", + physical_address, cecservice_client.physical_address); + return VC_CEC_ERROR_NO_PA; + } +} diff --git a/interface/vmcs_host/vc_vchi_dispmanx.c b/interface/vmcs_host/vc_vchi_dispmanx.c new file mode 100755 index 0000000..7a6cdcd --- /dev/null +++ b/interface/vmcs_host/vc_vchi_dispmanx.c @@ -0,0 +1,1321 @@ +/* +Copyright (c) 2012-2014, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "vchost_platform_config.h" +#include "vchost.h" + +#include "interface/vcos/vcos.h" +#include "vc_dispservice_x_defs.h" +#include "vc_dispmanx.h" +#include "interface/vchi/vchi.h" +#include "vcinclude/common.h" +#include "interface/vchi/common/endian.h" +#include "interface/vchi/message_drivers/message.h" +#include "vc_vchi_dispmanx.h" +/****************************************************************************** +Local types and defines. +******************************************************************************/ +//DispmanX service +typedef struct { + VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC + VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification + uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS]; + char command_buffer[DISPMANX_MSGFIFO_SIZE]; + char response_buffer[DISPMANX_MSGFIFO_SIZE]; + uint32_t response_length; + uint32_t notify_buffer[DISPMANX_MSGFIFO_SIZE/sizeof(uint32_t)]; + uint32_t notify_length; + uint32_t num_connections; + VCOS_MUTEX_T lock; + char dispmanx_devices[DISPMANX_MAX_HOST_DEVICES][DISPMANX_MAX_DEVICE_NAME_LEN]; + uint32_t num_devices; + uint32_t num_modes[DISPMANX_MAX_HOST_DEVICES]; + + //Callback for update + DISPMANX_CALLBACK_FUNC_T update_callback; + void *update_callback_param; + DISPMANX_UPDATE_HANDLE_T pending_update_handle; + + //Callback for vsync + DISPMANX_CALLBACK_FUNC_T vsync_callback; + void *vsync_callback_param; + int vsync_enabled; + + int initialised; +} DISPMANX_SERVICE_T; + +/****************************************************************************** +Static data. +******************************************************************************/ +static DISPMANX_SERVICE_T dispmanx_client; +static VCOS_EVENT_T dispmanx_message_available_event; +static VCOS_EVENT_T dispmanx_notify_available_event; +static VCOS_THREAD_T dispmanx_notify_task; + +/****************************************************************************** +Static functions. +******************************************************************************/ +//Lock the host state +static __inline void lock_obtain (void) { + VCOS_STATUS_T status; + uint32_t i; + vcos_assert(dispmanx_client.initialised); + status = vcos_mutex_lock( &dispmanx_client.lock ); + if(dispmanx_client.initialised) + { + for (i=0; ix = (int32_t) x_offset; + rect->y = (int32_t) y_offset; + rect->width = (int32_t) width; + rect->height = (int32_t) height; + return 0; +} + +/****************************************************************************** +NAME + vc_dispmanx_query_image_formats + +PARAMS + uint32_t *support_formats - the returned supported image formats + +FUNCTION + Returns the support image formats from the VMCS host + +RETURNS + Success: 0 + Otherwise non-zero +******************************************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_query_image_formats( uint32_t *supported_formats ) { + *supported_formats = dispmanx_get_handle(EDispmanQueryImageFormats, NULL, 0); + return (*supported_formats)? 0 : -1; +} + +/*********************************************************** + * Name: vc_dispmanx_resource_create + * + * Arguments: + * VC_IMAGE_TYPE_T type + * uint32_t width + * uint32_t height + * + * Description: Create a new resource (in Videocore memory) + * + * Returns: resource handle + * + ***********************************************************/ +VCHPRE_ DISPMANX_RESOURCE_HANDLE_T VCHPOST_ vc_dispmanx_resource_create( VC_IMAGE_TYPE_T type, uint32_t width, uint32_t height, uint32_t *native_image_handle ) { + uint32_t resourceCreateParams[] = { (uint32_t)VC_HTOV32(type), VC_HTOV32(width), VC_HTOV32(height) }; + + uint32_t resource = 0; + + resource = dispmanx_get_handle(EDispmanResourceCreate, resourceCreateParams, sizeof(resourceCreateParams)); + + //We don't get an image handle back, so explicitly set this to zero to let the caller know + *native_image_handle = 0; + //The caller should call vc_dispmanx_resource_get_image_handle below to get the VC_IMAGE_T * + //This will be deprecated soon, however + + return (DISPMANX_RESOURCE_HANDLE_T) resource; +} + +/*********************************************************** + * Name: vc_dispmanx_resource_get_image_handle + * + * Arguments: resource handle + * + * Description: xxx only for hacking purpose, will be obsolete soon + * + * Returns: vc_image pointer + * + ***********************************************************/ +VCHPRE_ uint32_t VCHPOST_ vc_dispmanx_resource_get_image_handle( DISPMANX_RESOURCE_HANDLE_T res) { + return dispmanx_get_handle(EDispmanResourceGetImage, &res, sizeof(res)); +} + +/*********************************************************** + * Name: vc_dispmanx_resource_delete + * + * Arguments: + * DISPMANX_RESOURCE_HANDLE_T res + * + * Description: + * + * Returns: 0 or failure + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_delete( DISPMANX_RESOURCE_HANDLE_T res ) { + int status; + res = VC_HTOV32(res); + //We block to make sure the memory is freed after the call + status = (int) dispmanx_send_command(EDispmanResourceDelete, &res, sizeof(res)); + + return status; +} + +/*********************************************************** + * Name: vc_dispmanx_resource_write_data + * + * Arguments: + * DISPMANX_RESOURCE_HANDLE_T res + * int src_pitch + * void * src_address + * const VC_RECT_T * rect + * + * Description: Copy the bitmap data to VideoCore memory + * + * Returns: 0 or failure + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data( DISPMANX_RESOURCE_HANDLE_T handle, VC_IMAGE_TYPE_T src_type /* not used */, + int src_pitch, void * src_address, const VC_RECT_T * rect ) { + (void)src_type; + + //Note that x coordinate of the rect is NOT used + //Address of data in host + uint8_t *host_start = (uint8_t *)src_address + src_pitch * rect->y; + int32_t bulk_len = src_pitch * rect->height, success = 0; + + //Now send the bulk transfer across + //command parameters: resource handle, destination y, bulk length + uint32_t param[] = {VC_HTOV32(handle), VC_HTOV32(rect->y), VC_HTOV32(bulk_len), VC_HTOV32(src_type) }; + success = dispmanx_send_command( EDispmanBulkWrite | DISPMANX_NO_REPLY_MASK, param, sizeof(param)); + if(success == 0) + { + lock_obtain(); + success = vchi_bulk_queue_transmit( dispmanx_client.client_handle[0], + host_start, + bulk_len, + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, + NULL ); + lock_release(); + } + return (int) success; +} +/*********************************************************** + * Name: vc_dispmanx_resource_read_data + * + * Arguments: + * DISPMANX_RESOURCE_HANDLE_T res + * int src_pitch + * void * src_address + * const VC_RECT_T * rect + * + * Description: Copy the bitmap data from VideoCore memory + * + * Returns: 0 or failure + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ +vc_dispmanx_resource_read_data( + DISPMANX_RESOURCE_HANDLE_T handle, + const VC_RECT_T* p_rect, + void * dst_address, + uint32_t dst_pitch ) +{ + uint8_t* host_start; + int32_t bulk_len; + int32_t success = 0; + + if ( p_rect == 0 || dst_address == 0 || dst_pitch == 0 ) + { + return -1; + } + + host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y); + bulk_len = (int32_t)dst_pitch * p_rect->height; + + // Now send the bulk transfer across + // command parameters: resource handle, destination y, bulk length + uint32_t param[] = { VC_HTOV32(handle), VC_HTOV32(p_rect->y), VC_HTOV32(bulk_len) }; + success = dispmanx_send_command( EDispmanBulkRead | DISPMANX_NO_REPLY_MASK, param, sizeof(param)); + if (success == 0) + { + lock_obtain(); + success = vchi_bulk_queue_receive( dispmanx_client.client_handle[0], + host_start, + bulk_len, + VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, + 0 ); + lock_release(); + } + return (int) success; +} + +/*********************************************************** + * Name: vc_dispmanx_resource_write_data_handle + * + * Arguments: + * DISPMANX_RESOURCE_HANDLE_T res + * int src_pitch + * MEM_HANDLE_T handle + * uint32_t offset + * const VC_RECT_T * rect + * + * Description: Copy the bitmap data to VideoCore memory + * + * Returns: 0 or failure + * + ***********************************************************/ +#ifdef SELF_HOSTED +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data_handle( DISPMANX_RESOURCE_HANDLE_T handle, VC_IMAGE_TYPE_T src_type /* not used */, + int src_pitch, VCHI_MEM_HANDLE_T mem_handle, uint32_t offset, + const VC_RECT_T * rect ) { + int32_t bulk_len; + uint32_t param[3]; + uint32_t success = 0; + + //Note that x coordinate of the rect is NOT used + //Address of data in host + offset += src_pitch * rect->y; + bulk_len = src_pitch * rect->height; + + //Now send the bulk transfer across + //command parameters: resource handle, destination y, bulk length + param[0] = VC_HTOV32(handle); + param[1] = VC_HTOV32(rect->y); + param[2] = VC_HTOV32(bulk_len); + success = dispmanx_send_command( EDispmanBulkWrite | DISPMANX_NO_REPLY_MASK, param, sizeof(param)); + if(success == 0) + { + lock_obtain(); + success = vchi_bulk_queue_transmit_reloc( dispmanx_client.client_handle[0], + mem_handle, offset, + bulk_len, + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, + NULL ); + lock_release(); + } + return (int) success; +} +#endif + +/*********************************************************** + * Name: vc_dispmanx_display_open + * + * Arguments: + * uint32_t device + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open( uint32_t device ) { + uint32_t display_handle; + char *env = getenv("VC_DISPLAY"); + + if (device == 0 && env) + { + device = atoi(env); + } + + device = VC_HTOV32(device); + display_handle = dispmanx_get_handle(EDispmanDisplayOpen, + &device, sizeof(device)); + + return (DISPMANX_DISPLAY_HANDLE_T) display_handle; +} + +/*********************************************************** + * Name: vc_dispmanx_display_open_mode + * + * Arguments: + * uint32_t device + * uint32_t mode + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open_mode( uint32_t device, uint32_t mode ) { + uint32_t display_open_param[] = {VC_HTOV32(device), VC_HTOV32(mode)}; + uint32_t display_handle = dispmanx_get_handle(EDispmanDisplayOpenMode, + &display_open_param, sizeof(display_open_param)); + + return (DISPMANX_DISPLAY_HANDLE_T) display_handle; +} + +/*********************************************************** + * Name: vc_dispmanx_display_open_offscreen + * + * Arguments: + * DISPMANX_RESOURCE_HANDLE_T dest + * DISPMANX_TRANSFORM_T orientation + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open_offscreen( DISPMANX_RESOURCE_HANDLE_T dest, DISPMANX_TRANSFORM_T orientation ) { + uint32_t display_open_param[] = {(uint32_t)VC_HTOV32(dest), (uint32_t)VC_HTOV32(orientation)}; + uint32_t display_handle = dispmanx_get_handle(EDispmanDisplayOpenOffscreen, + &display_open_param, sizeof(display_open_param)); + + return (DISPMANX_DISPLAY_HANDLE_T) display_handle; +} + +/*********************************************************** + * Name: vc_dispmanx_display_reconfigure + * + * Arguments: + * DISPMANX_DISPLAY_HANDLE_T display + * uint32_t mode + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_display_reconfigure( DISPMANX_DISPLAY_HANDLE_T device, uint32_t mode ) { + uint32_t display_param[] = {(uint32_t)VC_HTOV32(device), VC_HTOV32(mode)}; + int32_t success = dispmanx_send_command( EDispmanDisplayReconfigure | DISPMANX_NO_REPLY_MASK, + display_param, sizeof(display_param)); + return (int) success; +} + +/*********************************************************** + * Name: vc_dispmanx_display_set_destination + * + * Arguments: + * DISPMANX_DISPLAY_HANDLE_T display + * DISPMANX_RESOURCE_HANDLE_T dest + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_display_set_destination( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_RESOURCE_HANDLE_T dest ) { + uint32_t display_param[] = {(uint32_t)VC_HTOV32(display), (uint32_t)VC_HTOV32(dest)}; + int32_t success = dispmanx_send_command( EDispmanDisplaySetDestination | DISPMANX_NO_REPLY_MASK, + display_param, sizeof(display_param)); + return (int) success; +} + +/*********************************************************** + * Name: vc_dispmanx_display_set_background + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_DISPLAY_HANDLE_T display + * uint8_t red + * uint8_t green + * uint8_t blue + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display, + uint8_t red, uint8_t green, uint8_t blue ) { + uint32_t display_param[] = {(uint32_t)VC_HTOV32(update), (uint32_t) VC_HTOV32(display), VC_HTOV32(red), VC_HTOV32(green), VC_HTOV32(blue)}; + int success = (int) dispmanx_send_command( EDispmanDisplaySetBackground | DISPMANX_NO_REPLY_MASK, + display_param, sizeof(display_param)); + return success; +} + +/*********************************************************** + * Name: vc_dispmanx_display_get_info + * + * Arguments: + * DISPMANX_DISPLAY_HANDLE_T display + * DISPMANX_MODEINFO_T * pinfo + * + * Description: + * + * Returns: VCHI error + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ +vc_dispmanx_display_get_info (DISPMANX_DISPLAY_HANDLE_T display, + DISPMANX_MODEINFO_T *pinfo) +{ + GET_INFO_DATA_T info; + int32_t success; + display = VC_HTOV32(display); + success = dispmanx_send_command_reply (EDispmanDisplayGetInfo, + &display, sizeof(display), + &info, sizeof(info)); + if(success == 0) { + pinfo->width = VC_VTOH32(info.width); + pinfo->height = VC_VTOH32(info.height); + pinfo->transform = (DISPMANX_TRANSFORM_T)VC_VTOH32(info.transform); + pinfo->input_format = (DISPLAY_INPUT_FORMAT_T)VC_VTOH32(info.input_format); + } + + return (int) success; +} + +/*********************************************************** + * Name: vc_dispmanx_display_close + * + * Arguments: + * DISPMANX_DISPLAY_HANDLE_T display + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ) { + int success; + display = VC_HTOV32(display); + success = (int) dispmanx_send_command( EDispmanDisplayClose | DISPMANX_NO_REPLY_MASK, + &display, sizeof(display)); + return success; +} +/*********************************************************** + * Name: vc_dispmanx_update_start + * + * Arguments: + * int32_t priority + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ DISPMANX_UPDATE_HANDLE_T VCHPOST_ vc_dispmanx_update_start( int32_t priority ) { + uint32_t handle; + priority = VC_HTOV32(priority); + handle = dispmanx_get_handle(EDispmanUpdateStart, + &priority, sizeof(priority)); + + return (DISPMANX_UPDATE_HANDLE_T) handle; +} + + +/*********************************************************** + * Name: vc_dispmanx_update_submit + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_CALLBACK_FUNC_T cb_func + * void *cb_arg + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_update_submit( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg ) { + uint32_t update_param[] = {(uint32_t) VC_HTOV32(update), (uint32_t) ((cb_func)? VC_HTOV32(1) : 0)}; + int success; + + vcos_assert(update); // handles must be non-zero + if (update) + { + //Set the callback + dispmanx_client.update_callback = cb_func; + dispmanx_client.update_callback_param = cb_arg; + dispmanx_client.pending_update_handle = update; + vchi_service_use(dispmanx_client.notify_handle[0]); // corresponding release is in dispmanx_notify_func + success = (int) dispmanx_send_command( EDispmanUpdateSubmit | DISPMANX_NO_REPLY_MASK, + update_param, sizeof(update_param)); + } + else + { + success = -1; + } + return success; +} + +/*********************************************************** + * Name: vc_dispmanx_update_submit_sync + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * + * Description: + * + * Returns: VCHI error code + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) { + int success; + update = VC_HTOV32(update); + success = (int) dispmanx_send_command( EDispmanUpdateSubmitSync, + &update, sizeof(update)); + return success; +} + + +/*********************************************************** + * Name: vc_dispmanx_element_add + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_DISPLAY_HANDLE_T display + * int32_t layer + * const VC_RECT_T *dest_rect + * DISPMANX_RESOURCE_HANDLE_T src + * const VC_RECT_T *src_rect + * DISPMANX_FLAGS_T flags + * uint8_t opacity + * DISPMANX_RESOURCE_HANDLE_T mask + * DISPMANX_TRANSFORM_T transform + * + * Description: + * + * Returns: VCHI error code + * + ***********************************************************/ +VCHPRE_ DISPMANX_ELEMENT_HANDLE_T VCHPOST_ vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, + DISPMANX_DISPLAY_HANDLE_T display, + int32_t layer, + const VC_RECT_T *dest_rect, + DISPMANX_RESOURCE_HANDLE_T src, + const VC_RECT_T *src_rect, + DISPMANX_PROTECTION_T protection, + VC_DISPMANX_ALPHA_T *alpha, + DISPMANX_CLAMP_T *clamp, + DISPMANX_TRANSFORM_T transform ) { + + int32_t element_param[] = { + (int32_t) VC_HTOV32(update), + (int32_t) VC_HTOV32(display), + (int32_t) VC_HTOV32(layer), + (int32_t) VC_HTOV32(dest_rect->x), + (int32_t) VC_HTOV32(dest_rect->y), + (int32_t) VC_HTOV32(dest_rect->width), + (int32_t) VC_HTOV32(dest_rect->height), + (int32_t) VC_HTOV32(src), + (int32_t) VC_HTOV32(src_rect->x), + (int32_t) VC_HTOV32(src_rect->y), + (int32_t) VC_HTOV32(src_rect->width), + (int32_t) VC_HTOV32(src_rect->height), + (int32_t) VC_HTOV32(protection), + alpha ? (int32_t) VC_HTOV32(alpha->flags) : 0, + alpha ? (int32_t) VC_HTOV32(alpha->opacity) : 0, + alpha ? (int32_t) VC_HTOV32(alpha->mask) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->mode) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_mask) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.yy_upper) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.yy_lower) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cr_upper) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cr_lower) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cb_upper) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cb_lower) : 0, + clamp ? (int32_t) VC_HTOV32(clamp->replace_value) : 0, + (int32_t) VC_HTOV32(transform) + }; + + uint32_t handle = dispmanx_get_handle(EDispmanElementAdd, + element_param, sizeof(element_param)); + return (DISPMANX_ELEMENT_HANDLE_T) handle; +} + +/*********************************************************** + * Name: vc_dispmanx_element_change_source + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_ELEMENT_HANDLE_T element + * DISPMANX_RESOURCE_HANDLE_T src + * + * Description: + * + * Returns: VCHI error code + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_source( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element, + DISPMANX_RESOURCE_HANDLE_T src ) { + uint32_t element_param[] = { (uint32_t) VC_HTOV32(update), + (uint32_t) VC_HTOV32(element), + (uint32_t) VC_HTOV32(src) }; + + int success = (int) dispmanx_send_command( EDispmanElementChangeSource | DISPMANX_NO_REPLY_MASK, + element_param, sizeof(element_param)); + return success; + +} + +/*********************************************************** + * Name: vc_dispmanx_element_change_layer + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_ELEMENT_HANDLE_T element + * int32_t layer + * + * Description: + * + * Returns: VCHI error code + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_layer (DISPMANX_UPDATE_HANDLE_T update, + DISPMANX_ELEMENT_HANDLE_T element, + int32_t layer) +{ + uint32_t element_param[] = { (uint32_t) VC_HTOV32(update), + (uint32_t) VC_HTOV32(element), + (uint32_t) VC_HTOV32(layer) }; + + int success = (int) dispmanx_send_command( EDispmanElementChangeLayer | DISPMANX_NO_REPLY_MASK, + element_param, sizeof(element_param)); + return success; + +} + +/*********************************************************** + * Name: vc_dispmanx_element_modified + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_ELEMENT_HANDLE_T element + * const VC_RECT_T * rect + * + * Description: + * + * Returns: VCHI error code + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_element_modified( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element, const VC_RECT_T * rect ) { + + uint32_t element_param[6] = { (uint32_t) VC_HTOV32(update), + (uint32_t) VC_HTOV32(element), 0, 0, 0, 0}; + uint32_t param_length = 2*sizeof(uint32_t); + int success; + + if(rect) { + element_param[2] = VC_HTOV32(rect->x); + element_param[3] = VC_HTOV32(rect->y); + element_param[4] = VC_HTOV32(rect->width); + element_param[5] = VC_HTOV32(rect->height); + param_length = 6*sizeof(uint32_t); + } + success = (int) dispmanx_send_command( EDispmanElementModified | DISPMANX_NO_REPLY_MASK, + element_param, param_length); + return success; +} + +/*********************************************************** + * Name: vc_dispmanx_element_remove + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_ELEMENT_HANDLE_T element + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ) { + uint32_t element_param[] = {(uint32_t) VC_HTOV32(update), (uint32_t) VC_HTOV32(element)}; + int success = (int) dispmanx_send_command( EDispmanElementRemove | DISPMANX_NO_REPLY_MASK, + element_param, sizeof(element_param)); + return success; +} + +/*********************************************************** + * Name: vc_dispmanx_element_change_attributes + * + * Arguments: + * DISPMANX_UPDATE_HANDLE_T update + * DISPMANX_ELEMENT_HANDLE_T element + * uint32_t change flags (bit 0 layer, bit 1 opacity, bit 2 dest rect, bit 3 src rect, bit 4 mask, bit 5 transform + * uint32_t layer + * uint8_t opacity + * const VC_RECT_T *dest rect + * const VC_RECT_T *src rect + * DISPMANX_RESOURCE_HANDLE_T mask + * VC_DISPMAN_TRANSFORM_T transform + * + * Description: + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_attributes( DISPMANX_UPDATE_HANDLE_T update, + DISPMANX_ELEMENT_HANDLE_T element, + uint32_t change_flags, + int32_t layer, + uint8_t opacity, + const VC_RECT_T *dest_rect, + const VC_RECT_T *src_rect, + DISPMANX_RESOURCE_HANDLE_T mask, + DISPMANX_TRANSFORM_T transform ) { + + uint32_t element_param[15] = { (uint32_t) VC_HTOV32(update), + (uint32_t) VC_HTOV32(element), + VC_HTOV32(change_flags), + VC_HTOV32(layer), + VC_HTOV32(opacity), + (uint32_t) VC_HTOV32(mask), + (uint32_t) VC_HTOV32(transform), 0, 0, 0, 0, 0, 0, 0, 0}; + + uint32_t param_length = 7*sizeof(uint32_t); + int success; + if(dest_rect) { + element_param[7] = VC_HTOV32(dest_rect->x); + element_param[8] = VC_HTOV32(dest_rect->y); + element_param[9] = VC_HTOV32(dest_rect->width); + element_param[10] = VC_HTOV32(dest_rect->height); + element_param[2] |= ELEMENT_CHANGE_DEST_RECT; + param_length += 4*sizeof(uint32_t); + } + if(src_rect) { + element_param[11] = VC_HTOV32(src_rect->x); + element_param[12] = VC_HTOV32(src_rect->y); + element_param[13] = VC_HTOV32(src_rect->width); + element_param[14] = VC_HTOV32(src_rect->height); + element_param[2] |= ELEMENT_CHANGE_SRC_RECT; + param_length += 4*sizeof(uint32_t); + } + + + success = (int) dispmanx_send_command( EDispmanElementChangeAttributes | DISPMANX_NO_REPLY_MASK, + element_param, param_length); + return success; +} + + +/*********************************************************** + * Name: vc_dispmanx_snapshot + * + * Arguments: + * DISPMANX_DISPLAY_HANDLE_T display + * DISPMANX_RESOURCE_HANDLE_T snapshot_resource + * DISPMANX_TRANSFORM_T transform + * + * Description: Take a snapshot of a display in its current state + * + * Returns: + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_snapshot( DISPMANX_DISPLAY_HANDLE_T display, + DISPMANX_RESOURCE_HANDLE_T snapshot_resource, + DISPMANX_TRANSFORM_T transform ) +{ + uint32_t display_snapshot_param[] = { + VC_HTOV32(display), + VC_HTOV32(snapshot_resource), + VC_HTOV32(transform)}; + + int success = (int) dispmanx_send_command( EDispmanSnapshot, + display_snapshot_param, + sizeof(display_snapshot_param)); + return success; +} + +/*********************************************************** + * Name: vc_dispmanx_resource_set_palette + * + * Arguments: + * DISPMANX_RESOURCE_HANDLE_T res + * void * src_address + * int offset + * int size + * + * Description: Set the resource palette (for VC_IMAGE_4BPP and VC_IMAGE_8BPP) + * offset should be 0 + * size is 16*2 for 4BPP and 256*2 for 8BPP + * Returns: 0 or failure + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_resource_set_palette( DISPMANX_RESOURCE_HANDLE_T handle, + void * src_address, int offset, int size) { + //Note that x coordinate of the rect is NOT used + //Address of data in host + uint8_t *host_start = src_address; + int32_t bulk_len = size, success = 0; + + //Now send the bulk transfer across + //command parameters: resource size + uint32_t param[] = {VC_HTOV32(handle), VC_HTOV32(offset), VC_HTOV32(bulk_len) }; + success = dispmanx_send_command( EDispmanSetPalette | DISPMANX_NO_REPLY_MASK, param, sizeof(param)); + if(success == 0) + { + lock_obtain(); + success = vchi_bulk_queue_transmit( dispmanx_client.client_handle[0], + host_start, + bulk_len, + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, + NULL ); + lock_release(); + } + return (int) success; +} + + +/*********************************************************** + * Name: vc_dispmanx_vsync_callback + * + * Arguments: + * DISPMANX_DISPLAY_HANDLE_T display + * DISPMANX_CALLBACK_FUNC_T cb_func + * void *cb_arg + * + * Description: start sending callbacks on vsync events + * Use a NULL cb_func to stop the callbacks + * Returns: 0 or failure + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_dispmanx_vsync_callback( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg ) +{ + // Steal the invalid 0 handle to indicate this is a vsync request + DISPMANX_UPDATE_HANDLE_T update = 0; + int enable = (cb_func != NULL); + uint32_t update_param[] = {(uint32_t) VC_HTOV32(display), VC_HTOV32(update), (int32_t)enable}; + int success; + + // Set the callback + dispmanx_client.vsync_callback = cb_func; + dispmanx_client.vsync_callback_param = cb_arg; + + if (!dispmanx_client.vsync_enabled && enable) { + // An extra "use" is required while a vsync callback is registered. + // The corresponding "release" is below. + vchi_service_use(dispmanx_client.notify_handle[0]); + } + + success = (int) dispmanx_send_command( EDispmanVsyncCallback | DISPMANX_NO_REPLY_MASK, + update_param, sizeof(update_param)); + + if (dispmanx_client.vsync_enabled && !enable) { + // The extra "use" added above is no longer required. + vchi_service_release(dispmanx_client.notify_handle[0]); + } + + dispmanx_client.vsync_enabled = enable; + + return (int) success; +} + + +/********************************************************************************* + * + * Static functions definitions + * + *********************************************************************************/ +//TODO: Might need to handle multiple connections later +/*********************************************************** + * Name: dispmanx_client_callback + * + * Arguments: semaphore, callback reason and message handle + * + * Description: VCHI callback for the DISP service + * + ***********************************************************/ +static void dispmanx_client_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) { + + (void)msg_handle; + + VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; + + if ( reason != VCHI_CALLBACK_MSG_AVAILABLE ) + return; + + if ( event == NULL ) + return; + + vcos_event_signal(event); +} + +/*********************************************************** + * Name: dispmanx_notify_callback + * + * Arguments: semaphore, callback reason and message handle + * + * Description: VCHI callback for the update callback + * + ***********************************************************/ + +static void dispmanx_notify_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) { + VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; + + (void)msg_handle; + + if ( reason != VCHI_CALLBACK_MSG_AVAILABLE ) + return; + + if ( event == NULL ) + return; + + vcos_event_signal(event); +} + +/*********************************************************** + * Name: dispmanx_wait_for_reply + * + * Arguments: response buffer, buffer length + * + * Description: blocked until something is in the buffer + * + * Returns error code of vchi + * + ***********************************************************/ +static int32_t dispmanx_wait_for_reply(void *response, uint32_t max_length) { + int32_t success = 0; + uint32_t length_read = 0; + do { + //TODO : we need to deal with messages coming through on more than one connections properly + //At the moment it will always try to read the first connection if there is something there + //Check if there is something in the queue, if so return immediately + //otherwise wait for the semaphore and read again + success = vchi_msg_dequeue( dispmanx_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE ); + } while( length_read == 0 && vcos_event_wait(&dispmanx_message_available_event) == VCOS_SUCCESS ); + + return success; + +} +/*********************************************************** + * Name: dispmanx_send_command + * + * Arguments: command, parameter buffer, parameter legnth + * + * Description: send a command and wait for its response (int) + * + * Returns: response + * + ***********************************************************/ + +static int32_t dispmanx_send_command( uint32_t command, void *buffer, uint32_t length) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + int32_t success = 0, response = -1; + lock_obtain(); + success = vchi_msg_queuev( dispmanx_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); + if(success == 0 && !(command & DISPMANX_NO_REPLY_MASK)) { + //otherwise only wait for a reply if we ask for one + success = dispmanx_wait_for_reply(&response, sizeof(response)); + } else { + //Not waiting for a reply, send the success code back instead + response = success; + } + lock_release(); + return VC_VTOH32(response); +} + +int32_t vc_dispmanx_send_command (uint32_t command, void *buffer, + uint32_t length) +{ + return dispmanx_send_command (command, buffer, length); +} + +/*********************************************************** + * Name: dispmanx_send_command_reply + * + * Arguments: command, parameter buffer, parameter legnth, reply buffer, buffer length + * + * Description: send a command and wait for its response (in a buffer) + * + * Returns: error code + * + ***********************************************************/ + +static int32_t dispmanx_send_command_reply( uint32_t command, void *buffer, uint32_t length, + void *response, uint32_t max_length) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + + int32_t success = 0; + lock_obtain(); + success = vchi_msg_queuev( dispmanx_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); + if(success == 0) + success = dispmanx_wait_for_reply(response, max_length); + + lock_release(); + + return success; +} + +int32_t vc_dispmanx_send_command_reply (uint32_t command, void *buffer, uint32_t length, + void *response, uint32_t max_length) +{ + return dispmanx_send_command_reply (command, buffer, length, response, max_length); +} + +/*********************************************************** + * Name: dispmanx_get_handle + * + * Arguments: command, parameter buffer, parameter legnth + * + * Description: same as dispmanx_send_command but returns uint instead + * + * Returns: handle + * + ***********************************************************/ +static uint32_t dispmanx_get_handle( uint32_t command, void *buffer, uint32_t length) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + uint32_t success = 0; + uint32_t response = 0; + lock_obtain(); + success += vchi_msg_queuev( dispmanx_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); + if(success == 0) + success = dispmanx_wait_for_reply(&response, sizeof(response)); + + lock_release(); + return VC_VTOH32(response); +} + +/*********************************************************** + * Name: dispmanx_notify_handle + * + * Arguments: not used + * + * Description: this purely notifies the update callback + * + * Returns: does not return + * + ***********************************************************/ +static void *dispmanx_notify_func( void *arg ) { + int32_t success; + VCOS_STATUS_T status; + + (void)arg; + + while (1) { + DISPMANX_UPDATE_HANDLE_T handle; + status = vcos_event_wait(&dispmanx_notify_available_event); + if (status != VCOS_SUCCESS || !dispmanx_client.initialised) + break; + + while (1) { + success = vchi_msg_dequeue( dispmanx_client.notify_handle[0], dispmanx_client.notify_buffer, sizeof(dispmanx_client.notify_buffer), &dispmanx_client.notify_length, VCHI_FLAGS_NONE ); + if (success != 0) + break; + + handle = (DISPMANX_UPDATE_HANDLE_T)dispmanx_client.notify_buffer[0]; + if (handle) { + // This is the response to an update submit + // Decrement the use count - the corresponding "use" is in vc_dispmanx_update_submit. + vchi_service_release(dispmanx_client.notify_handle[0]); + if (dispmanx_client.update_callback ) { + vcos_assert( dispmanx_client.pending_update_handle == handle); + dispmanx_client.update_callback(handle, dispmanx_client.update_callback_param); + } + } else { + // This is a vsync notification + if (dispmanx_client.vsync_callback ) { + dispmanx_client.vsync_callback(handle, dispmanx_client.vsync_callback_param); + } + } + } + } + return 0; +} diff --git a/interface/vmcs_host/vc_vchi_dispmanx.h b/interface/vmcs_host/vc_vchi_dispmanx.h new file mode 100755 index 0000000..b723b76 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_dispmanx.h @@ -0,0 +1,69 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_VCHI_DISPMANX_H +#define VC_VCHI_DISPMANX_H + +#include "interface/peer/vc_vchi_dispmanx_common.h" + +#define VC_NUM_HOST_RESOURCES 64 +#define DISPMANX_MSGFIFO_SIZE 1024 +#define DISPMANX_CLIENT_NAME MAKE_FOURCC("DISP") +#define DISPMANX_NOTIFY_NAME MAKE_FOURCC("UPDH") + +//Or with command to indicate we don't need a response +#define DISPMANX_NO_REPLY_MASK (1<<31) + +typedef struct { + char description[32]; + uint32_t width; + uint32_t height; + uint32_t aspect_pixwidth; + uint32_t aspect_pixheight; + uint32_t fieldrate_num; + uint32_t fieldrate_denom; + uint32_t fields_per_frame; + uint32_t transform; +} GET_MODES_DATA_T; + +typedef struct { + int32_t response; + uint32_t width; + uint32_t height; + uint32_t transform; + uint32_t input_format; +} GET_INFO_DATA_T; + +//Attributes changes flag mask +#define ELEMENT_CHANGE_LAYER (1<<0) +#define ELEMENT_CHANGE_OPACITY (1<<1) +#define ELEMENT_CHANGE_DEST_RECT (1<<2) +#define ELEMENT_CHANGE_SRC_RECT (1<<3) +#define ELEMENT_CHANGE_MASK_RESOURCE (1<<4) +#define ELEMENT_CHANGE_TRANSFORM (1<<5) + +#endif diff --git a/interface/vmcs_host/vc_vchi_fileservice_defs.h b/interface/vmcs_host/vc_vchi_fileservice_defs.h new file mode 100755 index 0000000..0677076 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_fileservice_defs.h @@ -0,0 +1,75 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_VCHI_FILESERVICE_DEFS_H +#define VC_VCHI_FILESERVICE_DEFS_H + +#include "interface/vchi/vchi.h" + +/* Definitions (not used by API) */ + +/* structure used by both side to communicate */ +#define FILESERV_MAX_BULK_SECTOR 128 //must be power of two + +#define FILESERV_SECTOR_LENGTH 512 + +#define FILESERV_MAX_BULK (FILESERV_MAX_BULK_SECTOR*FILESERV_SECTOR_LENGTH) + +#define FILESERV_4CC MAKE_FOURCC("FSRV") + +typedef enum FILESERV_EVENT_T +{ + FILESERV_BULK_RX = 0, + FILESERV_BULK_TX, + FILESERV_BULK_RX_0, + FILESERV_BULK_RX_1 +}FILESERV_EVENT_T; +//this following structure has to equal VCHI_MAX_MSG_SIZE +#define FILESERV_MAX_DATA (VCHI_MAX_MSG_SIZE - 40) //(VCHI_MAX_MSG_SIZE - 24) + +typedef struct{ + uint32_t xid; //4 // transaction's ID, used to match cmds with response + uint32_t cmd_code; //4 + uint32_t params[4]; //16 + char data[FILESERV_MAX_DATA]; +}FILESERV_MSG_T; + +typedef enum +{ + FILESERV_RESP_OK, + FILESERV_RESP_ERROR, + FILESERV_BULK_READ, + FILESERV_BULK_WRITE, + +} FILESERV_RESP_CODE_T; + + +/* Protocol (not used by API) version 1.2 */ + + + +#endif diff --git a/interface/vmcs_host/vc_vchi_filesys.c b/interface/vmcs_host/vc_vchi_filesys.c new file mode 100755 index 0000000..26a34b9 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_filesys.c @@ -0,0 +1,2509 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include + +#include "vchost.h" + +#include "interface/vcos/vcos.h" +#include "interface/vchi/vchi.h" +#include "interface/vchi/common/endian.h" + +#include "vc_vchi_filesys.h" +#include "interface/vmcs_host/vc_vchi_fileservice_defs.h" + +/****************************************************************************** +Global data. +******************************************************************************/ + +/****************************************************************************** +Local types and defines. +******************************************************************************/ +typedef enum { + VC_SECTOR_IO_NONE, + VC_SECTOR_IO_READING, + VC_SECTOR_IO_WRITING +} VC_SECTOR_IO_T; + +typedef struct { + + VCHI_SERVICE_HANDLE_T open_handle; + + int32_t num_connections; + + //Host->2727 + FILESERV_MSG_T fileserv_msg; + + //2727->Host XXX + FILESERV_MSG_T vc_msg; + + VCOS_THREAD_T filesys_thread; + + // used to signal response has arrived + VCOS_EVENT_T response_event; + + //we lock each vc_filesys function call + VCOS_MUTEX_T filesys_lock; + + //used to signal msg arrivals + VCOS_EVENT_T filesys_msg_avail; + + // Outstanding transaction's ID + volatile uint32_t cur_xid; + + // Copy of the header code from responses + int32_t resp_code; + int32_t err_no; + + char *bulk_buffer; + int32_t initialised; + +} FILESYS_SERVICE_T; + +static FILESYS_SERVICE_T vc_filesys_client; + + +/****************************************************************************** +Static functions. +******************************************************************************/ + +//Lock the host state +static __inline int32_t lock_obtain (void) { + int ret = -1; + if(vc_filesys_client.initialised && vcos_mutex_lock(&vc_filesys_client.filesys_lock) == VCOS_SUCCESS) { + vchi_service_use(vc_filesys_client.open_handle); + ret = 0; + } + return ret; +} + +//Unlock the host state +static __inline void lock_release (void) { + vcos_assert(vc_filesys_client.initialised); + vchi_service_release(vc_filesys_client.open_handle); + vcos_mutex_unlock(&vc_filesys_client.filesys_lock); +} + +// File Service VCHI functions + +static int vchi_msg_stub(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len ); + +static int vchi_msg_stub_noblock(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len); + +static int vc_fs_message_handler( FILESERV_MSG_T* msg, uint32_t nbytes ); + +static void *filesys_task_func(void *arg); + +static void filesys_callback( void *callback_param, VCHI_CALLBACK_REASON_T reason, void *msg_handle ); + + + +#ifdef PRINTF +#ifdef WIN32 +#define printf tprintf +#endif +static void showmsg(VC_MSGFIFO_CMD_HEADER_T const * head, + struct file_service_msg_body const * body); +#endif +static int fs_host_direntbytestream_create(struct dirent *d, void *buffer); +static void fs_host_direntbytestream_interp(struct dirent *d, void *buffer); + +/*---------------------------------------------------------------------------*/ + +/****************************************************************************** +NAME + vc_filesys_init + +SYNOPSIS + vc_filesys_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) { + +FUNCTION + Initialise the file system for use. A negative return value + indicates failure (which may mean it has not been started on VideoCore). + +RETURNS + int +******************************************************************************/ +int vc_vchi_filesys_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) +{ + int32_t success = 0; + SERVICE_CREATION_T filesys_parameters; + VCOS_THREAD_ATTR_T attrs; + VCOS_STATUS_T status; + + // record the number of connections + memset( &vc_filesys_client, 0, sizeof(FILESYS_SERVICE_T) ); + vc_filesys_client.num_connections = num_connections; + + if(!vcos_verify(vc_filesys_client.num_connections < 2)) + return -1; + + status = vcos_mutex_create(&vc_filesys_client.filesys_lock, "HFilesys"); + vcos_assert(status == VCOS_SUCCESS); + + status = vcos_event_create(&vc_filesys_client.filesys_msg_avail, "HFilesys"); + vcos_assert(status == VCOS_SUCCESS); + + //create sema used to signal cmd response has arrived + status = vcos_event_create(&vc_filesys_client.response_event, "HFilesys"); + vcos_assert(status == VCOS_SUCCESS); + + vc_filesys_client.bulk_buffer = vcos_malloc_aligned(FILESERV_MAX_BULK, 16, "HFilesys bulk_recv"); + vc_filesys_client.cur_xid = 0; + + memset(&filesys_parameters, 0, sizeof(filesys_parameters)); + filesys_parameters.service_id = FILESERV_4CC; // 4cc service code + filesys_parameters.connection = connections[0]; // passed in fn ptrs + filesys_parameters.rx_fifo_size = 0; // rx fifo size (unused) + filesys_parameters.tx_fifo_size = 0; // tx fifo size (unused) + filesys_parameters.callback = &filesys_callback; + filesys_parameters.callback_param = &vc_filesys_client.filesys_msg_avail; + filesys_parameters.want_unaligned_bulk_rx = 0; + filesys_parameters.want_unaligned_bulk_tx = 0; + filesys_parameters.want_crc = 0; + filesys_parameters.version.version = VC_FILESERV_VER; + filesys_parameters.version.version_min = VC_FILESERV_VER; + + success = vchi_service_open( initialise_instance, &filesys_parameters, &vc_filesys_client.open_handle ); + vcos_assert( success == 0 ); + + vcos_thread_attr_init(&attrs); + vcos_thread_attr_setstacksize(&attrs, 4000); + vcos_thread_attr_settimeslice(&attrs, 1); + + vc_filesys_client.initialised = 1; + + status = vcos_thread_create(&vc_filesys_client.filesys_thread, "HFilesys", &attrs, filesys_task_func, NULL); + vcos_assert(status == VCOS_SUCCESS); + + /* Not using service immediately - release videocore */ + vchi_service_release(vc_filesys_client.open_handle); + + return (int)success; +} + +static void *filesys_task_func(void *arg) +{ + int32_t success; + uint32_t msg_len; + + (void)arg; + + vc_hostfs_init(); + + while(1) { + // wait for the semaphore to say that there is a message + if (vcos_event_wait(&vc_filesys_client.filesys_msg_avail) != VCOS_SUCCESS || vc_filesys_client.initialised == 0) + break; + + vchi_service_use(vc_filesys_client.open_handle); + // read the message - should we really "peek" this + while (1) { + success = vchi_msg_dequeue(vc_filesys_client.open_handle, &vc_filesys_client.vc_msg, + sizeof(vc_filesys_client.vc_msg), &msg_len, VCHI_FLAGS_NONE); + if (!success) + break; + + /* coverity[tainted_string_argument] */ + success = (int32_t) vc_fs_message_handler(&vc_filesys_client.vc_msg, msg_len); + (void)success; + } + vchi_service_release(vc_filesys_client.open_handle); + } + + return 0; +} + + +/****************************************************************************** +NAME + filesys_callback + +SYNOPSIS + void filesys_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + const void *msg_handle ) + +FUNCTION + VCHI callback + +RETURNS + int +******************************************************************************/ +static void filesys_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) +{ + (void)msg_handle; + + switch( reason ) { + + case VCHI_CALLBACK_MSG_AVAILABLE: + { + VCOS_EVENT_T *event = (VCOS_EVENT_T *) callback_param; + if(event) + vcos_event_signal(event); + } + break; + + case VCHI_CALLBACK_BULK_RECEIVED: + break; + case VCHI_CALLBACK_BULK_SENT: + break; + + default: + return; + } +} + +/****************************************************************************** +NAME + vc_filesys_stop + +SYNOPSIS + void vc_filesys_stop() + +FUNCTION + This tells us that the file system service has stopped, thereby preventing + any of the functions from doing anything. + +RETURNS + void +******************************************************************************/ + +void vc_filesys_stop () +{ + int32_t result; + void *dummy; + + if(lock_obtain() != 0) + return; + + result = vchi_service_close(vc_filesys_client.open_handle); + vcos_assert(result == 0); + + vc_filesys_client.initialised = 0; + + vcos_event_signal(&vc_filesys_client.filesys_msg_avail); + vcos_thread_join(&vc_filesys_client.filesys_thread, &dummy); + + vcos_event_delete(&vc_filesys_client.filesys_msg_avail); + vcos_event_delete(&vc_filesys_client.response_event); + vcos_mutex_delete(&vc_filesys_client.filesys_lock); + + if(vc_filesys_client.bulk_buffer) + vcos_free(vc_filesys_client.bulk_buffer); +} + +/****************************************************************************** +NAME + vc_filesys_single_param + +SYNOPSIS + int vc_filesys_single_param(uint32_t param, uint32_t fn) + +FUNCTION + Utility function for implementing filesys methods + +RETURNS + void +******************************************************************************/ +static int vc_filesys_single_param(uint32_t param, uint32_t fn) +{ + int success = -1; + + if(lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = param; + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, fn, 4) == FILESERV_RESP_OK) + success = 0; + + lock_release(); + } + + return success; +} + +/****************************************************************************** +NAME + vc_filesys_single_string + +SYNOPSIS + int vc_filesys_single_string(uint32_t param, const char *str, uint32_t fn, int return_param) + +FUNCTION + Utility function for implementing filesys methods + +RETURNS + void +******************************************************************************/ +static int vc_filesys_single_string(uint32_t param, const char *str, uint32_t fn, int return_param) +{ + int ret = -1; + int len = strlen(str); + + if(len < FILESERV_MAX_DATA && lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = param; + /* coverity[buffer_size_warning] - the length of str has already been checked */ + strncpy((char*)vc_filesys_client.fileserv_msg.data, str, FILESERV_MAX_DATA); + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, fn, len+1+16) == FILESERV_RESP_OK) + { + if(return_param) + ret = (int) vc_filesys_client.fileserv_msg.params[0]; + else + ret = 0; + } + + lock_release(); + } + + return ret; +} + +/* Standard UNIX low-level library functions (declared in unistd.h) */ +/****************************************************************************** +NAME + vc_filesys_close + +SYNOPSIS + int vc_filesys_close(int fildes) + +FUNCTION + Deallocates the file descriptor to a file. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_close(int fildes) +{ + return vc_filesys_single_param((uint32_t) fildes, VC_FILESYS_CLOSE); +} + + +/****************************************************************************** +NAME + vc_filesys_lseek + +SYNOPSIS + long vc_filesys_lseek(int fildes, long offset, int whence) + +FUNCTION + Sets the file pointer associated with the open file specified by fildes. + +RETURNS + Successful completion: offset + Otherwise: -1 +******************************************************************************/ + +long vc_filesys_lseek(int fildes, long offset, int whence) +{ + long set = -1; + + if(lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = (uint32_t) fildes; + vc_filesys_client.fileserv_msg.params[1] = (uint32_t) offset; + vc_filesys_client.fileserv_msg.params[2] = (uint32_t) whence; + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_LSEEK, 12) == FILESERV_RESP_OK) + set = (long) vc_filesys_client.fileserv_msg.params[0]; + + lock_release(); + } + + return set; +} + +/****************************************************************************** +NAME + vc_filesys_lseek64 + +SYNOPSIS + int64_t vc_filesys_lseek64(int fildes, int64_t offset, int whence) + +FUNCTION + Sets the file pointer associated with the open file specified by fildes. + +RETURNS + Successful completion: file pointer value + Otherwise: -1 +******************************************************************************/ + +int64_t vc_filesys_lseek64(int fildes, int64_t offset, int whence) +{ + int64_t set = -1; + + if(lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = (uint32_t) fildes; + vc_filesys_client.fileserv_msg.params[1] = (uint32_t) offset; // LSB + vc_filesys_client.fileserv_msg.params[2] = (uint32_t)(offset >> 32); // MSB + vc_filesys_client.fileserv_msg.params[3] = (uint32_t) whence; + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_LSEEK64, 16) == FILESERV_RESP_OK) + { + set = vc_filesys_client.fileserv_msg.params[0]; + set += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32; + } + + lock_release(); + } + + return set; +} + +/****************************************************************************** +NAME + vc_filesys_mount + +SYNOPSIS + int vc_filesys_mount(const char *device, const char *mountpoint, const char *options) + +FUNCTION + Mounts a filesystem at a given location + +RETURNS + Successful completion: 0 +******************************************************************************/ + +int vc_filesys_mount(const char *device, const char *mountpoint, const char *options) +{ + int set = -1, len; + int a = strlen(device); + int b = strlen(mountpoint); + int c = strlen(options); + + if(a + b + c + 3 < FILESERV_MAX_DATA && lock_obtain() == 0) + { + char *str = (char *) vc_filesys_client.fileserv_msg.data; + + memcpy(str, device, a); + str[a] = 0; + memcpy(str+a+1, mountpoint, b); + str[a+1+b] = 0; + memcpy(str+a+b+2, options, c); + str[a+b+c+2] = 0; + len = a + b + c + 3 + (int)(((FILESERV_MSG_T *)0)->data); + len = ((len + (VCHI_BULK_GRANULARITY-1)) & ~(VCHI_BULK_GRANULARITY-1)) + VCHI_BULK_GRANULARITY; + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_MOUNT, len) == FILESERV_RESP_OK) + set = (int) vc_filesys_client.fileserv_msg.params[0]; + + lock_release(); + } + + return set; +} + +/****************************************************************************** +NAME + vc_filesys_umount + +SYNOPSIS + int vc_filesys_mount(const char *mountpoint) + +FUNCTION + Un-mounts a removable device from the location that it has been mounted + to earlier in the session + +RETURNS + Successful completion: 0 +******************************************************************************/ + +int vc_filesys_umount(const char *mountpoint) +{ + return vc_filesys_single_string(0, mountpoint, VC_FILESYS_UMOUNT, 1); +} + +/****************************************************************************** +NAME + vc_filesys_open + +SYNOPSIS + int vc_filesys_open(const char *path, int vc_oflag) + +FUNCTION + Establishes a connection between a file and a file descriptor. + +RETURNS + Successful completion: file descriptor + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_open(const char *path, int vc_oflag) +{ + return vc_filesys_single_string((uint32_t) vc_oflag, path, VC_FILESYS_OPEN, 1); +} + + +/****************************************************************************** +NAME + vc_filesys_read + +SYNOPSIS + int vc_filesys_read(int fildes, void *buf, unsigned int nbyte) + +FUNCTION + Attempts to read nbyte bytes from the file associated with the file + descriptor, fildes, into the buffer pointed to by buf. + +RETURNS + Successful completion: number of bytes read + Otherwise: -1 +******************************************************************************/ + +/****************************************************************************** + +FUNCTION + + +RETURNS + Successful completion: the number of bytes received + Otherwise negative error code +******************************************************************************/ +static int vc_vchi_msg_bulk_read(FILESERV_MSG_T* msg, uint16_t cmd_id, uint32_t transfer_len, uint8_t* recv_addr ) +{ + uint32_t i; + int msg_len; + uint32_t host_align_bytes; + uint32_t num_bytes_read; + int32_t success; + + //this is current file_io_buffer size so assuming never more than this + //otherwise we will split the read into chunks + if(!vcos_verify(transfer_len <= FILESERV_MAX_BULK)) + return -1; + + //number of bytes required to align recv_addr + host_align_bytes = VCHI_BULK_ALIGN_NBYTES(recv_addr); + + i = vc_filesys_client.cur_xid + 1; + i &= 0x7fffffffUL; + vc_filesys_client.cur_xid = i; + + msg->xid = vc_filesys_client.cur_xid; + + //fill in cmd id: VC_FILESYS_READ etc + msg->cmd_code = cmd_id; + + msg->params[2] = transfer_len; + + msg->params[3] = host_align_bytes; + + //24 comes from the static size of FILESERV_MSG_T + msg_len = 24; + + if(vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ) != 0) + return -1; + + //wait to receive response + if(vcos_event_wait(&vc_filesys_client.response_event) != VCOS_SUCCESS || msg->cmd_code == FILESERV_RESP_ERROR) + return -1; + + //total bytes read + num_bytes_read = msg->params[0]; + + if(!vcos_verify(num_bytes_read <= FILESERV_MAX_BULK)) + return -1; + + //everything is in msg->data + if(msg->cmd_code == FILESERV_RESP_OK) { + if(!vcos_verify(num_bytes_read <= FILESERV_MAX_DATA)) + return -1; + + memcpy(recv_addr, msg->data, num_bytes_read); + return (int) num_bytes_read; + } + +// Make this code conditional to stop Coverity complaining about dead code +#if VCHI_BULK_ALIGN > 1 + //copy host_align_bytes bytes to recv_addr, now ready for bulk + if(host_align_bytes) { + memcpy(recv_addr, msg->data, host_align_bytes); + recv_addr += host_align_bytes; + transfer_len -= host_align_bytes; + } +#endif + + //receive bulk from host + if(msg->cmd_code == FILESERV_BULK_WRITE){ + //number of end bytes + uint32_t end_bytes = msg->params[1]; + //calculate what portion of read transfer by bulk + uint32_t bulk_bytes = (num_bytes_read-host_align_bytes-end_bytes); + + success = vchi_bulk_queue_receive(vc_filesys_client.open_handle, + recv_addr, + bulk_bytes, + VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, + NULL ); + if(!vcos_verify(success == 0)) + return -1; + + recv_addr+=bulk_bytes; + + //copy any left over end bytes from original msg + if(end_bytes) + memcpy(recv_addr, &msg->data[host_align_bytes], end_bytes); + } + else { + return -1; + } + + return (int) num_bytes_read; +} + + +int vc_filesys_read(int fildes, void *buf, unsigned int nbyte) +{ + int bulk_bytes = 0; + int actual_read = 0; + int total_byte = 0; + uint8_t* ptr = (uint8_t*)buf; + + if (nbyte == 0) { + return 0; + } + + if(lock_obtain() == 0) + { + do { + bulk_bytes = nbyte > FILESERV_MAX_BULK ? FILESERV_MAX_BULK : nbyte; + + //we overwrite the response here so fill in data again + vc_filesys_client.fileserv_msg.params[0] = (uint32_t)fildes; + vc_filesys_client.fileserv_msg.params[1] = 0xffffffffU; // offset: use -1 to indicate no offset + + actual_read = vc_vchi_msg_bulk_read(&vc_filesys_client.fileserv_msg , VC_FILESYS_READ, (uint32_t)bulk_bytes, (uint8_t*)ptr); + + if(bulk_bytes != actual_read) { + if(actual_read < 0) + total_byte = -1; + else + total_byte += actual_read; + //exit loop + break; + } + + ptr+=bulk_bytes; + nbyte -= actual_read; + total_byte += actual_read; + }while(nbyte > 0); + + lock_release(); + } + + return total_byte; +} + +/****************************************************************************** +NAME + vc_filesys_write + +SYNOPSIS + int vc_filesys_write(int fildes, const void *buf, unsigned int nbyte) + +FUNCTION + Attempts to write nbyte bytes from the buffer pointed to by buf to file + associated with the file descriptor, fildes. + +RETURNS + Successful completion: number of bytes written + Otherwise: -1 +******************************************************************************/ + +/****************************************************************************** + +FUNCTION + + +RETURNS + Successful completion: the number of bytes received + Otherwise negative error code +******************************************************************************/ +static int vc_vchi_msg_bulk_write(FILESERV_MSG_T* msg, uint16_t cmd_id, uint32_t transfer_len, uint8_t* send_addr ) +{ + uint32_t i; + int msg_len; + uint32_t align_bytes = 0; + uint32_t bulk_end_bytes = 0; + uint32_t bulk_bytes = 0; + int num_bytes_written = -1; + + //this is current file_io_buffer size so assuming never more than this + //otherwise we will split the read into chunks + if(!vcos_verify(transfer_len <= FILESERV_MAX_BULK)) + return -1; + + i = vc_filesys_client.cur_xid + 1; + i &= 0x7fffffffUL; + vc_filesys_client.cur_xid = i; + + msg->xid = vc_filesys_client.cur_xid; + + //fill in cmd id VC_FILESYS_OPEN etc + msg->cmd_code = cmd_id; + + msg->params[2] = transfer_len; + + //24 comes from the static size of FILESERV_MSG_T + msg_len = 24; + + //put it all in one msg + if(transfer_len <= FILESERV_MAX_DATA) { + memcpy(msg->data, send_addr, transfer_len); + msg->params[3] = 0; + msg_len += transfer_len; + //send request to write to host + if(vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ) != 0) + return -1; + + // wait to receive response + if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && + msg->cmd_code != FILESERV_RESP_ERROR && msg->params[0] == transfer_len) + { + //vc_fs_message_handler copies resp into outgoing msg struct + num_bytes_written = (int)msg->params[0]; + } + } + else { + //required to make send_addr for bulk + align_bytes = VCHI_BULK_ALIGN_NBYTES(send_addr); + +// Make this code conditional to stop Coverity complaining about dead code +#if VCHI_BULK_ALIGN > 1 + //copy vc_align bytes to msg->data, send_addr ready for bulk + if(align_bytes) { + msg->params[3] = align_bytes; + memcpy(msg->data, send_addr, align_bytes); + transfer_len -= align_bytes; + send_addr += align_bytes; + msg_len += align_bytes; + } + else +#endif + msg->params[3] = 0; + + // need to ensure we have the appropriate alignment + bulk_bytes = (transfer_len)&(~(VCHI_BULK_GRANULARITY-1)); + + bulk_end_bytes = transfer_len-bulk_bytes; + + if(bulk_end_bytes) { + memcpy(msg->data+align_bytes, send_addr+bulk_bytes, bulk_end_bytes); + msg_len += bulk_end_bytes; + } + + //send request to write to host + if(vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ) != 0) + return -1; + + //send bulk VCHI_FLAGS_BLOCK_UNTIL_QUEUED is ok because we wait for response msg with actual length written + if(vchi_bulk_queue_transmit( vc_filesys_client.open_handle, + send_addr, + bulk_bytes, + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, + NULL ) != 0) + return -1; + + // wait to receive response sent by filsys_task_func + if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && msg->cmd_code == FILESERV_BULK_READ) + num_bytes_written = (int)msg->params[0]; + } + + return num_bytes_written; +} + + +int vc_filesys_write(int fildes, const void *buf, unsigned int nbyte) +{ + int num_wrt = 0; + int bulk_bytes = 0; + int actual_written = 0; + uint8_t *ptr = (uint8_t*) buf; + + if (nbyte == 0) { + return 0; + } + + if(lock_obtain() == 0) + { + //will split the read into 4K chunks based on vc fwrite buffer size array size + //we will make this more dynamic later + do { + bulk_bytes = nbyte > FILESERV_MAX_BULK ? FILESERV_MAX_BULK : nbyte; + + //we overwrite the response here so fill in data again + vc_filesys_client.fileserv_msg.params[0] = (uint32_t)fildes; + vc_filesys_client.fileserv_msg.params[1] = 0xffffffffU; // offset: use -1 to indicate no offset + + actual_written = vc_vchi_msg_bulk_write(&vc_filesys_client.fileserv_msg , VC_FILESYS_WRITE, (uint32_t)bulk_bytes, (uint8_t*)ptr); + + if(bulk_bytes != actual_written) { + if(actual_written < 0) + num_wrt = -1; + else + num_wrt += actual_written; + break; + } + + ptr+=bulk_bytes; + nbyte -= actual_written; + num_wrt += actual_written; + }while(nbyte > 0); + + lock_release(); + } + + return num_wrt; +} + + +/* Directory management functions */ + +/****************************************************************************** +NAME + vc_filesys_closedir + +SYNOPSIS + int vc_filesys_closedir(void *dhandle) + +FUNCTION + Ends a directory list iteration. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_closedir(void *dhandle) +{ + return vc_filesys_single_param((uint32_t)dhandle, VC_FILESYS_CLOSEDIR); +} + + +/****************************************************************************** +NAME + vc_filesys_format + +SYNOPSIS + int vc_filesys_format(const char *path) + +FUNCTION + Formats the physical file system that contains path. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_format(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_FORMAT, 0); +} + + +/****************************************************************************** +NAME + vc_filesys_freespace + +SYNOPSIS + int vc_filesys_freespace(const char *path) + +FUNCTION + Returns the amount of free space on the physical file system that contains + path. + +RETURNS + Successful completion: free space + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_freespace(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_FREESPACE, 1); +} + + +/****************************************************************************** +NAME + vc_filesys_freespace64 + +SYNOPSIS + int64_t vc_filesys_freespace64(const char *path) + +FUNCTION + Returns the amount of free space on the physical file system that contains + path. + +RETURNS + Successful completion: free space + Otherwise: -1 +******************************************************************************/ + +int64_t vc_filesys_freespace64(const char *path) +{ + int64_t freespace = -1LL; + + if(lock_obtain() == 0) + { + strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH); + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_FREESPACE64, + (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK) + { + freespace = vc_filesys_client.fileserv_msg.params[0]; + freespace += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32; + } + + lock_release(); + } + + return freespace; +} + + +/****************************************************************************** +NAME + vc_filesys_get_attr + +SYNOPSIS + int vc_filesys_get_attr(const char *path, fattributes_t *attr) + +FUNCTION + Gets the file/directory attributes. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_get_attr(const char *path, fattributes_t *attr) +{ + int success = -1; + + if(lock_obtain() == 0) + { + strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH); + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_GET_ATTR, + (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK) + { + success = 0; + *attr = (fattributes_t) vc_filesys_client.fileserv_msg.params[0]; + } + + lock_release(); + } + + return success; +} + + +/****************************************************************************** +NAME + vc_filesys_fstat + +SYNOPSIS + int64_t vc_filesys_fstat(int fildes, FSTAT_T *buf) + +FUNCTION + Returns the file stat info structure for the specified file. + This structure includes date and size info. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_fstat(int fildes, FSTAT_T *buf) +{ + int success = -1; + + if (buf != NULL && lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = (uint32_t) fildes; + if ( vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_FSTAT, 4) == FILESERV_RESP_OK ) + { + buf->st_size = (int64_t)vc_filesys_client.fileserv_msg.params[0]; // LSB + buf->st_size |= (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32; // MSB + buf->st_modtime = (uint32_t)vc_filesys_client.fileserv_msg.params[2]; + // there's room for expansion here to pass across more elements of the structure if required... + success = 0; + } + + lock_release(); + } + + return success; +} + + +/****************************************************************************** +NAME + vc_filesys_mkdir + +SYNOPSIS + int vc_filesys_mkdir(const char *path) + +FUNCTION + Creates a new directory named by the pathname pointed to by path. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_mkdir(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_MKDIR, 0); +} + + +/****************************************************************************** +NAME + vc_filesys_opendir + +SYNOPSIS + void *vc_filesys_opendir(const char *dirname) + +FUNCTION + Starts a directory list iteration. + +RETURNS + Successful completion: dhandle (pointer) + Otherwise: NULL +******************************************************************************/ + +void *vc_filesys_opendir(const char *dirname) +{ + return (void *) vc_filesys_single_string(0, dirname, VC_FILESYS_OPENDIR, 1); +} + + +/****************************************************************************** +NAME + vc_filesys_dirsize + +SYNOPSIS + int vc_filesys_dirsize(const char *path) + +FUNCTION + Look through the specified directory tree and sum the file sizes. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int64_t vc_filesys_dirsize(const char *path, uint32_t *num_files, uint32_t *num_dirs) +{ + int64_t ret = -1; + + if(lock_obtain() == 0) + { + strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH); + + // FIXME: Should probably use a non-blocking call here since it may take a + // long time to do the operation... + if ( vchi_msg_stub(&vc_filesys_client.fileserv_msg, + VC_FILESYS_DIRSIZE, + (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK ) + { + ret = vc_filesys_client.fileserv_msg.params[0]; + ret += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32; + if (num_files) + *num_files = vc_filesys_client.fileserv_msg.params[2]; + if (num_dirs) + *num_dirs = vc_filesys_client.fileserv_msg.params[3]; + } + + lock_release(); + } + + return ret; +} + + +/****************************************************************************** +NAME + vc_filesys_readdir_r + +SYNOPSIS + struct dirent *vc_filesys_readdir_r(void *dhandle, struct dirent *result) + +FUNCTION + Fills in the passed result structure with details of the directory entry + at the current psition in the directory stream specified by the argument + dhandle, and positions the directory stream at the next entry. + +RETURNS + Successful completion: result + End of directory stream: NULL +******************************************************************************/ + +struct dirent *vc_filesys_readdir_r(void *dhandle, struct dirent *result) +{ + struct dirent *ret = NULL; + + if(lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = (uint32_t)dhandle; + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_READDIR, 4) == FILESERV_RESP_OK) + { + fs_host_direntbytestream_interp(result, (void *)vc_filesys_client.fileserv_msg.data); + ret = result; + } + + lock_release(); + } + + return ret; +} + + +/****************************************************************************** +NAME + vc_filesys_remove + +SYNOPSIS + int vc_filesys_remove(const char *path) + +FUNCTION + Removes a file or a directory. A directory must be empty before it can be + deleted. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_remove(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_REMOVE, 0); +} + + +/****************************************************************************** +NAME + vc_filesys_rename + +SYNOPSIS + int vc_filesys_rename(const char *oldfile, const char *newfile) + +FUNCTION + Changes the name of a file. The old and new pathnames must be on the same + physical file system. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_rename(const char *oldfile, const char *newfile) +{ + int a, b, success = -1; + + // Ensure the pathnames aren't too long + if ((a = strlen(oldfile)) < FS_MAX_PATH && (b = strlen(newfile)) < FS_MAX_PATH && lock_obtain() == 0) + { + strncpy((char *)vc_filesys_client.fileserv_msg.data, oldfile, FS_MAX_PATH); + strncpy((char *)&vc_filesys_client.fileserv_msg.data[a+1], newfile, FS_MAX_PATH); + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_RENAME, 16+a+1+b+1) == FILESERV_RESP_OK) + success = 0; + + lock_release(); + } + + return success; +} + + +/****************************************************************************** +NAME + vc_filesys_reset + +SYNOPSIS + int vc_filesys_reset() + +FUNCTION + Send a vc_FILESYS_RESET command. This will return immediately. + +RETURNS + Successful completion: FILESERV_RESP_OK + Otherwise: - +******************************************************************************/ + +int vc_filesys_reset() +{ + return vc_filesys_single_param(0, VC_FILESYS_RESET); +} + + +/****************************************************************************** +NAME + vc_filesys_set_attr + +SYNOPSIS + int vc_filesys_set_attr(const char *path, fattributes_t attr) + +FUNCTION + Sets file/directory attributes. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_set_attr(const char *path, fattributes_t attr) +{ + return vc_filesys_single_string((uint32_t) attr, path, VC_FILESYS_SET_ATTR, 0); +} + + +/****************************************************************************** +NAME + vc_filesys_setend + +SYNOPSIS + int vc_filesys_setend(int fildes) + +FUNCTION + Truncates file at current position. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_setend(int fildes) +{ + return vc_filesys_single_param((uint32_t) fildes, VC_FILESYS_SETEND); +} + + + +/****************************************************************************** +NAME + vc_filesys_scandisk + +SYNOPSIS + void vc_filesys_scandisk(const char *path) + +FUNCTION + Truncates file at current position. + +RETURNS + - +******************************************************************************/ + +void vc_filesys_scandisk(const char *path) +{ + vc_filesys_single_string(0, path, VC_FILESYS_SCANDISK, 0); + return; +} + +/****************************************************************************** +NAME + vc_filesys_chkdsk + +SYNOPSIS + int vc_filesys_chkdsk(const char *path, int fix_errors) + +FUNCTION + Truncates file at current position. + +RETURNS + - +******************************************************************************/ + +int vc_filesys_chkdsk(const char *path, int fix_errors) +{ + return vc_filesys_single_string(fix_errors, path, VC_FILESYS_CHKDSK, 1); +} + +/****************************************************************************** +NAME + vc_filesys_size + +SYNOPSIS + int vc_filesys_size(const char *path) + +FUNCTION + return size of file + +RETURNS + - +******************************************************************************/ + +int vc_filesys_size(const char *path) +{ + int fd; + int end_pos = 0; + int success = -1; + if((fd = vc_filesys_open(path, VC_O_RDONLY)) == 0) + { + end_pos = vc_filesys_lseek(fd, 0, SEEK_END); + success = vc_filesys_close(fd); + (void)success; + } + + return end_pos; +} +/****************************************************************************** +NAME + vc_filesys_totalspace + +SYNOPSIS + int vc_filesys_totalspace(const char *path) + +FUNCTION + Returns the total amount of space on the physical file system that contains + path. + +RETURNS + Successful completion: total space + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_totalspace(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_TOTALSPACE, 1); +} + + +/****************************************************************************** +NAME + vc_filesys_totalspace64 + +SYNOPSIS + int64_t vc_filesys_totalspace64(const char *path) + +FUNCTION + Returns the total amount of space on the physical file system that contains + path. + +RETURNS + Successful completion: total space + Otherwise: -1 +******************************************************************************/ + +int64_t vc_filesys_totalspace64(const char *path) +{ + int64_t totalspace = -1LL; + + if(lock_obtain() == 0) + { + strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH); + + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_TOTALSPACE64, + (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK) + { + totalspace = vc_filesys_client.fileserv_msg.params[0]; + totalspace += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32; + } + + lock_release(); + } + + return totalspace; +} + + +/****************************************************************************** +NAME + vc_filesys_diskwritable + +SYNOPSIS + int vc_filesys_diskwritable(const char *path) + +FUNCTION + Return whether the named disk is writable. + +RETURNS + Successful completion: 1 (disk writable) or 0 (disk not writable) + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_diskwritable(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_DISKWRITABLE, 1); +} + +/****************************************************************************** +NAME + vc_filesys_fstype + +SYNOPSIS + int vc_filesys_fstype(const char *path) + +FUNCTION + Return the filesystem type of the named disk. + +RETURNS + Successful completion: disk type (see vc_fileservice_defs.h) + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_fstype(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_FSTYPE, 1); +} + +/****************************************************************************** +NAME + vc_filesys_open_disk_raw + +SYNOPSIS + int vc_filesys_open_disk_raw(const char *path) + +FUNCTION + Open disk for access in raw mode. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_open_disk_raw(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_OPEN_DISK_RAW, 1); +} + +/****************************************************************************** +NAME + vc_filesys_close_disk_raw + +SYNOPSIS + int vc_filesys_close_disk_raw(const char *path) + +FUNCTION + Close disk from access in raw mode. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_close_disk_raw(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_CLOSE_DISK_RAW, 1); +} + + +/****************************************************************************** +NAME + vc_filesys_open_disk + +SYNOPSIS + int vc_filesys_open_disk(const char *path) + +FUNCTION + Open disk for normal access. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_open_disk(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_OPEN_DISK, 1); +} + +/****************************************************************************** +NAME + vc_filesys_close_disk + +SYNOPSIS + int vc_filesys_close_disk(const char *path) + +FUNCTION + Close disk from normal access. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_close_disk(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_CLOSE_DISK, 1); +} + +/****************************************************************************** +NAME + vc_filesys_numsectors + +SYNOPSIS + int vc_filesys_numsectors(const char *path) + +FUNCTION + Return number of sectors on disk + +RETURNS + Successful completion: greater than 0 + Otherwise: -1 +******************************************************************************/ + +int vc_filesys_numsectors(const char *path) +{ + return vc_filesys_single_string(0, path, VC_FILESYS_NUMSECTORS, 1); +} + +/****************************************************************************** +NAME + vc_filesys_read_sectors + +SYNOPSIS + int vc_filesys_read_sectors(const char *path, uint32_t sector_num, char *buffer, uint32_t num_sectors) + +FUNCTION + Start streaming sectors from 2727 + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ +int vc_filesys_read_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_read) +{ +#if VCHI_BULK_ALIGN > 1 + uint32_t align_bytes = 0; +#endif + char* bulk_addr = sectors; + int len; + int ret = -1; + + if((len = (int)strlen(path)) < FS_MAX_PATH && lock_obtain() == 0) + { + strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH); + vc_filesys_client.fileserv_msg.params[0] = sector_num; + vc_filesys_client.fileserv_msg.params[1] = num_sectors; + +#if VCHI_BULK_ALIGN > 1 + //required to make buffer aligned for bulk + align_bytes = VCHI_BULK_ALIGN_NBYTES(sectors); +#endif + //note for read we are currently doing memcpy on host to tell 2727 align is 0 + vc_filesys_client.fileserv_msg.params[2] = 0; + + //we send read request + if (vchi_msg_stub_noblock(&vc_filesys_client.fileserv_msg, VC_FILESYS_READ_SECTORS, 16+len+1) == 0) + { + while(num_sectors) + { + uint32_t bulk_sectors = (num_sectors > FILESERV_MAX_BULK_SECTOR) ? (uint32_t)FILESERV_MAX_BULK_SECTOR : num_sectors; + if(vchi_bulk_queue_receive( vc_filesys_client.open_handle, +#if VCHI_BULK_ALIGN > 1 + align_bytes ? vc_filesys_client.bulk_buffer : bulk_addr, +#else + bulk_addr, +#endif + (bulk_sectors*FILESERV_SECTOR_LENGTH), VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, NULL) != 0) + break; + +#if VCHI_BULK_ALIGN > 1 + if(align_bytes) { + //this is bad but will do for now.. + memcpy( bulk_addr, vc_filesys_client.bulk_buffer, (bulk_sectors*FILESERV_SECTOR_LENGTH)); + } +#endif + bulk_addr += (bulk_sectors*FILESERV_SECTOR_LENGTH); + num_sectors -= bulk_sectors; + } + + // now wait to receive resp from original msg... + if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && vc_filesys_client.fileserv_msg.cmd_code == FILESERV_RESP_OK) + { + *sectors_read = vc_filesys_client.fileserv_msg.params[0]; + ret = 0; + } + else + { + //error code in [0] + *sectors_read = vc_filesys_client.fileserv_msg.params[1]; + ret = vc_filesys_client.fileserv_msg.params[0]; + } + } + + lock_release(); + } + + return ret; +} + +/****************************************************************************** +NAME + vc_filesys_write_sectors + +SYNOPSIS + int vc_filesys_write_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_written) + +FUNCTION + Start streaming sectors to 2727 + +RETURNS + Successful completion: 0 + Otherwise: -error code +******************************************************************************/ +int vc_filesys_write_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_written) +{ + uint32_t align_bytes = 0; + char* bulk_addr = sectors; + int len; + int ret = -1; + + len = (int) strlen(path); + if((len = (int) strlen(path)) < FS_MAX_PATH && lock_obtain() == 0) + { + vc_filesys_client.fileserv_msg.params[0] = sector_num; + vc_filesys_client.fileserv_msg.params[1] = num_sectors; + + //required to make buffer aligned for bulk + align_bytes = ((unsigned long)sectors & (VCHI_BULK_ALIGN-1)); + vc_filesys_client.fileserv_msg.params[2] = align_bytes; + + //copy path at end of any alignbytes + strncpy(((char *)vc_filesys_client.fileserv_msg.data), path, FS_MAX_PATH); + + //we send write request + if (vchi_msg_stub_noblock(&vc_filesys_client.fileserv_msg, VC_FILESYS_WRITE_SECTORS, 16+len+1) == 0) + { + if(align_bytes) { + //note we are cheating and going backward to make addr aligned and sending more data... + bulk_addr -= align_bytes; + } + + while(num_sectors) { + uint32_t bulk_sectors = (num_sectors > FILESERV_MAX_BULK_SECTOR) ? (uint32_t)FILESERV_MAX_BULK_SECTOR : num_sectors; + //we send some extra data at the start + if(vchi_bulk_queue_transmit( vc_filesys_client.open_handle, bulk_addr, + VCHI_BULK_ROUND_UP((bulk_sectors*FILESERV_SECTOR_LENGTH)+align_bytes), + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, NULL) != 0) + break; + + //go to next ALIGNED address + bulk_addr += FILESERV_MAX_BULK; + num_sectors -= bulk_sectors; + } + + // now wait to receive resp from original msg... + if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && vc_filesys_client.fileserv_msg.cmd_code == FILESERV_RESP_OK) + { + *sectors_written = vc_filesys_client.fileserv_msg.params[0]; + ret = 0; + } + else + { + //error code in [0] + *sectors_written = vc_filesys_client.fileserv_msg.params[1]; + ret = vc_filesys_client.fileserv_msg.params[0]; + } + } + + lock_release(); + } + + return ret; +} + +/****************************************************************************** +NAME + vc_filesys_errno + +SYNOPSIS + int vc_filesys_errno(void) + +FUNCTION + Returns the error code of the last file system error that occurred. + +RETURNS + Error code +******************************************************************************/ + +int vc_filesys_errno(void) +{ + return (int) vc_filesys_client.err_no; +} + + +/* File Service Message FIFO functions */ + +/****************************************************************************** +NAME + vchi_msg_stub + +SYNOPSIS + static int vc_fs_stub(int verb, int ext_len) + +FUNCTION + Generates a request and sends it to the co-processor. It then suspends + until it receives a reply from the host. The calling task must hold + the filesys_lock. + +RETURNS + Successful completion: Response code of reply + Otherwise: - +******************************************************************************/ +static int vchi_msg_stub(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len ) +{ + int ret = -1; + vchi_msg_stub_noblock(msg, cmd_id, msg_len); + + // wait to receive response + if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS) + ret = (int) msg->cmd_code; + + return ret; +} + +static int vchi_msg_stub_noblock(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len) +{ + uint32_t i; + + if(!vcos_verify(msg_len <= VCHI_MAX_MSG_SIZE)) + return -1; + + //will get changed by response from command + vc_filesys_client.resp_code = FILESERV_RESP_ERROR; + + //the top bit is used for host/vc + i = vc_filesys_client.cur_xid + 1; + i &= 0x7fffffffUL; + vc_filesys_client.cur_xid = i; + + //fill in transaction id, used for response identification + vchi_writebuf_uint32( &(msg->xid), vc_filesys_client.cur_xid ); + + //fill in cmd id VC_FILESYS_OPEN etc + vchi_writebuf_uint32( &(msg->cmd_code), cmd_id ); + + //we always have cmd_id, xid + msg_len += 8; + + //return response code + return (int) vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); +} + +static int vc_send_response( FILESERV_MSG_T* msg, uint32_t retval, uint32_t nbytes ) +{ + int success = -1; + //convert all to over the wire values + vchi_writebuf_uint32(&msg->cmd_code, retval); + vchi_writebuf_uint32(&msg->xid, msg->xid); + vchi_writebuf_uint32(&msg->params[0], msg->params[0]); + vchi_writebuf_uint32(&msg->params[1], msg->params[1]); + vchi_writebuf_uint32(&msg->params[2], msg->params[2]); + vchi_writebuf_uint32(&msg->params[3], msg->params[3]); + + //start with 8 because always xid and retval + nbytes += 8; + + if(vcos_verify(nbytes <= VCHI_MAX_MSG_SIZE)) + success = (int) vchi_msg_queue( vc_filesys_client.open_handle, msg, nbytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); + + return success; +} + +/****************************************************************************** +NAME + vc_fs_message_handler + +SYNOPSIS + static int vc_fs_message_handler() + +FUNCTION + Handle messages from the co-processor. + +RETURNS + 0 - No message found. + 1 - Request received and actioned. + 2 - Reply received. +******************************************************************************/ + +static int vc_fs_message_handler( FILESERV_MSG_T* msg, uint32_t nbytes ) +{ + int rr = 0; + uint32_t xid = vchi_readbuf_uint32(&msg->xid); + + if (xid == vc_filesys_client.cur_xid) { + + //memcpy reply to msg should really peek before + vc_filesys_client.fileserv_msg.xid = xid; + vc_filesys_client.fileserv_msg.cmd_code = vchi_readbuf_uint32(&msg->cmd_code); + vc_filesys_client.fileserv_msg.params[0] = vchi_readbuf_uint32(&msg->params[0]); + vc_filesys_client.fileserv_msg.params[1] = vchi_readbuf_uint32(&msg->params[1]); + vc_filesys_client.fileserv_msg.params[2] = vchi_readbuf_uint32(&msg->params[2]); + vc_filesys_client.fileserv_msg.params[3] = vchi_readbuf_uint32(&msg->params[3]); + + //copy any data, 24 is size of header + if(nbytes >24) + memcpy(&vc_filesys_client.fileserv_msg.data, msg->data, (nbytes-24)); + + vc_filesys_client.resp_code = (int32_t)vc_filesys_client.fileserv_msg.cmd_code; + + if (vc_filesys_client.resp_code == FILESERV_RESP_ERROR) { + vc_filesys_client.err_no = (int32_t)vc_filesys_client.fileserv_msg.params[0]; + } + + // signal vchi_msg_stub which will be waiting for response + vcos_event_signal(&vc_filesys_client.response_event); + + rr = 2; + } + else if ((xid & 0x80000000UL) == 0x80000000UL) { + /* Process new requests from the co-processor */ + + uint32_t retval = FILESERV_RESP_OK; + + //this is the number of uint32_t param[] + data that we send back to VC in bytes + + uint32_t rlen = 0; + int i; + + switch (msg->cmd_code) { + + case VC_FILESYS_CLOSE: + + i = vc_hostfs_close((int)msg->params[0]); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_CLOSEDIR: + + i = vc_hostfs_closedir((void *)msg->params[0]); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_FORMAT: + + i = vc_hostfs_format((const char *)msg->data); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_FREESPACE: + + i = vc_hostfs_freespace((const char *)msg->data); + if (i < 0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t)i; + rlen = 4; + } + break; + + case VC_FILESYS_FREESPACE64: + { + int64_t freespace; + freespace = vc_hostfs_freespace64((const char *)msg->data); + if (freespace < (int64_t)0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t)freespace; + msg->params[1] = (uint32_t)(freespace>>32); + rlen = 8; + } + } + break; + + case VC_FILESYS_GET_ATTR: + { + fattributes_t attr; + + i = vc_hostfs_get_attr((const char *)msg->data, + &attr); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t) attr; + rlen = 4; + } + } + break; + case VC_FILESYS_LSEEK: + + i = vc_hostfs_lseek( (int)msg->params[0], + (int)msg->params[1], + (int)msg->params[2]); + if (i < 0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t) i; + rlen = 4; + } + break; + + case VC_FILESYS_LSEEK64: + { + int64_t offset; + offset = (((int64_t) msg->params[2]) << 32) + msg->params[1]; + + offset = vc_hostfs_lseek64( (int)msg->params[0], offset, (int)msg->params[3]); + if (offset < (int64_t)0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t)offset; + msg->params[1] = (uint32_t)(offset>>32); + rlen = 8; + } + } + break; + + case VC_FILESYS_MKDIR: + + i = vc_hostfs_mkdir((const char *)msg->data); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_OPEN: + + i = vc_hostfs_open((const char *)msg->data, + (int) msg->params[0]); + if (i < 0) { + retval = FILESERV_RESP_ERROR; + } else { + msg->params[0] = (uint32_t) i; + } + rlen = 4; + break; + + case VC_FILESYS_OPENDIR: + + msg->params[0] = (uint32_t)vc_hostfs_opendir( + (const char *)msg->data); + if ((void *)msg->params[0] == NULL) { + retval = FILESERV_RESP_ERROR; + } + rlen = 4; + break; + + case VC_FILESYS_READ: + { + uint32_t fd = msg->params[0]; + uint32_t offset = msg->params[1]; + int total_bytes = (int)msg->params[2]; + uint32_t nalign_bytes = msg->params[3]; + + i = 0; + + if(!vcos_verify(((int)vc_filesys_client.bulk_buffer & (VCHI_BULK_ALIGN-1)) == 0 && + total_bytes <= FILESERV_MAX_BULK)) + { + retval = FILESERV_RESP_ERROR; + rlen = 4; + break; + } + + rlen = 0; + + //perform any seeking + if ( (uint32_t)0xffffffffUL != offset) + { + i = vc_hostfs_lseek( (int)fd, + (long int) offset, + VC_FILESYS_SEEK_SET); + if ( 0 > i) + { + retval = FILESERV_RESP_ERROR; + rlen = 4; + break; + } + } + + + //put it all in one msg + if(total_bytes <= FILESERV_MAX_DATA) { + i = vc_hostfs_read( (int)fd, + msg->data, + (unsigned int) total_bytes); + + if(i < 0) { + retval = FILESERV_RESP_ERROR; + msg->params[0] = 0; + } + else { + retval = FILESERV_RESP_OK; + //send back length of read + msg->params[0] = (uint32_t) i; + } + + msg->params[1] = 0; + rlen = 16 + (uint32_t) i; + } + //bulk transfer required + else { + uint32_t end_bytes = 0; + retval = FILESERV_BULK_WRITE; + + //we send the bytes required for HOST buffer align + if(nalign_bytes) { + i = vc_hostfs_read( (int)fd, + msg->data, + (unsigned int)nalign_bytes); + if(i < 0) { + retval = FILESERV_RESP_ERROR; + rlen = 16; + break; + } + else if(i != (int)nalign_bytes) { + //all data will be in one msg + retval = FILESERV_RESP_OK; + msg->params[0] = (uint32_t) i; + msg->params[1] = 0; + rlen = 16 + (uint32_t) i; + //send response + break; + } + + total_bytes -= i; + rlen += (uint32_t) i; + } + + //bulk bytes + i = vc_hostfs_read((int)fd, vc_filesys_client.bulk_buffer, (unsigned int)total_bytes); + + if(i < 0) { + retval = FILESERV_RESP_ERROR; + rlen = 16; + break; + } + else if((i+nalign_bytes) <= FILESERV_MAX_DATA) { + retval = FILESERV_RESP_OK; + memcpy(&msg->data[nalign_bytes], &vc_filesys_client.bulk_buffer[0], (size_t) i); + //read size + msg->params[0] = (i + nalign_bytes); + msg->params[1] = 0; + rlen = i + nalign_bytes + 16; + break; + } + + //copy end unaligned length bytes into msg->data + end_bytes = (uint32_t) (i & (VCHI_BULK_GRANULARITY-1)); + if(end_bytes) { + int end_index = i - (int) end_bytes; + memcpy(&msg->data[nalign_bytes], &vc_filesys_client.bulk_buffer[end_index], end_bytes); + rlen += end_bytes; + } + + //send back total bytes + msg->params[0] = (uint32_t)(i + nalign_bytes); + //number of end bytes + msg->params[1] = end_bytes; + //number of bulk bytes + msg->params[2] = (uint32_t)(i - end_bytes); + //16 for param len + rlen += 16; + + //queue bulk to be sent + if(vchi_bulk_queue_transmit( vc_filesys_client.open_handle, + vc_filesys_client.bulk_buffer, + msg->params[2], + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, + NULL ) != 0) + { + retval = FILESERV_RESP_ERROR; + rlen = 4; + break; + } + } + } + //send response + break; + + case VC_FILESYS_READDIR: + { + struct dirent result; + if (vc_hostfs_readdir_r((void *)msg->params[0], + &result) == NULL) { + retval = FILESERV_RESP_ERROR; + rlen = 4; + } else { + rlen = (uint32_t) (16+fs_host_direntbytestream_create(&result, + (void *)msg->data)); + } + } + break; + + case VC_FILESYS_REMOVE: + + i = vc_hostfs_remove((const char *)msg->data); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_RENAME: + + i = (int) strlen((char *)msg->data); + if (vc_hostfs_rename((const char *)msg->data, + (const char *)&msg->data[i+1]) + != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_SETEND: + + i = vc_hostfs_setend( (int)msg->params[0] ); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_SET_ATTR: + + i = vc_hostfs_set_attr((const char *)msg->data, + (fattributes_t)msg->params[0]); + if (i != 0) { + retval = FILESERV_RESP_ERROR; + } + rlen = 0; + break; + + case VC_FILESYS_TOTALSPACE: + + i = vc_hostfs_totalspace((const char *)msg->data); + if (i < 0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t) i; + rlen = 4; + } + break; + + case VC_FILESYS_TOTALSPACE64: + { + int64_t totalspace; + totalspace = vc_hostfs_totalspace64((const char *)msg->data); + if (totalspace < (int64_t)0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t)totalspace; + msg->params[1] = (uint32_t)(totalspace>>32); + rlen = 8; + } + } + break; +#if 0 // I don't think host systems are ready for these yet + case VC_FILESYS_SCANDISK: + + vc_hostfs_scandisk((const char *)msg->data); + rlen = 0; + break; + + case VC_FILESYS_CHKDSK: + + i = vc_hostfs_chkdsk((const char *)msg->data, msg->params[0]); + if (i < 0) { + retval = FILESERV_RESP_ERROR; + rlen = 0; + } else { + msg->params[0] = (uint32_t)i; + rlen = 4; + } + break; +#endif + case VC_FILESYS_WRITE: + { + uint32_t fd = msg->params[0]; + // uint32_t offset = msg->params[1]; + int total_bytes = (int)msg->params[2]; + uint32_t nalign_bytes = msg->params[3]; + retval = FILESERV_RESP_OK; + i = 0; + + //everything in one msg + if(total_bytes <= FILESERV_MAX_DATA) + { + i = vc_hostfs_write( (int)fd, + msg->data, + (unsigned int) total_bytes); + if (i < 0) { + retval = FILESERV_RESP_ERROR; + } else { + msg->params[0] = (uint32_t) i; + } + rlen = 4; + //send response + break; + } + else + { + uint32_t end_bytes; + uint32_t bulk_bytes; + uint32_t total_bytes_written = 0; + i = 0; + //one return param + rlen = 4; + retval = FILESERV_BULK_READ; + + //write bytes required for VC buffer align + if(nalign_bytes) { + i = vc_hostfs_write( (int)fd, + msg->data, + (unsigned int)nalign_bytes); + if(i < 0) { + retval = FILESERV_RESP_ERROR; + msg->params[0] = 0; + //break from switch and send reply + break; + } + total_bytes_written += i; + total_bytes -= nalign_bytes; + } + + //calculate bytes that wil lbe sent by bulk + bulk_bytes = (uint32_t)((total_bytes)&(~(VCHI_BULK_ALIGN-1))); + + end_bytes = total_bytes-bulk_bytes; + + + + if(vchi_bulk_queue_receive(vc_filesys_client.open_handle, + vc_filesys_client.bulk_buffer, + bulk_bytes, + VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, + NULL) != 0 || + (i = vc_hostfs_write( (int)fd, + vc_filesys_client.bulk_buffer, + (unsigned int) bulk_bytes)) < 0) + { + retval = FILESERV_RESP_ERROR; + msg->params[0] = 0; + break; + } + + total_bytes_written += i; + + if(end_bytes) { + i = vc_hostfs_write( (int)fd, + msg->data+nalign_bytes, + (unsigned int)end_bytes); + + total_bytes_written += i; + } + + if(i < 0) { + retval = FILESERV_RESP_ERROR; + msg->params[0] = 0; + break; + } + + msg->params[0] = total_bytes_written; + + } + } + break; + + default: + rlen = 4; + retval = FILESERV_RESP_ERROR; + break; + } + + //convert all to over the wire values and send + vc_send_response( msg, retval, rlen ); + + rr = 1; + + } else { + /* A message has been left in the fifo and the host side has been reset. + The message needs to be flushed. It would be better to do this by resetting + the fifos. */ + } + + return rr; +} + + +/****************************************************************************** +NAME + showmsg + +SYNOPSIS + static void showmsg(VC_MSGFIFO_CMD_HEADER_T const * head, + struct file_service_msg_body const * body) + +FUNCTION + De-bug tool: prints out fifo message. + +RETURNS + void +******************************************************************************/ + +#ifdef PRINTF +static void showmsg(VC_MSGFIFO_CMD_HEADER_T const * head, + struct file_service_msg_body const * body) +{ + unsigned int ael = (head->ext_length + 15) & ~15; + printf("Sync=%08x XID=%08x Code=%08x Extlen=%08x (%d bytes follow)\n", + head->sync, head->xid, head->cmd_code, head->ext_length, ael); + if (ael) { + unsigned int i; + printf("Content:"); + for (i = 0; i < 4 && i*4 < head->ext_length; ++i) printf(" %08x", body->params[i]); + //for(i = 0; i+16 < head->ext_length; ++i) printf(" %02x", body->data[i]); + if (head->ext_length > 16) printf(" plus %d bytes\n", head->ext_length); + } + printf("\n"); +} +#endif + + +/****************************************************************************** +NAME + fs_host_direntbytestream_create + +SYNOPSIS + static int fs_host_direntbytestream_create(struct dirent *d, void *buffer) + +FUNCTION + Turns a variable of type struct dirent into a compiler independent byte + stream, which is stored in buffer. + +RETURNS + Successful completion: The length of the byte stream + Otherwise: -1 +******************************************************************************/ +static void write_bytestream(void *a, char *b, int n) +{ + int i; + for (i=0;id_name, D_NAME_MAX_SIZE); + buf += D_NAME_MAX_SIZE; + + // Write d_size (unsigned int) + write_bytestream((void *)&d->d_size, buf, (int)sizeof(d->d_size)); + buf += 4; + + // Write d_attrib (int) + write_bytestream((void *)&d->d_attrib, buf, (int)sizeof(d->d_attrib)); + buf += 4; + + // Write d_modtime (time_t) + write_bytestream((void *)&d->d_modtime, buf, (int)sizeof(d->d_modtime)); + buf += 4; + + return (int)(buf-(char *)buffer); +} + + +/****************************************************************************** +NAME + fs_host_direntbytestream_interp + +SYNOPSIS + static void fs_host_direntbytestream_interp(struct dirent *d, void *buffer) + +FUNCTION + Turns a compiler independent byte stream back into a struct of type dirent. + +RETURNS + Successful completion: 0 + Otherwise: -1 +******************************************************************************/ +static void read_bytestream(void *a, char *b, int n) +{ + int i; + for (i=0;id_name, buf, D_NAME_MAX_SIZE); + buf += D_NAME_MAX_SIZE; + + // Read d_size (unsigned int) + read_bytestream((void *)&d->d_size, buf, (int)sizeof(d->d_size)); + d->d_size = VC_VTOH32(d->d_size); + buf += 4; + + // Read d_attrib (int) + read_bytestream((void *)&d->d_attrib, buf, (int)sizeof(d->d_attrib)); + d->d_attrib = VC_VTOH32(d->d_attrib); + buf += 4; + + // Read d_modtime (time_t) + read_bytestream((void *)&d->d_modtime, buf, (int)sizeof(d->d_modtime)); + d->d_modtime = VC_VTOH32(d->d_modtime); + + return; +} + + + +void vc_filesys_sendreset(void) +{ + //TODO +} diff --git a/interface/vmcs_host/vc_vchi_filesys.h b/interface/vmcs_host/vc_vchi_filesys.h new file mode 100755 index 0000000..393a3eb --- /dev/null +++ b/interface/vmcs_host/vc_vchi_filesys.h @@ -0,0 +1,184 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_VCHI_VCFILESYS_H_ +#define VC_VCHI_VCFILESYS_H_ + +#include "vchost_platform_config.h" +#include "vcfilesys_defs.h" +#include "vc_fileservice_defs.h" +#include "interface/vchi/vchi.h" + +#ifndef _DIRENT_H // This should really be in a dirent.h header to avoid conflicts +typedef struct DIR_tag DIR; +#endif // ifndef _DIRENT_H + +typedef struct { + int64_t st_size; /* total size, in bytes (off_t)*/ + uint32_t st_modtime; /* time of last modification (time_t)*/ +} FSTAT_T; + + +VCHPRE_ int VCHPOST_ vc_vchi_filesys_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ); + +// Stop it to prevent the functions from trying to use it. +VCHPRE_ void VCHPOST_ vc_filesys_stop(void); + +// Return the service number (-1 if not running). +VCHPRE_ int VCHPOST_ vc_filesys_inum(void); + +// Low level file system functions equivalent to close(), lseek(), open(), read() and write() +VCHPRE_ int VCHPOST_ vc_filesys_close(int fildes); + +VCHPRE_ long VCHPOST_ vc_filesys_lseek(int fildes, long offset, int whence); + +VCHPRE_ int64_t VCHPOST_ vc_filesys_lseek64(int fildes, int64_t offset, int whence); + +VCHPRE_ int VCHPOST_ vc_filesys_open(const char *path, int vc_oflag); + +VCHPRE_ int VCHPOST_ vc_filesys_read(int fildes, void *buf, unsigned int nbyte); + +VCHPRE_ int VCHPOST_ vc_filesys_write(int fildes, const void *buf, unsigned int nbyte); + +VCHPRE_ int VCHPOST_ vc_filesys_mount(const char *device, const char *mountpoint, const char *options); +VCHPRE_ int VCHPOST_ vc_filesys_umount(const char *mountpoint); + + +// Ends a directory listing iteration +VCHPRE_ int VCHPOST_ vc_filesys_closedir(void *dhandle); + +// Formats the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_filesys_format(const char *path); + +// Returns the amount of free space on the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_filesys_freespace(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_filesys_freespace64(const char *path); + +// Gets the attributes of the named file +VCHPRE_ int VCHPOST_ vc_filesys_get_attr(const char *path, fattributes_t *attr); + +// Get the file stat info struct for the specified file. +VCHPRE_ int VCHPOST_ vc_filesys_fstat(int filedes, FSTAT_T *buf); + +// Creates a new directory +VCHPRE_ int VCHPOST_ vc_filesys_mkdir(const char *path); + +// Starts a directory listing iteration +VCHPRE_ void * VCHPOST_ vc_filesys_opendir(const char *dirname); + +// Directory listing iterator +VCHPRE_ struct dirent * VCHPOST_ vc_filesys_readdir_r(void *dhandle, struct dirent *result); + +// Get the sum of the filesizes, and the number of files under the specified directory path. +VCHPRE_ int64_t VCHPOST_ vc_filesys_dirsize(const char *path, uint32_t *num_files, uint32_t *num_dirs); + +// Deletes a file or (empty) directory +VCHPRE_ int VCHPOST_ vc_filesys_remove(const char *path); + +// Renames a file, provided the new name is on the same file system as the old +VCHPRE_ int VCHPOST_ vc_filesys_rename(const char *oldfile, const char *newfile); + +// Resets the co-processor side file system +VCHPRE_ int VCHPOST_ vc_filesys_reset(void); + +// Sets the attributes of the named file +VCHPRE_ int VCHPOST_ vc_filesys_set_attr(const char *path, fattributes_t attr); + +// Truncates a file at its current position +VCHPRE_ int VCHPOST_ vc_filesys_setend(int fildes); + +// Returns the size of a file in bytes. +VCHPRE_ int VCHPOST_ vc_filesys_size(const char *path); + +// Checks whether there are any messages in the incoming message fifo and responds to any such messages +VCHPRE_ int VCHPOST_ vc_filesys_poll_message_fifo(void); + +// Return the event used to wait for reads. +VCHPRE_ void * VCHPOST_ vc_filesys_read_event(void); + +// Sends a command for VC01 to reset the file system +VCHPRE_ void VCHPOST_ vc_filesys_sendreset(void); + +// Return the error code of the last file system error +VCHPRE_ int VCHPOST_ vc_filesys_errno(void); + +// Invalidates any cluster chains in the FAT that are not referenced in any directory structures +VCHPRE_ void VCHPOST_ vc_filesys_scandisk(const char *path); + +// Checks whether or not a FAT filesystem is corrupt or not. If fix_errors is TRUE behaves exactly as vc_filesys_scandisk. +VCHPRE_ int VCHPOST_ vc_filesys_chkdsk(const char *path, int fix_errors); + +// Return whether a disk is writeable or not. +VCHPRE_ int VCHPOST_ vc_filesys_diskwritable(const char *path); + +// Return file system type of a disk. +VCHPRE_ int VCHPOST_ vc_filesys_fstype(const char *path); + +// Returns the toatl amount of space on the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_filesys_totalspace(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_filesys_totalspace64(const char *path); + +// Open disk for block level access +VCHPRE_ int VCHPOST_ vc_filesys_open_disk_raw(const char *path); + +// Close disk from block level access mode +VCHPRE_ int VCHPOST_ vc_filesys_close_disk_raw(const char *path); + +// Open disk for normal access +VCHPRE_ int VCHPOST_ vc_filesys_open_disk(const char *path); + +// Close disk for normal access +VCHPRE_ int VCHPOST_ vc_filesys_close_disk(const char *path); + +// Return number of sectors. +VCHPRE_ int VCHPOST_ vc_filesys_numsectors(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_filesys_numsectors64(const char *path); + +// Read/Write sectors +VCHPRE_ int VCHPOST_ vc_filesys_read_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_read); +VCHPRE_ int VCHPOST_ vc_filesys_write_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_written); + +// Begin reading sectors from VideoCore. +VCHPRE_ int VCHPOST_ vc_filesys_read_sectors_begin(const char *path, uint32_t sector, uint32_t count); + +// Read the next sector. +VCHPRE_ int VCHPOST_ vc_filesys_read_sector(char *buf); + +// End streaming sectors. +VCHPRE_ int VCHPOST_ vc_filesys_read_sectors_end(uint32_t *sectors_read); + +// Begin writing sectors from VideoCore. +VCHPRE_ int VCHPOST_ vc_filesys_write_sectors_begin(const char *path, uint32_t sector, uint32_t count); + +// Write the next sector. +VCHPRE_ int VCHPOST_ vc_filesys_write_sector(const char *buf); + +// End streaming sectors. +VCHPRE_ int VCHPOST_ vc_filesys_write_sectors_end(uint32_t *sectors_written); + +#endif //VCFILESYS_H_ + diff --git a/interface/vmcs_host/vc_vchi_gencmd.c b/interface/vmcs_host/vc_vchi_gencmd.c new file mode 100755 index 0000000..2da81a4 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_gencmd.c @@ -0,0 +1,532 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include + +#include "vchost.h" + +#include "interface/vcos/vcos.h" +#include "vcinclude/common.h" +#include "vc_vchi_gencmd.h" +#include "interface/vchi/vchi.h" +#include "interface/vchi/common/endian.h" +#include "interface/vmcs_host/vc_gencmd_defs.h" + +#ifdef HAVE_GENCMD_VERSION +extern const char *gencmd_get_build_version(void); +#error +#endif + +/****************************************************************************** +Local types and defines. +******************************************************************************/ +#define GENCMD_MAX_LENGTH 512 +typedef struct { + VCHI_SERVICE_HANDLE_T open_handle[VCHI_MAX_NUM_CONNECTIONS]; + uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS]; + char command_buffer[GENCMD_MAX_LENGTH+1]; + char response_buffer[GENCMDSERVICE_MSGFIFO_SIZE]; + uint32_t response_length; //Length of response minus the error code + int num_connections; + VCOS_MUTEX_T lock; + int initialised; + VCOS_EVENT_T message_available_event; +} GENCMD_SERVICE_T; + +static GENCMD_SERVICE_T gencmd_client; + + +/****************************************************************************** +Static function. +******************************************************************************/ +static void gencmd_callback( void *callback_param, + VCHI_CALLBACK_REASON_T reason, + void *msg_handle ); + +static __inline int lock_obtain (void) { + int ret = -1; + if(gencmd_client.initialised && vcos_mutex_lock(&gencmd_client.lock) == VCOS_SUCCESS) + { + ret = 0; + } + + return ret; +} +static __inline void lock_release (void) { + vcos_mutex_unlock(&gencmd_client.lock); +} + +int use_gencmd_service(void) { + int ret = 0; + int i=0; + for(i = 0; i < gencmd_client.num_connections; i++) { + ret = (ret == 0) ? vchi_service_use(gencmd_client.open_handle[i]) : ret; + } + return ret; +} + +int release_gencmd_service(void) { + int ret = 0; + int i=0; + for(i = 0; i < gencmd_client.num_connections; i++) { + ret = (ret == 0) ? vchi_service_release(gencmd_client.open_handle[i]) : ret; + } + return ret; +} + + +//call vc_vchi_gencmd_init to initialise +int vc_gencmd_init() { + assert(0); + return 0; +} + +/****************************************************************************** +NAME + vc_vchi_gencmd_init + +SYNOPSIS + void vc_vchi_gencmd_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) + +FUNCTION + Initialise the general command service for use. A negative return value + indicates failure (which may mean it has not been started on VideoCore). + +RETURNS + int +******************************************************************************/ + +void vc_vchi_gencmd_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) +{ + VCOS_STATUS_T status; + int32_t success; + int i; + + if (gencmd_client.initialised) + return; + + // record the number of connections + memset( &gencmd_client, 0, sizeof(GENCMD_SERVICE_T) ); + gencmd_client.num_connections = (int) num_connections; + + status = vcos_mutex_create(&gencmd_client.lock, "HGencmd"); + vcos_assert(status == VCOS_SUCCESS); + status = vcos_event_create(&gencmd_client.message_available_event, "HGencmd"); + vcos_assert(status == VCOS_SUCCESS); + + for (i=0; i= 0 && length < GENCMD_MAX_LENGTH) + { + int i; + use_gencmd_service(); + for( i=0; i= 0) { + ret = vc_gencmd_read_response(response, maxlen); + } + + release_gencmd_service(); + + return ret; +} + +/****************************************************************************** +NAME + vc_gencmd_string_property + +SYNOPSIS + int vc_gencmd_string_property(char *text, char *property, char **value, int *length) + +FUNCTION + Given a text string, containing items of the form property=value, + look for the named property and return the value. The start of the value + is returned, along with its length. The value may contain spaces, in which + case it is enclosed in double quotes. The double quotes are not included in + the return parameters. Return non-zero if the property is found. + +RETURNS + int +******************************************************************************/ + +int vc_gencmd_string_property(char *text, const char *property, char **value, int *length) { +#define READING_PROPERTY 0 +#define READING_VALUE 1 +#define READING_VALUE_QUOTED 2 + int state = READING_PROPERTY; + int delimiter = 1, match = 0, len = (int)strlen(property); + char *prop_start=text, *value_start=text; + for (; *text; text++) { + int ch = *text; + switch (state) { + case READING_PROPERTY: + if (delimiter) prop_start = text; + if (isspace(ch)) delimiter = 1; + else if (ch == '=') { + delimiter = 1; + match = (text-prop_start==len && strncmp(prop_start, property, (size_t)(text-prop_start))==0); + state = READING_VALUE; + } + else delimiter = 0; + break; + case READING_VALUE: + if (delimiter) value_start = text; + if (isspace(ch)) { + if (match) goto success; + delimiter = 1; + state = READING_PROPERTY; + } + else if (delimiter && ch == '"') { + delimiter = 1; + state = READING_VALUE_QUOTED; + } + else delimiter = 0; + break; + case READING_VALUE_QUOTED: + if (delimiter) value_start = text; + if (ch == '"') { + if (match) goto success; + delimiter = 1; + state = READING_PROPERTY; + } + else delimiter = 0; + break; + } + } + if (match) goto success; + return 0; +success: + *value = value_start; + *length = text - value_start; + return 1; +} + +/****************************************************************************** +NAME + vc_gencmd_number_property + +SYNOPSIS + int vc_gencmd_number_property(char *text, char *property, int *number) + +FUNCTION + Given a text string, containing items of the form property=value, + look for the named property and return the numeric value. If such a numeric + value is successfully found, return 1; otherwise return 0. + +RETURNS + int +******************************************************************************/ + +int vc_gencmd_number_property(char *text, const char *property, int *number) { + char *value, temp; + int length, retval; + if (vc_gencmd_string_property(text, property, &value, &length) == 0) + return 0; + temp = value[length]; + value[length] = 0; + /* coverity[secure_coding] - this is not insecure */ + retval = sscanf(value, "0x%x", (unsigned int*)number); + if (retval != 1) + /* coverity[secure_coding] - this is not insecure */ + retval = sscanf(value, "%d", number); + value[length] = temp; + return retval; + +} + +/****************************************************************************** +NAME + vc_gencmd_until + +SYNOPSIS + int vc_gencmd_until(const char *cmd, const char *error_string, int timeout); + +FUNCTION + Sends the command repeatedly, until one of the following situations occurs: + The specified response string is found within the gencmd response. + The specified error string is found within the gencmd response. + The timeout is reached. + + The timeout is a rough value, do not use it for precise timing. + +RETURNS + 0 if the requested response was detected. + 1 if the error string is detected or the timeout is reached. +******************************************************************************/ +int vc_gencmd_until( char *cmd, + const char *property, + char *value, + const char *error_string, + int timeout) { + char response[128]; + int length; + char *ret_value; + int ret = 1; + + use_gencmd_service(); + for (;timeout > 0; timeout -= 10) { + vc_gencmd(response, (int)sizeof(response), cmd); + if (strstr(response,error_string)) { + ret = 1; + break; + } + else if (vc_gencmd_string_property(response, property, &ret_value, &length) && + strncmp(value,ret_value,(size_t)length)==0) { + ret = 0; + break; + } + vcos_sleep(10); + } + release_gencmd_service(); + + return ret; +} + diff --git a/interface/vmcs_host/vc_vchi_gencmd.h b/interface/vmcs_host/vc_vchi_gencmd.h new file mode 100755 index 0000000..da86b5d --- /dev/null +++ b/interface/vmcs_host/vc_vchi_gencmd.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_VCHI_GENCMD_H +#define VC_VCHI_GENCMD_H +#include "vchost_platform_config.h" +#include "interface/vchi/vchi.h" +#include "interface/vcos/vcos.h" //for VCHPRE_ abd VCHPOST_ macro's for func declaration + +/* Initialise general command service. Returns it's interface number. This initialises + the host side of the interface, it does not send anything to VideoCore. */ + +VCHPRE_ int VCHPOST_ vc_gencmd_init(void); + +VCHPRE_ void VCHPOST_ vc_vchi_gencmd_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ); + + +/* Stop the service from being used. */ + +VCHPRE_ void VCHPOST_ vc_gencmd_stop(void); + +/* Functions to support videocore suspend/resume for cases where vc_gencmd_send expects a response to + * ensure videocore is not shut down (done internally in vc_gencmd / vc_gencmd_until) */ + +VCHPRE_ int VCHPOST_ use_gencmd_service(void); +VCHPRE_ int VCHPOST_ release_gencmd_service(void); + +/****************************************************************************** +Send commands to VideoCore. +These all return 0 for success. They return VC_MSGFIFO_FIFO_FULL if there is +insufficient space for the whole message in the fifo, and none of the message is +sent. +******************************************************************************/ + +/* send command to general command serivce */ +VCHPRE_ int VCHPOST_ vc_gencmd_send( const char *format, ... ); + +/* get resonse from general command serivce */ +VCHPRE_ int VCHPOST_ vc_gencmd_read_response(char *response, int maxlen); + +/* convenience function to send command and receive the response */ +VCHPRE_ int VCHPOST_ vc_gencmd(char *response, int maxlen, const char *format, ...); + +/****************************************************************************** +Utilities to help interpret the responses. +******************************************************************************/ + +/* Read the value of a property=value type pair from a string (typically VideoCore's + response to a general command). Return non-zero if found. */ +VCHPRE_ int VCHPOST_ vc_gencmd_string_property(char *text, const char *property, char **value, int *length); + +/* Read the numeric value of a property=number field from a response string. Return + non-zero if found. */ +VCHPRE_ int VCHPOST_ vc_gencmd_number_property(char *text, const char *property, int *number); + +/* Send a command until the desired response is received, the error message is detected, or the timeout */ +VCHPRE_ int VCHPOST_ vc_gencmd_until( char *cmd, + const char *property, + char *value, + const char *error_string, + int timeout); + + +#endif diff --git a/interface/vmcs_host/vc_vchi_gpuserv.c b/interface/vmcs_host/vc_vchi_gpuserv.c new file mode 100755 index 0000000..695ed52 --- /dev/null +++ b/interface/vmcs_host/vc_vchi_gpuserv.c @@ -0,0 +1,261 @@ +/* +Copyright (c) 2016, Raspberry Pi (Trading) Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "vchost.h" + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_logging.h" +#include "vcinclude/common.h" +#include "vc_vchi_gpuserv.h" +#include "vchiq.h" +#include "interface/vcos/vcos_stdbool.h" + +// VCOS logging category +static VCOS_LOG_CAT_T vcos_log_category; +#define VCOS_LOG_CATEGORY (&vcos_log_category) + +static VCHIQ_INSTANCE_T gpuserv_client_vchiq_instance; +static VCOS_ONCE_T gpuserv_client_once = VCOS_ONCE_INIT; + +/****************************************************************************** +Local types and defines. +******************************************************************************/ +#define GPUSERV_MAX_LENGTH 512 +typedef struct { + VCHIQ_SERVICE_HANDLE_T service; + VCOS_MUTEX_T lock; + int initialised; + VCOS_EVENT_T message_available_event; + int refcount; +} GPUSERV_SERVICE_T; + +static GPUSERV_SERVICE_T gpuserv_client; + + +/****************************************************************************** +Static function. +******************************************************************************/ +static VCHIQ_STATUS_T gpuserv_callback( VCHIQ_REASON_T reason, + VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T vchiq_handle, + void *bulk_userdata ); + +static void init_once(void) +{ + vcos_mutex_create(&gpuserv_client.lock, VCOS_FUNCTION); +} + +/****************************************************************************** +NAME + + vc_gpuserv_init + +SYNOPSIS + int32_t vc_gpuserv_init( void ) + +FUNCTION + Initialise the gpu service for use. A negative return value + indicates failure (which may mean it has not been started on VideoCore). + +RETURNS + zero on success +******************************************************************************/ + +int32_t vc_gpuserv_init( void ) +{ + VCHIQ_SERVICE_PARAMS_T vchiq_params; + VCHIQ_STATUS_T vchiq_status; + + vcos_once(&gpuserv_client_once, init_once); + + vcos_mutex_lock(&gpuserv_client.lock); + + if (gpuserv_client.refcount++ > 0) + { + /* Already initialised so nothing to do */ + vcos_mutex_unlock(&gpuserv_client.lock); + return VCOS_SUCCESS; + } + + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE); + vcos_log_register("gpuserv", VCOS_LOG_CATEGORY); + + vcos_log_trace("%s: starting initialisation", VCOS_FUNCTION); + + /* Initialise a VCHIQ instance */ + vchiq_status = vchiq_initialise(&gpuserv_client_vchiq_instance); + if (vchiq_status != VCHIQ_SUCCESS) + { + vcos_log_error("%s: failed to initialise vchiq: %d", VCOS_FUNCTION, vchiq_status); + goto error; + } + + vchiq_status = vchiq_connect(gpuserv_client_vchiq_instance); + if (vchiq_status != VCHIQ_SUCCESS) + { + vcos_log_error("%s: failed to connect to vchiq: %d", VCOS_FUNCTION, vchiq_status); + goto error; + } + + memset(&vchiq_params, 0, sizeof(vchiq_params)); + vchiq_params.fourcc = VCHIQ_MAKE_FOURCC('G','P','U','S'); + vchiq_params.callback = gpuserv_callback; + vchiq_params.userdata = NULL; + vchiq_params.version = 1; + vchiq_params.version_min = 1; + + vchiq_status = vchiq_open_service(gpuserv_client_vchiq_instance, &vchiq_params, &gpuserv_client.service); + if (vchiq_status != VCHIQ_SUCCESS) + { + vcos_log_error("%s: could not open vchiq service: %d", VCOS_FUNCTION, vchiq_status); + goto error; + } + vcos_mutex_unlock(&gpuserv_client.lock); + return 0; +error: + vcos_mutex_unlock(&gpuserv_client.lock); + return -1; +} + +/****************************************************************************** +NAME + + vc_gpuserv_deinit + +SYNOPSIS + void vc_gpuserv_init( void ) + +FUNCTION + Deinitialise the gpu service. Should be called when gpu_service is no longer required + +RETURNS + zero on success +******************************************************************************/ + +void vc_gpuserv_deinit( void ) +{ + vcos_mutex_lock(&gpuserv_client.lock); + + if (gpuserv_client.refcount > 0 && --gpuserv_client.refcount == 0) + { + vchi_service_close(gpuserv_client.service); + gpuserv_client.service = 0; + } + vcos_mutex_unlock(&gpuserv_client.lock); +} + +/****************************************************************************** +NAME + gpuserv_callback + +SYNOPSIS + void gpuserv_callback( VCHIQ_REASON_T reason, + VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, + void *bulk_userdata ) +FUNCTION + VCHIQ callback + +RETURNS + zero on success +******************************************************************************/ +static VCHIQ_STATUS_T gpuserv_callback( VCHIQ_REASON_T reason, + VCHIQ_HEADER_T *header, + VCHIQ_SERVICE_HANDLE_T service, + void *bulk_userdata ) +{ + // reason is one of VCHIQ_MESSAGE_AVAILABLE, VCHIQ_BULK_TRANSMIT_DONE, VCHIQ_BULK_RECEIVE_DONE + switch (reason) + { + case VCHIQ_MESSAGE_AVAILABLE: + { + struct gpu_callback_s *c = (struct gpu_callback_s *)header->data; + if (c->func) + c->func(c->cookie); + vchiq_release_message(service, header); + break; + } + default: + ; + } + return 0; // Releases any command message (VCHIQ_MESSAGE_AVAILABLE), ignored otherwise +} + +/****************************************************************************** +NAME + vc_gpuserv_execute_code + +SYNOPSIS + int32_t vc_gpuserv_execute_code(int num_jobs, struct gpu_job_s jobs[]) + +FUNCTION + Submit a list of VPU/QPU jobs to be exeected by GPU + +RETURNS + zero on success +******************************************************************************/ +#define MAX_JOBS 8 +int32_t vc_gpuserv_execute_code(int num_jobs, struct gpu_job_s jobs[]) +{ + VCHIQ_ELEMENT_T elements[MAX_JOBS]; + int i; + + // hack: temporarily allow calling this function without calling vc_gpuserv_init + // will be removed later + if (!gpuserv_client.service) + { + vc_gpuserv_init(); + vcos_log_error("%s: called without calling vc_gpuserv_init", VCOS_FUNCTION); + } + + if (!gpuserv_client.service) + { + vcos_log_error("%s: vchiq service not initialised", VCOS_FUNCTION); + return -1; + } + if (num_jobs > MAX_JOBS) + return -1; + + for (i=0; i +#endif + +#include "interface/vcos/vcos.h" + +#include "vchost_platform_config.h" +#include "vchost.h" + +#include "interface/vchi/vchi.h" +#include "interface/vchi/common/endian.h" +#include "interface/vchi/message_drivers/message.h" +#include "vc_tvservice.h" + +/****************************************************************************** +Local types and defines. +******************************************************************************/ +#ifndef _min +#define _min(x,y) (((x) <= (y))? (x) : (y)) +#endif +#ifndef _max +#define _max(x,y) (((x) >= (y))? (x) : (y)) +#endif + +typedef struct +{ + TVSERVICE_CALLBACK_T notify_fn; + void *notify_data; +} TVSERVICE_HOST_CALLBACK_T; + +typedef struct { + uint32_t is_valid; + uint32_t max_modes; //How big the table have we allocated + uint32_t num_modes; //How many valid entries are there + TV_SUPPORTED_MODE_NEW_T *modes; +} TVSERVICE_MODE_CACHE_T; + +//TV service host side state (mostly the same as Videocore side - TVSERVICE_STATE_T) +typedef struct { + //Generic service stuff + VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC + VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification + uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS]; + char command_buffer[TVSERVICE_MSGFIFO_SIZE]; + char response_buffer[TVSERVICE_MSGFIFO_SIZE]; + uint32_t response_length; + uint32_t notify_buffer[TVSERVICE_MSGFIFO_SIZE/sizeof(uint32_t)]; + uint32_t notify_length; + uint32_t num_connections; + VCOS_MUTEX_T lock; + TVSERVICE_HOST_CALLBACK_T callbacks[TVSERVICE_MAX_CALLBACKS]; + int initialised; + int to_exit; + + //TV stuff + uint32_t copy_protect; + + //HDMI specific stuff + HDMI_RES_GROUP_T hdmi_current_group; + HDMI_MODE_T hdmi_current_mode; + HDMI_DISPLAY_OPTIONS_T hdmi_options; + + //If client ever asks for supported modes, we store them for quick return + HDMI_RES_GROUP_T hdmi_preferred_group; + uint32_t hdmi_preferred_mode; + TVSERVICE_MODE_CACHE_T dmt_cache; + TVSERVICE_MODE_CACHE_T cea_cache; + + //SDTV specific stuff + SDTV_COLOUR_T sdtv_current_colour; + SDTV_MODE_T sdtv_current_mode; + SDTV_OPTIONS_T sdtv_options; + SDTV_CP_MODE_T sdtv_current_cp_mode; +} TVSERVICE_HOST_STATE_T; + +/****************************************************************************** +Static data. +******************************************************************************/ +static TVSERVICE_HOST_STATE_T tvservice_client; +static VCOS_EVENT_T tvservice_message_available_event; +static VCOS_EVENT_T tvservice_notify_available_event; +static VCOS_THREAD_T tvservice_notify_task; + +#define VCOS_LOG_CATEGORY (&tvservice_log_category) +static VCOS_LOG_CAT_T tvservice_log_category; + +//Command strings - must match what's in vc_tvservice_defs.h +static char* tvservice_command_strings[] = { + "get_state", + "hdmi_on_preferred", + "hdmi_on_best", + "hdmi_on_explicit", + "sdtv_on", + "off", + "query_supported_modes", + "query_mode_support", + "query_audio_support", + "enable_copy_protect", + "disable_copy_protect", + "show_info", + "get_av_latency", + "hdcp_set_key", + "hdcp_set_srm", + "set_spd", + "set_display_options", + "test_mode_start", + "test_mode_stop", + "ddc_read", + "set_attached", + "set_property", + "get_property", + "get_display_state", + "end_of_list" +}; + +/****************************************************************************** +Static functions. +******************************************************************************/ +//Lock the host state +static __inline int tvservice_lock_obtain (void) { + if(tvservice_client.initialised && vcos_mutex_lock(&tvservice_client.lock) == VCOS_SUCCESS) { + //Check again in case the service has been stopped + if (tvservice_client.initialised) { + vchi_service_use(tvservice_client.client_handle[0]); + return 0; + } + else + vcos_mutex_unlock(&tvservice_client.lock); + } + + return -1; +} + +//Unlock the host state +static __inline void tvservice_lock_release (void) { + if (tvservice_client.initialised) { + vchi_service_release(tvservice_client.client_handle[0]); + } + vcos_mutex_unlock(&tvservice_client.lock); +} + +//Forward declarations +static void tvservice_client_callback( void *callback_param, + VCHI_CALLBACK_REASON_T reason, + void *msg_handle ); + +static void tvservice_notify_callback( void *callback_param, + VCHI_CALLBACK_REASON_T reason, + void *msg_handle ); + +static int32_t tvservice_wait_for_reply(void *response, uint32_t max_length); + +static int32_t tvservice_wait_for_bulk_receive(void *buffer, uint32_t max_length); + +static int32_t tvservice_send_command( uint32_t command, void *buffer, uint32_t length, uint32_t has_reply); + +static int32_t tvservice_send_command_reply( uint32_t command, void *buffer, uint32_t length, + void *response, uint32_t max_length); + +static void *tvservice_notify_func(void *arg); + + +/****************************************************************************** +TV service API +******************************************************************************/ +/****************************************************************************** +NAME + vc_vchi_tv_init + +SYNOPSIS + int vc_vchi_tv_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) + +FUNCTION + Initialise the TV service for use. A negative return value + indicates failure (which may mean it has not been started on VideoCore). + +RETURNS + int +******************************************************************************/ +VCHPRE_ int VCHPOST_ vc_vchi_tv_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) { + int32_t success; + uint32_t i; + VCOS_STATUS_T status; + VCOS_THREAD_ATTR_T attrs; + static const HDMI_DISPLAY_OPTIONS_T hdmi_default_display_options = + { + HDMI_ASPECT_UNKNOWN, + VC_FALSE, 0, 0, // No vertical bar information. + VC_FALSE, 0, 0, // No horizontal bar information. + 0 // No overscan flags. + }; + + if (tvservice_client.initialised) + return -2; + + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR); + vcos_log_register("tvservice-client", VCOS_LOG_CATEGORY); + + vcos_log_trace("[%s]", VCOS_FUNCTION); + + // record the number of connections + memset( &tvservice_client, 0, sizeof(TVSERVICE_HOST_STATE_T) ); + tvservice_client.num_connections = num_connections; + + status = vcos_mutex_create(&tvservice_client.lock, "HTV"); + vcos_assert(status == VCOS_SUCCESS); + status = vcos_event_create(&tvservice_message_available_event, "HTV"); + vcos_assert(status == VCOS_SUCCESS); + status = vcos_event_create(&tvservice_notify_available_event, "HTV"); + vcos_assert(status == VCOS_SUCCESS); + + //Initialise any other non-zero bits of the TV service state here + tvservice_client.sdtv_current_mode = SDTV_MODE_OFF; + tvservice_client.sdtv_options.aspect = SDTV_ASPECT_4_3; + memcpy(&tvservice_client.hdmi_options, &hdmi_default_display_options, sizeof(HDMI_DISPLAY_OPTIONS_T)); + + for (i=0; i < tvservice_client.num_connections; i++) { + + // Create a 'Client' service on the each of the connections + SERVICE_CREATION_T tvservice_parameters = { VCHI_VERSION(VC_TVSERVICE_VER), + TVSERVICE_CLIENT_NAME, // 4cc service code + connections[i], // passed in fn ptrs + 0, // tx fifo size (unused) + 0, // tx fifo size (unused) + &tvservice_client_callback, // service callback + &tvservice_message_available_event, // callback parameter + VC_TRUE, // want_unaligned_bulk_rx + VC_TRUE, // want_unaligned_bulk_tx + VC_FALSE, // want_crc + }; + + SERVICE_CREATION_T tvservice_parameters2 = { VCHI_VERSION(VC_TVSERVICE_VER), + TVSERVICE_NOTIFY_NAME, // 4cc service code + connections[i], // passed in fn ptrs + 0, // tx fifo size (unused) + 0, // tx fifo size (unused) + &tvservice_notify_callback,// service callback + &tvservice_notify_available_event, // callback parameter + VC_FALSE, // want_unaligned_bulk_rx + VC_FALSE, // want_unaligned_bulk_tx + VC_FALSE, // want_crc + }; + + //Create the client to normal TV service + success = vchi_service_open( initialise_instance, &tvservice_parameters, &tvservice_client.client_handle[i] ); + + //Create the client to the async TV service (any TV related notifications) + if (success == 0) + { + success = vchi_service_open( initialise_instance, &tvservice_parameters2, &tvservice_client.notify_handle[i] ); + if (success == 0) + { + vchi_service_release(tvservice_client.client_handle[i]); + vchi_service_release(tvservice_client.notify_handle[i]); + } + else + { + vchi_service_close(tvservice_client.client_handle[i]); + vcos_log_error("Failed to connect to async TV service: %d", success); + } + } else { + vcos_log_error("Failed to connect to TV service: %d", success); + } + + if (success != 0) + { + while (i > 0) + { + --i; + vchi_service_close(tvservice_client.client_handle[i]); + vchi_service_close(tvservice_client.notify_handle[i]); + } + return -1; + } + } + + //Create the notifier task + vcos_thread_attr_init(&attrs); + vcos_thread_attr_setstacksize(&attrs, 4096); + vcos_thread_attr_settimeslice(&attrs, 1); + + status = vcos_thread_create(&tvservice_notify_task, "HTV Notify", &attrs, tvservice_notify_func, &tvservice_client); + vcos_assert(status == VCOS_SUCCESS); + + tvservice_client.initialised = 1; + vcos_log_trace("TV service initialised"); + + return 0; +} + +/*********************************************************** + * Name: vc_vchi_tv_stop + * + * Arguments: + * - + * + * Description: Stops the Host side part of TV service + * + * Returns: - + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_vchi_tv_stop( void ) { + // Wait for the current lock-holder to finish before zapping TV service + uint32_t i; + + if (!tvservice_client.initialised) + return; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(tvservice_lock_obtain() == 0) + { + void *dummy; + vchi_service_release(tvservice_client.client_handle[0]); // to match the use in tvservice_lock_obtain() + + for (i=0; i < tvservice_client.num_connections; i++) { + int32_t result; + vchi_service_use(tvservice_client.client_handle[i]); + vchi_service_use(tvservice_client.notify_handle[i]); + result = vchi_service_close(tvservice_client.client_handle[i]); + vcos_assert( result == 0 ); + result = vchi_service_close(tvservice_client.notify_handle[i]); + vcos_assert( result == 0 ); + } + + // Unset the initialise flag so no other threads can obtain the lock + tvservice_client.initialised = 0; + + tvservice_lock_release(); + tvservice_client.to_exit = 1; //Signal to quit + vcos_event_signal(&tvservice_notify_available_event); + vcos_thread_join(&tvservice_notify_task, &dummy); + + if(tvservice_client.cea_cache.modes) + vcos_free(tvservice_client.cea_cache.modes); + + if(tvservice_client.dmt_cache.modes) + vcos_free(tvservice_client.dmt_cache.modes); + + vcos_mutex_delete(&tvservice_client.lock); + vcos_event_delete(&tvservice_message_available_event); + vcos_event_delete(&tvservice_notify_available_event); + } +} + + +/*********************************************************** + * Name: vc_tv_register_callback + * + * Arguments: + * callback function, context to be passed when function is called + * + * Description: Register a callback function for all TV notifications + * + * Returns: - + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) { + + vcos_assert_msg(callback != NULL, "Use vc_tv_unregister_callback() to remove a callback"); + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(tvservice_lock_obtain() == 0) + { + uint32_t done = 0; + uint32_t i; + for(i = 0; (i < TVSERVICE_MAX_CALLBACKS) && !done; i++) + { + if(tvservice_client.callbacks[i].notify_fn == NULL) + { + tvservice_client.callbacks[i].notify_fn = callback; + tvservice_client.callbacks[i].notify_data = callback_data; + done = 1; + } // if + } // for + vcos_assert(done); + tvservice_lock_release(); + } +} + +/*********************************************************** + * Name: vc_tv_unregister_callback + * + * Arguments: + * callback function + * + * Description: Unregister a previously-registered callback function for TV notifications + * + * Returns: - + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_tv_unregister_callback(TVSERVICE_CALLBACK_T callback) +{ + vcos_assert(callback != NULL); + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(tvservice_lock_obtain() == 0) + { + uint32_t done = 0; + uint32_t i; + for(i = 0; (i < TVSERVICE_MAX_CALLBACKS) && !done; i++) + { + if(tvservice_client.callbacks[i].notify_fn == callback) + { + tvservice_client.callbacks[i].notify_fn = NULL; + tvservice_client.callbacks[i].notify_data = NULL; + done = 1; + } // if + } // for + vcos_assert(done); + tvservice_lock_release(); + } +} + +/*********************************************************** + * Name: vc_tv_unregister_callback_full + * + * Arguments: + * callback function + * callback function context + * + * Description: Unregister a previously-registered callback function for TV notifications + * + * Returns: - + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_tv_unregister_callback_full(TVSERVICE_CALLBACK_T callback, void *callback_data) +{ + vcos_assert(callback != NULL); + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(tvservice_lock_obtain() == 0) + { + uint32_t done = 0; + uint32_t i; + for(i = 0; (i < TVSERVICE_MAX_CALLBACKS) && !done; i++) + { + if(tvservice_client.callbacks[i].notify_fn == callback && + tvservice_client.callbacks[i].notify_data == callback_data) + { + tvservice_client.callbacks[i].notify_fn = NULL; + tvservice_client.callbacks[i].notify_data = NULL; + done = 1; + } // if + } // for + vcos_assert(done); + tvservice_lock_release(); + } +} + +/********************************************************************************* + * + * Static functions definitions + * + *********************************************************************************/ +//TODO: Might need to handle multiple connections later +/*********************************************************** + * Name: tvservice_client_callback + * + * Arguments: semaphore, callback reason and message handle + * + * Description: Callback when a message is available for TV service + * + ***********************************************************/ +static void tvservice_client_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) { + VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + (void)msg_handle; + + if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL) + return; + + vcos_event_signal(event); +} + +/*********************************************************** + * Name: tvservice_notify_callback + * + * Arguments: semaphore, callback reason and message handle + * + * Description: Callback when a message is available for TV notify service + * + ***********************************************************/ +static void tvservice_notify_callback( void *callback_param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle ) { + VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + (void)msg_handle; + + if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL) + return; + + vcos_event_signal(event); +} + +/*********************************************************** + * Name: tvservice_wait_for_reply + * + * Arguments: response buffer, buffer length + * + * Description: blocked until something is in the buffer + * + * Returns error code of vchi + * + ***********************************************************/ +static int32_t tvservice_wait_for_reply(void *response, uint32_t max_length) { + int32_t success = 0; + uint32_t length_read = 0; + vcos_log_trace("[%s]", VCOS_FUNCTION); + do { + //TODO : we need to deal with messages coming through on more than one connections properly + //At the moment it will always try to read the first connection if there is something there + //Check if there is something in the queue, if so return immediately + //otherwise wait for the semaphore and read again + success = vchi_msg_dequeue( tvservice_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE ); + } while( length_read == 0 && vcos_event_wait(&tvservice_message_available_event) == VCOS_SUCCESS); + if(length_read) { + vcos_log_trace("TV service got reply %d bytes", length_read); + } else { + vcos_log_warn("TV service wait for reply failed"); + } + return success; +} + +/*********************************************************** + * Name: tvservice_wait_for_bulk_receive + * + * Arguments: response buffer, buffer length + * + * Description: blocked until bulk receive + * + * Returns error code of vchi + * + ***********************************************************/ +static int32_t tvservice_wait_for_bulk_receive(void *buffer, uint32_t max_length) { + /*if(!vcos_verify(((uint32_t) buffer & 0xf) == 0)) //should be 16 byte aligned + return -1;*/ + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(!vcos_verify(buffer)) { + vcos_log_error("TV service: NULL buffer passed to wait_for_bulk_receive"); + return -1; + } + + return vchi_bulk_queue_receive( tvservice_client.client_handle[0], + buffer, + max_length, + VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, + NULL ); +} + +/*********************************************************** + * Name: tvservice_send_command + * + * Arguments: command, parameter buffer, parameter length, has reply? (non-zero means yes) + * + * Description: send a command and optionally wait for its single value response (TV_GENERAL_RESP_T) + * + * Returns: response.ret (currently only 1 int in the wrapped response), endian translated if necessary + * + ***********************************************************/ + +static int32_t tvservice_send_command( uint32_t command, void *buffer, uint32_t length, uint32_t has_reply) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + int32_t success = 0; + TV_GENERAL_RESP_T response; + response.ret = -1; + + if(vcos_verify(command < VC_TV_END_OF_LIST)) { + vcos_log_trace("[%s] command:%s param length %d %s", VCOS_FUNCTION, + tvservice_command_strings[command], length, + (has_reply)? "has reply" : " no reply"); + } else { + vcos_log_error("[%s] not sending invalid command %d", VCOS_FUNCTION, command); + return -1; + } + + if(tvservice_lock_obtain() == 0) + { + success = vchi_msg_queuev( tvservice_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); + if(success == 0 && has_reply) { + //otherwise only wait for a reply if we ask for one + success = tvservice_wait_for_reply(&response, sizeof(response)); + response.ret = VC_VTOH32(response.ret); + } else { + if(success != 0) + vcos_log_error("TV service failed to send command %s length %d, error code %d", + tvservice_command_strings[command], length, success); + //No reply expected or failed to send, send the success code back instead + response.ret = success; + } + tvservice_lock_release(); + } + return response.ret; +} + +/*********************************************************** + * Name: tvservice_send_command_reply + * + * Arguments: command, parameter buffer, parameter length, reply buffer, buffer length + * + * Description: send a command and wait for its non-single value response (in a buffer) + * + * Returns: error code, host app is responsible to do endian translation + * + ***********************************************************/ +static int32_t tvservice_send_command_reply( uint32_t command, void *buffer, uint32_t length, + void *response, uint32_t max_length) { + VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, + {buffer, length} }; + int32_t success = 0; + + vcos_log_trace("[%s] sending command (with reply) %s param length %d", VCOS_FUNCTION, + tvservice_command_strings[command], length); + + if(tvservice_lock_obtain() == 0) + { + success = vchi_msg_queuev( tvservice_client.client_handle[0], + vector, sizeof(vector)/sizeof(vector[0]), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); + if(success == 0) + success = tvservice_wait_for_reply(response, max_length); + else + vcos_log_error("TV service failed to send command %s param length %d, error code %d", + tvservice_command_strings[command], length, success); + + tvservice_lock_release(); + } + + return success; +} + +/*********************************************************** + * Name: tvservice_notify_func + * + * Arguments: TV service state + * + * Description: This is the notification task which receives all TV + * service notifications + * + * Returns: does not return + * + ***********************************************************/ +static void *tvservice_notify_func(void *arg) { + int32_t success; + TVSERVICE_HOST_STATE_T *state = (TVSERVICE_HOST_STATE_T *) arg; + TV_DISPLAY_STATE_T tvstate; + + vcos_log_trace("TV service async thread started"); + /* Check starting state, and put service in use if necessary */ + success = tvservice_send_command_reply( VC_TV_GET_DISPLAY_STATE, NULL, 0, &tvstate, sizeof(TV_DISPLAY_STATE_T)); + if (success != 0) + return 0; + if (tvstate.state & VC_HDMI_ATTACHED) + { + /* Connected */ + if (tvstate.state & (VC_HDMI_HDMI | VC_HDMI_DVI)) + { + /* Mode already selected */ + vchi_service_use(state->notify_handle[0]); + } + } + // the state machine below only wants a single bit to be set + if (tvstate.state & (VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED)) + tvstate.state &= ~(VC_HDMI_HDMI | VC_HDMI_DVI); + + while(1) { + VCOS_STATUS_T status = vcos_event_wait(&tvservice_notify_available_event); + if(status != VCOS_SUCCESS || !state->initialised || state->to_exit) + break; + + do { + uint32_t reason, param1, param2; + //Get all notifications in the queue + success = vchi_msg_dequeue( state->notify_handle[0], state->notify_buffer, sizeof(state->notify_buffer), &state->notify_length, VCHI_FLAGS_NONE ); + if(success != 0 || state->notify_length < sizeof(uint32_t)*3 ) { + vcos_assert(state->notify_length == sizeof(uint32_t)*3); + break; + } + + if(tvservice_lock_obtain() != 0) + break; + + //Check what notification it is and update ourselves accordingly before notifying the host app + //All notifications are of format: reason, param1, param2 (all 32-bit unsigned int) + reason = VC_VTOH32(state->notify_buffer[0]), param1 = VC_VTOH32(state->notify_buffer[1]), param2 = VC_VTOH32(state->notify_buffer[2]); + vcos_log_trace("[%s] %s %d %d", VCOS_FUNCTION, vc_tv_notification_name(reason), + param1, param2); + switch(reason) { + case VC_HDMI_UNPLUGGED: + if(tvstate.state & (VC_HDMI_HDMI|VC_HDMI_DVI)) { + state->copy_protect = 0; + if((tvstate.state & VC_HDMI_ATTACHED) == 0) { + vchi_service_release(state->notify_handle[0]); + } + } + tvstate.state &= ~(VC_HDMI_HDMI|VC_HDMI_DVI|VC_HDMI_ATTACHED|VC_HDMI_HDCP_AUTH); + tvstate.state |= (VC_HDMI_UNPLUGGED | VC_HDMI_HDCP_UNAUTH); + vcos_log_trace("[%s] invalidating caches", VCOS_FUNCTION); + state->cea_cache.is_valid = state->cea_cache.num_modes = 0; + state->dmt_cache.is_valid = state->dmt_cache.num_modes = 0; + break; + + case VC_HDMI_ATTACHED: + if(tvstate.state & (VC_HDMI_HDMI|VC_HDMI_DVI)) { + state->copy_protect = 0; + vchi_service_release(state->notify_handle[0]); + } + tvstate.state &= ~(VC_HDMI_HDMI|VC_HDMI_DVI|VC_HDMI_UNPLUGGED|VC_HDMI_HDCP_AUTH); + tvstate.state |= VC_HDMI_ATTACHED; + state->hdmi_preferred_group = (HDMI_RES_GROUP_T) param1; + state->hdmi_preferred_mode = param2; + break; + + case VC_HDMI_DVI: + if(tvstate.state & (VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED)) { + vchi_service_use(state->notify_handle[0]); + } + tvstate.state &= ~(VC_HDMI_HDMI|VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED); + tvstate.state |= VC_HDMI_DVI; + state->hdmi_current_group = (HDMI_RES_GROUP_T) param1; + state->hdmi_current_mode = param2; + break; + + case VC_HDMI_HDMI: + if(tvstate.state & (VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED)) { + vchi_service_use(state->notify_handle[0]); + } + tvstate.state &= ~(VC_HDMI_DVI|VC_HDMI_ATTACHED|VC_HDMI_UNPLUGGED); + tvstate.state |= VC_HDMI_HDMI; + state->hdmi_current_group = (HDMI_RES_GROUP_T) param1; + state->hdmi_current_mode = param2; + break; + + case VC_HDMI_HDCP_UNAUTH: + tvstate.state &= ~VC_HDMI_HDCP_AUTH; + tvstate.state |= VC_HDMI_HDCP_UNAUTH; + state->copy_protect = 0; + //Do we care about the reason for HDCP unauth in param1? + break; + + case VC_HDMI_HDCP_AUTH: + tvstate.state &= ~VC_HDMI_HDCP_UNAUTH; + tvstate.state |= VC_HDMI_HDCP_AUTH; + state->copy_protect = 1; + break; + + case VC_HDMI_HDCP_KEY_DOWNLOAD: + case VC_HDMI_HDCP_SRM_DOWNLOAD: + //Nothing to do here, just tell the host app whether it is successful or not (in param1) + break; + + case VC_SDTV_UNPLUGGED: //Currently we don't get this + if(tvstate.state & (VC_SDTV_PAL | VC_SDTV_NTSC)) { + state->copy_protect = 0; + } + tvstate.state &= ~(VC_SDTV_ATTACHED | VC_SDTV_PAL | VC_SDTV_NTSC); + tvstate.state |= (VC_SDTV_UNPLUGGED | VC_SDTV_CP_INACTIVE); + state->sdtv_current_mode = SDTV_MODE_OFF; + break; + + case VC_SDTV_ATTACHED: //Currently we don't get this either + tvstate.state &= ~(VC_SDTV_UNPLUGGED | VC_SDTV_PAL | VC_SDTV_NTSC); + tvstate.state |= VC_SDTV_ATTACHED; + state->sdtv_current_mode = SDTV_MODE_OFF; + break; + + case VC_SDTV_NTSC: + tvstate.state &= ~(VC_SDTV_UNPLUGGED | VC_SDTV_ATTACHED | VC_SDTV_PAL); + tvstate.state |= VC_SDTV_NTSC; + state->sdtv_current_mode = (SDTV_MODE_T) param1; + state->sdtv_options.aspect = (SDTV_ASPECT_T) param2; + if(param1 & SDTV_COLOUR_RGB) { + state->sdtv_current_colour = SDTV_COLOUR_RGB; + } else if(param1 & SDTV_COLOUR_YPRPB) { + state->sdtv_current_colour = SDTV_COLOUR_YPRPB; + } else { + state->sdtv_current_colour = SDTV_COLOUR_UNKNOWN; + } + break; + + case VC_SDTV_PAL: + tvstate.state &= ~(VC_SDTV_UNPLUGGED | VC_SDTV_ATTACHED | VC_SDTV_NTSC); + tvstate.state |= VC_SDTV_PAL; + state->sdtv_current_mode = (SDTV_MODE_T) param1; + state->sdtv_options.aspect = (SDTV_ASPECT_T) param2; + if(param1 & SDTV_COLOUR_RGB) { + state->sdtv_current_colour = SDTV_COLOUR_RGB; + } else if(param1 & SDTV_COLOUR_YPRPB) { + state->sdtv_current_colour = SDTV_COLOUR_YPRPB; + } else { + state->sdtv_current_colour = SDTV_COLOUR_UNKNOWN; + } + break; + + case VC_SDTV_CP_INACTIVE: + tvstate.state &= ~VC_SDTV_CP_ACTIVE; + tvstate.state |= VC_SDTV_CP_INACTIVE; + state->copy_protect = 0; + state->sdtv_current_cp_mode = SDTV_CP_NONE; + break; + + case VC_SDTV_CP_ACTIVE: + tvstate.state &= ~VC_SDTV_CP_INACTIVE; + tvstate.state |= VC_SDTV_CP_ACTIVE; + state->copy_protect = 1; + state->sdtv_current_cp_mode = (SDTV_CP_MODE_T) param1; + break; + } + + tvservice_lock_release(); + + //Now callback the host app(s) + uint32_t i, called = 0; + for(i = 0; i < TVSERVICE_MAX_CALLBACKS; i++) + { + if(state->callbacks[i].notify_fn != NULL) + { + called++; + state->callbacks[i].notify_fn + (state->callbacks[i].notify_data, reason, param1, param2); + } // if + } // for + if(called == 0) { + vcos_log_info("TV service: No callback handler specified, callback [%s] swallowed", + vc_tv_notification_name(reason)); + } + } while(success == 0 && state->notify_length >= sizeof(uint32_t)*3); //read the next message if any + } //while (1) + + if(state->to_exit) + vcos_log_trace("TV service async thread exiting"); + + return 0; +} + +/*********************************************************** + Actual TV service API starts here +***********************************************************/ + +/*********************************************************** + * Name: vc_tv_get_state + * + * Arguments: + * Pointer to tvstate structure + * + * Description: Get the current TV state + * + * Returns: if the command is successful (zero) or not (non-zero) + * If the command fails to be sent, passed in state is unchanged + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate) { + int success = -1; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(vcos_verify(tvstate)) { + success = tvservice_send_command_reply( VC_TV_GET_STATE, NULL, 0, + tvstate, sizeof(TV_GET_STATE_RESP_T)); + if(success == 0) { + tvstate->state = VC_VTOH32(tvstate->state); + tvstate->width = VC_VTOH32(tvstate->width); + tvstate->height = VC_VTOH32(tvstate->height); + tvstate->frame_rate = VC_VTOH16(tvstate->frame_rate); + tvstate->scan_mode = VC_VTOH16(tvstate->scan_mode); + } + } + return success; +} + +/*********************************************************** + * Name: vc_tv_get_display_state + * + * Arguments: + * Pointer to tvstate structure + * + * Description: Get the current TV display state. + * + * Returns: if the command is successful (zero) or not (non-zero) + * If the command fails to be sent, passed in state is unchanged + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_get_display_state(TV_DISPLAY_STATE_T *tvstate) { + int success = -1; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(vcos_verify(tvstate)) { + success = tvservice_send_command_reply( VC_TV_GET_DISPLAY_STATE, NULL, 0, + tvstate, sizeof(TV_DISPLAY_STATE_T)); + } + return success; +} + +/*********************************************************** + * Name: vc_tv_hdmi_power_on_preferred/vc_tv_hdmi_power_on_preferred_3d + * + * Arguments: + * none + * + * Description: Power on HDMI at preferred resolution + * Enter 3d if the _3d function was called + * + * Analogue TV will be powered down if on (same for the following + * two HDMI power on functions below) + * + * Returns: single value interpreted as HDMI_RESULT_T (zero means success) + * if successful, there will be a callback when the power on is complete + * + ***********************************************************/ +static int vc_tv_hdmi_power_on_preferred_actual(uint32_t in_3d) { + TV_HDMI_ON_PREFERRED_PARAM_T param; + int success; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + param.in_3d = VC_HTOV32(in_3d); + + success = tvservice_send_command( VC_TV_HDMI_ON_PREFERRED, ¶m, sizeof(param), 1); + return success; +} + +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred( void ) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return vc_tv_hdmi_power_on_preferred_actual(0); +} + +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_preferred_3d( void ) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return vc_tv_hdmi_power_on_preferred_actual(1); +} + +/*********************************************************** + * Name: vc_tv_hdmi_power_on_best/vc_tv_hdmi_power_on_best_3d + * + * Arguments: + * screen width, height, frame rate, scan_mode (HDMI_NONINTERLACED / HDMI_INTERLACED) + * match flags + * + * Description: Power on HDMI at best matched resolution based on passed in parameters + * Enter 3d mode if the _3d function was called + * + * Returns: single value interpreted as HDMI_RESULT_T (zero means success) + * if successful, there will be a callback when the power on is complete + * + ***********************************************************/ +static int vc_tv_hdmi_power_on_best_actual(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags, + uint32_t in_3d) { + TV_HDMI_ON_BEST_PARAM_T param; + int success; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + param.width = VC_HTOV32(width); + param.height = VC_HTOV32(height); + param.frame_rate = VC_HTOV32(frame_rate); + param.scan_mode = VC_HTOV32(scan_mode); + param.match_flags = VC_HTOV32(match_flags); + param.in_3d = VC_HTOV32(in_3d); + + success = tvservice_send_command( VC_TV_HDMI_ON_BEST, ¶m, sizeof(TV_HDMI_ON_BEST_PARAM_T), 1); + return success; +} + +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return vc_tv_hdmi_power_on_best_actual(width, height, frame_rate, scan_mode, match_flags, 0); +} + +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate, + HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return vc_tv_hdmi_power_on_best_actual(width, height, frame_rate, scan_mode, match_flags, 1); +} + +/*********************************************************** + * Name: vc_tv_hdmi_power_on_explicit + * + * Arguments: + * mode (HDMI_MODE_HDMI/HDMI_MODE_DVI), + * group (HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT), + * code + * + * Description: Power on HDMI at explicit mode + * Enter 3d mode if supported by the TV and if mode was set to HDMI_MODE_3D + * If Videocore has EDID, this will still be subject to EDID restriction, + * otherwise HDMI will be powered on at the said mode + * + * Returns: single value interpreted as HDMI_RESULT_T (zero means success) + * if successful, there will be a callback when the power on is complete + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) { + TV_HDMI_ON_EXPLICIT_PARAM_T param; + int success; + + vcos_log_trace("[%s] mode %d group %d code %d", VCOS_FUNCTION, + mode, group, code); + param.hdmi_mode = mode; + param.group = group; + param.mode = code; + + success = tvservice_send_command( VC_TV_HDMI_ON_EXPLICIT, ¶m, sizeof(TV_HDMI_ON_EXPLICIT_PARAM_T), 1); + return success; +} + +/*********************************************************** + * Name: vc_tv_sdtv_power_on + * + * Arguments: + * SDTV mode, options (currently only aspect ratio) + * + * Description: Power on SDTV at required mode and aspect ratio (default 4:3) + * HDMI will be powered down if currently on + * + * Returns: single value (zero means success) + * if successful, there will be a callback when the power on is complete + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_sdtv_power_on(SDTV_MODE_T mode, SDTV_OPTIONS_T *options) { + TV_SDTV_ON_PARAM_T param; + int success; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + param.mode = VC_HTOV32(mode); + param.aspect = (options)? VC_HTOV32(options->aspect) : VC_HTOV32(SDTV_ASPECT_4_3); + + success = tvservice_send_command( VC_TV_SDTV_ON, ¶m, sizeof(TV_SDTV_ON_PARAM_T), 1); + return success; +} + + +/*********************************************************** + * Name: vc_tv_power_off + * + * Arguments: + * none + * + * Description: Power off whatever is on at the moment, no effect if nothing is on + * + * Returns: whether command is successfully sent (and callback for HDMI) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_power_off( void ) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_OFF, NULL, 0, 0); +} + +/*********************************************************** + * Name: vc_tv_hdmi_get_supported_modes + * + * Arguments: + * group (HDMI_RES_GROUP_CEA/HDMI_RES_GROUP_DMT), + * array of TV_SUPPORT_MODE_T structs, length of array, pointer to preferred group, + * pointer to prefer mode code (the last two pointers can be NULL, if the caller + * is not interested to learn what the preferred mode is) + * If passed in a null supported_modes array, or the length of array + * is zero, the number of supported modes in that particular group + * will be returned instead + * + * Description: Get supported modes for a particular group, + * the length of array limits no. of modes returned + * + * Returns: Returns the number of modes actually written (or the number + * of supported modes if passed in a null array or length of array==0). + * Returns < 0 for error. + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_T group, + TV_SUPPORTED_MODE_NEW_T *supported_modes, + uint32_t max_supported_modes, + HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode) { + uint32_t param[2] = {(uint32_t) group, 0}; + TV_QUERY_SUPPORTED_MODES_RESP_T response; + TVSERVICE_MODE_CACHE_T *cache = NULL; + int error = -1; + int modes_copied = 0; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + + switch(group) { + case HDMI_RES_GROUP_DMT: + cache = &tvservice_client.dmt_cache; + break; + case HDMI_RES_GROUP_CEA: + cache = &tvservice_client.cea_cache; + break; + default: + vcos_log_error("Invalid group %d in [%s]", group, VCOS_FUNCTION); + return -1; + } + vcos_log_trace("[%s] group %d cache valid %d", + VCOS_FUNCTION, group, cache->is_valid); + + memset(&response, 0, sizeof(response)); + if(!cache->is_valid) { + vchi_service_use(tvservice_client.client_handle[0]); + if((error = tvservice_send_command_reply(VC_TV_QUERY_SUPPORTED_MODES, ¶m[0], sizeof(uint32_t), + &response, sizeof(response))) == VC_HDMI_SUCCESS) { + //First ask how many modes there are, if the current table is big enough to hold + //all the modes, just copy over, otherwise allocate a new table + if(response.num_supported_modes) { + if(cache->max_modes < response.num_supported_modes) { + cache->max_modes = response.num_supported_modes; + if(cache->modes) { + vcos_free(cache->modes); + cache->modes = NULL; + } + } else { + vcos_assert(cache->modes); + memset(cache->modes, 0, cache->max_modes * sizeof(TV_SUPPORTED_MODE_NEW_T)); + } + if(cache->modes == NULL) { + cache->modes = vcos_calloc(cache->max_modes, sizeof(TV_SUPPORTED_MODE_NEW_T), "cached modes"); + } + if(vcos_verify(cache->modes)) { + //If we have successfully allocated the table, send + //another request to actually download the modes from Videocore + param[1] = response.num_supported_modes; + if((error = tvservice_send_command_reply(VC_TV_QUERY_SUPPORTED_MODES_ACTUAL, param, sizeof(param), + &response, sizeof(response))) == VC_HDMI_SUCCESS) { + //The response comes back may indicate a different number of modes to param[1]. + //This happens if a new EDID was read in between the two requests (should be rare), + //in which case we just download as many as VC says will be sent + cache->num_modes = response.num_supported_modes; + vcos_assert(response.num_supported_modes <= param[1]); //VC will not return more than what we have allocated + if(cache->num_modes) { + error = tvservice_wait_for_bulk_receive(cache->modes, cache->num_modes * sizeof(TV_SUPPORTED_MODE_NEW_T)); + if(error) + vcos_log_error("Failed to download %s cache in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION); + } else { + vcos_log_error("First query of supported modes indicated there are %d, but now there are none, has the TV been unplugged? [%s]", + param[1], VCOS_FUNCTION); + } + } else { + vcos_log_error("Failed to request %s cache in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION); + } + } else { + //If we failed to allocate memory, the request stops here + vcos_log_error("Failed to allocate memory for %s cache in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION); + } + } else { + //The request also terminates if there are no supported modes reported + vcos_log_trace("No supported modes returned for group %s in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION); + } + } else { + vcos_log_error("Failed to query supported modes for group %s in [%s]", HDMI_RES_GROUP_NAME(group), VCOS_FUNCTION); + } + vchi_service_release(tvservice_client.client_handle[0]); + + if(!error) { + cache->is_valid = 1; + vcos_log_trace("[%s] cached %d %s resolutions", VCOS_FUNCTION, response.num_supported_modes, HDMI_RES_GROUP_NAME(group)); + tvservice_client.hdmi_preferred_group = response.preferred_group; + tvservice_client.hdmi_preferred_mode = response.preferred_mode; + } + } + + if(cache->is_valid) { + if(supported_modes && max_supported_modes) { + modes_copied = _min(max_supported_modes, cache->num_modes); + memcpy(supported_modes, cache->modes, modes_copied*sizeof(TV_SUPPORTED_MODE_NEW_T)); + } else { + //If we pass in a null pointer, return the size of table instead + modes_copied = cache->num_modes; + } + } + + if(preferred_group && preferred_mode) { + *preferred_group = tvservice_client.hdmi_preferred_group; + *preferred_mode = tvservice_client.hdmi_preferred_mode; + } + + return modes_copied; //If there was an error, this will be zero +} + +/*********************************************************** + * Name: vc_tv_hdmi_mode_supported + * + * Arguments: + * resolution standard (CEA/DMT), mode code + * + * Description: Query if a particular mode is supported + * + * Returns: single value return > 0 means supported, 0 means unsupported, < 0 means error + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_mode_supported(HDMI_RES_GROUP_T group, + uint32_t mode) { + TV_QUERY_MODE_SUPPORT_PARAM_T param = {group, mode}; + vcos_log_trace("[%s]", VCOS_FUNCTION); + + return tvservice_send_command( VC_TV_QUERY_MODE_SUPPORT, ¶m, sizeof(TV_QUERY_MODE_SUPPORT_PARAM_T), 1); +} + +/*********************************************************** + * Name: vc_tv_hdmi_audio_supported + * + * Arguments: + * audio format (EDID_AudioFormat + EDID_AudioCodingExtension), + * no. of channels (1-8), + * sample rate (EDID_AudioSampleRate except "refer to header"), + * bit rate (or sample size if pcm) + * use EDID_AudioSampleSize as sample size argument + * + * Description: Query if a particular audio format is supported + * + * Returns: single value return which will be flags in EDID_AUDIO_SUPPORT_FLAG_T + * zero means everything is supported, < 0 means error + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_audio_supported(uint32_t audio_format, uint32_t num_channels, + EDID_AudioSampleRate fs, uint32_t bitrate) { + TV_QUERY_AUDIO_SUPPORT_PARAM_T param = { VC_HTOV32(audio_format), + VC_HTOV32(num_channels), + VC_HTOV32(fs), + VC_HTOV32(bitrate) }; + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(!vcos_verify(num_channels > 0 && num_channels <= 8 && fs != EDID_AudioSampleRate_eReferToHeader)) + return -1; + + return tvservice_send_command( VC_TV_QUERY_AUDIO_SUPPORT, ¶m, sizeof(TV_QUERY_AUDIO_SUPPORT_PARAM_T), 1); +} + +/*********************************************************** + * Name: vc_tv_enable_copyprotect + * + * Arguments: + * copy protect mode (only used for SDTV), time out in milliseconds + * + * Description: Enable copy protection (either HDMI or SDTV must be powered on) + * + * Returns: single value return 0 means success, additional result via callback + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_enable_copyprotect(uint32_t cp_mode, uint32_t timeout) { + TV_ENABLE_COPY_PROTECT_PARAM_T param = {VC_HTOV32(cp_mode), VC_HTOV32(timeout)}; + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_ENABLE_COPY_PROTECT, ¶m, sizeof(TV_ENABLE_COPY_PROTECT_PARAM_T), 1); +} + +/*********************************************************** + * Name: vc_tv_disable_copyprotect + * + * Arguments: + * none + * + * Description: Disable copy protection (either HDMI or SDTV must be powered on) + * + * Returns: single value return 0 means success, additional result via callback + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_disable_copyprotect( void ) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_DISABLE_COPY_PROTECT, NULL, 0, 1); +} + +/*********************************************************** + * Name: vc_tv_show_info + * + * Arguments: + * show (1) or hide (0) info screen + * + * Description: Show or hide info screen, only works in HDMI at the moment + * + * Returns: zero if command is successfully sent + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_show_info(uint32_t show) { + TV_SHOW_INFO_PARAM_T param = {VC_HTOV32(show)}; + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_SHOW_INFO, ¶m, sizeof(TV_SHOW_INFO_PARAM_T), 0); +} + +/*********************************************************** + * Name: vc_tv_hdmi_get_av_latency + * + * Arguments: + * none + * + * Description: Get the AV latency (in ms) for HDMI (lipsync), only valid if + * HDMI is currently powered on, otherwise you get zero + * + * Returns: latency (zero if error or latency is not defined), < 0 if failed to send command) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_av_latency( void ) { + + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_GET_AV_LATENCY, NULL, 0, 1); +} + +/*********************************************************** + * Name: vc_tv_hdmi_set_hdcp_key + * + * Arguments: + * key block, whether we wait (1) or not (0) for the key to download + * + * Description: Download HDCP key + * + * Returns: single value return indicating download status + * (or queued status if we don't wait) + * Callback indicates the validity of key + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_key(const uint8_t *key) { + TV_HDCP_SET_KEY_PARAM_T param; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(!vcos_verify(key)) + return -1; + memcpy(param.key, key, HDCP_KEY_BLOCK_SIZE); + return tvservice_send_command( VC_TV_HDCP_SET_KEY, ¶m, sizeof(param), 0); +} + +/*********************************************************** + * Name: vc_tv_hdmi_set_hdcp_revoked_list + * + * Arguments: + * list, size of list + * + * Description: Download HDCP revoked list + * + * Returns: single value return indicating download status + * (or queued status if we don't wait) + * Callback indicates the number of keys set (zero if failed, unless you are clearing the list) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_hdcp_revoked_list(const uint8_t *list, uint32_t num_keys) { + TV_HDCP_SET_SRM_PARAM_T param = {VC_HTOV32(num_keys)}; + int success = tvservice_send_command( VC_TV_HDCP_SET_SRM, ¶m, sizeof(TV_HDCP_SET_SRM_PARAM_T), 0); + + vcos_log_trace("[%s]", VCOS_FUNCTION); + if(success == 0 && num_keys && list) { //Set num_keys to zero if we are clearing the list + //Sent the command, now download the list + if(tvservice_lock_obtain() == 0) + { + success = vchi_bulk_queue_transmit( tvservice_client.client_handle[0], + list, + num_keys * HDCP_KSV_LENGTH, + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, + 0 ); + tvservice_lock_release(); + } + else + success = -1; + } + return success; +} + +/*********************************************************** + * Name: vc_tv_hdmi_set_spd + * + * Arguments: + * manufacturer, description, product type (HDMI_SPD_TYPE_CODE_T) + * + * Description: Set SPD + * + * Returns: whether command was sent successfully (zero means success) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_spd(const char *manufacturer, const char *description, HDMI_SPD_TYPE_CODE_T type) { + TV_SET_SPD_PARAM_T param; + vcos_log_trace("[%s]", VCOS_FUNCTION); + + if(!vcos_verify(manufacturer && description)) + return -1; + + memcpy(param.manufacturer, manufacturer, TV_SPD_NAME_LEN); + memcpy(param.description, description, TV_SPD_DESC_LEN); + param.type = VC_HTOV32(type); + return tvservice_send_command( VC_TV_SET_SPD, ¶m, sizeof(TV_SET_SPD_PARAM_T), 0); +} + +/*********************************************************** + * Name: vc_tv_hdmi_set_display_options + * + * Arguments: + * aspect ratio (HDMI_ASPECT_T enum), left/right bar width, top/bottom bar height + * + * Description: Set active area for HDMI (bar width/height should be set to zero if absent) + * + * Returns: whether command was sent successfully (zero means success) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_display_options(HDMI_ASPECT_T aspect, + uint32_t left_bar_width, uint32_t right_bar_width, + uint32_t top_bar_height, uint32_t bottom_bar_height, + uint32_t overscan_flags) { + TV_SET_DISPLAY_OPTIONS_PARAM_T param; + vcos_log_trace("[%s]", VCOS_FUNCTION); + + param.aspect = VC_HTOV32(aspect); + param.vertical_bar_present = VC_HTOV32((left_bar_width || right_bar_width)? VC_TRUE : VC_FALSE); + param.left_bar_width = VC_HTOV32(left_bar_width); + param.right_bar_width = VC_HTOV32(right_bar_width); + param.horizontal_bar_present = VC_HTOV32((top_bar_height || bottom_bar_height)? VC_TRUE : VC_FALSE); + param.top_bar_height = VC_HTOV32(top_bar_height); + param.bottom_bar_height = VC_HTOV32(bottom_bar_height); + param.overscan_flags = VC_HTOV32(overscan_flags); + return tvservice_send_command( VC_TV_SET_DISPLAY_OPTIONS, ¶m, sizeof(TV_SET_DISPLAY_OPTIONS_PARAM_T), 0); +} + +/*********************************************************** + * Name: vc_tv_test_mode_start + * + * Arguments: + * 24-bit colour, test mode (TV_TEST_MODE_T enum) + * + * Description: Power on HDMI to test mode, HDMI must be off to start with + * + * Returns: whether command was sent successfully (zero means success) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_test_mode_start(uint32_t colour, TV_TEST_MODE_T test_mode) { + TV_TEST_MODE_START_PARAM_T param = {VC_HTOV32(colour), VC_HTOV32(test_mode)}; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_TEST_MODE_START, ¶m, sizeof(TV_TEST_MODE_START_PARAM_T), 0); +} + +/*********************************************************** + * Name: vc_tv_test_mode_stop + * + * Arguments: + * none + * + * Description: Stop test mode and power down HDMI + * + * Returns: whether command was sent successfully (zero means success) + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_test_mode_stop( void ) { + vcos_log_trace("[%s]", VCOS_FUNCTION); + return tvservice_send_command( VC_TV_TEST_MODE_STOP, NULL, 0, 0); +} + +/*********************************************************** + * Name: vc_tv_hdmi_ddc_read + * + * Arguments: + * offset, length to read, pointer to buffer, must be 16 byte aligned + * + * Description: ddc read over i2c (HDMI only at the moment) + * + * Returns: length of data read (so zero means error) and the buffer will be filled + * only if no error + * + ***********************************************************/ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_ddc_read(uint32_t offset, uint32_t length, uint8_t *buffer) { + int success; + TV_DDC_READ_PARAM_T param = {VC_HTOV32(offset), VC_HTOV32(length)}; + + vcos_log_trace("[%s]", VCOS_FUNCTION); + + /*if(!vcos_verify(buffer && (((uint32_t) buffer) % 16) == 0)) + return -1;*/ + + vchi_service_use(tvservice_client.client_handle[0]); + success = tvservice_send_command( VC_TV_DDC_READ, ¶m, sizeof(TV_DDC_READ_PARAM_T), 1); + + if(success == 0) { + success = tvservice_wait_for_bulk_receive(buffer, length); + } + vchi_service_release(tvservice_client.client_handle[0]); + return (success == 0)? length : 0; //Either return the whole block or nothing +} + +/** + * Sets whether the TV is attached or unplugged. + * Required when hotplug interrupt is not handled by VideoCore. + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_attached(uint32_t attached) +{ + vcos_log_trace("[%s] attached %d", VCOS_FUNCTION, attached); + return tvservice_send_command(VC_TV_SET_ATTACHED, &attached, sizeof(uint32_t), 0); +} + +/** + * Sets a property in HDMI output + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_set_property(const HDMI_PROPERTY_PARAM_T *property) { + HDMI_PROPERTY_PARAM_T _property; + if(vcos_verify(property)) { + memcpy(&_property, property, sizeof(_property)); + vcos_log_trace("[%s] property:%d values:%d,%d", VCOS_FUNCTION, property->property, property->param1, property->param2); + return tvservice_send_command(VC_TV_SET_PROP, &_property, sizeof(_property), 1); + } + return -1; +} + +/** + * Gets a property from HDMI + */ +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_property(HDMI_PROPERTY_PARAM_T *property) { + int ret = -1; + if(vcos_verify(property)) { + TV_GET_PROP_PARAM_T param = {0, {HDMI_PROPERTY_MAX, 0, 0}}; + uint32_t prop = (uint32_t) property->property; + property->param1 = property->param2 = 0; + vcos_log_trace("[%s] property:%d", VCOS_FUNCTION, property->property); + if((ret = tvservice_send_command_reply( VC_TV_GET_PROP, &prop, sizeof(prop), + ¶m, sizeof(param))) == VC_HDMI_SUCCESS) { + property->param1 = param.property.param1; + property->param2 = param.property.param2; + } + } + return ret; +} + +/** + * Converts the notification reason to a string. + * + * @param reason is the notification reason + * @return The notification reason as a string. + */ +VCHPRE_ const char* vc_tv_notification_name(VC_HDMI_NOTIFY_T reason) +{ + switch (reason) + { + case VC_HDMI_UNPLUGGED: + return "VC_HDMI_UNPLUGGED"; + case VC_HDMI_ATTACHED: + return "VC_HDMI_ATTACHED"; + case VC_HDMI_DVI: + return "VC_HDMI_DVI"; + case VC_HDMI_HDMI: + return "VC_HDMI_HDMI"; + case VC_HDMI_HDCP_UNAUTH: + return "VC_HDMI_HDCP_UNAUTH"; + case VC_HDMI_HDCP_AUTH: + return "VC_HDMI_HDCP_AUTH"; + case VC_HDMI_HDCP_KEY_DOWNLOAD: + return "VC_HDMI_HDCP_KEY_DOWNLOAD"; + case VC_HDMI_HDCP_SRM_DOWNLOAD: + return "VC_HDMI_HDCP_SRM_DOWNLOAD"; + case VC_HDMI_CHANGING_MODE: + return "VC_HDMI_CHANGING_MODE"; + default: + return "VC_HDMI_UNKNOWN"; + } +} + +// temporary: maintain backwards compatibility +VCHPRE_ int VCHPOST_ vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, + TV_SUPPORTED_MODE_T *supported_modes_deprecated, + uint32_t max_supported_modes, + HDMI_RES_GROUP_T *preferred_group, + uint32_t *preferred_mode) { + TV_SUPPORTED_MODE_NEW_T *supported_modes_new = malloc(max_supported_modes * sizeof *supported_modes_new); + int modes_copied = vc_tv_hdmi_get_supported_modes_new(group==3 ? HDMI_RES_GROUP_CEA:group, supported_modes_new, max_supported_modes, preferred_group, preferred_mode); + int i, j=0; + + for (i=0; istruct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)) { + q->scan_mode = p->scan_mode; + q->native = p->native; + q->code = p->code; + q->frame_rate = p->frame_rate; + q->width = p->width; + q->height = p->height; + j++; + } + } + free(supported_modes_new); + + return 0; +} + +/** + * Get the unique device ID from the EDID + * @param pointer to device ID struct + * @return zero if successful, non-zero if failed. + */ +VCHPRE_ int VCHPOST_ vc_tv_get_device_id(TV_DEVICE_ID_T *id) { + int ret = -1; + TV_DEVICE_ID_T param; + memset(¶m, 0, sizeof(TV_DEVICE_ID_T)); + if(vcos_verify(id)) { + if((ret = tvservice_send_command_reply( VC_TV_GET_DEVICE_ID, NULL, 0, + ¶m, sizeof(param))) == VC_HDMI_SUCCESS) { + memcpy(id, ¶m, sizeof(TV_DEVICE_ID_T)); + } else { + id->vendor[0] = '\0'; + id->monitor_name[0] = '\0'; + id->serial_num = 0; + } + } + return ret; +} + +// temporary: maintain backwards compatibility +VCHPRE_ int VCHPOST_ vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) { + if (group == HDMI_RES_GROUP_CEA_3D) { + HDMI_PROPERTY_PARAM_T property; + property.property = HDMI_PROPERTY_3D_STRUCTURE; + property.param1 = HDMI_RES_GROUP_CEA; + property.param2 = 0; + vc_tv_hdmi_set_property(&property); + group = HDMI_RES_GROUP_CEA; + } + return vc_tv_hdmi_power_on_explicit_new(mode, group, code); +} + diff --git a/interface/vmcs_host/vcfilesys.h b/interface/vmcs_host/vcfilesys.h new file mode 100755 index 0000000..c8617cf --- /dev/null +++ b/interface/vmcs_host/vcfilesys.h @@ -0,0 +1,166 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "vchost_platform_config.h" +#include "vcfilesys_defs.h" +#include "vc_fileservice_defs.h" + +#ifndef VCFILESYS_H_ +#define VCFILESYS_H_ + +#ifndef FILESYS_DIR_DEFINED +#define FILESYS_DIR_DEFINED +typedef struct DIR_tag DIR; +#endif + +// Initialises the file system for use +VCHPRE_ int VCHPOST_ vc_filesys_init (void); + +// Stop it to prevent the functions from trying to use it. +VCHPRE_ void VCHPOST_ vc_filesys_stop(void); + +// Return the service number (-1 if not running). +VCHPRE_ int VCHPOST_ vc_filesys_inum(void); + +// Low level file system functions equivalent to close(), lseek(), open(), read() and write() +VCHPRE_ int VCHPOST_ vc_filesys_close(int fildes); + +VCHPRE_ long VCHPOST_ vc_filesys_lseek(int fildes, long offset, int whence); + +VCHPRE_ int64_t VCHPOST_ vc_filesys_lseek64(int fildes, int64_t offset, int whence); + +VCHPRE_ int VCHPOST_ vc_filesys_open(const char *path, int vc_oflag); + +VCHPRE_ int VCHPOST_ vc_filesys_read(int fildes, void *buf, unsigned int nbyte); + +VCHPRE_ int VCHPOST_ vc_filesys_write(int fildes, const void *buf, unsigned int nbyte); + +VCHPRE_ int VCHPOST_ vc_filesys_mount(const char *device, const char *mountpoint, const char *options); +VCHPRE_ int VCHPOST_ vc_filesys_umount(const char *mountpoint); + + +// Ends a directory listing iteration +VCHPRE_ int VCHPOST_ vc_filesys_closedir(void *dhandle); + +// Formats the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_filesys_format(const char *path); + +// Returns the amount of free space on the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_filesys_freespace(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_filesys_freespace64(const char *path); + +// Gets the attributes of the named file +VCHPRE_ int VCHPOST_ vc_filesys_get_attr(const char *path, fattributes_t *attr); + +// Creates a new directory +VCHPRE_ int VCHPOST_ vc_filesys_mkdir(const char *path); + +// Starts a directory listing iteration +VCHPRE_ void * VCHPOST_ vc_filesys_opendir(const char *dirname); + +// Directory listing iterator +VCHPRE_ struct dirent * VCHPOST_ vc_filesys_readdir_r(void *dhandle, struct dirent *result); + +// Deletes a file or (empty) directory +VCHPRE_ int VCHPOST_ vc_filesys_remove(const char *path); + +// Renames a file, provided the new name is on the same file system as the old +VCHPRE_ int VCHPOST_ vc_filesys_rename(const char *oldfile, const char *newfile); + +// Resets the co-processor side file system +VCHPRE_ int VCHPOST_ vc_filesys_reset(void); + +// Sets the attributes of the named file +VCHPRE_ int VCHPOST_ vc_filesys_set_attr(const char *path, fattributes_t attr); + +// Truncates a file at its current position +VCHPRE_ int VCHPOST_ vc_filesys_setend(int fildes); + +// Checks whether there are any messages in the incoming message fifo and responds to any such messages +VCHPRE_ int VCHPOST_ vc_filesys_poll_message_fifo(void); + +// Return the event used to wait for reads. +VCHPRE_ void * VCHPOST_ vc_filesys_read_event(void); + +// Sends a command for VC01 to reset the file system +VCHPRE_ void VCHPOST_ vc_filesys_sendreset(void); + +// Return the error code of the last file system error +VCHPRE_ int VCHPOST_ vc_filesys_errno(void); + +// Invalidates any cluster chains in the FAT that are not referenced in any directory structures +VCHPRE_ void VCHPOST_ vc_filesys_scandisk(const char *path); + +// Checks whether or not a FAT filesystem is corrupt or not. If fix_errors is TRUE behaves exactly as vc_filesys_scandisk. +VCHPRE_ int VCHPOST_ vc_filesys_chkdsk(const char *path, int fix_errors); + +// Return whether a disk is writeable or not. +VCHPRE_ int VCHPOST_ vc_filesys_diskwritable(const char *path); + +// Return file system type of a disk. +VCHPRE_ int VCHPOST_ vc_filesys_fstype(const char *path); + +// Returns the toatl amount of space on the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_filesys_totalspace(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_filesys_totalspace64(const char *path); + +// Open disk for block level access +VCHPRE_ int VCHPOST_ vc_filesys_open_disk_raw(const char *path); + +// Close disk from block level access mode +VCHPRE_ int VCHPOST_ vc_filesys_close_disk_raw(const char *path); + +// Open disk for normal access +VCHPRE_ int VCHPOST_ vc_filesys_open_disk(const char *path); + +// Close disk for normal access +VCHPRE_ int VCHPOST_ vc_filesys_close_disk(const char *path); + +// Return number of sectors. +VCHPRE_ int VCHPOST_ vc_filesys_numsectors(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_filesys_numsectors64(const char *path); + +// Begin reading sectors from VideoCore. +VCHPRE_ int VCHPOST_ vc_filesys_read_sectors_begin(const char *path, uint32_t sector, uint32_t count); + +// Read the next sector. +VCHPRE_ int VCHPOST_ vc_filesys_read_sector(char *buf); + +// End streaming sectors. +VCHPRE_ int VCHPOST_ vc_filesys_read_sectors_end(uint32_t *sectors_read); + +// Begin writing sectors from VideoCore. +VCHPRE_ int VCHPOST_ vc_filesys_write_sectors_begin(const char *path, uint32_t sector, uint32_t count); + +// Write the next sector. +VCHPRE_ int VCHPOST_ vc_filesys_write_sector(const char *buf); + +// End streaming sectors. +VCHPRE_ int VCHPOST_ vc_filesys_write_sectors_end(uint32_t *sectors_written); + +#endif //VCFILESYS_H_ + diff --git a/interface/vmcs_host/vcfilesys_defs.h b/interface/vmcs_host/vcfilesys_defs.h new file mode 100755 index 0000000..cf7d70e --- /dev/null +++ b/interface/vmcs_host/vcfilesys_defs.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// File service required types + +#ifndef VCFILESYS_DEFS_H +#define VCFILESYS_DEFS_H + +#include // for time_t + +/* Define type fattributes_t and struct dirent for use in file system functions */ + +typedef int fattributes_t; +//enum {ATTR_RDONLY=1, ATTR_DIRENT=2}; +#define ATTR_RDONLY 0x01 /* Read only file attributes */ +#define ATTR_HIDDEN 0x02 /* Hidden file attributes */ +#define ATTR_SYSTEM 0x04 /* System file attributes */ +#define ATTR_VOLUME 0x08 /* Volume Label file attributes */ +#define ATTR_DIRENT 0x10 /* Dirrectory file attributes */ +#define ATTR_ARCHIVE 0x20 /* Archives file attributes */ +#define ATTR_NORMAL 0x00 /* Normal file attributes */ + +#define D_NAME_MAX_SIZE 256 + +#ifndef _DIRENT_H // This should really be in a dirent.h header to avoid conflicts +struct dirent +{ + char d_name[D_NAME_MAX_SIZE]; + unsigned int d_size; + fattributes_t d_attrib; + time_t d_creatime; + time_t d_modtime; +}; +#endif // ifndef _DIRENT_H + +#define FS_MAX_PATH 256 // The maximum length of a pathname +/* Although not used in the API, this value is required on the host and +VC01 sides of the file system, even if there is no host side. Putting it in +vc_fileservice_defs.h is not appropriate as it would only be included if there +was a host side. */ + +/* File system error codes */ +#define FS_BAD_USER -7000 // The task isn't registered as a file system user + +#define FS_BAD_FILE -7001 // The path or filename or file descriptor is invalid +#define FS_BAD_PARM -7002 // Invalid parameter given +#define FS_ACCESS -7003 // File access conflict +#define FS_MAX_FILES -7004 // Maximum number of files already open +#define FS_NOEMPTY -7005 // Directory isn't empty +#define FS_MAX_SIZE -7006 // File is over the maximum file size + +#define FS_NO_DISK -7007 // No disk is present, or the disk has not been opened +#define FS_DISK_ERR -7008 // There is a problem with the disk + +#define FS_IO_ERROR -7009 // Driver level error + +#define FS_FMT_ERR -7010 // Format error + +#define FS_NO_BUFFER -7011 // Internal Nucleus File buffer not available +#define FS_NUF_INT -7012 // Internal Nucleus File error + +#define FS_UNSPEC_ERR -7013 // Unspecified error + +#endif diff --git a/interface/vmcs_host/vcgencmd.h b/interface/vmcs_host/vcgencmd.h new file mode 100755 index 0000000..31f00af --- /dev/null +++ b/interface/vmcs_host/vcgencmd.h @@ -0,0 +1,95 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// General command service API + +#ifndef GENCMD_H +#define GENCMD_H + +#include "vchost_platform_config.h" +#include "interface/vchi/vchi.h" + +VCHPRE_ void VCHPOST_ vc_vchi_gencmd_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ); + + +/* Initialise general command service. Returns it's interface number. This initialises + the host side of the interface, it does not send anything to VideoCore. */ + +VCHPRE_ int VCHPOST_ vc_gencmd_init(void); + +/* Stop the service from being used. */ + +VCHPRE_ void VCHPOST_ vc_gencmd_stop(void); + +/* Return the service number (-1 if not running). */ +VCHPRE_ int VCHPOST_ vc_gencmd_inum(void); + +/****************************************************************************** +Send commands to VideoCore. +These all return 0 for success. They return VC_MSGFIFO_FIFO_FULL if there is +insufficient space for the whole message in the fifo, and none of the message is +sent. +******************************************************************************/ + +/* send command to general command serivce */ +VCHPRE_ int VCHPOST_ vc_gencmd_send( const char *format, ... ); + +/* get resonse from general command serivce */ +VCHPRE_ int VCHPOST_ vc_gencmd_read_response(char *response, int maxlen); + +/* convenience function to send command and receive the response */ +VCHPRE_ int VCHPOST_ vc_gencmd(char *response, int maxlen, const char *format, ...); + +/* read part of a response from the general command service */ +VCHPRE_ int VCHPOST_ vc_gencmd_read_response_partial(char *response, int nbytes); + +/* if reading with vc_gencmd_read_response_partial end response reads with this */ +VCHPRE_ int VCHPOST_ vc_gencmd_close_response_partial(void); + +/* get state of reading of response */ +VCHPRE_ int VCHPOST_ vc_gencmd_read_partial_state(void); + +/****************************************************************************** +Utilities to help interpret the responses. +******************************************************************************/ + +/* Read the value of a property=value type pair from a string (typically VideoCore's + response to a general command). Return non-zero if found. */ +VCHPRE_ int VCHPOST_ vc_gencmd_string_property(char *text, const char *property, char **value, int *length); + +/* Read the numeric value of a property=number field from a response string. Return + non-zero if found. */ +VCHPRE_ int VCHPOST_ vc_gencmd_number_property(char *text, const char *property, int *number); + +/* Send a command until the desired response is received, the error message is detected, or the timeout */ +VCHPRE_ int VCHPOST_ vc_gencmd_until( char *cmd, + const char *property, + char *value, + const char *error_string, + int timeout); + +#endif diff --git a/interface/vmcs_host/vchost.h b/interface/vmcs_host/vchost.h new file mode 100755 index 0000000..23ffa63 --- /dev/null +++ b/interface/vmcs_host/vchost.h @@ -0,0 +1,273 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCHOST_H +#define VCHOST_H + +#include "vchost_platform_config.h" +#include "vcfilesys_defs.h" +#include "interface/vcos/vcos.h" //for VCHPRE_ abd VCHPOST_ macro's for func declaration +#include "interface/vmcs_host/vc_fileservice_defs.h" // for VC_O_XXX file definitions +#include "interface/vchi/vchi.h" + +#define UNUSED_PARAMETER(x) ((void)(x))/* macro to suppress not use warning */ + +/*---------------------------------------------------------------------------*/ +/* Byte-swapping, dependent on host's orientation */ +/*---------------------------------------------------------------------------*/ + +#ifndef VC_HOST_IS_BIG_ENDIAN +#define VC_HTOV32(val) (val) +#define VC_HTOV16(val) (val) +#define VC_VTOH32(val) (val) +#define VC_VTOH16(val) (val) +#else +static unsigned long VC_HTOV32(unsigned long val) { + return ((val<<24) | ((val&0xff00)<<8) | ((val>>8)&0xff00) | ((val>>24)&0xff)); } +static unsigned short VC_HTOV16(unsigned short val) { + return ((val<<8)|(val>>8)); } +static unsigned long VC_VTOH32(unsigned long val) { + return ((val<<24) | ((val&0xff00)<<8) | ((val>>8)&0xff00) | ((val>>24)&0xff)); } +static unsigned short VC_VTOH16(unsigned short val) { + return ((val<<8)|(val>>8)); } +#endif + +/*---------------------------------------------------------------------------*/ +/* Host port related functions */ +/*---------------------------------------------------------------------------*/ + +/* Boot a bin file from flash into RAM. Returns the id of the application running */ + +VCHPRE_ int VCHPOST_ vc_host_boot(char *cmd_line, void *binimg, int nbytes, int bootloader); + +/* Perform any platform specific initialisations. */ + +VCHPRE_ int VCHPOST_ vc_host_init(void); + +/* Read a multiple of 16 bytes from VideoCore. host_addr has no particular alignment, + but it is important that it transfers the data in 16-bit chunks if this is possible. */ + +VCHPRE_ int VCHPOST_ vc_host_read_consecutive(void *host_addr, uint32_t vc_addr, int nbytes, int channel); + +#ifdef VC_HOST_IS_BIG_ENDIAN +// Reads from VideoCore with an implicit swap of each pair of bytes. +VCHPRE_ int VCHPOST_ vc_host_read_byteswapped(void *host_addr, uint32_t vc_addr, int nbytes, int channel); +#endif + +/* Write a multiple of 16 bytes to VideoCore. host_addr has no particular alignment, + but it is important that it transfers the data in 16-bit chunks if this is possible. */ + +VCHPRE_ int VCHPOST_ vc_host_write_consecutive(uint32_t vc_addr, void *host_addr, int nbytes, int channel); + +#ifdef VC_HOST_IS_BIG_ENDIAN +// Write to VideoCore with an implicit swap of each pair of bytes. +VCHPRE_ int VCHPOST_ vc_host_write_byteswapped(uint32_t vc_addr, void *host_addr, int nbytes, int channel); +#endif + +/* Send an interrupt to VideoCore. */ + +VCHPRE_ int VCHPOST_ vc_host_send_interrupt(int channel); + +/* Wait for an interrupt from VideoCore. This can return immediately if applications + are happy to busy-wait. */ + +VCHPRE_ int VCHPOST_ vc_host_wait_interrupt(void); + +/* Tell the host to act on or ignore interrupts. */ + +VCHPRE_ void VCHPOST_ vc_host_interrupts(int on); + +/* Function called when there is some kind of internal error. Breakpoints can be set on + this for debugging. */ + +VCHPRE_ void VCHPOST_ vc_error(void); + + +/*---------------------------------------------------------------------------*/ +/* Event (interrupt) related functions */ +/*---------------------------------------------------------------------------*/ + +// Minimum number of event objects an implementation should support. +// Sufficient for 2 per 8 interfaces/services + 4 others +#define VC_EVENT_MAX_NUM 20 + +/* Create (and clear) an event. Returns a pointer to the event object. */ +VCHPRE_ void * VCHPOST_ vc_event_create(void); + +/* Wait for an event to be set, blocking until it is set. + Only one thread may be waiting at any one time. + The event is automatically cleared on leaving this function. */ +VCHPRE_ void VCHPOST_ vc_event_wait(void *sig); + +/* Reads the state of an event (for polling systems) */ +VCHPRE_ int VCHPOST_ vc_event_status(void *sig); + +/* Forcibly clears any pending event */ +VCHPRE_ void VCHPOST_ vc_event_clear(void *sig); + +/* Sets an event - can be called from any thread */ +VCHPRE_ void VCHPOST_ vc_event_set(void *sig); + +/* Register the calling task to be notified of an event. */ +VCHPRE_ void VCHPOST_ vc_event_register(void *ievent); + +/* Set events to block, stopping polling mode. */ +VCHPRE_ void VCHPOST_ vc_event_blocking(void); + +/*---------------------------------------------------------------------------*/ +/* Semaphore related functions */ +/*---------------------------------------------------------------------------*/ + +// Minimum number of locks an implementation should support. + +#define VC_LOCK_MAX_NUM 32 + +// Create a lock. Returns a pointer to the lock object. A lock is initially available +// just once. + +VCHPRE_ void * VCHPOST_ vc_lock_create(void); + +// Obtain a lock. Block until we have it. Locks are not re-entrant for the same thread. + +VCHPRE_ void VCHPOST_ vc_lock_obtain(void *lock); + +// Release a lock. Anyone can call this, even if they didn't obtain the lock first. + +VCHPRE_ void VCHPOST_ vc_lock_release(void *lock); + +/*---------------------------------------------------------------------------*/ +/* File system related functions */ +/*---------------------------------------------------------------------------*/ + +// Initialises the host dependent file system functions for use +VCHPRE_ void VCHPOST_ vc_hostfs_init(void); +VCHPRE_ void VCHPOST_ vc_hostfs_exit(void); + +// Low level file system functions equivalent to close(), lseek(), open(), read() and write() +VCHPRE_ int VCHPOST_ vc_hostfs_close(int fildes); + +VCHPRE_ long VCHPOST_ vc_hostfs_lseek(int fildes, long offset, int whence); + +VCHPRE_ int64_t VCHPOST_ vc_hostfs_lseek64(int fildes, int64_t offset, int whence); + +VCHPRE_ int VCHPOST_ vc_hostfs_open(const char *path, int vc_oflag); + +VCHPRE_ int VCHPOST_ vc_hostfs_read(int fildes, void *buf, unsigned int nbyte); + +VCHPRE_ int VCHPOST_ vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte); + +// Ends a directory listing iteration +VCHPRE_ int VCHPOST_ vc_hostfs_closedir(void *dhandle); + +// Formats the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_hostfs_format(const char *path); + +// Returns the amount of free space on the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_hostfs_freespace(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_hostfs_freespace64(const char *path); + +// Gets the attributes of the named file +VCHPRE_ int VCHPOST_ vc_hostfs_get_attr(const char *path, fattributes_t *attr); + +// Creates a new directory +VCHPRE_ int VCHPOST_ vc_hostfs_mkdir(const char *path); + +// Starts a directory listing iteration +VCHPRE_ void * VCHPOST_ vc_hostfs_opendir(const char *dirname); + +// Directory listing iterator +VCHPRE_ struct dirent * VCHPOST_ vc_hostfs_readdir_r(void *dhandle, struct dirent *result); + +// Deletes a file or (empty) directory +VCHPRE_ int VCHPOST_ vc_hostfs_remove(const char *path); + +// Renames a file, provided the new name is on the same file system as the old +VCHPRE_ int VCHPOST_ vc_hostfs_rename(const char *oldfile, const char *newfile); + +// Sets the attributes of the named file +VCHPRE_ int VCHPOST_ vc_hostfs_set_attr(const char *path, fattributes_t attr); + +// Truncates a file at its current position +VCHPRE_ int VCHPOST_ vc_hostfs_setend(int fildes); + +// Returns the total amount of space on the drive that contains the given path +VCHPRE_ int VCHPOST_ vc_hostfs_totalspace(const char *path); +VCHPRE_ int64_t VCHPOST_ vc_hostfs_totalspace64(const char *path); + +// Return millisecond resolution system time, only used for differences +VCHPRE_ int VCHPOST_ vc_millitime(void); + +// Invalidates any cluster chains in the FAT that are not referenced in any directory structures +VCHPRE_ void VCHPOST_ vc_hostfs_scandisk(const char *path); + +// Checks whether or not a FAT filesystem is corrupt or not. If fix_errors is TRUE behaves exactly as vc_filesys_scandisk. +VCHPRE_ int VCHPOST_ vc_hostfs_chkdsk(const char *path, int fix_errors); + +/*---------------------------------------------------------------------------*/ +/* These functions only need to be implemented for the test system. */ +/*---------------------------------------------------------------------------*/ + +// Open a log file. +VCHPRE_ void VCHPOST_ vc_log_open(const char *fname); + +// Flush any pending data to the log file. +VCHPRE_ void VCHPOST_ vc_log_flush(void); + +// Close the log file. +VCHPRE_ void VCHPOST_ vc_log_close(void); + +// Log an error. +VCHPRE_ void VCHPOST_ vc_log_error(const char *format, ...); + +// Log a warning. +VCHPRE_ void VCHPOST_ vc_log_warning(const char *format, ...); + +// Write a message to the log. +VCHPRE_ void VCHPOST_ vc_log_msg(const char *format, ...); + +// Flush the log. +VCHPRE_ void VCHPOST_ vc_log_flush(void); + +// Return the total number of warnings and errors logged. +VCHPRE_ void VCHPOST_ vc_log_counts(int *warnings, int *errors); + +// Wait for the specified number of microseconds. Used in test system only. +VCHPRE_ void VCHPOST_ vc_sleep(int ms); + +// Get a time value in milliseconds. Used for measuring time differences +VCHPRE_ uint32_t VCHPOST_ vc_time(void); + +// Check timing functions are available. Use in calibrating tests. +VCHPRE_ int VCHPOST_ calibrate_sleep (const char *data_dir); + +/*---------------------------------------------------------------------------*/ +/* Functions to allow dynamic service creation */ +/*---------------------------------------------------------------------------*/ + +VCHPRE_ void VCHPOST_ vc_host_get_vchi_state(VCHI_INSTANCE_T *initialise_instance, VCHI_CONNECTION_T **connection); + +#endif diff --git a/interface/vmcs_host/vchost_platform_config.h b/interface/vmcs_host/vchost_platform_config.h new file mode 100755 index 0000000..fc9bcd2 --- /dev/null +++ b/interface/vmcs_host/vchost_platform_config.h @@ -0,0 +1,32 @@ +/* +Copyright (c) 2016, Raspberry Pi (Trading) Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if defined(__unix__) && !defined(__ANDROID__) +#include "linux/vchost_config.h" +#else +#include "vchost_config.h" +#endif diff --git a/interface/vmcs_host/vcilcs.c b/interface/vmcs_host/vcilcs.c new file mode 100755 index 0000000..980a21d --- /dev/null +++ b/interface/vmcs_host/vcilcs.c @@ -0,0 +1,1071 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +/* Project includes */ +#include "vcinclude/common.h" + +#include "interface/vchiq_arm/vchiq.h" +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/vc_ilcs_defs.h" +#include "interface/vmcs_host/vcilcs.h" + +#ifdef USE_VCHI +#include "interface/vchi/vchiq_wrapper.h" +#endif + +#ifdef _VIDEOCORE +#include "applications/vmcs/ilcs/ilcs_common.h" +#endif + +/****************************************************************************** +Private types and defines. +******************************************************************************/ + +// number of threads that can use ilcs +#define ILCS_MAX_WAITING 8 + +// maximum number of retries to grab a wait slot, +// before the message is discarded and failure returned. +#define ILCS_WAIT_TIMEOUT 250 + +// maximum number of concurrent function calls that can +// be going at once. Each function call requires to copy +// the message data so we can dequeue the message from vchi +// before executing the function, otherwise ILCS may cause +// a deadlock. Must be larger than ILCS_MAX_WAITING +#define ILCS_MAX_NUM_MSGS (ILCS_MAX_WAITING+1) +#define ILCS_MSG_INUSE_MASK ((1<wait and ->next_xid + ILCS_WAIT_T wait[ILCS_MAX_WAITING]; + int next_xid; + VCOS_EVENT_T wait_event; // for signalling when a wait becomes free + + // don't need locking around msg_inuse as only touched by + // the server thread in ilcs_process_message + unsigned int msg_inuse; + unsigned char msg[ILCS_MAX_NUM_MSGS][VCHIQ_SLOT_SIZE]; + uint32_t header_array[(sizeof(VCHIQ_HEADER_T)+8)/4]; +}; + +/****************************************************************************** +Private functions in this file. +Define as static. +******************************************************************************/ +#ifdef USE_VCHIQ_ARM +static VCHIQ_STATUS_T ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service_user, void *bulk_user); +#else +static int ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, void *service_user, void *bulk_user); +#endif +static void *ilcs_task(void *param); +static void ilcs_response(ILCS_SERVICE_T *st, uint32_t xid, const unsigned char *msg, int len ); +static void ilcs_transmit(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, + const unsigned char *msg, int len, + const unsigned char *msg2, int len2); +static void ilcs_command(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, unsigned char *msg, int len); +static void ilcs_timer(void *param); +static int ilcs_process_message(ILCS_SERVICE_T *st, int block); + +/* ---------------------------------------------------------------------- + * initialise OpenMAX IL component service + * -------------------------------------------------------------------- */ +#ifdef USE_VCHIQ_ARM +ILCS_SERVICE_T *ilcs_init(VCHIQ_INSTANCE_T state, void **connection, ILCS_CONFIG_T *config, int use_memmgr) +#else +ILCS_SERVICE_T *ilcs_init(VCHIQ_STATE_T *state, void **connection, ILCS_CONFIG_T *config, int use_memmgr) +#endif +{ + int32_t i; + VCOS_THREAD_ATTR_T thread_attrs; + ILCS_SERVICE_T *st; + VCHIQ_SERVICE_PARAMS_T params; + + st = vcos_malloc(sizeof(ILCS_SERVICE_T), "ILCS State"); + if(!st) + goto fail_alloc; + + memset(st, 0, sizeof(ILCS_SERVICE_T)); + st->vchiq = state; + st->fourcc = VCHIQ_MAKE_FOURCC('I', 'L', 'C', 'S'); + st->config = *config; + + // setting this to true implies we have relocatable handles as + // buffer pointers, otherwise we interpret them to be real pointers + st->use_memmgr = use_memmgr; + + // create semaphore for protecting wait/xid structures + if(vcos_mutex_create(&st->wait_mtx, "ILCS") != VCOS_SUCCESS) + goto fail_all; + + // create smaphore for control+bulk protection + if(vcos_semaphore_create(&st->send_sem, "ILCS", 1) != VCOS_SUCCESS) + goto fail_send_sem; + + // create event group for signalling when a waiting slot becomes free + if(vcos_event_create(&st->wait_event, "ILCS") != VCOS_SUCCESS) + goto fail_wait_event; + + for(i=0; iwait[i].event, "ILCS") != VCOS_SUCCESS) + { + while(--i >= 0) + vcos_event_delete(&st->wait[i].event); + goto fail_wait_events; + } + + if(vcos_timer_create(&st->timer, "ILCS", ilcs_timer, st) != VCOS_SUCCESS) + goto fail_timer; + + // create the queue of incoming messages + if(!vchiu_queue_init(&st->queue, 256)) + goto fail_queue; + + // create the bulk receive event + if(vcos_event_create(&st->bulk_rx, "ILCS") != VCOS_SUCCESS) + goto fail_bulk_event; + + // create an 'ILCS' service +#ifdef USE_VCHIQ_ARM + /* VCHIQ_ARM distinguishes between servers and clients. Use use_memmgr + parameter to detect usage by the client. + */ + + memset(¶ms,0,sizeof(params)); + params.fourcc = st->fourcc; + params.callback = ilcs_callback; + params.userdata = st; + params.version = VC_ILCS_VERSION; + params.version_min = VC_ILCS_VERSION; + + if (use_memmgr == 0) + { + // Host side, which will connect to a listening VideoCore side + if (vchiq_open_service(st->vchiq, ¶ms, &st->service) != VCHIQ_SUCCESS) + goto fail_service; + } + else + { + // VideoCore side, a listening service not connected + if (vchiq_add_service(st->vchiq, ¶ms, &st->service) != VCHIQ_SUCCESS) + goto fail_service; + + // On VC shutdown we defer calling vchiq_remove_service until after the callback has + // returned, so we require not to have the autoclose behaviour + vchiq_set_service_option(st->service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 0); + } +#else +#ifdef USE_VCHI + if(!vchiq_wrapper_add_service(st->vchiq, connection, st->fourcc, ilcs_callback, st)) + goto fail_service; +#else + if(!vchiq_add_service(st->vchiq, st->fourcc, ilcs_callback, st)) + goto fail_service; +#endif +#endif + + if((st->ilcs_common = st->config.ilcs_common_init(st)) == NULL) + goto fail_ilcs_common; + + vcos_thread_attr_init(&thread_attrs); + vcos_thread_attr_setstacksize(&thread_attrs, 4096); + + snprintf(st->name, sizeof(st->name), "ILCS_%s", use_memmgr ? "VC" : "HOST"); + + if(vcos_thread_create(&st->thread, st->name, &thread_attrs, ilcs_task, st) != VCOS_SUCCESS) + goto fail_thread; + + return st; + + fail_thread: + st->config.ilcs_common_deinit(st->ilcs_common); + fail_ilcs_common: +#ifdef USE_VCHIQ_ARM + vchiq_remove_service(st->service); +#endif + fail_service: + vcos_event_delete(&st->bulk_rx); + fail_bulk_event: + vchiu_queue_delete(&st->queue); + fail_queue: + vcos_timer_delete(&st->timer); + fail_timer: + for(i=0; iwait[i].event); + fail_wait_events: + vcos_event_delete(&st->wait_event); + fail_wait_event: + vcos_semaphore_delete(&st->send_sem); + fail_send_sem: + vcos_mutex_delete(&st->wait_mtx); + fail_all: + vcos_free(st); + fail_alloc: + return NULL; +} + +/* ---------------------------------------------------------------------- + * sends a message to the thread to quit + * -------------------------------------------------------------------- */ +static void ilcs_send_quit(ILCS_SERVICE_T *st) +{ + // We're closing, so tell the task to cleanup + VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)st->header_array; + char *msg; + int i; + header->size = 8; + msg = header->data; + msg[0] = IL_SERVICE_QUIT & 0xff; + msg[1] = (IL_SERVICE_QUIT >> 8) & 0xff; + msg[2] = (IL_SERVICE_QUIT >> 16) & 0xff; + msg[3] = IL_SERVICE_QUIT >> 24; + + vchiu_queue_push(&st->queue, header); + + // force all currently waiting clients to wake up + for(i=0; iwait[i].resp) + vcos_event_signal(&st->wait[i].event); + + vcos_event_signal(&st->wait_event); +} + +/* ---------------------------------------------------------------------- + * deinitialises the OpenMAX IL Component Service. + * This is the usual way that the host side service shuts down, called + * from OMX_Deinit(). + * -------------------------------------------------------------------- */ +void ilcs_deinit(ILCS_SERVICE_T *st) +{ + void *data; + st->kill_service = DEINIT_CALLED; + ilcs_send_quit(st); + vcos_thread_join(&st->thread, &data); + vcos_free(st); +} + +/* ---------------------------------------------------------------------- + * sets the wait event, to timeout blocked threads + * -------------------------------------------------------------------- */ +static void ilcs_timer(void *param) +{ + ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) param; + + vcos_assert(st->timer_expired == 0); + st->timer_expired = 1; + vcos_event_signal(&st->wait_event); +} + +/* ---------------------------------------------------------------------- + * returns pointer to common object + * -------------------------------------------------------------------- */ +ILCS_COMMON_T *ilcs_get_common(ILCS_SERVICE_T *st) +{ + return st->ilcs_common; +} + +/* ---------------------------------------------------------------------- + * whether the ilcsg thread is currently running + * returns 1 if the ilcsg is the current thread, 0 otherwise + * -------------------------------------------------------------------- */ +int ilcs_thread_current(void *param) +{ + ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) param; + return vcos_thread_current() == &st->thread; +} + +/* ---------------------------------------------------------------------- + * called from the vchiq layer whenever an event happens. + * here, we are only interested in the 'message available' callback + * -------------------------------------------------------------------- */ +#ifdef USE_VCHIQ_ARM +static VCHIQ_STATUS_T ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service_user, void *bulk_user) +{ + ILCS_SERVICE_T *st = (ILCS_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(service_user); +#else +static int ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, void *service_user, void *bulk_user) +{ + ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) service_user; +#endif + + switch(reason) { + +#ifdef USE_VCHIQ_ARM + case VCHIQ_SERVICE_OPENED: + { +#ifdef _VIDEOCORE + // We're on the VideoCore side and we've been connected to, so we need to spawn another + // listening service. Create another ILCS instance. + ILCS_CONFIG_T config; + ilcs_config(&config); + ilcs_init(st->vchiq, NULL, &config, st->use_memmgr); +#else + vcos_abort(); +#endif + } + break; + + case VCHIQ_SERVICE_CLOSED: + if(st && st->kill_service < CLOSED_CALLBACK) + { + st->kill_service = CLOSED_CALLBACK; + ilcs_send_quit(st); + } + break; + + case VCHIQ_BULK_RECEIVE_ABORTED: + // bulk rx only aborted if we're about to close the service, + // so signal this now so that the person waiting for this + // bulk rx can return a failure to the user + st->kill_service = ABORTED_BULK; + vcos_event_signal(&st->bulk_rx); + break; +#endif + + case VCHIQ_MESSAGE_AVAILABLE: +#ifndef _VIDEOCORE + { + static int queue_warn = 0; + int queue_len = st->queue.write - st->queue.read; + if (!queue_warn) + queue_warn = getenv("ILCS_WARN") ? (st->queue.size/2) : st->queue.size; + if (queue_len >= queue_warn) + { + if (queue_len == st->queue.size) + VCOS_ALERT("ILCS queue full"); + else + VCOS_ALERT("ILCS queue len = %d", queue_len); + queue_warn = queue_warn + (st->queue.size - queue_warn)/2; + } + } +#endif + vchiu_queue_push(&st->queue, header); + break; + + case VCHIQ_BULK_RECEIVE_DONE: + vcos_event_signal(&st->bulk_rx); + break; + + default: + break; + } + +#ifdef USE_VCHIQ_ARM + return VCHIQ_SUCCESS; +#else + return 1; +#endif +} + +/* ---------------------------------------------------------------------- + * send a message and wait for reply. + * repeats continuously, on each connection + * -------------------------------------------------------------------- */ +static void *ilcs_task(void *param) +{ + ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) param; + int i; + + st->config.ilcs_thread_init(st->ilcs_common); + + while(st->kill_service < CLOSED_CALLBACK) + ilcs_process_message(st, 1); + + // tidy up after ourselves + st->config.ilcs_common_deinit(st->ilcs_common); +#ifdef USE_VCHIQ_ARM + vchiq_remove_service(st->service); +#endif + vcos_event_delete(&st->bulk_rx); + vchiu_queue_delete(&st->queue); + vcos_timer_delete(&st->timer); + for(i=0; iwait[i].event); + vcos_event_delete(&st->wait_event); + vcos_semaphore_delete(&st->send_sem); + vcos_mutex_delete(&st->wait_mtx); + + if(st->kill_service == CLOSED_CALLBACK) + { +#ifdef _VIDEOCORE + // need vcos reaper thread to do join/free for us + vcos_thread_reap(&st->thread, vcos_free, st); +#else + // we've got a CLOSED callback from vchiq without ilcs_deinit being called. + // this shouldn't really happen, so we just want to abort at this point. + vcos_abort(); +#endif + } + + return 0; +} + +/* ---------------------------------------------------------------------- + * check to see if there are any pending messages + * + * if there are no messages, return 0 + * + * otherwise, fetch and process the first queued message (which will + * be either a command or response from host) + * -------------------------------------------------------------------- */ +#define UINT32(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)) + +static int ilcs_process_message(ILCS_SERVICE_T *st, int block) +{ + unsigned char *msg; + VCHIQ_HEADER_T *header; + uint32_t i, msg_len, cmd, xid; + + if(!block && vchiu_queue_is_empty(&st->queue)) + return 0; // no more messages + + header = vchiu_queue_pop(&st->queue); + + msg = (unsigned char *) header->data; + + cmd = UINT32(msg); + xid = UINT32(msg+4); + + msg += 8; + msg_len = header->size - 8; + + if(cmd == IL_RESPONSE) + { + ilcs_response(st, xid, msg, msg_len); +#ifdef USE_VCHIQ_ARM + vchiq_release_message(st->service, header); +#else + vchiq_release_message(st->vchiq, header); +#endif + } + else if(cmd == IL_SERVICE_QUIT) + { + return 1; + } + else + { + // we can only handle commands if we have space to copy the message first + if(st->msg_inuse == ILCS_MSG_INUSE_MASK) + { + // this shouldn't happen, since we have more msg slots than the + // remote side is allowed concurrent clients. this is classed + // as a failure case, so we discard the message knowing that things + // will surely lock up fairly soon after. + vcos_assert(0); + return 1; + } + + i = 0; + while(st->msg_inuse & (1<msg_inuse |= (1<msg[i], msg, msg_len); +#ifdef USE_VCHIQ_ARM + vchiq_release_message(st->service, header); +#else + vchiq_release_message(st->vchiq, header); +#endif + ilcs_command(st, cmd, xid, st->msg[i], msg_len); + + // mark the message copy as free + st->msg_inuse &= ~(1<wait entry + vcos_mutex_lock(&st->wait_mtx); + for (i=0; iwait[i]; + if(wait->resp && wait->xid == xid) + break; + } + vcos_mutex_unlock(&st->wait_mtx); + + if(i == ILCS_MAX_WAITING) { + // something bad happened, someone has sent a response back + // when the caller said they weren't expecting a response + vcos_assert(0); + return; + } + + // check that we have enough space to copy into. + // if we haven't the user can tell by the updated rlen value. + if(len > *wait->rlen) + copy = *wait->rlen; + + *wait->rlen = len; + + // extract command from fifo and place in response buffer. + memcpy(wait->resp, msg, copy); + + vcos_event_signal(&wait->event); +} + +/* ---------------------------------------------------------------------- + * helper function to transmit an ilcs command/response + payload + * -------------------------------------------------------------------- */ +static void ilcs_transmit(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, + const unsigned char *msg, int len, + const unsigned char *msg2, int len2) +{ + VCHIQ_ELEMENT_T vec[4]; + int32_t count = 3; + + vec[0].data = &cmd; + vec[0].size = sizeof(cmd); + vec[1].data = &xid; + vec[1].size = sizeof(xid); + vec[2].data = msg; + vec[2].size = len; + + if(msg2) + { + vec[3].data = msg2; + vec[3].size = len2; + count++; + } + +#ifdef USE_VCHIQ_ARM + vchiq_queue_message(st->service, vec, count); +#else + vchiq_queue_message(st->vchiq, st->fourcc, vec, count); +#endif +} + +/* ---------------------------------------------------------------------- + * received response to an ILCS command + * -------------------------------------------------------------------- */ +static void +ilcs_command(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, unsigned char *msg, int len) +{ + // execute this function call + unsigned char resp[VC_ILCS_MAX_CMD_LENGTH]; + unsigned char *rbuf = resp; + int rlen = -1; + IL_FN_T fn; + + if(cmd >= IL_FUNCTION_MAX_NUM) { + vcos_assert(0); + return; + } + + fn = st->config.fns[cmd]; + if(!fn) { + vcos_assert(0); + return; + } + + // for one method we allow the response to go in the same slot as the + // msg, since it wants to return quite a big amount of debug information + // and we know this is safe. + if(cmd == IL_GET_DEBUG_INFORMATION) + { + int max = VCHIQ_SLOT_SIZE - 8; + IL_GET_DEBUG_INFORMATION_EXECUTE_T *exe = (IL_GET_DEBUG_INFORMATION_EXECUTE_T *) msg; + if(exe->len > max) + exe->len = max; + + rbuf = msg; + } + + // at this point we are executing in ILCS task context + // NOTE: this can cause ilcs_execute_function() calls from within guts of openmaxil! + fn(st->ilcs_common, msg, len, rbuf, &rlen); + + // make sure rlen has been initialised by the function + vcos_assert(rlen != -1); + + if(rlen > 0) + ilcs_transmit(st, IL_RESPONSE, xid, rbuf, rlen, NULL, 0); +} + +/** + * send a string to the host side IL component service. if resp is NULL + * then there is no response to this call, so we should not wait for one. + * + * returns 0 on successful call made, -1 on failure to send call. + * on success, the response is written to 'resp' pointer + * + * @param data function parameter data + * @param len length of function parameter data + * @param data optional second function parameter data + * @param len2 length of second function parameter data + * @param msg_mem_handle option mem handle to be sent as part of msg data + * @param msg_offset Offset with msg mem handle + * @param msg_len Length of msg mem handle + * @param bulk_mem_handle Mem handle sent using VCHI bulk transfer + * @param bulk_offset Offset within memory handle + * @param bulk_len Length of bulk transfer + * + * -------------------------------------------------------------------- */ + +static int ilcs_execute_function_ex(ILCS_SERVICE_T *st, IL_FUNCTION_T func, + void *data, int len, + void *data2, int len2, + VCHI_MEM_HANDLE_T bulk_mem_handle, void *bulk_offset, int bulk_len, + void *resp, int *rlen) +{ + ILCS_WAIT_T *wait = NULL; + int num = 0; + uint32_t xid; + + if(st->kill_service) + return -1; + + // need to atomically find free ->wait entry + vcos_mutex_lock(&st->wait_mtx); + + // if resp is NULL, we do not expect any response + if(resp == NULL) { + xid = st->next_xid++; + } + else + { + int i; + + if(st->timer_needed++ == 0) + { + vcos_timer_set(&st->timer, 10); + } + + // we try a number of times then give up with an error message + // rather than just deadlocking + + // Note: the real reason for the timeout is nothing to do with hardware + // errors, but is to ensure that if the ILCS thread is calling this function + // (because the client makes an OMX call from one of the callbacks) then + // the queue of messages from VideoCore still gets serviced. + + for (i=0; iwait[num].resp != NULL) + num++; + + if(num < ILCS_MAX_WAITING || i == ILCS_WAIT_TIMEOUT-1) + break; + + // the previous time round this loop, we woke up because the timer + // expired, so restart it + if (st->timer_expired) + { + st->timer_expired = 0; + vcos_timer_set(&st->timer, 10); + } + + // might be a fatal error if another thread is relying + // on this call completing before it can complete + // we'll pause until we can carry on and hope that's sufficient. + vcos_mutex_unlock(&st->wait_mtx); + + // if we're the ilcs thread, then the waiters might need + // us to handle their response, so try and clear those now + if(vcos_thread_current() == &st->thread) + { + while (vcos_event_try(&st->wait_event) != VCOS_SUCCESS) + { + while(ilcs_process_message(st, 0)) + if(st->kill_service >= CLOSED_CALLBACK) + return -1; + if (vcos_event_try(&st->wait_event) == VCOS_SUCCESS) + break; + vcos_sleep(1); + } + } + else + { + vcos_event_wait(&st->wait_event); + } + + vcos_mutex_lock(&st->wait_mtx); + } + + if(--st->timer_needed == 0) + { + vcos_timer_cancel(&st->timer); + st->timer_expired = 0; + } + + if(num == ILCS_MAX_WAITING) + { + // failed to send message. + vcos_mutex_unlock(&st->wait_mtx); + return -1; + } + + wait = &st->wait[num]; + + wait->resp = resp; + wait->rlen = rlen; + xid = wait->xid = st->next_xid++; + } + + vcos_mutex_unlock(&st->wait_mtx); + + if(bulk_len != 0) + vcos_semaphore_wait(&st->send_sem); + + ilcs_transmit(st, func, xid, data, len, data2, len2); + + if(bulk_len != 0) + { +#ifdef USE_VCHIQ_ARM + vchiq_queue_bulk_transmit_handle(st->service, bulk_mem_handle, bulk_offset, bulk_len, NULL); +#else + vchiq_queue_bulk_transmit(st->vchiq, st->fourcc, bulk_mem_handle, bulk_offset, bulk_len, NULL); +#endif + vcos_semaphore_post(&st->send_sem); + } + + if(!wait) + { + // nothing more to do + return 0; + } + + if(vcos_thread_current() != &st->thread) + { + // block waiting for response + vcos_event_wait(&wait->event); + } + else + { + // since we're the server task, to receive our own response code + // we need to keep reading messages from the other side. In + // addition, our function executing on the host may also call + // functions on VideoCore before replying, so we need to handle + // all incoming messages until our response arrives. + for (;;) + { + // wait->sem will not be released until we process the response message + // so handle one incoming message + ilcs_process_message(st, 1); + + // did the last message release wait->sem ? + if(st->kill_service >= CLOSED_CALLBACK || vcos_event_try(&wait->event) == VCOS_SUCCESS) + break; + } + } + + // safe to do the following - the assignment of NULL is effectively atomic + wait->resp = NULL; + vcos_event_signal(&st->wait_event); + + return st->kill_service >= CLOSED_CALLBACK ? -1 : 0; +} + +int ilcs_execute_function(ILCS_SERVICE_T *st, IL_FUNCTION_T func, void *data, int len, void *resp, int *rlen) +{ + return ilcs_execute_function_ex(st, func, data, len, NULL, 0, VCHI_MEM_HANDLE_INVALID, 0, 0, resp, rlen); +} + +/* ---------------------------------------------------------------------- + * send a buffer via the IL component service. + * -------------------------------------------------------------------- */ + +OMX_ERRORTYPE ilcs_pass_buffer(ILCS_SERVICE_T *st, IL_FUNCTION_T func, void *reference, + OMX_BUFFERHEADERTYPE *pBuffer) +{ + IL_PASS_BUFFER_EXECUTE_T exe; + IL_BUFFER_BULK_T fixup; + IL_RESPONSE_HEADER_T resp; + VCHI_MEM_HANDLE_T mem_handle = VCHI_MEM_HANDLE_INVALID; + void *ret = NULL, *data2 = NULL, *bulk_offset = NULL; + int len2 = 0, bulk_len = 0; + OMX_U8 *ptr = NULL; + int rlen = sizeof(resp); + + if(st->kill_service) + return OMX_ErrorHardware; + + if((func == IL_EMPTY_THIS_BUFFER && pBuffer->pInputPortPrivate == NULL) || + (func == IL_FILL_THIS_BUFFER && pBuffer->pOutputPortPrivate == NULL)) + { + // return this to pass conformance + // the actual error is using a buffer that hasn't be registered with usebuffer/allocatebuffer + return OMX_ErrorIncorrectStateOperation; + } + + if((pBuffer->nFlags & OMX_BUFFERFLAG_EXTRADATA) || pBuffer->nFilledLen) + ptr = st->config.ilcs_mem_lock(pBuffer) + pBuffer->nOffset; + + exe.reference = reference; + memcpy(&exe.bufferHeader, pBuffer, sizeof(OMX_BUFFERHEADERTYPE)); + + exe.bufferLen = pBuffer->nFilledLen; + if(pBuffer->nFlags & OMX_BUFFERFLAG_EXTRADATA) + { + // walk down extra-data's appended to the buffer data to work out their length + OMX_U8 *end = ptr + pBuffer->nAllocLen - pBuffer->nOffset; + OMX_OTHER_EXTRADATATYPE *extra = + (OMX_OTHER_EXTRADATATYPE *) (((uint32_t) (ptr + pBuffer->nFilledLen + 3)) & ~3); + OMX_BOOL b_corrupt = OMX_FALSE; + OMX_EXTRADATATYPE extra_type; + + do + { + // sanity check the extra data before doing anything with it + if(((uint8_t *)extra) + sizeof(OMX_OTHER_EXTRADATATYPE) > end || + ((uint8_t *)extra) + extra->nSize > end || + extra->nSize < sizeof(OMX_OTHER_EXTRADATATYPE) || + (extra->nSize & 3)) + { + // shouldn't happen. probably a problem with the component + b_corrupt = OMX_TRUE; + break; + } + + extra_type = extra->eType; + extra = (OMX_OTHER_EXTRADATATYPE *) (((uint8_t *) extra) + extra->nSize); + } + while(extra_type != OMX_ExtraDataNone); + + // if corrupt then drop the extra data since we can't do anything with it + if(b_corrupt) + pBuffer->nFlags &= ~OMX_BUFFERFLAG_EXTRADATA; + else + exe.bufferLen = ((uint8_t *) extra) - ptr; + } + + // check that the buffer fits in the allocated region + if(exe.bufferLen + pBuffer->nOffset > pBuffer->nAllocLen) + { + if(ptr != NULL) + st->config.ilcs_mem_unlock(pBuffer); + + return OMX_ErrorBadParameter; + } + + if(exe.bufferLen) + { + if(exe.bufferLen + sizeof(IL_PASS_BUFFER_EXECUTE_T) <= VC_ILCS_MAX_INLINE) + { + // Pass the data in the message itself, and avoid doing a bulk transfer at all... + exe.method = IL_BUFFER_INLINE; + + data2 = ptr; + len2 = exe.bufferLen; + } + else + { + // Pass the misaligned area at the start at end inline within the + // message, and the bulk of the message using a separate bulk + // transfer + const uint8_t *start = ptr; + const uint8_t *end = start + exe.bufferLen; + const uint8_t *round_start = (const OMX_U8*)ILCS_ROUND_UP(start); + const uint8_t *round_end = (const OMX_U8*)ILCS_ROUND_DOWN(end); + + exe.method = IL_BUFFER_BULK; + + if(st->use_memmgr) + { + bulk_offset = (void *) (round_start-(ptr-pBuffer->nOffset)); + mem_handle = (VCHI_MEM_HANDLE_T) pBuffer->pBuffer; + } + else + bulk_offset = (void *) round_start; + + bulk_len = round_end-round_start; + + if((fixup.headerlen = round_start - start) > 0) + memcpy(fixup.header, start, fixup.headerlen); + + if((fixup.trailerlen = end - round_end) > 0) + memcpy(fixup.trailer, round_end, fixup.trailerlen); + + data2 = &fixup; + len2 = sizeof(fixup); + } + } + else + { + exe.method = IL_BUFFER_NONE; + } + + // when used for callbacks to client, no need for response + // so only set ret when use component to component + if(func == IL_EMPTY_THIS_BUFFER || func == IL_FILL_THIS_BUFFER) + ret = &resp; + + if(ilcs_execute_function_ex(st, func, &exe, sizeof(IL_PASS_BUFFER_EXECUTE_T), + data2, len2, mem_handle, bulk_offset, bulk_len, ret, &rlen) < 0 || rlen != sizeof(resp)) + { + ret = &resp; + resp.err = OMX_ErrorHardware; + } + + if(ptr != NULL) + st->config.ilcs_mem_unlock(pBuffer); + + return ret ? resp.err : OMX_ErrorNone; +} + + +/* ---------------------------------------------------------------------- + * receive a buffer via the IL component service. + * -------------------------------------------------------------------- */ + +OMX_BUFFERHEADERTYPE *ilcs_receive_buffer(ILCS_SERVICE_T *st, void *call, int clen, OMX_COMPONENTTYPE **pComp) +{ + IL_PASS_BUFFER_EXECUTE_T *exe = call; + OMX_BUFFERHEADERTYPE *pHeader = exe->bufferHeader.pInputPortPrivate; + OMX_U8 *dest, *pBuffer = pHeader->pBuffer; + OMX_PTR *pAppPrivate = pHeader->pAppPrivate; + OMX_PTR *pPlatformPrivate = pHeader->pPlatformPrivate; + OMX_PTR *pInputPortPrivate = pHeader->pInputPortPrivate; + OMX_PTR *pOutputPortPrivate = pHeader->pOutputPortPrivate; + + if(st->kill_service) + return NULL; + + vcos_assert(pHeader); + memcpy(pHeader, &exe->bufferHeader, sizeof(OMX_BUFFERHEADERTYPE)); + + *pComp = exe->reference; + + pHeader->pBuffer = pBuffer; + pHeader->pAppPrivate = pAppPrivate; + pHeader->pPlatformPrivate = pPlatformPrivate; + pHeader->pInputPortPrivate = pInputPortPrivate; + pHeader->pOutputPortPrivate = pOutputPortPrivate; + + dest = st->config.ilcs_mem_lock(pHeader) + pHeader->nOffset; + + if(exe->method == IL_BUFFER_BULK) + { + IL_BUFFER_BULK_T *fixup = (IL_BUFFER_BULK_T *) (exe+1); + VCHI_MEM_HANDLE_T mem_handle = VCHI_MEM_HANDLE_INVALID; + void *bulk_offset; + int32_t bulk_len = exe->bufferLen - fixup->headerlen - fixup->trailerlen; + + vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T) + sizeof(IL_BUFFER_BULK_T)); + + if(st->use_memmgr) + { + mem_handle = (VCHI_MEM_HANDLE_T) pBuffer; + bulk_offset = (void*)(pHeader->nOffset + fixup->headerlen); + } + else + bulk_offset = dest + fixup->headerlen; + +#ifdef USE_VCHIQ_ARM + vchiq_queue_bulk_receive_handle(st->service, mem_handle, bulk_offset, bulk_len, NULL); +#else + vchiq_queue_bulk_receive(st->vchiq, st->fourcc, mem_handle, bulk_offset, bulk_len, NULL); +#endif + + vcos_event_wait(&st->bulk_rx); + + if(st->kill_service) + { + // the bulk receive was aborted, and we're about the quit, however this function + // being called means the buffer header control message made it across, so we + // need to think that this buffer is on VideoCore. So pretend this is all okay, + // but zero the buffer contents so we don't process bad data + pHeader->nFilledLen = 0; + pHeader->nFlags = 0; + } + else if(fixup->headerlen || fixup->trailerlen) + { + uint8_t *end = dest + exe->bufferLen; + + if(fixup->headerlen) + memcpy(dest, fixup->header, fixup->headerlen); + if(fixup->trailerlen) + memcpy(end-fixup->trailerlen, fixup->trailer, fixup->trailerlen); + } + } + else if(exe->method == IL_BUFFER_INLINE) + { + IL_BUFFER_INLINE_T *buffer = (IL_BUFFER_INLINE_T *) (exe+1); + + vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T) + exe->bufferLen); + memcpy(dest, buffer->buffer, exe->bufferLen); + } + else if(exe->method == IL_BUFFER_NONE) + { + vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T)); + } + else + { + vcos_assert(0); + } + + st->config.ilcs_mem_unlock(pHeader); + return pHeader; +} diff --git a/interface/vmcs_host/vcilcs.h b/interface/vmcs_host/vcilcs.h new file mode 100755 index 0000000..3a11b84 --- /dev/null +++ b/interface/vmcs_host/vcilcs.h @@ -0,0 +1,103 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// OpenMAX IL Component Service definitions + +#ifndef ILCS_H +#define ILCS_H + +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/vc_ilcs_defs.h" + +struct ILCS_SERVICE_T; +typedef struct ILCS_SERVICE_T ILCS_SERVICE_T; + +struct ILCS_COMMON_T; +typedef struct ILCS_COMMON_T ILCS_COMMON_T; + +typedef void (*IL_FN_T)(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); + +typedef struct { + IL_FN_T *fns; + ILCS_COMMON_T *(*ilcs_common_init)(ILCS_SERVICE_T *); + void (*ilcs_common_deinit)(ILCS_COMMON_T *st); + void (*ilcs_thread_init)(ILCS_COMMON_T *st); + unsigned char *(*ilcs_mem_lock)(OMX_BUFFERHEADERTYPE *buffer); + void (*ilcs_mem_unlock)(OMX_BUFFERHEADERTYPE *buffer); +} ILCS_CONFIG_T; + +// initialise the VideoCore IL Component service +// returns pointer to state on success, NULL on failure +#ifdef USE_VCHIQ_ARM +VCHPRE_ ILCS_SERVICE_T VCHPOST_ *ilcs_init(VCHIQ_INSTANCE_T state, void **connection, ILCS_CONFIG_T *config, int use_memmgr); +#else +VCHPRE_ ILCS_SERVICE_T VCHPOST_ *ilcs_init(VCHIQ_STATE_T *state, void **connection, ILCS_CONFIG_T *config, int use_memmgr); +#endif + +// deinitialises the IL Component service +VCHPRE_ void VCHPOST_ ilcs_deinit(ILCS_SERVICE_T *ilcs); + +// returns 1 if the current thread is the ilcs thread, 0 otherwise +VCHPRE_ int VCHPOST_ ilcs_thread_current(void *param); + +// returns pointer to shared state +VCHPRE_ ILCS_COMMON_T *ilcs_get_common(ILCS_SERVICE_T *ilcs); + +VCHPRE_ int VCHPOST_ ilcs_execute_function(ILCS_SERVICE_T *ilcs, IL_FUNCTION_T func, void *data, int len, void *resp, int *rlen); +VCHPRE_ OMX_ERRORTYPE VCHPOST_ ilcs_pass_buffer(ILCS_SERVICE_T *ilcs, IL_FUNCTION_T func, void *reference, OMX_BUFFERHEADERTYPE *pBuffer); +VCHPRE_ OMX_BUFFERHEADERTYPE * VCHPOST_ ilcs_receive_buffer(ILCS_SERVICE_T *ilcs, void *call, int clen, OMX_COMPONENTTYPE **pComp); + +// bulks are 16 bytes aligned, implicit in use of vchiq +#define ILCS_ALIGN 16 + +#define ILCS_ROUND_UP(x) ((((unsigned long)(x))+ILCS_ALIGN-1) & ~(ILCS_ALIGN-1)) +#define ILCS_ROUND_DOWN(x) (((unsigned long)(x)) & ~(ILCS_ALIGN-1)) +#define ILCS_ALIGNED(x) (((unsigned long)(x) & (ILCS_ALIGN-1)) == 0) + + +#ifdef _VIDEOCORE +#include "vcfw/logging/logging.h" + +#ifdef ILCS_LOGGING + +#define LOG_MSG ILCS_LOGGING +extern void ilcs_log_event_handler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, + OMX_U32 nData1,OMX_U32 nData2,OMX_PTR pEventData); + +#else + +#define LOG_MSG LOGGING_GENERAL +#define ilcs_log_event_handler(...) +extern void dummy_logging_message(int level, const char *format, ...); +#undef logging_message +#define logging_message if (1) {} else dummy_logging_message + +#endif // ILCS_LOGGING +#endif // _VIDEOCORE + +#endif // ILCS_H + diff --git a/interface/vmcs_host/vcilcs_common.c b/interface/vmcs_host/vcilcs_common.c new file mode 100755 index 0000000..ada38da --- /dev/null +++ b/interface/vmcs_host/vcilcs_common.c @@ -0,0 +1,122 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" + +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vcilcs_common.h" + +static IL_FN_T vcilcs_fns[] = {NULL, // response + NULL, // create component + + vcil_in_get_component_version, + NULL, // send command + vcil_in_get_parameter, + vcil_in_set_parameter, + vcil_in_get_config, + vcil_in_set_config, + vcil_in_get_extension_index, + vcil_in_get_state, + NULL, // tunnel request + vcil_in_use_buffer, + NULL, // use egl image + NULL, // allocate buffer + vcil_in_free_buffer, + vcil_in_empty_this_buffer, + vcil_in_fill_this_buffer, + NULL, // set callbacks + NULL, // component role enum + + NULL, // deinit + + vcil_out_event_handler, + vcil_out_empty_buffer_done, + vcil_out_fill_buffer_done, + + NULL, // component name enum + NULL, // get debug information + + NULL +}; + +static ILCS_COMMON_T *vcilcs_common_init(ILCS_SERVICE_T *ilcs) +{ + ILCS_COMMON_T *st; + + st = vcos_malloc(sizeof(ILCS_COMMON_T), "ILCS_HOST_COMMON"); + if(!st) + return NULL; + + if(vcos_semaphore_create(&st->component_lock, "ILCS", 1) != VCOS_SUCCESS) + { + vcos_free(st); + return NULL; + } + + st->ilcs = ilcs; + st->component_list = NULL; + return st; +} + +static void vcilcs_common_deinit(ILCS_COMMON_T *st) +{ + vcos_semaphore_delete(&st->component_lock); + + while(st->component_list) + { + VC_PRIVATE_COMPONENT_T *comp = st->component_list; + st->component_list = comp->next; + vcos_free(comp); + } + + vcos_free(st); +} + +static void vcilcs_thread_init(ILCS_COMMON_T *st) +{ +} + +static unsigned char *vcilcs_mem_lock(OMX_BUFFERHEADERTYPE *buffer) +{ + return buffer->pBuffer; +} + +static void vcilcs_mem_unlock(OMX_BUFFERHEADERTYPE *buffer) +{ +} + + +void vcilcs_config(ILCS_CONFIG_T *config) +{ + config->fns = vcilcs_fns; + config->ilcs_common_init = vcilcs_common_init; + config->ilcs_common_deinit = vcilcs_common_deinit; + config->ilcs_thread_init = vcilcs_thread_init; + config->ilcs_mem_lock = vcilcs_mem_lock; + config->ilcs_mem_unlock = vcilcs_mem_unlock; +} + diff --git a/interface/vmcs_host/vcilcs_common.h b/interface/vmcs_host/vcilcs_common.h new file mode 100755 index 0000000..0659466 --- /dev/null +++ b/interface/vmcs_host/vcilcs_common.h @@ -0,0 +1,83 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// OpenMAX IL Component Service common - Host side header + +typedef struct { + OMX_U32 port; + IL_FUNCTION_T func; + OMX_BOOL bEGL; + OMX_U32 numBuffers; + OMX_DIRTYPE dir; +} VC_PRIVATE_PORT_T; + +struct _VC_PRIVATE_COMPONENT_T { + OMX_COMPONENTTYPE *comp; + void *reference; + OMX_U32 numPorts; + OMX_CALLBACKTYPE callbacks; + OMX_PTR callback_state; + VC_PRIVATE_PORT_T *port; + struct _VC_PRIVATE_COMPONENT_T *next; +}; +typedef struct _VC_PRIVATE_COMPONENT_T VC_PRIVATE_COMPONENT_T; + +struct ILCS_COMMON_T { + VCOS_SEMAPHORE_T component_lock; + VC_PRIVATE_COMPONENT_T *component_list; + ILCS_SERVICE_T *ilcs; +}; + +VCHPRE_ void VCHPOST_ vcilcs_config(ILCS_CONFIG_T *config); + +// functions that implement incoming functions calls +// from VideoCore components to host based components +VCHPRE_ void VCHPOST_ vcil_in_get_state(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_get_parameter(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_set_parameter(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_get_config(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_set_config(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_use_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_free_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_empty_this_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_fill_this_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_get_component_version(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_get_extension_index(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_in_component_role_enum(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); + +// functions that implement callbacks from VideoCore +// components to the host core. +// The prefix is vcil_out since they implement part +// of the API that the host uses out to VideoCore +VCHPRE_ void VCHPOST_ vcil_out_event_handler(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_out_empty_buffer_done(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); +VCHPRE_ void VCHPOST_ vcil_out_fill_buffer_done(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen); + +// functions used by the host IL core +VCHPRE_ OMX_ERRORTYPE VCHPOST_ vcil_out_get_debug_information(ILCS_COMMON_T *st, OMX_STRING debugInfo, OMX_S32 *pLen); +VCHPRE_ OMX_ERRORTYPE VCHPOST_ vcil_out_create_component(ILCS_COMMON_T *st, OMX_HANDLETYPE hComponent, OMX_STRING component_name); +VCHPRE_ OMX_ERRORTYPE VCHPOST_ vcil_out_component_name_enum(ILCS_COMMON_T *st, OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex); diff --git a/interface/vmcs_host/vcilcs_in.c b/interface/vmcs_host/vcilcs_in.c new file mode 100755 index 0000000..15efebc --- /dev/null +++ b/interface/vmcs_host/vcilcs_in.c @@ -0,0 +1,271 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" + +#include "interface/vmcs_host/vc_ilcs_defs.h" +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vcilcs_common.h" + +#ifndef NDEBUG +static int is_valid_hostside_buffer(OMX_BUFFERHEADERTYPE *pBuf) +{ + if (!pBuf->pBuffer) + return 0; + if ((unsigned long)pBuf->pBuffer < 0x100) + return 0; // not believable + return 1; +} +#endif + +void vcil_in_get_state(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_EXECUTE_HEADER_T *exe = call; + IL_GET_STATE_RESPONSE_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + + *rlen = sizeof(IL_GET_STATE_RESPONSE_T); + ret->func = IL_GET_STATE; + ret->err = pComp->GetState(pComp, &ret->state); +} + +void vcil_in_get_parameter(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_GET_EXECUTE_T *exe = call; + IL_GET_RESPONSE_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + OMX_U32 size = *((OMX_U32 *) (&exe->param)); + + ret->func = IL_GET_PARAMETER; + + if(size > VC_ILCS_MAX_PARAM_SIZE) + { + *rlen = IL_GET_RESPONSE_HEADER_SIZE; + ret->err = OMX_ErrorHardware; + } + else + { + *rlen = size + IL_GET_RESPONSE_HEADER_SIZE; + ret->err = pComp->GetParameter(pComp, exe->index, exe->param); + memcpy(ret->param, exe->param, size); + } +} + +void vcil_in_set_parameter(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_SET_EXECUTE_T *exe = call; + IL_RESPONSE_HEADER_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + + *rlen = sizeof(IL_RESPONSE_HEADER_T); + ret->func = IL_SET_PARAMETER; + ret->err = pComp->SetParameter(pComp, exe->index, exe->param); +} + +void vcil_in_get_config(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_GET_EXECUTE_T *exe = call; + IL_GET_RESPONSE_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + OMX_U32 size = *((OMX_U32 *) (&exe->param)); + + ret->func = IL_GET_CONFIG; + + if(size > VC_ILCS_MAX_PARAM_SIZE) + { + *rlen = IL_GET_RESPONSE_HEADER_SIZE; + ret->err = OMX_ErrorHardware; + } + else + { + *rlen = size + IL_GET_RESPONSE_HEADER_SIZE; + ret->func = IL_GET_CONFIG; + ret->err = pComp->GetConfig(pComp, exe->index, exe->param); + memcpy(ret->param, exe->param, size); + } +} + +void vcil_in_set_config(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_SET_EXECUTE_T *exe = call; + IL_RESPONSE_HEADER_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + + *rlen = sizeof(IL_RESPONSE_HEADER_T); + ret->func = IL_SET_CONFIG; + ret->err = pComp->SetConfig(pComp, exe->index, exe->param); +} + +void vcil_in_use_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_ADD_BUFFER_EXECUTE_T *exe = call; + IL_ADD_BUFFER_RESPONSE_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + OMX_U8 *buffer; + OMX_BUFFERHEADERTYPE *bufferHeader; + + *rlen = sizeof(IL_ADD_BUFFER_RESPONSE_T); + + buffer = vcos_malloc_aligned(exe->size, 32, "vcin mapping buffer"); // 32-byte aligned + if (!buffer) + { + ret->err = OMX_ErrorInsufficientResources; + return; + } + + //OMX_OSAL_Trace(OMX_OSAL_TRACE_COMPONENT, "hostcomp: use buffer(%p)\n", buffer); + ret->func = IL_USE_BUFFER; + ret->err = pComp->UseBuffer(pComp, &bufferHeader, exe->port, exe->bufferReference, exe->size, buffer); + + if (ret->err == OMX_ErrorNone) + { + // we're going to pass this buffer to VC + // initialise our private field in their copy with the host buffer reference + OMX_PARAM_PORTDEFINITIONTYPE def; + OMX_ERRORTYPE error; + def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + def.nVersion.nVersion = OMX_VERSION; + def.nPortIndex = exe->port; + error = pComp->GetParameter(pComp, OMX_IndexParamPortDefinition, &def); + vc_assert(error == OMX_ErrorNone); + + ret->reference = bufferHeader; + memcpy(&ret->bufferHeader, bufferHeader, sizeof(OMX_BUFFERHEADERTYPE)); + + if (def.eDir == OMX_DirInput) + ret->bufferHeader.pInputPortPrivate = bufferHeader; + else + ret->bufferHeader.pOutputPortPrivate = bufferHeader; + } + else + vcos_free(buffer); +} + +void vcil_in_free_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_FREE_BUFFER_EXECUTE_T *exe = call; + IL_RESPONSE_HEADER_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + OMX_BUFFERHEADERTYPE *pHeader; + OMX_U8 *buffer; + OMX_PARAM_PORTDEFINITIONTYPE def; + OMX_ERRORTYPE error; + + def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + def.nVersion.nVersion = OMX_VERSION; + def.nPortIndex = exe->port; + error = pComp->GetParameter(pComp, OMX_IndexParamPortDefinition, &def); + vc_assert(error == OMX_ErrorNone); + if (def.eDir == OMX_DirInput) + pHeader = exe->inputPrivate; + else + pHeader = exe->outputPrivate; + + buffer = pHeader->pBuffer; + + *rlen = sizeof(IL_RESPONSE_HEADER_T); + ret->func = IL_FREE_BUFFER; + ret->err = pComp->FreeBuffer(pComp, exe->port, pHeader); + if (ret->err == OMX_ErrorNone) + vcos_free(buffer); +} + +void vcil_in_empty_this_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_RESPONSE_HEADER_T *ret = resp; + OMX_COMPONENTTYPE *pComp; + OMX_BUFFERHEADERTYPE *pHeader; + + pHeader = ilcs_receive_buffer(st->ilcs, call, clen, &pComp); + + *rlen = sizeof(IL_RESPONSE_HEADER_T); + ret->func = IL_EMPTY_THIS_BUFFER; + + if(pHeader) + { + vc_assert(is_valid_hostside_buffer(pHeader)); + ret->err = pComp->EmptyThisBuffer(pComp, pHeader); + } + else + ret->err = OMX_ErrorHardware; +} + +void vcil_in_fill_this_buffer(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_PASS_BUFFER_EXECUTE_T *exe = call; + IL_RESPONSE_HEADER_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + OMX_BUFFERHEADERTYPE *pHeader = exe->bufferHeader.pOutputPortPrivate; + OMX_U8 *pBuffer = pHeader->pBuffer; + OMX_PTR *pAppPrivate = pHeader->pAppPrivate; + OMX_PTR *pPlatformPrivate = pHeader->pPlatformPrivate; + OMX_PTR *pInputPortPrivate = pHeader->pInputPortPrivate; + OMX_PTR *pOutputPortPrivate = pHeader->pOutputPortPrivate; + + vc_assert(pHeader); + memcpy(pHeader, &exe->bufferHeader, sizeof(OMX_BUFFERHEADERTYPE)); + + pHeader->pBuffer = pBuffer; + pHeader->pAppPrivate = pAppPrivate; + pHeader->pPlatformPrivate = pPlatformPrivate; + pHeader->pInputPortPrivate = pInputPortPrivate; + pHeader->pOutputPortPrivate = pOutputPortPrivate; + + vc_assert(is_valid_hostside_buffer(pHeader)); + + *rlen = sizeof(IL_RESPONSE_HEADER_T); + ret->func = IL_FILL_THIS_BUFFER; + ret->err = pComp->FillThisBuffer(pComp, pHeader); +} + +void vcil_in_get_component_version(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_EXECUTE_HEADER_T *exe = call; + IL_GET_VERSION_RESPONSE_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + + *rlen = sizeof(IL_GET_VERSION_RESPONSE_T); + ret->func = IL_GET_COMPONENT_VERSION; + ret->err = pComp->GetComponentVersion(pComp, ret->name, &ret->component_version, &ret->spec_version, &ret->uuid); +} + +void vcil_in_get_extension_index(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_GET_EXTENSION_EXECUTE_T *exe = call; + IL_GET_EXTENSION_RESPONSE_T *ret = resp; + OMX_COMPONENTTYPE *pComp = exe->reference; + + *rlen = sizeof(IL_GET_EXTENSION_RESPONSE_T); + ret->func = IL_GET_EXTENSION_INDEX; + ret->err = pComp->GetExtensionIndex(pComp, exe->name, &ret->index); +} diff --git a/interface/vmcs_host/vcilcs_out.c b/interface/vmcs_host/vcilcs_out.c new file mode 100755 index 0000000..545ddc7 --- /dev/null +++ b/interface/vmcs_host/vcilcs_out.c @@ -0,0 +1,917 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "interface/vchi/vchi.h" +#include "interface/vcos/vcos_dlfcn.h" +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/khronos/IL/OMX_ILCS.h" +#include "interface/vmcs_host/vc_ilcs_defs.h" +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vcilcs_common.h" +#include "interface/vcos/vcos_dlfcn.h" + +static VC_PRIVATE_PORT_T *find_port(VC_PRIVATE_COMPONENT_T *comp, OMX_U32 nPortIndex) +{ + OMX_U32 i=0; + while (inumPorts && comp->port[i].port != nPortIndex) + i++; + + if (i < comp->numPorts) + return &comp->port[i]; + + return NULL; +} + +#ifndef NDEBUG +static int is_valid_hostside_buffer(OMX_BUFFERHEADERTYPE *pBuf) +{ + if (!pBuf) + return 0; + if (!pBuf->pBuffer) + return 0; + if ((unsigned long)pBuf->pBuffer < 0x100) + return 0; // not believable + return 1; +} +#endif + +static OMX_ERRORTYPE vcil_out_ComponentDeInit(OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_EXECUTE_HEADER_T exe; + IL_RESPONSE_HEADER_T resp; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if(!pComp) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + + if(ilcs_execute_function(st->ilcs, IL_COMPONENT_DEINIT, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp) || + resp.err == OMX_ErrorNone) + { + // remove from list, assuming that we successfully managed to deinit + // this component, or that ilcs has returned an error. The assumption + // here is that if the component has managed to correctly signal an + // error, it still exists, but if the transport has failed then we might + // as well try and cleanup + VC_PRIVATE_COMPONENT_T *list, *prev; + + vcos_semaphore_wait(&st->component_lock); + + list = st->component_list; + prev = NULL; + + while (list != NULL && list != comp) + { + prev = list; + list = list->next; + } + + // failing to find this component is not a good sign. + if(vcos_verify(list)) + { + if (prev == NULL) + st->component_list = list->next; + else + prev->next = list->next; + } + + vcos_semaphore_post(&st->component_lock); + vcos_free(comp); + } + + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_GetComponentVersion(OMX_IN OMX_HANDLETYPE hComponent, + OMX_OUT OMX_STRING pComponentName, + OMX_OUT OMX_VERSIONTYPE* pComponentVersion, + OMX_OUT OMX_VERSIONTYPE* pSpecVersion, + OMX_OUT OMX_UUIDTYPE* pComponentUUID) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_EXECUTE_HEADER_T exe; + IL_GET_VERSION_RESPONSE_T resp; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && pComponentName && pComponentVersion && pSpecVersion && pComponentUUID)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + + if(ilcs_execute_function(st->ilcs, IL_GET_COMPONENT_VERSION, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + strncpy(pComponentName, resp.name, 128); + pComponentName[127] = 0; + *pComponentVersion = resp.component_version; + *pSpecVersion = resp.spec_version; + memcpy(pComponentUUID, resp.uuid, sizeof(OMX_UUIDTYPE)); + + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_SetCallbacks(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_CALLBACKTYPE* pCallbacks, + OMX_IN OMX_PTR pAppData) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_SET_CALLBACKS_EXECUTE_T exe; + IL_RESPONSE_HEADER_T resp; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if(!(pComp && pCallbacks)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + comp->callbacks = *pCallbacks; + comp->callback_state = pAppData; + + exe.reference = comp->reference; + exe.pAppData = pComp; + + if(ilcs_execute_function(st->ilcs, IL_SET_CALLBACKS, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_GetState(OMX_IN OMX_HANDLETYPE hComponent, + OMX_OUT OMX_STATETYPE* pState) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_EXECUTE_HEADER_T exe; + IL_GET_STATE_RESPONSE_T resp; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && pState)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + + if(ilcs_execute_function(st->ilcs, IL_GET_STATE, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + *pState = resp.state; + + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_get(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_INOUT OMX_PTR pComponentParameterStructure, + IL_FUNCTION_T func) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_GET_EXECUTE_T exe; + IL_GET_RESPONSE_T resp; + OMX_U32 size; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && pComponentParameterStructure)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + exe.index = nParamIndex; + + size = *((OMX_U32 *) pComponentParameterStructure); + + if(size > VC_ILCS_MAX_PARAM_SIZE) + return OMX_ErrorHardware; + + memcpy(exe.param, pComponentParameterStructure, size); + + if(ilcs_execute_function(st->ilcs, func, &exe, size + IL_GET_EXECUTE_HEADER_SIZE, &resp, &rlen) < 0 || rlen > sizeof(resp)) + return OMX_ErrorHardware; + + memcpy(pComponentParameterStructure, resp.param, size); + + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_set(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_IN OMX_PTR pComponentParameterStructure, + IL_FUNCTION_T func) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_SET_EXECUTE_T exe; + IL_RESPONSE_HEADER_T resp; + OMX_U32 size; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && pComponentParameterStructure)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + exe.index = nParamIndex; + + size = *((OMX_U32 *) pComponentParameterStructure); + + if(size > VC_ILCS_MAX_PARAM_SIZE) + return OMX_ErrorHardware; + + memcpy(exe.param, pComponentParameterStructure, size); + + if(ilcs_execute_function(st->ilcs, func, &exe, size + IL_SET_EXECUTE_HEADER_SIZE, &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_GetParameter(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_INOUT OMX_PTR pComponentParameterStructure) +{ + return vcil_out_get(hComponent, nParamIndex, pComponentParameterStructure, IL_GET_PARAMETER); +} + +static OMX_ERRORTYPE vcil_out_SetParameter(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_IN OMX_PTR pComponentParameterStructure) +{ + return vcil_out_set(hComponent, nParamIndex, pComponentParameterStructure, IL_SET_PARAMETER); +} + +static OMX_ERRORTYPE vcil_out_GetConfig(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_INOUT OMX_PTR pComponentParameterStructure) +{ + return vcil_out_get(hComponent, nParamIndex, pComponentParameterStructure, IL_GET_CONFIG); +} + +static OMX_ERRORTYPE vcil_out_SetConfig(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_INDEXTYPE nParamIndex, + OMX_IN OMX_PTR pComponentParameterStructure) +{ + return vcil_out_set(hComponent, nParamIndex, pComponentParameterStructure, IL_SET_CONFIG); +} + +static OMX_ERRORTYPE vcil_out_SendCommand(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_COMMANDTYPE Cmd, + OMX_IN OMX_U32 nParam1, + OMX_IN OMX_PTR pCmdData) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_SEND_COMMAND_EXECUTE_T exe; + IL_RESPONSE_HEADER_T resp; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!pComp) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + exe.cmd = Cmd; + exe.param = nParam1; + + if (Cmd == OMX_CommandMarkBuffer) + { + exe.mark = *((OMX_MARKTYPE *) pCmdData); + } + else + { + exe.mark.hMarkTargetComponent = 0; + exe.mark.pMarkData = 0; + } + + if(ilcs_execute_function(st->ilcs, IL_SEND_COMMAND, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + return resp.err; +} + +// Called to pass a buffer from the host-side across the interface to videcore. + +static OMX_ERRORTYPE vcil_out_addBuffer(OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN OMX_U32 nSizeBytes, + OMX_IN OMX_U8* pBuffer, + OMX_IN void *eglImage, + IL_FUNCTION_T func) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_ADD_BUFFER_EXECUTE_T exe; + IL_ADD_BUFFER_RESPONSE_T resp; + OMX_BUFFERHEADERTYPE *pHeader; + VC_PRIVATE_PORT_T *port; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && ppBufferHdr)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + port = find_port(comp, nPortIndex); + if (!port) // bad port index + return OMX_ErrorBadPortIndex; + + if (port->numBuffers > 0 && port->func != func) + { + // inconsistent use of usebuffer/allocatebuffer/eglimage + // all ports must receive all buffers by exactly one of these methods + vc_assert(port->func != func); + return OMX_ErrorInsufficientResources; + } + port->func = func; + + if (!VCHI_BULK_ALIGNED(pBuffer)) + { + // cannot transfer this buffer across the host interface + return OMX_ErrorBadParameter; + } + + pHeader = vcos_malloc(sizeof(*pHeader), "vcout buffer header"); + + if (!pHeader) + return OMX_ErrorInsufficientResources; + + if (func == IL_ALLOCATE_BUFFER) + { + pBuffer = vcos_malloc_aligned(nSizeBytes, ILCS_ALIGN, "vcout mapping buffer"); + if (!pBuffer) + { + vcos_free(pHeader); + return OMX_ErrorInsufficientResources; + } + } + + exe.reference = comp->reference; + exe.bufferReference = pHeader; + exe.port = nPortIndex; + exe.size = nSizeBytes; + exe.eglImage = eglImage; + + if(ilcs_execute_function(st->ilcs, func, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + resp.err = OMX_ErrorHardware; + + if (resp.err == OMX_ErrorNone) + { + memcpy(pHeader, &resp.bufferHeader, sizeof(OMX_BUFFERHEADERTYPE)); + if (port->dir == OMX_DirOutput) + pHeader->pOutputPortPrivate = resp.reference; + else + pHeader->pInputPortPrivate = resp.reference; + + if (func == IL_USE_EGL_IMAGE) + { + pHeader->pBuffer = (OMX_U8*)eglImage; + port->bEGL = OMX_TRUE; + } + else + { + pHeader->pBuffer = pBuffer; + port->bEGL = OMX_FALSE; + } + + pHeader->pAppPrivate = pAppPrivate; + *ppBufferHdr = pHeader; + port->numBuffers++; + } + else + { + if (func == IL_ALLOCATE_BUFFER) + vcos_free(pBuffer); + vcos_free(pHeader); + } + + return resp.err; +} + +static VCOS_ONCE_T loaded_eglIntOpenMAXILDoneMarker = VCOS_ONCE_INIT; +static int (*local_eglIntOpenMAXILDoneMarker) (void* component_handle, void *egl_image) = NULL; + +static void load_eglIntOpenMAXILDoneMarker(void) +{ + void *handle; + + /* First try to load from the current process, this will succeed + * if something that is linked to libEGL is already loaded or + * something explicitly loaded libEGL with RTLD_GLOBAL + */ + handle = vcos_dlopen(NULL, VCOS_DL_GLOBAL); + local_eglIntOpenMAXILDoneMarker = (void * )vcos_dlsym(handle, "eglIntOpenMAXILDoneMarker"); + if (local_eglIntOpenMAXILDoneMarker == NULL) + { + vcos_dlclose(handle); + /* If that failed try to load libEGL.so explicitely */ + handle = vcos_dlopen("libEGL.so", VCOS_DL_LAZY | VCOS_DL_LOCAL); + vc_assert(handle != NULL); + local_eglIntOpenMAXILDoneMarker = (void * )vcos_dlsym(handle, "eglIntOpenMAXILDoneMarker"); + vc_assert(local_eglIntOpenMAXILDoneMarker != NULL); + } +} + +static OMX_ERRORTYPE vcil_out_UseEGLImage(OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN void* eglImage) +{ + /* Load eglIntOpenMAXILDoneMarker() and libEGL here, it will be needed later */ + vcos_once(&loaded_eglIntOpenMAXILDoneMarker, load_eglIntOpenMAXILDoneMarker); + + return vcil_out_addBuffer(hComponent, ppBufferHdr, nPortIndex, pAppPrivate, 0, NULL, eglImage, IL_USE_EGL_IMAGE); +} + +static OMX_ERRORTYPE vcil_out_UseBuffer(OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN OMX_U32 nSizeBytes, + OMX_IN OMX_U8* pBuffer) +{ + return vcil_out_addBuffer(hComponent, ppBufferHdr, nPortIndex, pAppPrivate, nSizeBytes, pBuffer, NULL, IL_USE_BUFFER); +} + +static OMX_ERRORTYPE vcil_out_AllocateBuffer(OMX_IN OMX_HANDLETYPE hComponent, + OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_PTR pAppPrivate, + OMX_IN OMX_U32 nSizeBytes) +{ + return vcil_out_addBuffer(hComponent, ppBufferHdr, nPortIndex, pAppPrivate, nSizeBytes, NULL, NULL, IL_ALLOCATE_BUFFER); +} + +static OMX_ERRORTYPE vcil_out_FreeBuffer(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_U32 nPortIndex, + OMX_IN OMX_BUFFERHEADERTYPE* pBufferHdr) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_FREE_BUFFER_EXECUTE_T exe; + IL_RESPONSE_HEADER_T resp; + VC_PRIVATE_PORT_T *port; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && pBufferHdr)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + port = find_port(comp, nPortIndex); + if (!port) + return OMX_ErrorBadPortIndex; + + if (port->numBuffers == 0) + return OMX_ErrorIncorrectStateTransition; + + exe.reference = comp->reference; + exe.port = nPortIndex; + if (port->dir == OMX_DirOutput) + exe.bufferReference = pBufferHdr->pOutputPortPrivate; + else + exe.bufferReference = pBufferHdr->pInputPortPrivate; + exe.func = port->func; + exe.inputPrivate = NULL; + exe.outputPrivate = NULL; + + if(ilcs_execute_function(st->ilcs, IL_FREE_BUFFER, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + if (resp.err == OMX_ErrorNone) + { + if (port->func == IL_ALLOCATE_BUFFER) + vcos_free(pBufferHdr->pBuffer); + vcos_free(pBufferHdr); + port->numBuffers--; + } + + return resp.err; +} + +// Called on host-side to pass a buffer to VideoCore to be emptied +static OMX_ERRORTYPE vcil_out_EmptyThisBuffer(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + ILCS_COMMON_T *st; + + if (!(pComp && pBuffer)) + return (OMX_ErrorBadParameter); + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + return ilcs_pass_buffer(st->ilcs, IL_EMPTY_THIS_BUFFER, comp->reference, pBuffer); +} + +// Called from ril_top as OMX_FillThisBuffer(). +// ->pBuffer field is expected to be a memory handle. + +static OMX_ERRORTYPE vcil_out_FillThisBuffer(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + OMX_ERRORTYPE err; + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + VC_PRIVATE_PORT_T *port; + ILCS_COMMON_T *st; + + if (!(pComp && pBuffer)) + return (OMX_ErrorBadParameter); + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + port = find_port(comp, pBuffer->nOutputPortIndex); + if(!port) + return OMX_ErrorBadPortIndex; + + if(pBuffer->pBuffer == 0) + return OMX_ErrorIncorrectStateOperation; + + vcos_assert(pComp != NULL && comp != NULL && port != NULL && st != NULL); + + // The lower layers will attempt to transfer the bytes specified if we don't + // clear these - callers should ideally do this themselves, but it is not + // mandated in the specification. + pBuffer->nFilledLen = 0; + pBuffer->nFlags = 0; + + vc_assert(port->bEGL == OMX_TRUE || is_valid_hostside_buffer(pBuffer)); + + err = ilcs_pass_buffer(st->ilcs, IL_FILL_THIS_BUFFER, comp->reference, pBuffer); + + if (err == OMX_ErrorNone && port->bEGL == OMX_TRUE) + { + // If an output port is marked as an EGL port, we request EGL to notify the IL component + // when it's allowed to render into the buffer/EGLImage. + vc_assert(local_eglIntOpenMAXILDoneMarker != NULL); + local_eglIntOpenMAXILDoneMarker(comp->reference, pBuffer->pBuffer); + } + + return err; +} + +static OMX_ERRORTYPE vcil_out_ComponentTunnelRequest(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_U32 nPort, + OMX_IN OMX_HANDLETYPE hTunneledComp, + OMX_IN OMX_U32 nTunneledPort, + OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_TUNNEL_REQUEST_EXECUTE_T exe; + IL_TUNNEL_REQUEST_RESPONSE_T resp; + VC_PRIVATE_COMPONENT_T *list; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if(!pComp) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + exe.port = nPort; + exe.tunnel_port = nTunneledPort; + if (pTunnelSetup) + exe.setup = *pTunnelSetup; + + // the other component may be on the host or on VC. Look through our list + // so we can tell, and tell ILCS on VC the details. + vcos_semaphore_wait(&st->component_lock); + + list = st->component_list; + while (list != NULL && list->comp != (void *) hTunneledComp) + list = list->next; + + vcos_semaphore_post(&st->component_lock); + + if (list == NULL) + { + exe.tunnel_ref = hTunneledComp; + exe.tunnel_host = OMX_TRUE; + } + else + { + exe.tunnel_ref = list->reference; + exe.tunnel_host = OMX_FALSE; + } + + if(ilcs_execute_function(st->ilcs, IL_COMPONENT_TUNNEL_REQUEST, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + if (pTunnelSetup) + *pTunnelSetup = resp.setup; + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_GetExtensionIndex(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_STRING cParameterName, + OMX_OUT OMX_INDEXTYPE* pIndexType) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp; + IL_GET_EXTENSION_EXECUTE_T exe; + IL_GET_EXTENSION_RESPONSE_T resp; + ILCS_COMMON_T *st; + int rlen = sizeof(resp); + + if (!(pComp && cParameterName && pIndexType)) + return OMX_ErrorBadParameter; + + st = pComp->pApplicationPrivate; + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + exe.reference = comp->reference; + strncpy(exe.name, cParameterName, 128); + exe.name[127] = 0; + + if(ilcs_execute_function(st->ilcs, IL_GET_EXTENSION_INDEX, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + *pIndexType = resp.index; + return resp.err; +} + +static OMX_ERRORTYPE vcil_out_ComponentRoleEnum(OMX_IN OMX_HANDLETYPE hComponent, + OMX_OUT OMX_U8 *cRole, + OMX_IN OMX_U32 nIndex) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + VC_PRIVATE_COMPONENT_T *comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + IL_COMPONENT_ROLE_ENUM_EXECUTE_T exe; + IL_COMPONENT_ROLE_ENUM_RESPONSE_T resp; + ILCS_COMMON_T *st = pComp->pApplicationPrivate; + int rlen = sizeof(resp); + + exe.reference = comp->reference; + exe.index = nIndex; + + if(ilcs_execute_function(st->ilcs, IL_COMPONENT_ROLE_ENUM, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + strncpy((char *) cRole, (char *) resp.role, 128); + cRole[127] = 0; + return resp.err; +} + +OMX_ERRORTYPE vcil_out_component_name_enum(ILCS_COMMON_T *st, OMX_STRING cComponentName, OMX_U32 nNameLength, OMX_U32 nIndex) +{ + IL_COMPONENT_NAME_ENUM_EXECUTE_T exe; + IL_COMPONENT_NAME_ENUM_RESPONSE_T resp; + int rlen = sizeof(resp); + + exe.index = nIndex; + + if(ilcs_execute_function(st->ilcs, IL_COMPONENT_NAME_ENUM, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + if (sizeof(resp.name) < nNameLength) + nNameLength = sizeof(resp.name); + + strncpy((char *)cComponentName, (char *) resp.name, nNameLength); + cComponentName[127] = 0; + return resp.err; +} + +OMX_ERRORTYPE vcil_out_get_debug_information(ILCS_COMMON_T *st, OMX_STRING debugInfo, OMX_S32 *pLen) +{ + IL_GET_DEBUG_INFORMATION_EXECUTE_T exe; + + exe.len = *pLen; + + if(ilcs_execute_function(st->ilcs, IL_GET_DEBUG_INFORMATION, &exe, sizeof(exe), debugInfo, (int *) pLen) < 0) + return OMX_ErrorHardware; + + return OMX_ErrorNone; +} + +// Called on the host side to create an OMX component. +OMX_ERRORTYPE vcil_out_create_component(ILCS_COMMON_T *st, OMX_HANDLETYPE hComponent, OMX_STRING component_name) +{ + OMX_COMPONENTTYPE *pComp = (OMX_COMPONENTTYPE *) hComponent; + IL_CREATE_COMPONENT_EXECUTE_T exe; + IL_CREATE_COMPONENT_RESPONSE_T resp; + VC_PRIVATE_COMPONENT_T *comp; + OMX_U32 i; + int rlen = sizeof(resp); + + if (strlen(component_name) >= sizeof(exe.name)) + return OMX_ErrorInvalidComponent; + + strcpy(exe.name, component_name); + exe.mark = pComp; + + if(ilcs_execute_function(st->ilcs, IL_CREATE_COMPONENT, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) + return OMX_ErrorHardware; + + if (resp.err != OMX_ErrorNone) + return resp.err; + + comp = vcos_malloc(sizeof(VC_PRIVATE_COMPONENT_T) + (sizeof(VC_PRIVATE_PORT_T) * resp.numPorts), "ILCS Host Comp"); + if (!comp) + { + IL_EXECUTE_HEADER_T dexe; + IL_RESPONSE_HEADER_T dresp; + int dlen = sizeof(dresp); + + dexe.reference = resp.reference; + + ilcs_execute_function(st->ilcs, IL_COMPONENT_DEINIT, &dexe, sizeof(dexe), &dresp, &dlen); + return OMX_ErrorInsufficientResources; + } + + memset(comp, 0, sizeof(VC_PRIVATE_COMPONENT_T) + (sizeof(VC_PRIVATE_PORT_T) * resp.numPorts)); + + comp->reference = resp.reference; + comp->comp = pComp; + comp->numPorts = resp.numPorts; + comp->port = (VC_PRIVATE_PORT_T *) ((unsigned char *) comp + sizeof(VC_PRIVATE_COMPONENT_T)); + + for (i=0; inumPorts; i++) + { + if (i && !(i&0x1f)) + { + IL_GET_EXECUTE_T gexe; + IL_GET_RESPONSE_T gresp; + OMX_PARAM_PORTSUMMARYTYPE *summary; + int glen = sizeof(gresp); + + gexe.reference = comp->reference; + gexe.index = OMX_IndexParamPortSummary; + + summary = (OMX_PARAM_PORTSUMMARYTYPE *) &gexe.param; + summary->nSize = sizeof(OMX_PARAM_PORTSUMMARYTYPE); + summary->nVersion.nVersion = OMX_VERSION; + summary->reqSet = i>>5; + + ilcs_execute_function(st->ilcs, IL_GET_PARAMETER, &gexe, + sizeof(OMX_PARAM_PORTSUMMARYTYPE)+IL_GET_EXECUTE_HEADER_SIZE, + &gresp, &glen); + + summary = (OMX_PARAM_PORTSUMMARYTYPE *) &gresp.param; + resp.portDir = summary->portDir; + memcpy(resp.portIndex, summary->portIndex, sizeof(OMX_U32) * 32); + } + + comp->port[i].port = resp.portIndex[i&0x1f]; + comp->port[i].dir = ((resp.portDir >> (i&0x1f)) & 1) ? OMX_DirOutput : OMX_DirInput; + } + + vcos_semaphore_wait(&st->component_lock); + // insert into head of list + comp->next = st->component_list; + st->component_list = comp; + vcos_semaphore_post(&st->component_lock); + + pComp->pComponentPrivate = comp; + pComp->pApplicationPrivate = st; + + pComp->GetComponentVersion = vcil_out_GetComponentVersion; + pComp->ComponentDeInit = vcil_out_ComponentDeInit; + pComp->SetCallbacks = vcil_out_SetCallbacks; + pComp->GetState = vcil_out_GetState; + pComp->GetParameter = vcil_out_GetParameter; + pComp->SetParameter = vcil_out_SetParameter; + pComp->GetConfig = vcil_out_GetConfig; + pComp->SetConfig = vcil_out_SetConfig; + pComp->SendCommand = vcil_out_SendCommand; + pComp->UseBuffer = vcil_out_UseBuffer; + pComp->AllocateBuffer = vcil_out_AllocateBuffer; + pComp->FreeBuffer = vcil_out_FreeBuffer; + pComp->EmptyThisBuffer = vcil_out_EmptyThisBuffer; + pComp->FillThisBuffer = vcil_out_FillThisBuffer; + pComp->ComponentTunnelRequest = vcil_out_ComponentTunnelRequest; + pComp->GetExtensionIndex = vcil_out_GetExtensionIndex; + pComp->UseEGLImage = vcil_out_UseEGLImage; + pComp->ComponentRoleEnum = vcil_out_ComponentRoleEnum; + + return resp.err; +} + +/* callbacks */ + +void vcil_out_event_handler(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_EVENT_HANDLER_EXECUTE_T *exe = call; + OMX_COMPONENTTYPE *pComp = exe->reference; + VC_PRIVATE_COMPONENT_T *comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + *rlen = 0; + + vcos_assert(comp->callbacks.EventHandler); + comp->callbacks.EventHandler(pComp, comp->callback_state, exe->event, exe->data1, exe->data2, exe->eventdata); +} + +// Called on host side via RPC in response to empty buffer completing +void vcil_out_empty_buffer_done(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + IL_PASS_BUFFER_EXECUTE_T *exe = call; + OMX_COMPONENTTYPE *pComp = exe->reference; + VC_PRIVATE_COMPONENT_T *comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + OMX_BUFFERHEADERTYPE *pHeader = exe->bufferHeader.pOutputPortPrivate; + OMX_U8 *pBuffer = pHeader->pBuffer; + OMX_PTR *pAppPrivate = pHeader->pAppPrivate; + OMX_PTR *pPlatformPrivate = pHeader->pPlatformPrivate; + OMX_PTR *pInputPortPrivate = pHeader->pInputPortPrivate; + OMX_PTR *pOutputPortPrivate = pHeader->pOutputPortPrivate; + + memcpy(pHeader, &exe->bufferHeader, sizeof(OMX_BUFFERHEADERTYPE)); + + pHeader->pBuffer = pBuffer; + pHeader->pAppPrivate = pAppPrivate; + pHeader->pPlatformPrivate = pPlatformPrivate; + pHeader->pInputPortPrivate = pInputPortPrivate; + pHeader->pOutputPortPrivate = pOutputPortPrivate; + + *rlen = 0; + + vcos_assert(comp->callbacks.EmptyBufferDone); + comp->callbacks.EmptyBufferDone(pComp, comp->callback_state, pHeader); +} + +// Called on host side via RPC in response to a fill-buffer completing +// on the VideoCore side. ->pBuffer is a real pointer. +void vcil_out_fill_buffer_done(ILCS_COMMON_T *st, void *call, int clen, void *resp, int *rlen) +{ + OMX_COMPONENTTYPE *pComp; + VC_PRIVATE_COMPONENT_T *comp; + OMX_BUFFERHEADERTYPE *pHeader; + + pHeader = ilcs_receive_buffer(st->ilcs, call, clen, &pComp); + *rlen = 0; + + if(pHeader) + { + comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; + + vc_assert(comp->callbacks.FillBufferDone); + comp->callbacks.FillBufferDone(pComp, comp->callback_state, pHeader); + } +} diff --git a/makefiles/cmake/arm-linux.cmake b/makefiles/cmake/arm-linux.cmake new file mode 100755 index 0000000..957846a --- /dev/null +++ b/makefiles/cmake/arm-linux.cmake @@ -0,0 +1,130 @@ +# setup environment for cross compile to arm-linux + +if (DEFINED CMAKE_TOOLCHAIN_FILE) +else() + message(WARNING + " *********************************************************\n" + " * CMAKE_TOOLCHAIN_FILE not defined *\n" + " * This is correct for compiling on the Raspberry Pi *\n" + " * *\n" + " * If you are cross-compiling on some other machine *\n" + " * then DELETE the build directory and re-run with: *\n" + " * -DCMAKE_TOOLCHAIN_FILE=toolchain_file.cmake *\n" + " * *\n" + " * Toolchain files are in makefiles/cmake/toolchains. *\n" + " *********************************************************" + ) +endif() + +# pull in headers for android +if(ANDROID) + # + # work out where android headers and library are + # + + set(ANDROID_NDK_ROOT $ENV{ANDROID_NDK_ROOT} CACHE INTERNAL "" FORCE) + set(ANDROID_LIBS $ENV{ANDROID_LIBS} CACHE INTERNAL "" FORCE) + set(ANDROID_BIONIC $ENV{ANDROID_BIONIC} CACHE INTERNAL "" FORCE) + set(ANDROID_LDSCRIPTS $ENV{ANDROID_LDSCRIPTS} CACHE INTERNAL "" FORCE) + + if("${ANDROID_NDK_ROOT}" STREQUAL "") + find_program(ANDROID_COMPILER arm-eabi-gcc) + get_filename_component(ANDROID_BIN ${ANDROID_COMPILER} PATH CACHE) + find_path(_ANDROID_ROOT Makefile PATHS ${ANDROID_BIN} + PATH_SUFFIXES ../../../../.. + NO_DEFAULT_PATH) + if("${_ANDROID_ROOT}" STREQUAL "_ANDROID_ROOT-NOTFOUND") + set(_ANDROID_ROOT "" CACHE INTERNAL "" FORCE) + endif() + if("${_ANDROID_ROOT}" STREQUAL "") + message(FATAL_ERROR "Cannot find android root directory") + endif() + get_filename_component(ANDROID_ROOT ${_ANDROID_ROOT} ABSOLUTE CACHE) + # + # top level of cross-compiler target include and lib directory structure + # + set(ANDROID_NDK_ROOT + "${ANDROID_ROOT}/prebuilt/ndk" CACHE INTERNAL "" FORCE) + set(ANDROID_BIONIC + "${ANDROID_ROOT}/bionic" CACHE INTERNAL "" FORCE) + set(ANDROID_LDSCRIPTS + "${ANDROID_ROOT}/build/core" CACHE INTERNAL "" FORCE) + set(ANDROID_LIBS + "${ANDROID_ROOT}/out/target/product/${ANDROID_PRODUCT}/obj/lib" + CACHE INTERNAL "" FORCE) + endif() + + if("${ANDROID_NDK_ROOT}" STREQUAL "") + message(FATAL_ERROR "Cannot find Android NDK root directory") + endif() + if("${ANDROID_BIONIC}" STREQUAL "") + message(FATAL_ERROR "Cannot find Android BIONIC directory") + endif() + if("${ANDROID_LDSCRIPTS}" STREQUAL "") + message(FATAL_ERROR "Cannot find Android LD scripts directory") + endif() + + set(CMAKE_SYSTEM_PREFIX_PATH "${ANDROID_NDK_ROOT}/android-ndk-r${ANDROID_NDK_RELEASE}/platforms/android-${ANDROID_NDK_PLATFORM}/arch-${CMAKE_SYSTEM_PROCESSOR}/usr") + + if("${ANDROID_LIBS}" STREQUAL "") + set(ANDROID_LIBS "${CMAKE_SYSTEM_PREFIX_PATH}/lib" + CACHE INTERNAL "" FORCE) + # message(FATAL_ERROR "Cannot find android libraries") + endif() + + # + # add include directories for pthreads + # + include_directories("${CMAKE_SYSTEM_PREFIX_PATH}/include" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libc/include" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libc/include/arch-arm/include" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libc/kernel/arch-arm" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libc/kernel/common" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libm/include" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libm/include/arch/arm" BEFORE SYSTEM) + include_directories("${ANDROID_BIONIC}/libstdc++/include" BEFORE SYSTEM) + + + # + # Pull in Android link options manually + # + set(ANDROID_CRTBEGIN "${ANDROID_LIBS}/crtbegin_dynamic.o") + set(ANDROID_CRTEND "${ANDROID_LIBS}/crtend_android.o") + set(CMAKE_SHARED_LINKER_FLAGS "-nostdlib ${ANDROID_CRTBEGIN} -Wl,-Bdynamic -Wl,-T${ANDROID_LDSCRIPTS}/armelf.x") + + link_directories(${ANDROID_LIBS}) + set(CMAKE_EXE_LINKER_FLAGS "-nostdlib ${ANDROID_CRTBEGIN} -nostdlib -Wl,-z,noexecstack") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dynamic-linker,/system/bin/linker -Wl,-rpath,${CMAKE_INSTALL_PREFIX}/lib") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-T${ANDROID_LDSCRIPTS}/armelf.x -Wl,--gc-sections") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc -Wl,-z,noexecstack -Wl,--fix-cortex-a8 -Wl,--no-undefined") + + set(CMAKE_C_STANDARD_LIBRARIES "-llog -lc -lgcc ${ANDROID_CRTEND}" CACHE INTERNAL "" FORCE) + + set(SHARED "") +else() + set(SHARED "SHARED") +endif() + + +# All linux systems have sbrk() +add_definitions(-D_HAVE_SBRK) + +# pull in declarations of lseek64 and friends +add_definitions(-D_LARGEFILE64_SOURCE) + +# test for glibc malloc debugging extensions +try_compile(HAVE_MTRACE + ${CMAKE_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/makefiles/cmake/srcs/test-mtrace.c + OUTPUT_VARIABLE foo) + +# test for existence of execinfo.h header +include(CheckIncludeFile) +check_include_file(execinfo.h HAVE_EXECINFO_H) + +add_definitions(-DHAVE_CMAKE_CONFIG) +configure_file ( + "makefiles/cmake/cmake_config.h.in" + "${PROJECT_BINARY_DIR}/cmake_config.h" + ) + diff --git a/makefiles/cmake/cmake_config.h.in b/makefiles/cmake/cmake_config.h.in new file mode 100755 index 0000000..080b8e8 --- /dev/null +++ b/makefiles/cmake/cmake_config.h.in @@ -0,0 +1,15 @@ +#ifndef CMAKE_CONFIG_H +#define CMAKE_CONFIG_H + +/* + * Autogenerated by cmake from makefiles/cmake/cmake_config.h.in + */ + +/** Do we have support for GLIBC mtrace() ? */ +#cmakedefine HAVE_MTRACE + +/** Do we have the execinfo.h include file ? */ +#cmakedefine HAVE_EXECINFO_H + +#endif + diff --git a/makefiles/cmake/global_settings.cmake b/makefiles/cmake/global_settings.cmake new file mode 100755 index 0000000..8de2e7e --- /dev/null +++ b/makefiles/cmake/global_settings.cmake @@ -0,0 +1,83 @@ +# Run this file only once per cmake run. This is so that +# users projects can override those global settings before +# parsing subdirectories (which can also include this file +# directly so that they can be built as standalone projects). +if (DEFINED GLOBAL_SETTINGS_SET) + return () +endif () +set (GLOBAL_SETTINGS_SET "TRUE") + +if (NOT DEFINED VIDEOCORE_ROOT) + message (FATAL_ERROR + " **************************************************\n" + " * Variable VIDEOCORE_ROOT is not defined. please *\n" + " * define it before including this cmake file. *\n" + " * this variable has to be set to the *absolute* *\n" + " * path to the top of the videocore source tree *\n" + " **************************************************\n" + ) +endif () + +if (NOT IS_ABSOLUTE "${VIDEOCORE_ROOT}") + message (FATAL_ERROR + " **************************************************\n" + " * Variable VIDEOCORE_ROOT doesn't contain *\n" + " * absolute path *\n" + " **************************************************\n" + ) +endif () + +set (VIDEOCORE_RTOSES threadx none win32 pthreads nucleus) +foreach (possible_rtos ${VIDEOCORE_RTOSES}) + set (VIDEOCORE_RTOSES_STR "${possible_rtos}, ${VIDEOCORE_RTOSES_STR}") + set (VIDEOCORE_RTOSES_RE "${VIDEOCORE_RTOSES_RE}|(${possible_rtos})") +endforeach () + +if (NOT DEFINED RTOS) + # Guess which OS we are on to maintain backwards compatibility + if (WIN32) + set (RTOS win32) + else () + set (RTOS pthreads) + endif () +endif () + +if (NOT ${RTOS} MATCHES ${VIDEOCORE_RTOSES_RE}) + message (FATAL_ERROR + " **************************************************\n" + " * RTOS incorrectly defined. Please define it *\n" + " * correctly and rerun cmake (possibly after *\n" + " * removing files from this run). *\n" + " * Possible options are: ${VIDEOCORE_RTOSES_STR}\n" + " **************************************************\n" + ) +endif () + +if (NOT DEFINED VIDEOCORE_BUILD_DIR) + set (VIDEOCORE_BUILD_DIR "${VIDEOCORE_ROOT}/build") +endif () +set (VIDEOCORE_ARCHIVE_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/lib") +set (VIDEOCORE_HEADERS_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/inc") +set (VIDEOCORE_RUNTIME_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/bin") +if (VIDEOCORE_ALL_RUNTIMES_AND_LIBRARIES_IN_SAME_DIRECTORY) + # this is useful on windows to avoid problems with dlls not being found at + # runtime... + set (VIDEOCORE_LIBRARY_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/bin") + set (VIDEOCORE_TESTAPP_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/bin") +else () + set (VIDEOCORE_LIBRARY_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/lib") + set (VIDEOCORE_TESTAPP_BUILD_DIR "${VIDEOCORE_BUILD_DIR}/test") +endif () + +set (VCOS_HEADERS_BUILD_DIR "${VIDEOCORE_HEADERS_BUILD_DIR}/interface/vcos") + +set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${VIDEOCORE_ARCHIVE_BUILD_DIR}") +set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${VIDEOCORE_LIBRARY_BUILD_DIR}") +set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${VIDEOCORE_RUNTIME_BUILD_DIR}") + +include_directories ("${VIDEOCORE_HEADERS_BUILD_DIR}") + +function (add_testapp_subdirectory name) + set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${VIDEOCORE_TESTAPP_BUILD_DIR}") + add_subdirectory (${name}) +endfunction () diff --git a/makefiles/cmake/srcs/test-mtrace.c b/makefiles/cmake/srcs/test-mtrace.c new file mode 100755 index 0000000..d4cd615 --- /dev/null +++ b/makefiles/cmake/srcs/test-mtrace.c @@ -0,0 +1,35 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +int main(int argc, const char **argv) +{ + mtrace(); + muntrace(); +} + diff --git a/makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake b/makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake new file mode 100755 index 0000000..aa1457c --- /dev/null +++ b/makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake @@ -0,0 +1,22 @@ + +# +# CMake defines to cross-compile to ARM/Linux on BCM2708 using glibc. +# + +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +SET(CMAKE_ASM_COMPILER arm-linux-gnueabihf-gcc) +SET(CMAKE_SYSTEM_PROCESSOR arm) + +#ADD_DEFINITIONS("-march=armv6") +add_definitions("-mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -marm") + +# rdynamic means the backtrace should work +IF (CMAKE_BUILD_TYPE MATCHES "Debug") + add_definitions(-rdynamic) +ENDIF() + +# avoids annoying and pointless warnings from gcc +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE") +SET(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -c") diff --git a/makefiles/cmake/toolchains/bcm2708-glibc-linux.cmake b/makefiles/cmake/toolchains/bcm2708-glibc-linux.cmake new file mode 100755 index 0000000..8c3314d --- /dev/null +++ b/makefiles/cmake/toolchains/bcm2708-glibc-linux.cmake @@ -0,0 +1,21 @@ + +# +# CMake defines to cross-compile to ARM/Linux on BCM2708 using glibc. +# + +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_C_COMPILER bcm2708-gcc) +SET(CMAKE_CXX_COMPILER bcm2708-g++) +SET(CMAKE_ASM_COMPILER bcm2708-gcc) +SET(CMAKE_SYSTEM_PROCESSOR arm) + +ADD_DEFINITIONS("-march=armv6") + +# rdynamic means the backtrace should work +IF (CMAKE_BUILD_TYPE MATCHES "Debug") + add_definitions(-rdynamic) +ENDIF() + +# avoids annoying and pointless warnings from gcc +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE") +SET(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -c") diff --git a/makefiles/cmake/vmcs.cmake b/makefiles/cmake/vmcs.cmake new file mode 100755 index 0000000..718cd3b --- /dev/null +++ b/makefiles/cmake/vmcs.cmake @@ -0,0 +1,80 @@ + + +SET(CPACK_PACKAGE_VERSION_MAJOR "1") +SET(CPACK_PACKAGE_VERSION_MINOR "0") +SET(CPACK_PACKAGE_VERSION_PATCH "pre-1") + +INCLUDE(CPack) + +# Where shall we install? +if (ANDROID) + SET(VMCS_INSTALL_PREFIX "/vendor/brcm/islands" CACHE PATH "Prefix prepended to install directories" FORCE) +elseif(NOT DEFINED VMCS_INSTALL_PREFIX) + SET(VMCS_INSTALL_PREFIX "/opt/vc" CACHE PATH "Prefix prepended to install directories" FORCE) +endif() + +SET(CMAKE_INSTALL_PREFIX "${VMCS_INSTALL_PREFIX}" CACHE INTERNAL "Prefix + prepended to install directories" FORCE) +if(NOT DEFINED VMCS_PLUGIN_DIR) + SET(VMCS_PLUGIN_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}/plugins) +endif() + +# What kind of system are we? +if (${UNIX}) + set (VMCS_TARGET linux) +elseif (${SYMBIAN}) + set (VMCS_TARGET symbian) +elseif (${WIN32}) + set (VMCS_TARGET win32) +else() + message(FATAL_ERROR,"Unknown system type") +endif() +set (ARM64 OFF CACHE BOOL "Whether target is ARM64") + +# construct the vmcs config header file +add_definitions(-DHAVE_VMCS_CONFIG) +configure_file ( + "${vmcs_root}/host_applications/vmcs/vmcs_config.h.in" + "${PROJECT_BINARY_DIR}/vmcs_config.h" + ) + +# install an ld.so.conf file to pick up our shared libraries +#configure_file (${vmcs_root}/makefiles/cmake/srcs/vmcs.conf.in +# ${PROJECT_BINARY_DIR}/vmcs.conf) +#if(NOT DEFINED ANDROID) +# install(FILES ${PROJECT_BINARY_DIR}/vmcs.conf DESTINATION /etc/ld.so.conf.d) +#endif() + +# also put it in /opt/vc for access by install script +#install(FILES ${PROJECT_BINARY_DIR}/vmcs.conf +# DESTINATION ${VMCS_INSTALL_PREFIX}/share/install) +# provide headers the libraries need in /opt/vc too +#install(DIRECTORY interface/khronos/include +# DESTINATION ${VMCS_INSTALL_PREFIX}) +#install(DIRECTORY interface/vmcs_host/khronos/IL +# DESTINATION ${VMCS_INSTALL_PREFIX}/include) +# provide an install script +#install(PROGRAMS ${vmcs_root}/makefiles/cmake/scripts/install_vmcs +# DESTINATION ${VMCS_INSTALL_PREFIX}/sbin +# PERMISSIONS OWNER_WRITE WORLD_READ) + +# provide hello_pi demos +install(DIRECTORY host_applications/linux/apps/hello_pi + DESTINATION ${VMCS_INSTALL_PREFIX}/src) + +# provide header files +#install(DIRECTORY host_applications/linux/libs/bcm_host/include +# DESTINATION ${VMCS_INSTALL_PREFIX}/) + +install(DIRECTORY ${vmcs_root}/interface/vcos DESTINATION ${VMCS_INSTALL_PREFIX}/include/interface FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${vmcs_root}/interface/vchiq_arm DESTINATION ${VMCS_INSTALL_PREFIX}/include/interface FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${vmcs_root}/interface/vchi DESTINATION ${VMCS_INSTALL_PREFIX}/include/interface FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${vmcs_root}/interface/vctypes DESTINATION ${VMCS_INSTALL_PREFIX}/include/interface FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${vmcs_root}/vcinclude DESTINATION ${VMCS_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${vmcs_root}/interface/vmcs_host DESTINATION ${VMCS_INSTALL_PREFIX}/include/interface FILES_MATCHING PATTERN "*.h" PATTERN "${vmcs_root}/interface/vmcs_host/khronos" EXCLUDE) + +install(DIRECTORY ${vmcs_root}/interface/khronos/include DESTINATION ${VMCS_INSTALL_PREFIX} FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${vmcs_root}/interface/vmcs_host/khronos/IL DESTINATION ${VMCS_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h") + +install(DIRECTORY ${vmcs_root}/host_applications/linux/libs/bcm_host/include DESTINATION ${VMCS_INSTALL_PREFIX} FILES_MATCHING PATTERN "*.h") + diff --git a/middleware/dlloader/dlfcn.h b/middleware/dlloader/dlfcn.h new file mode 100755 index 0000000..6ad2d6d --- /dev/null +++ b/middleware/dlloader/dlfcn.h @@ -0,0 +1,99 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _DLLOADER_H +#define _DLLOADER_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* + * Declarations used for dynamic linking support routines. + */ + extern void dlloader_init(void); + extern void *dlopen (const char *dllName, int mode); + extern void *dlopen_at(const char *dllName, void *kept, int keptspace, void *unkept, int unkeptspace); + extern void *dlopen_pmm(const char *dllName, void *(*pmm_alloc)(void *, unsigned int, unsigned int, const char *, unsigned int), void (*pmm_free)(void *, void *), void *pmm_priv); + extern void (*dlsym(void *, const char *))(); + extern int dlclose(void *); + extern const char *dlerror(void); + extern void dldone(void *handle); + extern int dlcheck(const char *pathName); + extern void *dlshared_vll_load(const char *vll_name, + const char **symbols, + void *(*pmm_alloc)(void *, unsigned int, unsigned int, const char *, unsigned int), + void (*pmm_free)(void *, void *), + void *pmm_priv, + int *vll_init_required); + extern void dlshared_vll_init_done(void *vll); + extern void (*dlshared_get_vll_symbol(void *vll, const char *symbol))(); + extern int dlshared_vll_closing(void *vll); + extern int dlshared_vll_unload(void *vll); + + + /* + * Valid values for mode argument to dlopen. + */ +#define RTLD_LAZY 0x00001 /* deferred function binding */ +#define RTLD_NOW 0x00002 /* immediate function binding */ + +#define RTLD_GLOBAL 0x00100 /* export symbols to others */ +#define RTLD_LOCAL 0x00000 /* symbols are only available */ + /* to group members */ + +#ifdef __cplusplus +} +#endif + +#ifdef FOR_VMCS +// Set the path where vlls are to be retrieved from. Return non-zero for success. +int dl_set_vll_dir(char const *dir_name); +#endif + +enum dl_pmm_flags { + DL_PMM_NORMAL = 0, + DL_PMM_TEMPORARY = 1, + DL_PMM_DEBUGINFO = 2, +}; + +enum dlpoolflags { + DL_POOLFLAGS_EXECUTABLE = 1, + DL_POOLFLAGS_WRITABLE = 2, + DL_POOLFLAGS_TEMPORARY = 4, + DL_POOLFLAGS_DEBUGINFO = 8, +}; + +struct dlsegmentsizedata { + int size; + int align; + enum dlpoolflags flags; +}; + +extern int dlgetsegmentsizes(const char *vll, int *nrows, struct dlsegmentsizedata *segdata); + +#endif /* _DLFCN_H */ diff --git a/middleware/imageconv/imageconv.h b/middleware/imageconv/imageconv.h new file mode 100755 index 0000000..f7a9a0e --- /dev/null +++ b/middleware/imageconv/imageconv.h @@ -0,0 +1,448 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef IMAGECONV_H +#define IMAGECONV_H + +#include "interface/vcos/vcos.h" +#include "interface/vctypes/vc_image_types.h" +#include "vcfw/rtos/common/rtos_common_mem.h" +#include "interface/vmcs_host/vc_imageconv_defs.h" + +typedef struct +{ + void *data; + /** Width of the converted image in pixels */ + uint32_t width; + /** Height of the converted image in pixels */ + uint32_t height; + /** Cropped width of the converted image in pixels */ + uint32_t crop_width; + /** Cropped height of the converted image in pixels */ + uint32_t crop_height; + /** The pitch of the converted image */ + uint32_t pitch; + /** The size required for the converted image */ + uint32_t size; + /** The type of the converted image */ + VC_IMAGE_TYPE_T type; + /** Whether the destination has been sub-sampled */ + uint32_t subsampled; + /** Non-zero = image is V/U interleaved */ + uint32_t is_vu; + /** Vertical pitch of the buffer */ + uint32_t vpitch; +} IMAGECONV_IMAGE_DATA_T; + + +typedef struct IMAGECONV_DRIVER_IMAGE_T IMAGECONV_DRIVER_IMAGE_T; + +/** Converter class */ +typedef struct IMAGE_CONVERT_CLASS_T IMAGE_CONVERT_CLASS_T; + +/** + * Create an image. + * The initial reference count is 1. + * @param data Image data. Not all fields are used by all convert classes. 'data' must be + * filled in for all classes. + * @param handle On success, this points to the newly created image handle. + * @return 0 on success; -1 on failure. + */ +typedef int (*IMAGECONV_CREATE)(const IMAGE_CONVERT_CLASS_T *converter, + const IMAGECONV_IMAGE_DATA_T *data, MEM_HANDLE_T *handle); + +typedef int (*IMAGECONV_GET_SIZE)(const IMAGE_CONVERT_CLASS_T *converter, + const IMAGECONV_DRIVER_IMAGE_T *image, uint32_t *width, + uint32_t *height, uint32_t *pitch, VC_IMAGE_TYPE_T *type); + +/** Get an image which can be used in one of the other conversion function, + * given a mem handle. + */ +typedef int (*IMAGECONV_GET_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, + IMAGECONV_DRIVER_IMAGE_T **image); + +/** Gets the dimensions and allocation size of the destination image + * required by IMAGECONV_CONVERT. + * + * Must NOT be NULL. + * + * @return 0 if successful + */ +typedef int (*IMAGECONV_GET_CONVERTED_SIZE)( + const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, + VC_IMAGE_TYPE_T type, IMAGECONV_IMAGE_DATA_T *dest_info); + +/* + * Tell the converter that we've finished with the image for now (so it can + * unlock it etc.). + * + * May be NULL if the converter does not support or require this. + */ +typedef void (*IMAGECONV_UNGET_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T src, + IMAGECONV_DRIVER_IMAGE_T *image); + +/* + * Add a reference count to an image, preventing it from being returned to its + * image pool. + * + * May be NULL if the converter does not support or require this. + */ +typedef int (*IMAGECONV_ACQUIRE_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +/* + * Remove a reference count to an image, releasing it back to its + * image pool. + * + * May be NULL if the converter does not support or require this. + */ +typedef void (*IMAGECONV_RELEASE_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +/* mem-lock an image and return the data pointer(s). + */ +typedef int (*IMAGECONV_LOCK)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, IMAGECONV_IMAGE_DATA_T *data); + +/* mem-unlock an image. + */ +typedef void (*IMAGECONV_UNLOCK)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +/* Convert src to dest */ +typedef int (*IMAGECONV_CONVERT)(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T dest, uint32_t dest_offset, + MEM_HANDLE_T src, uint32_t src_offset, VC_IMAGE_TYPE_T dest_type); + +/** Set the description of the source image data. Optional function to aid memory debugging. + * This envisages the source image data is in the relocatable heap, so will call mem_set_desc_vprintf() to + * set the description. + * + * May be NULL if the converter does not support or require this. + */ +typedef void (*IMAGECONV_SET_SRC_DESC)(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, + const char *fmt, va_list ap); + +/** Get the description of the source image data. Optional function to aid memory debugging. + * This envisages the source image data is in the relocatable heap, so will call mem_get_desc() to + * get the description. + * + * May be NULL if the converter does not support or require this. + */ +typedef const char *(*IMAGECONV_GET_SRC_DESC)(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src); + +/* Callback made in response to IMAGECONV_NOTIFY + * + * A status of 0 indicates that a reference to the image can now be acquired + * via imageconv_acquire(_image). + * + * Any other value indicates that the request timed out. + */ +typedef void (*IMAGECONV_NOTIFY_CALLBACK)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, + void *context, + int status); + +/* Request notification when a reference to the image can be acquired. + * + * May be NULL if the converter does not support or require this. + */ +typedef int (*IMAGECONV_NOTIFY)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, + uint32_t timeout, + IMAGECONV_NOTIFY_CALLBACK callback, + void *context); + +/* Cancel an outstanding IMAGECONV_NOTIFY request. +*/ +typedef void (*IMAGECONV_CANCEL_NOTIFY)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +typedef enum IMAGECONV_ID_T { + IMAGECONV_ID_MMAL, /**< MMAL opaque buffer to Khronos */ + IMAGECONV_ID_KHRONOS, /**< YV12 to Khronos */ + IMAGECONV_ID_EGL_VC, /**< EGL image to VC_IMAGE_T */ + IMAGECONV_ID_GENERIC, + IMAGECONV_ID_KHRONOS_VC, /**< Khronos to VC_IMAGE_T */ + IMAGECONV_ID_MAX +} IMAGECONV_ID_T; + +/* Do not call these directly! */ +struct IMAGE_CONVERT_CLASS_T { + IMAGECONV_CREATE create; + IMAGECONV_GET_SIZE get_size; + IMAGECONV_GET_IMAGE get; + IMAGECONV_GET_CONVERTED_SIZE get_converted_size; + IMAGECONV_UNGET_IMAGE unget; + IMAGECONV_ACQUIRE_IMAGE acquire; + IMAGECONV_RELEASE_IMAGE release; + IMAGECONV_LOCK lock; + IMAGECONV_UNLOCK unlock; + IMAGECONV_CONVERT convert; + IMAGECONV_SET_SRC_DESC set_src_desc; + IMAGECONV_GET_SRC_DESC get_src_desc; + IMAGECONV_NOTIFY notify; + IMAGECONV_CANCEL_NOTIFY cancel_notify; + IMAGECONV_ID_T id; +}; + +typedef enum IMAGECONV_ERR_T { + IMAGECONV_ERR_NONE = 0, + IMAGECONV_ERR_GENERAL = -1, + IMAGECONV_ERR_NOT_SUPPORTED = -2, + IMAGECONV_ERR_NOT_READY = -3, + IMAGECONV_ERR_ALREADY = -4, +} IMAGECONV_ERR_T; + +/** Initialise the library + */ +void imageconv_init(void); + +/** Retrieve the conversion function routines for a given type of + * image. + * + * @param converter_id type of image to support + * @param converter function pointers for this image type + * + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_NOT_SUPPORTED if image type unknown. + */ +int imageconv_get_convert_class( + IMAGECONV_ID_T converter_id, + const IMAGE_CONVERT_CLASS_T **converter); + +/** Set the image conversion functions to use for a given type of + * image. + * + * @param converter_class type of image to support + * @param converter function pointers for this image type + */ +void imageconv_set_convert_class(IMAGECONV_ID_T converter_id, + const IMAGE_CONVERT_CLASS_T *converter); + +/** + * Create an image. + * + * The initial reference count is 1. + * + * @param converter Convert class. + * @param data Image data. Not all fields are used by all convert classes. 'data' must be + * filled in for all classes. + * @param handle On success, this points to the newly created image handle. The actual type + * depends on the convert class, but is probably a MEM_HANDLE_T in most cases. + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL on failure + */ +int imageconv_create(const IMAGE_CONVERT_CLASS_T *converter, const IMAGECONV_IMAGE_DATA_T *data, + MEM_HANDLE_T *handle); + +/** Get the size of an image. + * + * @param converter converter functions for this image + * @param self MEM_HANDLE to image structure. + * @param width width of image + * @param height height of image + * @param pitch pitch of image in bytes + * @param type type of image + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid. + */ +int imageconv_get_size(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T self, + uint32_t *width, uint32_t *height, uint32_t *pitch, VC_IMAGE_TYPE_T *type); + +/** Converts the image writing the result to the memory specified at + * dst, dst_offset. + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL on failure + */ +int imageconv_convert(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T dest, uint32_t dest_offset, + MEM_HANDLE_T src, uint32_t src_offset, + VC_IMAGE_TYPE_T dest_type); + +/** Convert a MEM_HANDLE_T handle to an actual image pointer (typically by + * calling mem_lock() but possibly involving other operations). The image + * must be unlocked later with imageconv_unget(). + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to image structure + * @param image Return image pointer. + * + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid. + */ +int imageconv_get(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T src, + IMAGECONV_DRIVER_IMAGE_T **image); + +/* Gets the dimensions of the image that will be created if the image + * is converted. + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to image structure + * @param type The desired type of the destination image. + * @param dest_info A pointer to the image data structure which will be + * updated with the image metadata. The data pointer to + * the image storage is not modified; all other fields + * will be filled in. + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid. + */ +int imageconv_get_converted_size(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T src, VC_IMAGE_TYPE_T type, IMAGECONV_IMAGE_DATA_T *dest_info); + +/** Release an image that was previously taken with imageconv_get(). + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to image structure + * @param image image obtained from imageconv_get + */ +void imageconv_unget(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T src, + IMAGECONV_DRIVER_IMAGE_T *image); + +/** Acquire a lock on an image to prevent it being recycled into its + * image pool. + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to image structure + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid + * @return IMAGECONV_ERR_NOT_READY if reference cannot be currently acquired + */ +int imageconv_acquire(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T src); + + +/** Acquire a lock on an image to prevent it being recycled into its + * image pool. + * + * @param converter converter functions for this image + * @param image image pointer from imageconv_get + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid + * @return IMAGECONV_ERR_NOT_READY if reference cannot be currently acquired + */ +int imageconv_acquire_image(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +/** Release a lock on an image to allow it to be recycled back + * to its image pool. + * + * @param converter converter functions for this image + * @param image image pointer from imageconv_get + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid + */ +int imageconv_release_image(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +/** Set the description of the source image data. Function to aid memory debugging; may not be + * supported by every converter class (in which case it becomes a no-op). + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to source image + * @param desc Memory block description + */ +void imageconv_set_src_desc(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, + const char *desc); + +/** Set the description of the source image data. Function to aid memory debugging; may not be + * supported by every converter class (in which case it becomes a no-op). + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to source image + * @param fmt,... Memory block description + */ +void imageconv_set_src_desc_printf(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, + const char *fmt, ...); + +/** Get the description of the source image data. Function to aid memory debugging; may not be + * supported by every converter class (in which case it returns NULL). + * The returned string will be valid for the lifetime of the src handle, or + * until a new description is set. + * + * @param converter converter functions for this image + * @param src MEM_HANDLE_T to source image + * + * @return Memory block description, or NULL if unavailable + */ +const char *imageconv_get_src_desc(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src); + +/* Request notification when a reference to the image can be acquired. + * + * If the reference can already be acquired at the time when the request is made, + * this is indicated via the return code, and no callback will be made. + * + * Otherwise the callback is made when the reference can be acquired, or when + * the specified time period has elapsed. + * + * The thread context in which callbacks are executed depends upon the converter + * class implementation. The callback function should therefore do as little + * work as possible, deferring to a known thread context for any further + * processing. + * + * @param converter converter functions for this image + * @param image image pointer + * @param timeout time to wait in milliseconds; 0 means wait indefinitely + * @param callback callback to be invoked when image is valid, or timeout expires + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_ALREADY if reference can be acquired immediately + * @return IMAGECONV_ERR_NONE if reference cannot be acquire immediately; callback will be made + */ +int imageconv_notify(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, + uint32_t timeout, + IMAGECONV_NOTIFY_CALLBACK callback, + void *context); + +/* Cancel an outstanding IMAGECONV_NOTIFY request. + * + * @param converter converter functions for this image + * @param image return image pointer + */ +void imageconv_cancel_notify(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +#endif + diff --git a/middleware/khronos/common/2708/khrn_interlock_filler_4.h b/middleware/khronos/common/2708/khrn_interlock_filler_4.h new file mode 100755 index 0000000..cd6f18b --- /dev/null +++ b/middleware/khronos/common/2708/khrn_interlock_filler_4.h @@ -0,0 +1,50 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_INTERLOCK_FILLER_4_H +#define KHRN_INTERLOCK_FILLER_4_H + +#include "interface/khronos/common/khrn_int_util.h" + +/* users are render states. user = 1 << render state index */ +typedef enum { + KHRN_INTERLOCK_USER_NONE = 0, + KHRN_INTERLOCK_USER_INVALID = 1 << 29, /* <= 29 render states, so can use top 3 bits for temp/writing/inv */ + KHRN_INTERLOCK_USER_TEMP = 1 << 30, + KHRN_INTERLOCK_USER_WRITING = 1 << 31 +} KHRN_INTERLOCK_USER_T; +static INLINE KHRN_INTERLOCK_USER_T khrn_interlock_user(uint32_t i) { return (KHRN_INTERLOCK_USER_T)(1 << i); } +static INLINE uint32_t khrn_interlock_render_state_i(KHRN_INTERLOCK_USER_T user) { return _msb(user); } + +typedef struct { + /* top bits: who? */ + uint64_t hw_read_pos; + uint64_t worker_read_pos; + uint64_t write_pos; +} KHRN_INTERLOCK_EXTRA_T; + +#endif diff --git a/middleware/khronos/common/2708/khrn_prod_4.h b/middleware/khronos/common/2708/khrn_prod_4.h new file mode 100755 index 0000000..4fa9477 --- /dev/null +++ b/middleware/khronos/common/2708/khrn_prod_4.h @@ -0,0 +1,545 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_PROD_4_H +#define KHRN_PROD_4_H + +#include "middleware/khronos/egl/egl_disp.h" +#include "middleware/khronos/common/khrn_hw.h" +#include "interface/khronos/common/khrn_int_util.h" +#if defined(SIMPENROSE) + #include "tools/v3d/simpenrose/simpenrose.h" + #ifdef SIMPENROSE_RECORD_OUTPUT + #include "tools/v3d/simpenrose/record.h" + #endif +#elif defined(ABSTRACT_PLATFORM) + /* nothing included */ +#else + #include "vcfw/vclib/vclib.h" + #include "hardware_alias.h" +#include "hardware_v3d.h" +#endif + +#if defined(KHRN_CARBON) +#include "tools/v3d/carbon/carbon_lib/carbon_functions.h" +#endif + +/****************************************************************************** +constants that come from the hw +******************************************************************************/ + +#if defined(SIMPENROSE) || defined(KHRN_CARBON) + #define KHRN_HW_SYSTEM_CACHE_LINE_SIZE 4 +#else + #define KHRN_HW_SYSTEM_CACHE_LINE_SIZE 32 +#endif +#define KHRN_HW_TLB_ALIGN 16 /* todo: is this right? */ +#define KHRN_HW_LOG2_BRCM1_WIDTH 6 /* non-ms */ +#define KHRN_HW_LOG2_BRCM1_HEIGHT 6 /* non-ms */ +#define KHRN_HW_BRCM1_WIDTH (1 << KHRN_HW_LOG2_BRCM1_WIDTH) +#define KHRN_HW_BRCM1_HEIGHT (1 << KHRN_HW_LOG2_BRCM1_HEIGHT) +#define KHRN_HW_BRCM1_STATE_SIZE 48 +#define KHRN_HW_BIN_MEM_ALIGN 256 +#define KHRN_HW_BIN_MEM_GRANULARITY 4096 +#define KHRN_HW_CL_BLOCK_SIZE_MIN 32 +#define KHRN_HW_CL_BLOCK_SIZE_MAX 256 +#define KHRN_HW_TEX_SIZE_MAX 2048 /* max width/height */ +#define KHRN_HW_TEX_ALIGN 4096 +#define KHRN_HW_VPM_BLOCKS_N (192 / 4) +#define KHRN_HW_USER_QUEUE_LENGTH 16 +#define KHRN_HW_QPUS_N 12 + +/****************************************************************************** +misc stuff +******************************************************************************/ + +#if defined(SIMPENROSE) || defined(KHRN_CARBON) + #define khrn_hw_alias_direct(addr) (addr) + #define khrn_hw_alias_normal(addr) (addr) + #define khrn_hw_alias_l1_nonallocating(addr) (addr) +#if defined(SIMPENROSE) + #define khrn_hw_addr simpenrose_hw_addr + #define khrn_hw_unaddr simpenrose_pc_addr +#else + #define khrn_hw_addr(addr) carbon_hw_addr(addr) + #define khrn_hw_unaddr(addr) carbon_hw_unaddr(addr) +#endif + static INLINE void khrn_hw_flush_dcache(void) {} + static INLINE void khrn_hw_flush_dcache_range(void *p, uint32_t size) {} + static INLINE void khrn_hw_flush_l1cache_range(void *p, uint32_t size) {} + static INLINE void khrn_hw_invalidate_dcache_range(void *p, uint32_t size) {} + static INLINE void khrn_hw_invalidate_l1cache_range(void *p, uint32_t size) {} +#elif defined(ABSTRACT_PLATFORM) + /* NEXUS has to convert to physical addresses from the cached version and back */ + #define khrn_hw_alias_direct(addr) (addr) + #define khrn_hw_alias_normal(addr) (addr) + #define khrn_hw_alias_l1_nonallocating(addr) (addr) + #define khrn_hw_alias_l1l2_nonallocating(addr) (addr) + + static INLINE uint32_t khrn_hw_addr(const void *addr) + { + uint32_t addr_device; + addr_device = mem_map_cached_to_physical((void*)addr); + return addr_device; + } + + static INLINE void *khrn_hw_unaddr(uint32_t addr) + { + void * addr_cached = mem_map_physical_to_cached(addr); + return addr_cached; + } + + static INLINE void *khrn_hw_cached_to_uncached(const void *addr) + { + void * addr_uncached = mem_map_cached_to_uncached((void*)addr); + return addr_uncached; + } + + static INLINE void *khrn_hw_uncached_to_cached(const void *addr) + { + void * addr_cached = mem_map_uncached_to_cached((void*)addr); + return addr_cached; + } + + static INLINE void khrn_hw_full_memory_barrier(void) {} + + static INLINE void khrn_hw_flush_dcache(void) { mem_flush_cache(); } + static INLINE void khrn_hw_flush_dcache_range(void *p, uint32_t size) { mem_flush_cache_range(p, size); } + static INLINE void khrn_hw_flush_l1cache_range(void *p, uint32_t size) { UNUSED(p); UNUSED(size); } + static INLINE void khrn_hw_invalidate_dcache_range(void *p, uint32_t size) { mem_flush_cache_range(p, size); } + static INLINE void khrn_hw_invalidate_l1cache_range(void *p, uint32_t size) { UNUSED(p); UNUSED(size); } +#else + #define khrn_hw_alias_direct ALIAS_DIRECT + #define khrn_hw_alias_normal ALIAS_NORMAL + #define khrn_hw_alias_l1_nonallocating ALIAS_L1_NONALLOCATING + + static INLINE uint32_t khrn_hw_addr(const void *addr) + { + return (uint32_t)(uintptr_t)addr; + } + + static INLINE void *khrn_hw_unaddr(uint32_t addr) + { + return (void *)(uintptr_t)addr; + } + + static INLINE void khrn_hw_flush_dcache(void) { vclib_dcache_flush(); } + static INLINE void khrn_hw_flush_dcache_range(void *p, uint32_t size) { vclib_dcache_flush_range(p, size); } + static INLINE void khrn_hw_flush_l1cache_range(void *p, uint32_t size) { vclib_l1cache_flush_range(p, size); } + static INLINE void khrn_hw_invalidate_dcache_range(void *p, uint32_t size) { vclib_dcache_invalidate_range(p, size); } + static INLINE void khrn_hw_invalidate_l1cache_range(void *p, uint32_t size) { vclib_l1cache_invalidate_range(p, size); } +#endif + +/****************************************************************************** +stuff for writing control lists +******************************************************************************/ + +typedef uint64_t KHRN_SHADER_T; +static INLINE uint8_t get_byte(const uint8_t *p) +{ + #ifdef BIG_ENDIAN_CPU + return ((uint8_t)*(uint8_t*)((uint32_t)&p[0]^0x3)); + #else + return p[0]; + #endif +} + +static INLINE uint16_t get_short(const uint8_t *p) +{ + #ifdef BIG_ENDIAN_CPU + return ((uint16_t)*(uint8_t*)((uint32_t)&p[0]^0x3)) | + ((uint16_t)*(uint8_t*)(((uint32_t)&p[1]^0x3)) << 8); + #else + return (uint16_t)p[0] | + ((uint16_t)p[1] << 8); + #endif +} + +static INLINE uint32_t get_word(const uint8_t *p) +{ + #ifdef BIG_ENDIAN_CPU + return ((uint32_t)*(uint8_t*)((uint32_t)&p[0]^0x3)) | + ((uint32_t)*(uint8_t*)(((uint32_t)&p[1]^0x3)) << 8) | + ((uint32_t)*(uint8_t*)(((uint32_t)&p[2]^0x3)) << 16) | + ((uint32_t)*(uint8_t*)(((uint32_t)&p[3]^0x3)) << 24); + #else + return (uint32_t)p[0] | + ((uint32_t)p[1] << 8) | + ((uint32_t)p[2] << 16) | + ((uint32_t)p[3] << 24); + #endif +} + +static INLINE void put_byte(uint8_t *p, uint8_t n) +{ + #ifdef BIG_ENDIAN_CPU + *(uint8_t*)((uint32_t)&p[0]^0x3) = n; + #else + p[0] = n; + #endif +} +static INLINE void put_short(uint8_t *p, uint16_t n) +{ + #ifdef BIG_ENDIAN_CPU + *(uint8_t*)((uint32_t)&p[0]^0x3) = (uint8_t)n; + *(uint8_t*)((uint32_t)&p[1]^0x3) = (uint8_t)(n >> 8); + #else + p[0] = (uint8_t)n; + p[1] = (uint8_t)(n >> 8); + #endif +} + +static INLINE void put_word(uint8_t *p, uint32_t n) +{ + #ifdef BIG_ENDIAN_CPU + *(uint8_t*)((uint32_t)&p[0]^0x3) = (uint8_t)n; + *(uint8_t*)((uint32_t)&p[1]^0x3) = (uint8_t)(n >> 8); + *(uint8_t*)((uint32_t)&p[2]^0x3) = (uint8_t)(n >> 16); + *(uint8_t*)((uint32_t)&p[3]^0x3) = (uint8_t)(n >> 24); + #else + p[0] = (uint8_t)n; + p[1] = (uint8_t)(n >> 8); + p[2] = (uint8_t)(n >> 16); + p[3] = (uint8_t)(n >> 24); + #endif +} + +static INLINE void put_float(uint8_t *p, float f) +{ + put_word(p, float_to_bits(f)); +} + +static INLINE void add_byte(uint8_t **p, uint8_t n) +{ + put_byte(*p, n); + (*p) += 1; +} + +static INLINE void add_short(uint8_t **p, uint16_t n) +{ + put_short(*p, n); + (*p) += 2; +} + +static INLINE void add_word(uint8_t **p, uint32_t n) +{ + put_word(*p, n); + (*p) += 4; +} + +static INLINE void add_float(uint8_t **p, float f) +{ + add_word(p, float_to_bits(f)); +} + +#if V3D_VER_AT_LEAST(3, 0) +static INLINE void add_float16(uint8_t **p, float16_t f16) +{ + add_short(p, (uint16_t) f16); +} +#endif + +static INLINE void add_pointer(uint8_t **p, void *ptr) +{ + add_word(p, khrn_hw_addr(khrn_hw_alias_direct(ptr))); //PTR +} + +#define ADD_BYTE(p, n) add_byte(&(p), (n)) +#define ADD_SHORT(p, n) add_short(&(p), (n)) +#define ADD_WORD(p, n) add_word(&(p), (n)) +#define ADD_FLOAT(p, n) add_float(&(p), (n)) +#define ADD_POINTER(p, n) add_pointer(&(p), (n)) + +#define KHRN_HW_INSTR_HALT 0 +#define KHRN_HW_INSTR_NOP 1 +#define KHRN_HW_INSTR_MARKER 2 +#define KHRN_HW_INSTR_RESET_MARKER_COUNT 3 +#define KHRN_HW_INSTR_FLUSH 4 +#define KHRN_HW_INSTR_FLUSH_ALL_STATE 5 +#define KHRN_HW_INSTR_START_BRCM1_BINNING 6 +#define KHRN_HW_INSTR_INCR_SEMAPHORE 7 +#define KHRN_HW_INSTR_WAIT_SEMAPHORE 8 +#define KHRN_HW_INSTR_BRANCH 16 +#define KHRN_HW_INSTR_BRANCH_SUB 17 +#define KHRN_HW_INSTR_RETURN 18 +#define KHRN_HW_INSTR_REPEAT_START_MARKER 19 +#define KHRN_HW_INSTR_REPEAT_FROM_START_MARKER 20 +#define KHRN_HW_INSTR_STORE_SUBSAMPLE 24 +#define KHRN_HW_INSTR_STORE_SUBSAMPLE_EOF 25 +#define KHRN_HW_INSTR_STORE_FULL 26 +#define KHRN_HW_INSTR_LOAD_FULL 27 +#ifndef __BCM2708A0__ +#define KHRN_HW_INSTR_STORE_GENERAL 28 +#define KHRN_HW_INSTR_LOAD_GENERAL 29 +#endif +#define KHRN_HW_INSTR_GLDRAWELEMENTS 32 +#define KHRN_HW_INSTR_GLDRAWARRAYS 33 +#define KHRN_HW_INSTR_VG_COORD_LIST 41 +#ifndef __BCM2708A0__ +#define KHRN_HW_INSTR_VG_INLINE_PRIMS 42 +#endif +#define KHRN_HW_INSTR_COMPRESSED_LIST 48 +#define KHRN_HW_INSTR_CLIPPED_PRIM 49 +#define KHRN_HW_INSTR_PRIMITIVE_LIST_FORMAT 56 +#define KHRN_HW_INSTR_GL_SHADER 64 +#define KHRN_HW_INSTR_NV_SHADER 65 +#define KHRN_HW_INSTR_VG_SHADER 66 +#ifndef __BCM2708A0__ +#define KHRN_HW_INSTR_INLINE_VG_SHADER 67 +#endif +/* ... */ +#if V3D_VER_AT_LEAST(3, 0) +#define KHRN_HW_INSTR_STATE_BLEND_MODE 84 +#define KHRN_HW_INSTR_STATE_BLEND_CCOLOR_RGBA32 85 +#define KHRN_HW_INSTR_STATE_BLEND_CCOLOR_HDR16 86 +#endif +/* ... */ +#define KHRN_HW_INSTR_STATE_CFG 96 +#define KHRN_HW_INSTR_STATE_FLATSHADE 97 +#define KHRN_HW_INSTR_STATE_POINT_SIZE 98 +#define KHRN_HW_INSTR_STATE_LINE_WIDTH 99 +#define KHRN_HW_INSTR_STATE_RHTX 100 +#define KHRN_HW_INSTR_STATE_DEPTH_OFFSET 101 +#define KHRN_HW_INSTR_STATE_CLIP 102 +#define KHRN_HW_INSTR_STATE_VIEWPORT_OFFSET 103 +#define KHRN_HW_INSTR_STATE_CLIPZ 104 +#define KHRN_HW_INSTR_STATE_CLIPPER_XY 105 +#define KHRN_HW_INSTR_STATE_CLIPPER_Z 106 +#define KHRN_HW_INSTR_STATE_BRCM1_BINNING_MODE 112 +#define KHRN_HW_INSTR_STATE_BRCM1_RENDERING_MODE 113 +#define KHRN_HW_INSTR_STATE_CLEARCOL 114 +#define KHRN_HW_INSTR_STATE_BRCM1_COORDS 115 + +/****************************************************************************** +simpenrose recording +******************************************************************************/ + +#ifdef SIMPENROSE_RECORD_OUTPUT + +static INLINE void record_map_pointer(const void *p, unsigned int len, LABEL_T type, unsigned int flags, unsigned int align) +{ + record_map_buffer(khrn_hw_addr(p), len, type, flags, align); +} + +static INLINE void record_map_extent(const void *begin, const void *end, LABEL_T type, unsigned int flags, unsigned int align) +{ + record_map_pointer(begin, (uint8_t *)end - (uint8_t *)begin, type, flags, align); +} + +void record_map_mem_buffer_section(MEM_HANDLE_T handle, unsigned int begin, unsigned int len, LABEL_T type, unsigned int flags, unsigned int align); + +static INLINE void record_map_mem_buffer(MEM_HANDLE_T handle, LABEL_T type, unsigned int flags, unsigned int align) +{ + record_map_mem_buffer_section(handle, 0, mem_get_size(handle), type, flags, align); +} + +void record_set_frame_config(uint32_t width, uint32_t height, uint32_t bpp, bool tformat); + +#endif + +/****************************************************************************** +hw fifo +******************************************************************************/ + +extern bool khrn_hw_init(void); +extern void khrn_hw_term(void); + +#define KHRN_HW_SPECIAL_0 ((MEM_HANDLE_T)0) /* used for extra thrsw removing hack. todo: no need for this hack on b0 */ +#define KHRN_HW_SPECIAL_BIN_MEM ((MEM_HANDLE_T)1) +#define KHRN_HW_SPECIAL_BIN_MEM_END ((MEM_HANDLE_T)2) +#define KHRN_HW_SPECIAL_BIN_MEM_SIZE ((MEM_HANDLE_T)3) + +/* + if we don't run out of memory at any point, callback will be called: + - after locking everything in place, with reason + KHRN_HW_CALLBACK_REASON_FIXUP, from the llat task. khrn_hw_fixup_done + should be called at some point after this to signal that fixup is complete + and binning can begin + - after binning has completed, first with reason + KHRN_HW_CALLBACK_REASON_BIN_FINISHED_LLAT from the llat task, then with + KHRN_HW_CALLBACK_REASON_BIN_FINISHED from the master task + - after rendering has completed, with reason + KHRN_HW_CALLBACK_REASON_RENDER_FINISHED, from the master task + + we can run out of memory before or during binning (but not after). if we do + run out of memory, callback will be called first with reason + KHRN_HW_CALLBACK_REASON_OUT_OF_MEM_LLAT from the llat task, then with reason + KHRN_HW_CALLBACK_REASON_OUT_OF_MEM from the master task, after which it will + not be called again +*/ + +typedef enum { + KHRN_HW_CALLBACK_REASON_FIXUP, + KHRN_HW_CALLBACK_REASON_OUT_OF_MEM_LLAT, + KHRN_HW_CALLBACK_REASON_OUT_OF_MEM, + KHRN_HW_CALLBACK_REASON_BIN_FINISHED_LLAT, + KHRN_HW_CALLBACK_REASON_BIN_FINISHED, + KHRN_HW_CALLBACK_REASON_RENDER_FINISHED, + KHRN_HW_CALLBACK_REASON_UNFIXUP, +} KHRN_HW_CALLBACK_REASON_T; + +typedef void (*KHRN_HW_CALLBACK_T)(KHRN_HW_CALLBACK_REASON_T reason, void *data, const uint32_t *specials); + +/* + calling code should look something like this: + + void my_callback(KHRN_HW_CALLBACK_REASON_T reason, void *data) + { + MY_STRUCT_T *my_struct = (MY_STRUCT_T *)data; + ... + } + + MY_STRUCT_T *my_struct = (MY_STRUCT_T *)khrn_hw_queue(..., sizeof(MY_STRUCT_T)); + my_struct->x = x; + ... + + khrn_hw_ready(true, my_struct); // may be called at some later point from any thread +*/ + +typedef enum { + KHRN_HW_CC_FLAG_NONE = 0, + + KHRN_HW_CC_FLAG_L2 = 1 << 0, + KHRN_HW_CC_FLAG_IC = 1 << 1, + KHRN_HW_CC_FLAG_UC = 1 << 2, + KHRN_HW_CC_FLAG_TU = 1 << 3 +} KHRN_HW_CC_FLAG_T; + +typedef enum { + KHRN_HW_TYPE_GL, + KHRN_HW_TYPE_VG +} KHRN_HW_TYPE_T; + +extern void *khrn_hw_queue( + uint8_t *bin_begin, uint8_t *bin_end, KHRN_HW_CC_FLAG_T bin_cc, + uint8_t *render_begin, uint8_t *render_end, KHRN_HW_CC_FLAG_T render_cc, uint32_t render_n, + uint32_t special_0, /* used for extra thrsw removing hack. todo: no need for this hack on b0 */ + uint32_t bin_mem_size_min, /* KHRN_HW_SPECIAL_BIN_MEM_SIZE will be >= this */ + uint32_t actual_user_vpm, uint32_t max_user_vpm, + KHRN_HW_TYPE_T type, + KHRN_HW_CALLBACK_T callback, + uint32_t callback_data_size); +extern void khrn_hw_ready(bool ok, void *callback_data); /* if ok is false, callback will later be called with KHRN_HW_CALLBACK_REASON_OUT_OF_MEM_LLAT/KHRN_HW_CALLBACK_REASON_OUT_OF_MEM */ +extern void khrn_hw_fixup_done(bool in_callback, void *callback_data); /* todo: i'm not sure i like this fixup done thing... */ + +extern void khrn_hw_queue_wait_for_worker(uint64_t pos); + +extern void khrn_hw_queue_wait_for_display(EGL_DISP_HANDLE_T disp_handle, uint32_t pos); +extern void khrn_hw_queue_display(EGL_DISP_SLOT_HANDLE_T slot_handle); + +extern void khrn_hw_wait(void); +#ifdef KHRN_BCG_LINUX_GNUC +extern void khrn_monitored_event_wait(void * sem); +extern bool khrn_event_wait_timeout(void * sem, unsigned int t); +#endif + +extern void khrn_hw_update_perf_counters(KHRN_DRIVER_COUNTERS_T *counters); +extern void khrn_hw_reset_perf_counters(uint32_t hw_bank, uint32_t l3c_bank); + +extern void khrn_hw_notify(void); +extern void khrn_hw_cleanup(void); /* called from khrn_sync_master_wait */ + +/* + fifo pos stuff for interlocking with other fifos (eg worker) + + khrn_hw_get_enter_pos should only be called from the master task and should + always return the right value + + khrn_hw_get_bin_exit_pos/khrn_hw_get_render_exit_pos can be called from any + task, but might return the wrong value. the value returned will always be <= + the right value however +*/ + +#if defined(SIMPENROSE) +/* no fifo... */ +static INLINE void khrn_hw_advance_enter_pos(void) {} +static INLINE uint64_t khrn_hw_get_enter_pos(void) { return 0; } +static INLINE void khrn_hw_advance_bin_exit_pos(void) {} +static INLINE uint64_t khrn_hw_get_bin_exit_pos(void) { return 0; } +static INLINE void khrn_hw_advance_render_exit_pos(void) {} +static INLINE uint64_t khrn_hw_get_render_exit_pos(void) { return 0; } +#else +extern uint64_t khrn_hw_enter_pos; + +static INLINE void khrn_hw_advance_enter_pos(void) +{ + ++khrn_hw_enter_pos; +} + +static INLINE uint64_t khrn_hw_get_enter_pos(void) +{ + return khrn_hw_enter_pos; +} + +extern uint32_t khrn_hw_bin_exit_pos_0, khrn_hw_bin_exit_pos_1; + +static INLINE void khrn_hw_advance_bin_exit_pos(void) +{ + uint64_t next_exit_pos = (((uint64_t)khrn_hw_bin_exit_pos_1 << 32) | khrn_hw_bin_exit_pos_0) + 1; + khrn_barrier(); + khrn_hw_bin_exit_pos_0 = (uint32_t)next_exit_pos; + khrn_barrier(); + khrn_hw_bin_exit_pos_1 = (uint32_t)(next_exit_pos >> 32); +} + +static INLINE uint64_t khrn_hw_get_bin_exit_pos(void) +{ + uint32_t exit_pos_1; + uint32_t exit_pos_0; + + exit_pos_1 = khrn_hw_bin_exit_pos_1; + khrn_barrier(); + exit_pos_0 = khrn_hw_bin_exit_pos_0; + khrn_barrier(); + return ((uint64_t)exit_pos_1 << 32) | exit_pos_0; +} + +extern uint32_t khrn_hw_render_exit_pos_0, khrn_hw_render_exit_pos_1; + +static INLINE void khrn_hw_advance_render_exit_pos(void) +{ + uint64_t next_exit_pos = (((uint64_t)khrn_hw_render_exit_pos_1 << 32) | khrn_hw_render_exit_pos_0) + 1; + khrn_barrier(); + khrn_hw_render_exit_pos_0 = (uint32_t)next_exit_pos; + khrn_barrier(); + khrn_hw_render_exit_pos_1 = (uint32_t)(next_exit_pos >> 32); +} + +static INLINE uint64_t khrn_hw_get_render_exit_pos(void) +{ + uint32_t exit_pos_1; + uint32_t exit_pos_0; + + exit_pos_1 = khrn_hw_render_exit_pos_1; + khrn_barrier(); + exit_pos_0 = khrn_hw_render_exit_pos_0; + khrn_barrier(); + return ((uint64_t)exit_pos_1 << 32) | exit_pos_0; +} +#endif + +#endif diff --git a/middleware/khronos/common/khrn_hw.h b/middleware/khronos/common/khrn_hw.h new file mode 100755 index 0000000..486b748 --- /dev/null +++ b/middleware/khronos/common/khrn_hw.h @@ -0,0 +1,236 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_HW_H +#define KHRN_HW_H + +#include "interface/khronos/common/khrn_int_common.h" +#include "middleware/khronos/common/khrn_image.h" +#include "middleware/khronos/egl/egl_disp.h" +#include "interface/khronos/include/EGL/egl.h" +#include "interface/khronos/include/EGL/eglext.h" +#include "interface/vcos/vcos.h" + +bool khrn_hw_common_init(void); +void khrn_hw_common_term(void); + +void khrn_delayed_display(KHRN_IMAGE_T *image, EGL_DISP_SLOT_HANDLE_T slot_handle); +void khrn_delayed_wait_for_display(uint32_t fifo, EGL_DISP_HANDLE_T disp_handle, uint32_t pos); +void khrn_delayed_copy_buffer(MEM_HANDLE_T dst_handle, MEM_HANDLE_T src_handle); + +bool khrn_hw_supports_early_z(void); + +/* flush all queued stuff */ +extern void khrn_hw_common_flush(void); + +/* wait for all flushed stuff to finish */ +extern void khrn_hw_common_wait(void); + +/* + void khrn_hw_common_finish() + + Flush all queued stuff. Wait for all flushed stuff to finish. + + Preconditions: + + - + + Postcondtions: + + For all KHRN_IMAGE_T image: + image.conceptual_readable_by_master is true + image.conceptual_writeable_by_master is true + conceptual_buffers_owned_by_master is true + conceptual_programs_owned_by_master is true +*/ + +static INLINE void khrn_hw_common_finish(void) +{ + khrn_hw_common_flush(); + khrn_hw_common_wait(); +} + +extern void khrn_sync_init(void); +extern void khrn_sync_notify_master(void); +extern void khrn_sync_master_wait(void); +#ifdef KHRN_BCG_WAIT_TIMEOUT +extern bool khrn_sync_master_wait_timeout(unsigned int t); +#endif +extern void khrn_sync_display_returned_notify(void); /* called when something comes off the display */ +extern void khrn_specify_event(VCOS_EVENT_T *ev); +extern int32_t khrn_do_suspend_resume(uint32_t up); + +typedef void KHRN_SYNC_MASTER_WAIT_FUNC(void); +typedef void KHRN_SPECIFY_EVENT_FUNC(VCOS_EVENT_T *ev); +typedef int32_t KHRN_DO_SUSPEND_RESUME_FUNC(uint32_t up); + +typedef struct { + uint32_t acquire; + uint32_t release; +} KHRN_CACHE_COUNTERS_T; + +extern void khrn_memcpy(void *dest, const void *src, uint32_t size); +extern void khrn_memset(void *dest, uint32_t val, uint32_t size); + +typedef struct { + /* + primitive counters + */ + + uint32_t fovcull; + uint32_t fovclip; + uint32_t revcull; + uint32_t nofepix; + + /* + texture units + */ + + uint32_t tu0access; + uint32_t tu0miss; + uint32_t tu1access; + uint32_t tu1miss; + + /* + backend + */ + + uint32_t dpthfail; + uint32_t stclfail; + uint32_t dpthstclpass; +} KHRN_PERF_COUNTERS_T; + +typedef struct { + uint64_t qpu_cycles_idle; + uint64_t qpu_cycles_vert_shade; + uint64_t qpu_cycles_frag_shade; + uint64_t qpu_cycles_exe_valid; + uint64_t qpu_cycles_wait_tmu; + uint64_t qpu_cycles_wait_scb; + uint64_t qpu_cycles_wait_vary; + uint64_t qpu_icache_hits; + uint64_t qpu_icache_miss; + uint64_t qpu_ucache_hits; + uint64_t qpu_ucache_miss; + + uint64_t tmu_total_quads; + uint64_t tmu_cache_miss; + + uint64_t l2c_hits; + uint64_t l2c_miss; +} KHRN_DRIVER_HW_COUNTERS0_T; + +typedef struct { + uint64_t fep_valid_prims; + uint64_t fep_valid_prims_no_pixels; + uint64_t fep_earlyz_clipped_quads; + uint64_t fep_valid_quads; + + uint64_t tlb_quads_no_stencil_pass_pixels; + uint64_t tlb_quads_no_z_stencil_pass_pixels; + uint64_t tlb_quads_z_stencil_pass_pixels; + uint64_t tlb_quads_all_pixels_zero_cvg; + uint64_t tlb_quads_all_pixels_nonzero_cvg; + uint64_t tlb_quads_valid_pixels_written; + + uint64_t ptb_prims_viewport_discarded; + uint64_t ptb_prims_needing_clip; + + uint64_t pse_prims_reverse_discarded; + + uint64_t vpm_cycles_vdw_stalled; + uint64_t vpm_cycles_vcd_stalled; +} KHRN_DRIVER_HW_COUNTERS1_T; + +typedef struct { + uint32_t hard_clears; + uint32_t soft_clears; + uint32_t tb_grp_color_loads; + uint32_t tb_grp_ms_color_loads; + uint32_t tb_grp_ds_loads; + uint32_t tb_grp_color_stores; + uint32_t tb_grp_ms_color_stores; + uint32_t tb_grp_ds_stores; + uint32_t tb_color_loads; + uint32_t tb_ms_color_loads; + uint32_t tb_ds_loads; + uint32_t tb_color_stores; + uint32_t tb_ms_color_stores; + uint32_t tb_ds_stores; + uint32_t tex_submissions; + uint32_t tex_fast_paths; + uint32_t mipmap_gens; + uint32_t mipmap_gens_fast; + uint32_t draw_calls; + uint32_t num_swaps; + + uint32_t hw_group_active; + +#ifdef __linux__ + uint32_t reset_time; /* time in ms when the statistics were last reset */ + uint32_t in_time; /* time at which acquire happened */ + uint32_t total_time; /* total of all (release time - acquire time) */ + + uint32_t last_cpu_time; + uint32_t last_cpu_ticks; +#endif + + union + { + KHRN_DRIVER_HW_COUNTERS0_T hw_0; + KHRN_DRIVER_HW_COUNTERS1_T hw_1; + } hw; + + uint32_t l3c_group_active; + union + { + uint64_t l3c_read_bw_0; + uint64_t l3c_write_bw_1; + } l3c; + union + { + uint64_t l3c_mem_read_bw_0; + uint64_t l3c_mem_write_bw_1; + } l3c_mem; + +} KHRN_DRIVER_COUNTERS_T; + +extern void khrn_hw_register_perf_counters(KHRN_DRIVER_COUNTERS_T *counters); +extern void khrn_hw_unregister_perf_counters(void); +extern void khrn_reset_driver_counters(int32_t hw_bank, int32_t l3c_bank); + +extern KHRN_DRIVER_COUNTERS_T *khrn_driver_counters(void); + +#if EGL_BRCM_driver_monitor +#define INCR_DRIVER_COUNTER(counter) khrn_driver_counters()->counter++; +#else +#define INCR_DRIVER_COUNTER(counter) ; +#endif + +extern void khrn_hw_kick(void); + +#endif diff --git a/middleware/khronos/common/khrn_image.h b/middleware/khronos/common/khrn_image.h new file mode 100755 index 0000000..d3a01db --- /dev/null +++ b/middleware/khronos/common/khrn_image.h @@ -0,0 +1,339 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_IMAGE_H +#define KHRN_IMAGE_H + +#include "interface/khronos/common/khrn_int_image.h" +#include "middleware/khronos/common/khrn_interlock.h" +#include "middleware/khronos/common/khrn_mem.h" + +#ifdef _VIDEOCORE +#include "helpers/vc_image/vc_image.h" +#endif + +#include + +/****************************************************************************** +image handling +******************************************************************************/ + +/* + Binding flags + These specify which, if any, of the 3 EGL binding mechanisms this image is + taking part in. This information is stored in the image itself rather than + the API objects which use it, to enable communication between objects. + (For example if a pbuffer is bound to a texture, releasing the texture means + the pbuffer can now be bound to a different texture). + The IMAGE_FLAG_BOUND_CLIENTBUFFER and IMAGE_FLAG_BOUND_TEXIMAGE flags may be + used together, but not with IMAGE_FLAG_BOUND_EGLIMAGE (giving a total of 5 + possible states). + + texture is responsible for setting and clearing IMAGE_FLAG_BOUND_TEXIMAGE. + EGL is responsible for setting and clearing IMAGE_FLAG_BOUND_CLIENTBUFFER. + EGL is responsible for setting IMAGE_FLAG_BOUND_EGLIMAGE (this flag never + gets cleared once set). +*/ +#define IMAGE_FLAG_BOUND_CLIENTBUFFER (1<<0) +#define IMAGE_FLAG_BOUND_TEXIMAGE (1<<1) +#define IMAGE_FLAG_BOUND_EGLIMAGE (1<<2) + +/* if these flags are set, the image should be usable in that way (eg if the + * texture flag is set it should be ok to use this image as a texture). the + * opposite may not be true. khrn_image_create_dup preserves these flags (so if + * you duplicate an image that can be used as a texture, the new image will also + * be usable as a texture) */ +#define IMAGE_FLAG_RENDER_TARGET (1<<3) +#define IMAGE_FLAG_TEXTURE (1<<4) +#define IMAGE_FLAG_RSO_TEXTURE (1<<5) +#define IMAGE_FLAG_DISPLAY (1<<6) + +typedef struct { + KHRN_IMAGE_FORMAT_T format; + + uint16_t width; + uint16_t height; + + int32_t stride; /* in bytes */ + + MEM_HANDLE_T mh_aux; /* palette or early z */ + + /* For external images mh_storage is not initialised until + * the it needs to be drawn */ + MEM_HANDLE_T mh_storage; + uint32_t offset; + + uint16_t flags; + + KHRN_INTERLOCK_T interlock; + +#ifdef ABSTRACT_PLATFORM + void * opaque_buffer_handle; +#endif + +} KHRN_IMAGE_T; + +typedef struct { + KHRN_IMAGE_WRAP_T w; + KHRN_INTERLOCK_T *interlock; +} KHRN_IMAGE_INTERLOCK_WRAP_T; + +typedef enum { + /* + select whether the image data should be uninitialized, or initialized to 0xff or 0x00 + */ + + IMAGE_CREATE_FLAG_NONE = 0 << 0, + IMAGE_CREATE_FLAG_ONE = 1 << 0, + IMAGE_CREATE_FLAG_ZERO = 2 << 0, + + /* + select whether the image data should be padded + */ + + IMAGE_CREATE_FLAG_PAD_ROTATE = 1 << 2, + + /* + usage bits. image parameters will be fudged to ensure the created image + can be used in the specified manner (assuming the format is acceptable). + this might involve eg changing the memory layout to brcm1 or forcing a + large alignment. once created, the corresponding image flags will be set + on the image (eg if you pass IMAGE_CREATE_FLAG_TEXTURE to + khrn_image_create, the created image will have the IMAGE_FLAG_TEXTURE flag + set) + */ + + IMAGE_CREATE_FLAG_TEXTURE = 1 << 3, + IMAGE_CREATE_FLAG_RSO_TEXTURE = 1 << 4, + IMAGE_CREATE_FLAG_RENDER_TARGET = 1 << 5, + IMAGE_CREATE_FLAG_DISPLAY = 1 << 6, + + /* + don't allocate storage for the image yet? + */ + + IMAGE_CREATE_FLAG_NO_STORAGE = 1 << 7, + + /* + mark the image as invalid (khrn_interlock_invalidate)? + */ + + IMAGE_CREATE_FLAG_INVALID = 1 << 8 +} KHRN_IMAGE_CREATE_FLAG_T; + +#define IMAGE_CREATE_FLAG_INIT_MASK (3 << 0) + +extern bool khrn_image_prefer_lt(KHRN_IMAGE_FORMAT_T format, uint32_t width, uint32_t height); +extern uint32_t khrn_image_get_width_ut(const KHRN_IMAGE_T *image); +extern uint32_t khrn_image_get_width_brcm1s(const KHRN_IMAGE_T *image); +extern uint32_t khrn_image_get_aux_width_ut(const KHRN_IMAGE_T *image); +extern uint32_t khrn_image_wrap_get_width_ut(const KHRN_IMAGE_WRAP_T *wrap); +extern uint32_t khrn_image_wrap_get_width_brcm1s(const KHRN_IMAGE_WRAP_T *wrap); +extern uint32_t khrn_image_get_align(const KHRN_IMAGE_T *image); +extern uint32_t khrn_image_get_space(const KHRN_IMAGE_T *image); + +/* these should only be called on color formats */ +extern bool khrn_image_is_ok_for_render_target(KHRN_IMAGE_FORMAT_T format, bool ignore_mem_layout); +extern bool khrn_image_can_use_as_render_target(KHRN_IMAGE_T *image); /* should only be called if khrn_image_is_ok_for_render_target() */ + +extern void khrn_image_platform_fudge( + KHRN_IMAGE_FORMAT_T *format, + uint32_t *padded_width, uint32_t *padded_height, + uint32_t *align, uint32_t *stagger, + KHRN_IMAGE_CREATE_FLAG_T flags); + +extern void khrn_image_term(void *v, uint32_t); + +extern MEM_HANDLE_T khrn_image_create_from_storage(KHRN_IMAGE_FORMAT_T format, + uint32_t width, uint32_t height, int32_t stride, + MEM_HANDLE_T aux_handle, MEM_HANDLE_T storage_handle, uint32_t offset, + KHRN_IMAGE_CREATE_FLAG_T flags); /* just used for setting up usage flags */ +extern MEM_HANDLE_T khrn_image_create(KHRN_IMAGE_FORMAT_T format, + uint32_t width, uint32_t height, + KHRN_IMAGE_CREATE_FLAG_T flags); +extern MEM_HANDLE_T khrn_image_create_dup(const KHRN_IMAGE_T *src, + KHRN_IMAGE_CREATE_FLAG_T flags); /* flags are in addition to implicit flags from src */ + +extern bool khrn_image_resize(KHRN_IMAGE_T *image, uint32_t width, uint32_t height); + +static INLINE void *khrn_image_lock(const KHRN_IMAGE_T *image) +{ + return (uint8_t *)mem_lock(image->mh_storage) + image->offset; +} + +static INLINE void khrn_image_unlock(const KHRN_IMAGE_T *image) +{ + mem_unlock(image->mh_storage); +} + +extern void khrn_image_lock_wrap(const KHRN_IMAGE_T *image, KHRN_IMAGE_WRAP_T *wrap); +extern void khrn_image_unlock_wrap(const KHRN_IMAGE_T *image); +extern void khrn_image_lock_interlock_wrap(const KHRN_IMAGE_T *image, KHRN_IMAGE_INTERLOCK_WRAP_T *wrap); +extern void khrn_image_interlock_wrap(KHRN_IMAGE_INTERLOCK_WRAP_T *wrap, KHRN_IMAGE_FORMAT_T format, uint32_t width, uint32_t height, int32_t stride, void *storage, KHRN_INTERLOCK_T *interlock); + +#ifdef _VIDEOCORE +/* vc_image->image_data will be set to NULL */ +static INLINE void khrn_image_fill_vcimage(const KHRN_IMAGE_T *image, VC_IMAGE_T *vc_image) +{ + memset(vc_image, 0, sizeof(*vc_image)); + + if (khrn_image_is_color(image->format)) { + switch (image->format & ~(IMAGE_FORMAT_PRE | IMAGE_FORMAT_LIN)) { + case ABGR_8888_TF: vc_image->type = VC_IMAGE_TF_RGBA32; break; + case XBGR_8888_TF: vc_image->type = VC_IMAGE_TF_RGBX32; break; + case RGBA_4444_TF: vc_image->type = VC_IMAGE_TF_RGBA16; break; + case RGBA_5551_TF: vc_image->type = VC_IMAGE_TF_RGBA5551; break; + case RGB_565_TF: vc_image->type = VC_IMAGE_TF_RGB565; break; + case RGBA_8888_RSO: + case ABGR_8888_RSO: + case ARGB_8888_RSO: vc_image->type = VC_IMAGE_RGBA32; break; //TODO: color channels in the right order? Right upside-downness? (one of these is wrong!) + case RGBX_8888_RSO: vc_image->type = VC_IMAGE_RGBX32; break; + case XBGR_8888_RSO: vc_image->type = VC_IMAGE_RGBX32; break; //TODO: color channels in the right order? Right upside-downness? (one of these is wrong!) + case RGB_565_RSO: vc_image->type = VC_IMAGE_RGB565; break; //if you change these, make sure it doesn't break EGL_KHR_lock_surface + case ARGB_4444_RSO: vc_image->type = VC_IMAGE_RGBA16; break; + default: + UNREACHABLE(); + vc_image->type = 0; + } + } else { + UNREACHABLE(); + vc_image->type = 0; + } + vc_image->width = image->width; + vc_image->height = image->height; + vc_image->pitch = image->stride; + vc_image->size = mem_get_size(image->mh_storage); + vc_image->mem_handle = image->mh_storage; +} + +static INLINE void khrn_image_lock_vcimage(const KHRN_IMAGE_T *image, VC_IMAGE_T *vc_image) +{ + khrn_image_fill_vcimage(image, vc_image); + vc_image->image_data = khrn_image_lock(image); +} + +static INLINE void khrn_image_unlock_vcimage(const KHRN_IMAGE_T *image) +{ + khrn_image_unlock(image); +} +#endif + +/****************************************************************************** +blitting etc +******************************************************************************/ + +typedef enum { + /* + alpha-only images are implicitly black + to convert rgb to luminance, just take the red channel + ignore premultiplied and linear flags + */ + + IMAGE_CONV_GL, + + /* + alpha-only images are implicitly white + to convert rgb to luminance, take a weighted average + observe premultiplied and linear flags + */ + + IMAGE_CONV_VG +} KHRN_IMAGE_CONV_T; + +extern uint32_t khrn_image_wrap_get_pixel(const KHRN_IMAGE_WRAP_T *wrap, uint32_t x, uint32_t y); +extern void khrn_image_wrap_put_pixel(KHRN_IMAGE_WRAP_T *wrap, uint32_t x, uint32_t y, uint32_t pixel); +extern void khrn_image_wrap_put_etc1_block(KHRN_IMAGE_WRAP_T *wrap, uint32_t x, uint32_t y, uint32_t word0, uint32_t word1); + +extern uint32_t khrn_image_pixel_to_rgba(KHRN_IMAGE_FORMAT_T format, uint32_t pixel, KHRN_IMAGE_CONV_T conv); +extern uint32_t khrn_image_rgba_to_pixel(KHRN_IMAGE_FORMAT_T format, uint32_t rgba, KHRN_IMAGE_CONV_T conv); + +extern uint32_t khrn_image_rgba_convert_pre_lin(KHRN_IMAGE_FORMAT_T dst_format, KHRN_IMAGE_FORMAT_T src_format, uint32_t rgba); +extern uint32_t khrn_image_rgba_convert_l_pre_lin(KHRN_IMAGE_FORMAT_T dst_format, KHRN_IMAGE_FORMAT_T src_format, uint32_t rgba); + +extern void khrn_image_wrap_clear_region( + KHRN_IMAGE_WRAP_T *wrap, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + uint32_t rgba, /* rgba non-lin, unpre */ + KHRN_IMAGE_CONV_T conv); +extern void khrn_image_wrap_copy_region( + KHRN_IMAGE_WRAP_T *dst, uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + const KHRN_IMAGE_WRAP_T *src, uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv); +extern void khrn_image_wrap_copy_scissor_regions( + KHRN_IMAGE_WRAP_T *dst, uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + const KHRN_IMAGE_WRAP_T *src, uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv, + const int32_t *scissor_rects, uint32_t scissor_rects_count); +extern void khrn_image_wrap_convert(KHRN_IMAGE_WRAP_T *dst, const KHRN_IMAGE_WRAP_T *src, KHRN_IMAGE_CONV_T conv); +extern void khrn_image_wrap_copy_stencil_channel(KHRN_IMAGE_WRAP_T *dst, const KHRN_IMAGE_WRAP_T *src); +extern void khrn_image_wrap_subsample(KHRN_IMAGE_WRAP_T *dst, const KHRN_IMAGE_WRAP_T *src); +bool khrn_image_wrap_copy_region_tlb( + MEM_HANDLE_T dst_handle, KHRN_IMAGE_INTERLOCK_WRAP_T *dst, + uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + MEM_HANDLE_T src_handle, KHRN_IMAGE_INTERLOCK_WRAP_T *src, + uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv); +void khrn_image_wrap_copy_region_client( + KHRN_IMAGE_INTERLOCK_WRAP_T *dst, uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + KHRN_IMAGE_INTERLOCK_WRAP_T *src, uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv); +void khrn_image_wrap_copy_region_server( + KHRN_IMAGE_INTERLOCK_WRAP_T *dst, uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + KHRN_IMAGE_INTERLOCK_WRAP_T *src, uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv); + +/* + KHRN_IMAGE_T functions (forward to the KHRN_IMAGE_WRAP_T functions above) +*/ + +extern void khrn_image_clear_region( + KHRN_IMAGE_T *image, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + uint32_t rgba, /* non-lin, unpre */ + KHRN_IMAGE_CONV_T conv); +extern void khrn_image_copy_region( + KHRN_IMAGE_T *dst, uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + const KHRN_IMAGE_T *src, uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv); +extern void khrn_image_copy_region_flip( + KHRN_IMAGE_T *dst, uint32_t dst_x, uint32_t dst_y, + uint32_t width, uint32_t height, + const KHRN_IMAGE_T *src, uint32_t src_x, uint32_t src_y, + KHRN_IMAGE_CONV_T conv); +extern void khrn_image_convert_master(KHRN_IMAGE_T *dst, const KHRN_IMAGE_T *src, KHRN_IMAGE_CONV_T conv); +extern void khrn_image_copy_stencil_channel(KHRN_IMAGE_T *dst, const KHRN_IMAGE_T *src); +extern void khrn_image_subsample(KHRN_IMAGE_T *dst, const KHRN_IMAGE_T *src); + +extern bool khrn_image_alloc_storage(KHRN_IMAGE_T *image, const char *description); + +#endif diff --git a/middleware/khronos/common/khrn_interlock.h b/middleware/khronos/common/khrn_interlock.h new file mode 100755 index 0000000..c02a17a --- /dev/null +++ b/middleware/khronos/common/khrn_interlock.h @@ -0,0 +1,74 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_INTERLOCK_H +#define KHRN_INTERLOCK_H + +#include "middleware/khronos/common/khrn_mem.h" +#include "middleware/khronos/egl/egl_disp.h" + +/* should define KHRN_INTERLOCK_USER_T, KHRN_INTERLOCK_USER_NONE, + * KHRN_INTERLOCK_USER_TEMP, KHRN_INTERLOCK_USER_WRITING, and + * KHRN_INTERLOCK_EXTRA_T */ +#include "middleware/khronos/common/2708/khrn_interlock_filler_4.h" + +typedef struct { + EGL_DISP_IMAGE_HANDLE_T disp_image_handle; + KHRN_INTERLOCK_USER_T users; + KHRN_INTERLOCK_EXTRA_T extra; +} KHRN_INTERLOCK_T; + +/* + platform-independent implementations +*/ + +extern void khrn_interlock_init(KHRN_INTERLOCK_T *interlock); +extern void khrn_interlock_term(KHRN_INTERLOCK_T *interlock); + +extern bool khrn_interlock_read(KHRN_INTERLOCK_T *interlock, KHRN_INTERLOCK_USER_T user); /* user allowed to be KHRN_INTERLOCK_USER_NONE */ +extern bool khrn_interlock_write(KHRN_INTERLOCK_T *interlock, KHRN_INTERLOCK_USER_T user); /* user allowed to be KHRN_INTERLOCK_USER_NONE */ +extern KHRN_INTERLOCK_USER_T khrn_interlock_get_writer(KHRN_INTERLOCK_T *interlock); +extern bool khrn_interlock_release(KHRN_INTERLOCK_T *interlock, KHRN_INTERLOCK_USER_T user); + +extern bool khrn_interlock_write_would_block(KHRN_INTERLOCK_T *interlock); + +extern void khrn_interlock_invalidate(KHRN_INTERLOCK_T *interlock); +extern bool khrn_interlock_is_invalid(KHRN_INTERLOCK_T *interlock); + +/* + platform-dependent implementations +*/ + +extern void khrn_interlock_extra_init(KHRN_INTERLOCK_T *interlock); +extern void khrn_interlock_extra_term(KHRN_INTERLOCK_T *interlock); + +extern void khrn_interlock_read_immediate(KHRN_INTERLOCK_T *interlock); +extern void khrn_interlock_write_immediate(KHRN_INTERLOCK_T *interlock); + +extern void khrn_interlock_flush(KHRN_INTERLOCK_USER_T user); + +#endif diff --git a/middleware/khronos/common/khrn_map.h b/middleware/khronos/common/khrn_map.h new file mode 100755 index 0000000..84967cf --- /dev/null +++ b/middleware/khronos/common/khrn_map.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_MAP_H +#define KHRN_MAP_H + +#define khrn_generic_map(X) khrn_map_##X +#define KHRN_GENERIC_MAP(X) KHRN_MAP_##X +#define KHRN_GENERIC_MAP_KEY_T uint32_t +#define KHRN_GENERIC_MAP_VALUE_T MEM_HANDLE_T +#define KHRN_GENERIC_MAP_RELOCATABLE + +#ifdef KHRN_MAP_C + #include "interface/khronos/common/khrn_int_generic_map.c" +#else + #include "interface/khronos/common/khrn_int_generic_map.h" +#endif + +#undef KHRN_GENERIC_MAP_RELOCATABLE +#undef KHRN_GENERIC_MAP_VALUE_T +#undef KHRN_GENERIC_MAP_KEY_T +#undef KHRN_GENERIC_MAP +#undef khrn_generic_map + +#endif diff --git a/middleware/khronos/common/khrn_mem.h b/middleware/khronos/common/khrn_mem.h new file mode 100755 index 0000000..6c4bd60 --- /dev/null +++ b/middleware/khronos/common/khrn_mem.h @@ -0,0 +1,30 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/khronos/common/khrn_int_util.h" + +#include "vcfw/rtos/common/rtos_common_mem.h" diff --git a/middleware/khronos/common/khrn_misc.h b/middleware/khronos/common/khrn_misc.h new file mode 100755 index 0000000..589e903 --- /dev/null +++ b/middleware/khronos/common/khrn_misc.h @@ -0,0 +1,90 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_MISC_H +#define KHRN_MISC_H + +//if you want KHRN_USE_VCHIQ define it in the platform makefile +//e.g in vcfw/platform/broadcom/2763dbrev2/2763dbrev2_linux.mk +//#ifndef KHRN_USE_VCHIQ +//#define KHRN_USE_VCHIQ +//#endif + +#include "middleware/khronos/common/khrn_hw.h" + +#ifndef V3D_LEAN +#include "middleware/khronos/dispatch/khrn_dispatch.h" +#endif + +#include "interface/khronos/common/khrn_int_misc_impl.h" + +#include "interface/khronos/include/GLES/gl.h" +#include "interface/khronos/include/GLES/glext.h" +#include "interface/khronos/glxx/glxx_int_attrib.h" +#include "interface/khronos/include/VG/openvg.h" +#include "interface/khronos/include/VG/vgext.h" +#include "interface/khronos/include/VG/vgu.h" +#include "interface/khronos/vg/vg_int.h" +#include "interface/khronos/vg/vg_int_mat3x3.h" +#include "interface/khronos/include/EGL/egl.h" +#include "interface/khronos/include/EGL/eglext.h" +#include "interface/khronos/common/khrn_int_image.h" +#include "interface/khronos/egl/egl_int.h" +#ifndef KHRN_NO_WFC +#include "interface/khronos/wf/wfc_int.h" +#include "middleware/khronos/wf/wfc_server_stream.h" +#endif + +typedef struct { + KHRONOS_DISPATCH_FUNC *dispatch; + KHRN_SYNC_MASTER_WAIT_FUNC *sync_master_wait; + KHRN_SPECIFY_EVENT_FUNC *specify_event; + KHRN_DO_SUSPEND_RESUME_FUNC *do_suspend_resume; + +#define KHRN_IMPL_STRUCT +#include "interface/khronos/glxx/gl11_int_impl.h" +#include "interface/khronos/glxx/gl20_int_impl.h" +#include "interface/khronos/glxx/glxx_int_impl.h" +#include "interface/khronos/vg/vg_int_impl.h" +#include "interface/khronos/egl/egl_int_impl.h" +#include "interface/khronos/common/khrn_int_misc_impl.h" +#undef KHRN_IMPL_STRUCT +} KHRONOS_FUNC_TABLE_T; + +typedef const KHRONOS_FUNC_TABLE_T *KHRONOS_GET_FUNC_TABLE_FUNC(void); + +#ifdef USE_VCHIQ_ARM +extern void khrn_misc_set_connection_pid(uint32_t pid_0, uint32_t pid_1); +#endif +#ifdef RPC_DIRECT +/* No way of checking this in RPC_DIRECT mode, that I can think of */ +#define KHRN_ASSERT_IN_MASTER_THREAD +#else +extern bool khrn_in_master_thread(); +#define KHRN_ASSERT_IN_MASTER_THREAD vcos_assert(khrn_in_master_thread()) +#endif +#endif diff --git a/middleware/khronos/common/khrn_pid_map.h b/middleware/khronos/common/khrn_pid_map.h new file mode 100755 index 0000000..7b117d0 --- /dev/null +++ b/middleware/khronos/common/khrn_pid_map.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_PID_MAP_H +#define KHRN_PID_MAP_H + +#include "middleware/khronos/common/khrn_pid_map_value.h" + +#define khrn_generic_map(X) khrn_pid_map_##X +#define KHRN_GENERIC_MAP(X) KHRN_PID_MAP_##X +#define KHRN_GENERIC_MAP_KEY_T uint32_t +#define KHRN_GENERIC_MAP_VALUE_T KHRN_PID_MAP_VALUE_T +#define KHRN_GENERIC_MAP_RELOCATABLE + +#ifdef KHRN_PID_MAP_C + #include "interface/khronos/common/khrn_int_generic_map.c" +#else + #include "interface/khronos/common/khrn_int_generic_map.h" +#endif + +#undef KHRN_GENERIC_MAP_RELOCATABLE +#undef KHRN_GENERIC_MAP_VALUE_T +#undef KHRN_GENERIC_MAP_KEY_T +#undef KHRN_GENERIC_MAP +#undef khrn_generic_map + +#endif diff --git a/middleware/khronos/common/khrn_pid_map_value.h b/middleware/khronos/common/khrn_pid_map_value.h new file mode 100755 index 0000000..ed0f956 --- /dev/null +++ b/middleware/khronos/common/khrn_pid_map_value.h @@ -0,0 +1,64 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef KHRN_PID_MAP_VALUE_H +#define KHRN_PID_MAP_VALUE_H + +typedef struct KHRN_PID_MAP_VALUE_S { + MEM_HANDLE_T handle; + uint64_t pid; +} KHRN_PID_MAP_VALUE_T; + +static INLINE KHRN_PID_MAP_VALUE_T khrn_pid_map_value_get_none(void) +{ + KHRN_PID_MAP_VALUE_T x = {MEM_INVALID_HANDLE, 0L}; + return x; +} + +static INLINE KHRN_PID_MAP_VALUE_T khrn_pid_map_value_get_deleted(void) +{ + KHRN_PID_MAP_VALUE_T x = {(MEM_HANDLE_T)(MEM_INVALID_HANDLE - 1), 0L}; + return x; +} + +static INLINE void khrn_pid_map_value_acquire(KHRN_PID_MAP_VALUE_T value) +{ + mem_acquire(value.handle); +} + +static INLINE void khrn_pid_map_value_release(KHRN_PID_MAP_VALUE_T value) +{ + mem_release(value.handle); +} + +static INLINE bool khrn_pid_map_value_cmp( + KHRN_PID_MAP_VALUE_T x, + KHRN_PID_MAP_VALUE_T y) +{ + return (x.handle == y.handle) && (x.pid == y.pid); +} + +#endif diff --git a/middleware/khronos/common/khrn_server_pointermap.h b/middleware/khronos/common/khrn_server_pointermap.h new file mode 100755 index 0000000..b9a6a4a --- /dev/null +++ b/middleware/khronos/common/khrn_server_pointermap.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KHRN_SERVER_POINTERMAP_H +#define KHRN_SERVER_POINTERMAP_H + +#define khrn_generic_map(X) khrn_server_pointer_map_##X +#define KHRN_GENERIC_MAP(X) KHRN_SERVER_POINTER_MAP_##X +#define KHRN_GENERIC_MAP_KEY_T uint32_t +#define KHRN_GENERIC_MAP_VALUE_T void * +#define KHRN_GENERIC_MAP_RELOCATABLE + +#ifdef SERVER_POINTER_MAP_C + #include "interface/khronos/common/khrn_int_generic_map.c" +#else + #include "interface/khronos/common/khrn_int_generic_map.h" +#endif + +#undef KHRN_GENERIC_MAP_VALUE_T +#undef KHRN_GENERIC_MAP_KEY_T +#undef KHRN_GENERIC_MAP +#undef khrn_generic_map + +#endif diff --git a/middleware/khronos/dispatch/khrn_dispatch.h b/middleware/khronos/dispatch/khrn_dispatch.h new file mode 100755 index 0000000..5abf709 --- /dev/null +++ b/middleware/khronos/dispatch/khrn_dispatch.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef KHRN_DISPATCH_H +#define KHRN_DISPATCH_H + +#include + +#ifdef KHRN_USE_VCHIQ +#include "interface/vchiq_arm/vchiq.h" + +extern int khronos_dispatch( void *_message, int _length, VCHIQ_SERVICE_HANDLE_T khrn_handle, VCHIU_QUEUE_T *queue, VCOS_SEMAPHORE_T *sem ); +typedef int KHRONOS_DISPATCH_FUNC( void *_message, int _length, VCHIQ_SERVICE_HANDLE_T khrn_handle, VCHIU_QUEUE_T *queue, VCOS_SEMAPHORE_T *sem ); +extern void khdispatch_push_local( uint64_t pid_in, const void *out, uint32_t len); + +#else +#include "interface/vchi/vchi.h" + +extern int khronos_dispatch( void *_message, int _length, VCHI_SERVICE_HANDLE_T _khrn_handle, VCHI_SERVICE_HANDLE_T _khan_handle ); + +typedef int KHRONOS_DISPATCH_FUNC( void *_message, int _length, VCHI_SERVICE_HANDLE_T _khrn_handle, VCHI_SERVICE_HANDLE_T _khan_handle ); +#endif + +extern bool khdispatch_within_workspace( const void *ptr, size_t length ); + +extern void khdispatch_send_async_len(uint32_t command, uint64_t pid_in, uint32_t len, void * msg); + +#endif /* SERVER_DISPATCH_H */ diff --git a/middleware/khronos/egl/egl_disp.h b/middleware/khronos/egl/egl_disp.h new file mode 100755 index 0000000..0ee8200 --- /dev/null +++ b/middleware/khronos/egl/egl_disp.h @@ -0,0 +1,123 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef EGL_DISP_H +#define EGL_DISP_H + +#include "interface/khronos/common/khrn_int_common.h" +#include "middleware/khronos/common/khrn_mem.h" + +extern bool egl_disp_init(void); +extern void egl_disp_term(void); + +typedef enum { + EGL_DISP_HANDLE_INVALID = -1, + EGL_DISP_HANDLE_FORCE_32BIT = (int)0x80000000 +} EGL_DISP_HANDLE_T; + +/* there are only a fixed number of handles available; EGL_DISP_HANDLE_INVALID + * may be returned even when there is plenty of free memory. todo: fix this? + * + * khdispatch_send_async(ASYNC_COMMAND_POST, pid, sem) is called every time an + * image comes off the display + * + * call from master task */ +extern EGL_DISP_HANDLE_T egl_disp_alloc( + uint64_t pid, uint32_t sem, + uint32_t n, const MEM_HANDLE_T *images); + +/* this will not return until all images are off the display + * + * call from master task */ +extern void egl_disp_free(EGL_DISP_HANDLE_T disp_handle); + +/* wait until we're ready to display the next image, ie we're idle. this implies + * we won't call egl_server_platform_display until the next image comes along */ +extern void egl_disp_finish(EGL_DISP_HANDLE_T disp_handle); + +/* equivalent to calling egl_disp_finish on all valid handles */ +extern void egl_disp_finish_all(void); + +/* display the next image in the chain. image_handle is optional -- if provided, + * it is merely checked against the handle of the image to be displayed + * + * we will wait (asynchronously) until the image is actually ready to be + * displayed (ie there are no outstanding writes) before calling + * egl_server_platform_display + * + * images are displayed in sequence (even if they become ready out of sequence) + * + * swap_interval is the minimum display time of the image in v-sync periods. a + * swap_interval of 0 is special: in addition to not requiring any display time + * at all, we won't hold back rendering to wait for the (previous) image to + * come off the display. this can result in tearing + * + * the image parameters (size/stride) are read before returning from this + * function to make resizing (in the case where the storage handle and size + * aren't changed) safe. this won't prevent visual glitches with swap interval + * 0 + * + * call from master task */ +extern void egl_disp_next(EGL_DISP_HANDLE_T disp_handle, + MEM_HANDLE_T image_handle, uint32_t win, uint32_t swap_interval); + +typedef enum { + EGL_DISP_SLOT_HANDLE_INVALID = -1, + EGL_DISP_SLOT_HANDLE_FORCE_32BIT = (int)0x80000000 +} EGL_DISP_SLOT_HANDLE_T; + +/* mark the slot as ready. if skip, the swap interval of the image is forced to + * 0 and we won't actually display the image + * + * call from any task */ +extern void egl_disp_ready(EGL_DISP_SLOT_HANDLE_T slot_handle, bool skip); + +typedef enum { + EGL_DISP_IMAGE_HANDLE_INVALID = -1, + EGL_DISP_IMAGE_HANDLE_FORCE_32BIT = (int)0x80000000 +} EGL_DISP_IMAGE_HANDLE_T; + +/* wait until the image is not on the display. an image is considered to be on + * the display between an egl_disp_next call that queues the image for display + * and the image coming off the display + * + * call from master task */ +extern void egl_disp_wait(EGL_DISP_IMAGE_HANDLE_T image_handle); + +/* post a wait-for-display message to fifo. this function will filter out + * unnecessary waits, but otherwise just forwards (with transformed arguments) + * to khrn_delayed_wait_for_display(). the wait-for-display message itself + * should wait until egl_disp_on() returns false + * + * call egl_disp_post_wait() from master task. call egl_disp_on() from any + * task */ +extern void egl_disp_post_wait(uint32_t fifo, EGL_DISP_IMAGE_HANDLE_T image_handle); +extern bool egl_disp_on(EGL_DISP_HANDLE_T disp_handle, uint32_t pos); + +extern void egl_disp_callback(uint32_t cb_arg); + +#endif diff --git a/middleware/khronos/egl/egl_server.h b/middleware/khronos/egl/egl_server.h new file mode 100755 index 0000000..011ec8f --- /dev/null +++ b/middleware/khronos/egl/egl_server.h @@ -0,0 +1,255 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef EGL_SERVER_H +#define EGL_SERVER_H + +#include "interface/khronos/egl/egl_int.h" +#include "middleware/khronos/common/khrn_map.h" +#include "middleware/khronos/common/khrn_pid_map.h" +#include "middleware/khronos/common/khrn_server_pointermap.h" +#include "middleware/khronos/common/khrn_image.h" +#include "middleware/khronos/common/khrn_hw.h" +#include "middleware/khronos/egl/egl_disp.h" +#include "interface/khronos/include/EGL/egl.h" +#include "interface/khronos/include/EGL/eglext.h" +#include "middleware/khronos/vg/vg_image.h" + +#include "interface/khronos/include/WF/wfc.h" + +// Must be enough for triple-buffering (windows) and mipmaps (pbuffers) +#define EGL_MAX_BUFFERS 12 + +// There is a single global instance of this +typedef struct +{ + KHRN_MAP_T surfaces; + KHRN_MAP_T glcontexts; + KHRN_MAP_T vgcontexts; + KHRN_PID_MAP_T eglimages; + VCOS_MUTEX_T eglimages_lock; + KHRN_MAP_T wintoeglimage;//TODO window ids should be per process? + VCOS_MUTEX_T wintoeglimage_mutex; + +#if EGL_KHR_sync + KHRN_MAP_T syncs; +#endif + + uint32_t next_surface; + uint32_t next_context; + uint32_t next_eglimage; +#if EGL_KHR_sync + uint32_t next_sync; +#endif + + uint64_t pid; //currently selected process id + + uint32_t glversion; //EGL_SERVER_GL11 or EGL_SERVER_GL20. (0 if invalid) + MEM_HANDLE_T glcontext; + MEM_HANDLE_T gldrawsurface; //EGL_SERVER_SURFACE_T + MEM_HANDLE_T glreadsurface; //EGL_SERVER_SURFACE_T + MEM_HANDLE_T vgcontext; + MEM_HANDLE_T vgsurface; //EGL_SERVER_SURFACE_T + + /* + locked_glcontext + + Invariants: + + (EGL_SERVER_STATE_LOCKED_GLCONTEXT) + locked_glcontext == NULL or locked_glcontext is the locked version of glcontext (and we own the lock) + */ + void *locked_glcontext; + /* + locked_vgcontext + + Invariants: + + (EGL_SERVER_STATE_LOCKED_VGCONTEXT) + locked_vgcontext == NULL or locked_vgcontext is the locked version of vgcontext (and we own the lock) + */ + void *locked_vgcontext; + /* + locked_vgcontext_shared + + Invariants: + + (EGL_SERVER_STATE_LOCKED_VGCONTEXT_SHARED) + locked_vgcontext_shared == NULL or locked_vgcontext_shared is the locked version of vgcontext->shared_state (and we own the lock) + */ + void *locked_vgcontext_shared; + /* + locked_vgcontext_shared_objects_storage + + Invariants: + + (EGL_SERVER_STATE_LOCKED_VGCONTEXT_SHARED_OBJECTS_STORAGE) + locked_vgcontext_shared_objects_storage == NULL or locked_vgcontext_shared_objects_storage is the locked version of vgcontext->shared_state->objects->storage (and we own the lock) + */ + void *locked_vgcontext_shared_objects_storage; + +#if EGL_BRCM_perf_monitor + uint32_t perf_monitor_refcount; + uint32_t perf_monitor_lasttime; + + KHRN_PERF_COUNTERS_T perf_monitor_counters; + + MEM_HANDLE_T perf_monitor_images[2]; //KHRN_IMAGE_T +#endif + +#if EGL_BRCM_driver_monitor + uint32_t driver_monitor_refcount; + KHRN_DRIVER_COUNTERS_T driver_monitor_counters; +#endif + +} EGL_SERVER_STATE_T; + +typedef struct +{ + uint32_t name; + + bool mipmap; + uint32_t buffers; + uint32_t back_buffer_index; + /* + mh_color + + Invariant: + + For 0 <= i < buffers + mh_color[i] is a handle to a valid KHRN_IMAGE_T + */ + MEM_HANDLE_T mh_color[EGL_MAX_BUFFERS]; + MEM_HANDLE_T mh_depth; //floating KHRN_IMAGE_T + MEM_HANDLE_T mh_multi; //floating KHRN_IMAGE_T + MEM_HANDLE_T mh_mask; //floating KHRN_IMAGE_T + + uint8_t config_depth_bits; // How many depth bits were requested in config. May not match actual buffer. + uint8_t config_stencil_bits; // How many stencil bits were requested in config. May not match actual buffer. + + uint32_t win; // Opaque handle passed to egl_server_platform_display + uint64_t pid; // Opaque handle to creating process + uint32_t sem; // Opaque handle (probably semaphore name) passed on KHAN channel + + MEM_HANDLE_T mh_bound_texture; + uint32_t swap_interval; + uint32_t semaphoreId; //Symbian needs a handle passed back in Khan, not just surface number + + + //for android, the mh_storage this points at needs updating to point to where rendering happened + uint32_t egl_render_image; + + EGL_DISP_HANDLE_T disp_handle; +} EGL_SERVER_SURFACE_T; + +typedef struct +{ + uint32_t type; + uint32_t condition; + int32_t threshold; + + uint64_t pid; + uint32_t sem; + + bool state; +} EGL_SERVER_SYNC_T; + +#define EGL_SERVER_FIFO_LEN 4 + +typedef struct +{ + uint64_t pid; + + EGLImageKHR egl_image_id; + + struct { + EGLImageKHR egl_images[EGL_SERVER_FIFO_LEN]; + unsigned count; + unsigned read; + unsigned write; + } fifo; + +} EGL_SERVER_WIN_TO_EGL_IMAGE_T; + +#ifdef __cplusplus +extern "C" { +#endif +EGLAPI void EGLAPIENTRY egl_server_startup_hack(void); +#ifdef __cplusplus +} +#endif + +extern bool egl_server_is_empty(void); +extern void egl_server_shutdown(void); + +/* + egl_server_state_initted + + Invariants: + + True iff valid EGL server state exists +*/ +extern bool egl_server_state_initted; +extern EGL_SERVER_STATE_T egl_server_state; + +/* + EGL_SERVER_STATE_T *EGL_GET_SERVER_STATE() + + Returns pointer to EGL server state. + + Implementation notes: + + There is only one of these globally, and it does not need locking and unlocking. + + Preconditions: + + Valid EGL server state exists + + Postconditions: + + Return value is a valid pointer +*/ + +static INLINE EGL_SERVER_STATE_T *EGL_GET_SERVER_STATE(void) +{ + vcos_assert(egl_server_state_initted); + return &egl_server_state; +} + +extern void egl_server_unlock(void); + +#include "interface/khronos/egl/egl_int_impl.h" + +extern void egl_khr_fence_update(void); + +extern void egl_update_current_rendering_image(uint64_t pid, uint32_t window, MEM_HANDLE_T himage); + +#if EGL_BRCM_perf_monitor +extern void egl_brcm_perf_monitor_update(); +#endif + +#endif diff --git a/middleware/khronos/ext/egl_khr_image.h b/middleware/khronos/ext/egl_khr_image.h new file mode 100755 index 0000000..5a073fe --- /dev/null +++ b/middleware/khronos/ext/egl_khr_image.h @@ -0,0 +1,68 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "interface/khronos/common/khrn_int_common.h" +#include "interface/khronos/include/EGL/egl.h" +#include "interface/khronos/include/EGL/eglext.h" +#include "middleware/khronos/egl/egl_server.h" +#include "middleware/imageconv/imageconv.h" +#include "vcinclude/vc_image_types.h" + + +typedef struct EGL_IMAGE_T { + uint64_t pid; + + /* + * Handle to a KHRN_IMAGE_T, whose format is required to be something + * suitable for texturing directly from. If NULL, then use external.convert + * below to make one (in glBindTexture_impl probably). + */ + MEM_HANDLE_T mh_image; + + bool flip_y; + + /* + * Any kind of "external" image-- i.e. that can't be used directly for + * texturing. + */ + struct + { + /* + * Handle to an object that convert knows how to convert into a + * KHRN_IMAGE_T suitable for texturing from, e.g. a multimedia image. + */ + MEM_HANDLE_T src; + const IMAGE_CONVERT_CLASS_T *convert; + KHRN_IMAGE_FORMAT_T conv_khrn_format; + VC_IMAGE_TYPE_T conv_vc_format; + uint32_t src_updated; + uint32_t src_converted; + } external; + +} EGL_IMAGE_T; + +extern void egl_image_term(void *v, uint32_t size); diff --git a/middleware/khronos/vg/2708/vg_config_filler_4.h b/middleware/khronos/vg/2708/vg_config_filler_4.h new file mode 100755 index 0000000..045360b --- /dev/null +++ b/middleware/khronos/vg/2708/vg_config_filler_4.h @@ -0,0 +1,33 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VG_CONFIG_FILLER_4_H +#define VG_CONFIG_FILLER_4_H + +#define VG_CONFIG_RENDERER "VideoCore IV HW" + +#endif diff --git a/middleware/khronos/vg/vg_image.h b/middleware/khronos/vg/vg_image.h new file mode 100755 index 0000000..e8fb0d7 --- /dev/null +++ b/middleware/khronos/vg/vg_image.h @@ -0,0 +1,132 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VG_IMAGE_H +#define VG_IMAGE_H + +#include "middleware/khronos/common/khrn_image.h" +#include "middleware/khronos/common/khrn_mem.h" +#include "interface/khronos/include/VG/openvg.h" + +/****************************************************************************** +image +******************************************************************************/ + +typedef struct { + VGImageFormat format; + uint32_t allowed_quality; + int32_t width; + int32_t height; +} VG_IMAGE_BPRINT_T; + +extern void vg_image_bprint_from_stem( + MEM_HANDLE_T handle, + VGImageFormat format, + uint32_t allowed_quality, + int32_t width, int32_t height); + +extern void vg_image_bprint_term(void *, uint32_t); + +static INLINE bool vg_is_image_bprint(MEM_HANDLE_T handle) +{ + return mem_get_term(handle) == vg_image_bprint_term; +} + +typedef struct { + uint32_t allowed_quality; + + MEM_HANDLE_T image; /* KHRN_IMAGE_T */ + uint32_t image_width; + uint32_t image_height; + KHRN_IMAGE_FORMAT_T image_format; +} VG_IMAGE_T; + +extern bool vg_image_from_bprint(MEM_HANDLE_T handle); +extern VGImageFormat vg_image_get_external_format(KHRN_IMAGE_FORMAT_T format); /* (VGImageFormat)-1 if internal format isn't permitted */ +extern MEM_HANDLE_T vg_image_alloc_from_image(MEM_HANDLE_T src_handle); +extern void vg_image_from_stem_and_image(MEM_HANDLE_T handle, MEM_HANDLE_T src_handle); + +extern void vg_image_term(void *, uint32_t); + +static INLINE bool vg_is_image(MEM_HANDLE_T handle) +{ + return mem_get_term(handle) == vg_image_term; +} + +extern VG_IMAGE_T *vg_image_lock_adam( + uint32_t *x, uint32_t *y, uint32_t *width, uint32_t *height, + MEM_HANDLE_T *handle); + +static INLINE bool vg_image_share_storage(VG_IMAGE_T *image, VG_IMAGE_T *other) +{ + return image->image == other->image; +} + +extern bool vg_image_leak(VG_IMAGE_T *image); + +/****************************************************************************** +child image +******************************************************************************/ + +/* + the same structure is used for both blueprints and instantiated child images + + in the blueprint case, parent is either a VG_IMAGE_T, a VG_IMAGE_BPRINT_T, or + a VG_CHILD_IMAGE_T (of either type) + + in the instantiated case, parent is either a VG_IMAGE_T or an + instantiated-type VG_CHILD_IMAGE_T +*/ + +typedef struct { + MEM_HANDLE_T parent; + uint16_t x, y, width, height; +} VG_CHILD_IMAGE_T; + +extern void vg_child_image_bprint_from_stem( + MEM_HANDLE_T handle, + MEM_HANDLE_T parent_handle, + int32_t x, int32_t y, + int32_t width, int32_t height); + +extern void vg_child_image_bprint_term(void *, uint32_t); + +static INLINE bool vg_is_child_image_bprint(MEM_HANDLE_T handle) +{ + return mem_get_term(handle) == vg_child_image_bprint_term; +} + +extern bool vg_child_image_from_bprint(MEM_HANDLE_T handle); + +extern void vg_child_image_term(void *, uint32_t); + +static INLINE bool vg_is_child_image(MEM_HANDLE_T handle) +{ + return mem_get_term(handle) == vg_child_image_term; +} + +#endif diff --git a/middleware/khronos/wf/wfc_server_stream.h b/middleware/khronos/wf/wfc_server_stream.h new file mode 100755 index 0000000..727b575 --- /dev/null +++ b/middleware/khronos/wf/wfc_server_stream.h @@ -0,0 +1,95 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WFC_SERVER_STREAM_H +#define WFC_SERVER_STREAM_H + +#include "interface/khronos/include/WF/wfc.h" +#include "interface/khronos/wf/wfc_int.h" +#include "helpers/vc_image/vc_image.h" +#include "interface/khronos/common/khrn_int_common.h" +#include "interface/khronos/include/EGL/eglext.h" + +#ifdef ANDROID +// Special SurfaceFlinger stream ID (cf SF_DISPMANX_ID in khrn_client_platform_android.c) +#define SF_STREAM_ID 0x2000000F +#endif + +#define WFC_QUEUE_OK 0 +#define WFC_QUEUE_NO_IMAGE (1 << 0) +#define WFC_QUEUE_WRONG_DIMENSIONS (1 << 1) +#define WFC_QUEUE_WRONG_PIXEL_FORMAT (1 << 2) +#define WFC_QUEUE_NO_ASSOC_ELEMENT (1 << 3) +#define WFC_QUEUE_STREAM_PLACEHOLDER (1 << 4) +#define WFC_QUEUE_BUSY (1 << 5) +#define WFC_QUEUE_NOT_REGISTERED (1 << 6) +#define WFC_QUEUE_NO_MEMORY (1 << 7) + +/** Set the given buffer to be the new front buffer for the stream. + * + * If the vc_image is NULL, it means that there is no front buffer for the stream, + * and NULL will be returned when the front buffer is requested. The callback function + * shall not be called in this situation. + * + * If the callback function is not NULL, it shall either be called when the buffer + * is no longer in use (either as the current front buffer, or because it is part of a + * current composition), or when it has been used in composition, according to whether + * the WFC_IMAGE_CB_ON_COMPOSE flag is set. + * + * @param stream The client handle for the stream. + * @param vc_image Details of the new buffer, or NULL. + * @param flags Flags associated with the new buffer, combination of WFC_IMAGE_FLAGS_T + * values. + * @param cb_func Function to call when either the buffer is no longer in use or + * has been used in composition, or NULL. + * @param cb_data Data to be passed to the callback function, or NULL. + * @return TBD + */ +uint32_t wfc_server_stream_set_front_image( + WFCNativeStreamType stream, const VC_IMAGE_T *vc_image, int flags, + WFC_SERVER_STREAM_CALLBACK_T cb_func, void *cb_data); + +/** Signal to client that images are available for writing to. For use when + * renderer is allocating its images. + * + * @param stream The client handle for the stream. + * @param num_of_images The number of images allocated by the renderer. + */ +void wfc_server_stream_signal_images_avail(WFCNativeStreamType stream, uint32_t num_of_images); + +// Request update to destination and/or source rectangles. Sends request to host +// for executing via WF-C. +void wfc_server_stream_update_rects(WFCNativeStreamType stream, + const VC_RECT_T *vc_dest_rect, const VC_RECT_T *vc_src_rect); + +//------------------------------------------------------------------------------ +// Functions called only by Khronos dispatcher + +bool wfc_server_stream_is_empty(void); + +#endif /* WFC_SERVER_STREAM_H_ */ + diff --git a/middleware/openmaxil/CMakeLists.txt b/middleware/openmaxil/CMakeLists.txt new file mode 100755 index 0000000..3e9c5f9 --- /dev/null +++ b/middleware/openmaxil/CMakeLists.txt @@ -0,0 +1,52 @@ +# ============================================================================= +# Copyright (c) 2012 Broadcom Europe Limited. +# All rights reserved. +# +# FILE DESCRIPTION +# CMake build file for OpenMAX IL. +# ============================================================================= + +cmake_minimum_required (VERSION 2.8) + +if (VIDEOCORE_SIMULATION) + + # VC-simulation build. + + add_library (openmaxil SHARED + # core/ilcore.c + core/ril_top.c + core/ilmalloc.c + core/ilvlls.c + core/ilfifo.c + core/ilutil.c) + + add_subdirectory (components) + +else () + + # Host build. + # + # Provides a host OpenMAX IL core as well as access to OpenMAX IL core and + # components running on Videocore with the help of the ILCS library. + # + # Ideally, we would want the native 64-bit OMX_TICKS datatype but on + # Videocore, structures are word-aligned, even if they have 64-bit members. + # + # remove_definitions(-DOMX_SKIP64BIT) + + add_definitions (-DVLL_PATH_PREFIX="${VMCS_PLUGIN_DIR}/") + + add_library (openmaxil SHARED + ../../host_applications/framework/common/ilcore.c +# core/ilcore.c +# core/ril_top.c +# core/ilmalloc.c +# core/ilvlls.c +# core/ilfifo.c + ) + + target_link_libraries (openmaxil vcilcs vcos dl) + +endif () + +install (TARGETS openmaxil DESTINATION lib) diff --git a/opensrc/helpers/libfdt/CMakeLists.txt b/opensrc/helpers/libfdt/CMakeLists.txt new file mode 100755 index 0000000..f57c61c --- /dev/null +++ b/opensrc/helpers/libfdt/CMakeLists.txt @@ -0,0 +1,13 @@ +include_directories(.) + +add_library(fdt + fdt.c + fdt_empty_tree.c + fdt_ro.c + fdt_rw.c + fdt_sw.c + fdt_strerror.c + fdt_wip.c) + +INSTALL(FILES) + diff --git a/opensrc/helpers/libfdt/fdt.c b/opensrc/helpers/libfdt/fdt.c new file mode 100755 index 0000000..22286a1 --- /dev/null +++ b/opensrc/helpers/libfdt/fdt.c @@ -0,0 +1,251 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int fdt_check_header(const void *fdt) +{ + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + /* Unfinished sequential-write blob */ + if (fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) +{ + unsigned absoffset = offset + fdt_off_dt_struct(fdt); + + if ((absoffset < offset) + || ((absoffset + len) < absoffset) + || (absoffset + len) > fdt_totalsize(fdt)) + return NULL; + + if (fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + return _fdt_offset_ptr(fdt, offset); +} + +uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) +{ + const fdt32_t *tagp, *lenp; + uint32_t tag; + int offset = startoffset; + const char *p; + + *nextoffset = -FDT_ERR_TRUNCATED; + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (!tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu(*tagp); + offset += FDT_TAGSIZE; + + *nextoffset = -FDT_ERR_BADSTRUCTURE; + switch (tag) { + case FDT_BEGIN_NODE: + /* skip name */ + do { + p = fdt_offset_ptr(fdt, offset++, 1); + } while (p && (*p != '\0')); + if (!p) + return FDT_END; /* premature end */ + break; + + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (!lenp) + return FDT_END; /* premature end */ + /* skip-name offset, length and value */ + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + + fdt32_to_cpu(*lenp); + break; + + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return FDT_END; + } + + if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) + return FDT_END; /* premature end */ + + *nextoffset = FDT_TAGALIGN(offset); + return tag; +} + +int _fdt_check_node_offset(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int _fdt_check_prop_offset(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_next_node(const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) + if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) + return nextoffset; + + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth && ((--(*depth)) < 0)) + return nextoffset; + break; + + case FDT_END: + if ((nextoffset >= 0) + || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) + return -FDT_ERR_NOTFOUND; + else + return nextoffset; + } + } while (tag != FDT_BEGIN_NODE); + + return offset; +} + +int fdt_first_subnode(const void *fdt, int offset) +{ + int depth = 0; + + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth != 1) + return -FDT_ERR_NOTFOUND; + + return offset; +} + +int fdt_next_subnode(const void *fdt, int offset) +{ + int depth = 1; + + /* + * With respect to the parent, the depth of the next subnode will be + * the same as the last. + */ + do { + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth < 1) + return -FDT_ERR_NOTFOUND; + } while (depth > 1); + + return offset; +} + +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) +{ + int len = strlen(s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memcmp(p, s, len) == 0) + return p; + return NULL; +} + +int fdt_move(const void *fdt, void *buf, int bufsize) +{ + FDT_CHECK_HEADER(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/opensrc/helpers/libfdt/fdt.h b/opensrc/helpers/libfdt/fdt.h new file mode 100755 index 0000000..526aedb --- /dev/null +++ b/opensrc/helpers/libfdt/fdt.h @@ -0,0 +1,111 @@ +#ifndef _FDT_H +#define _FDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 __ASSEMBLY__ + +struct fdt_header { + fdt32_t magic; /* magic word FDT_MAGIC */ + fdt32_t totalsize; /* total size of DT block */ + fdt32_t off_dt_struct; /* offset to structure */ + fdt32_t off_dt_strings; /* offset to strings */ + fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ + fdt32_t version; /* format version */ + fdt32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + fdt32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + fdt32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + fdt32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + fdt64_t address; + fdt64_t size; +}; + +struct fdt_node_header { + fdt32_t tag; + char name[0]; +}; + +struct fdt_property { + fdt32_t tag; + fdt32_t len; + fdt32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(fdt32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(fdt32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) + +#endif /* _FDT_H */ diff --git a/opensrc/helpers/libfdt/fdt_empty_tree.c b/opensrc/helpers/libfdt/fdt_empty_tree.c new file mode 100755 index 0000000..f72d13b --- /dev/null +++ b/opensrc/helpers/libfdt/fdt_empty_tree.c @@ -0,0 +1,84 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2012 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int fdt_create_empty_tree(void *buf, int bufsize) +{ + int err; + + err = fdt_create(buf, bufsize); + if (err) + return err; + + err = fdt_finish_reservemap(buf); + if (err) + return err; + + err = fdt_begin_node(buf, ""); + if (err) + return err; + + err = fdt_end_node(buf); + if (err) + return err; + + err = fdt_finish(buf); + if (err) + return err; + + return fdt_open_into(buf, buf, bufsize); +} + diff --git a/opensrc/helpers/libfdt/fdt_ro.c b/opensrc/helpers/libfdt/fdt_ro.c new file mode 100755 index 0000000..5f59f72 --- /dev/null +++ b/opensrc/helpers/libfdt/fdt_ro.c @@ -0,0 +1,679 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int _fdt_nodename_eq(const void *fdt, int offset, + const char *s, int len) +{ + const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); + + if (! p) + /* short match */ + return 0; + + if (memcmp(p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr(s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; +} + +static int _fdt_string_eq(const void *fdt, int stroffset, + const char *s, int len) +{ + const char *p = fdt_string(fdt, stroffset); + + return (strlen(p) == len) && (memcmp(p, s, len) == 0); +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + FDT_CHECK_HEADER(fdt); + *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); + *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i = 0; + + while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) + i++; + return i; +} + +static int _nextprop(const void *fdt, int offset) +{ + uint32_t tag; + int nextoffset; + + do { + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + if (nextoffset >= 0) + return -FDT_ERR_BADSTRUCTURE; + else + return nextoffset; + + case FDT_PROP: + return offset; + } + offset = nextoffset; + } while (tag == FDT_NOP); + + return -FDT_ERR_NOTFOUND; +} + +int fdt_subnode_offset_namelen(const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + FDT_CHECK_HEADER(fdt); + + for (depth = 0; + (offset >= 0) && (depth >= 0); + offset = fdt_next_node(fdt, offset, &depth)) + if ((depth == 1) + && _fdt_nodename_eq(fdt, offset, name, namelen)) + return offset; + + if (depth < 0) + return -FDT_ERR_NOTFOUND; + return offset; /* error */ +} + +int fdt_subnode_offset(const void *fdt, int parentoffset, + const char *name) +{ + return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) +{ + const char *end = path + namelen; + const char *p = path; + int offset = 0; + + FDT_CHECK_HEADER(fdt); + + /* see if we have an alias */ + if (*path != '/') { + const char *q = memchr(path, '/', end - p); + + if (!q) + q = end; + + p = fdt_get_alias_namelen(fdt, p, q - p); + if (!p) + return -FDT_ERR_BADPATH; + offset = fdt_path_offset(fdt, p); + + p = q; + } + + while (p < end) { + const char *q; + + while (*p == '/') { + p++; + if (p == end) + return offset; + } + q = memchr(p, '/', end - p); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +int fdt_path_offset(const void *fdt, const char *path) +{ + return fdt_path_offset_namelen(fdt, path, strlen(path)); +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); + int err; + + if (((err = fdt_check_header(fdt)) != 0) + || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) + goto fail; + + if (len) + *len = strlen(nh->name); + + return nh->name; + + fail: + if (len) + *len = err; + return NULL; +} + +int fdt_first_property_offset(const void *fdt, int nodeoffset) +{ + int offset; + + if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) + return offset; + + return _nextprop(fdt, offset); +} + +int fdt_next_property_offset(const void *fdt, int offset) +{ + if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) + return offset; + + return _nextprop(fdt, offset); +} + +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp) +{ + int err; + const struct fdt_property *prop; + + if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { + if (lenp) + *lenp = err; + return NULL; + } + + prop = _fdt_offset_ptr(fdt, offset); + + if (lenp) + *lenp = fdt32_to_cpu(prop->len); + + return prop; +} + +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int offset, + const char *name, + int namelen, int *lenp) +{ + for (offset = fdt_first_property_offset(fdt, offset); + (offset >= 0); + (offset = fdt_next_property_offset(fdt, offset))) { + const struct fdt_property *prop; + + if ((prop = fdt_get_property_by_offset(fdt, offset, lenp)) == 0) { + offset = -FDT_ERR_INTERNAL; + break; + } + if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), + name, namelen)) + return prop; + } + + if (lenp) + *lenp = offset; + return NULL; +} + +const struct fdt_property *fdt_get_property(const void *fdt, + int nodeoffset, + const char *name, int *lenp) +{ + return fdt_get_property_namelen(fdt, nodeoffset, name, + strlen(name), lenp); +} + +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); + if (! prop) + return NULL; + + return prop->data; +} + +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset(fdt, offset, lenp); + if (!prop) + return NULL; + if (namep) + *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + return prop->data; +} + +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); +} + +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) +{ + const fdt32_t *php; + int len; + + /* FIXME: This is a bit sub-optimal, since we potentially scan + * over all the properties twice. */ + php = fdt_getprop(fdt, nodeoffset, "phandle", &len); + if (!php || (len != sizeof(*php))) { + php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof(*php))) + return 0; + } + + return fdt32_to_cpu(*php); +} + +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen) +{ + int aliasoffset; + + aliasoffset = fdt_path_offset(fdt, "/aliases"); + if (aliasoffset < 0) + return NULL; + + return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); +} + +const char *fdt_get_alias(const void *fdt, const char *name) +{ + return fdt_get_alias_namelen(fdt, name, strlen(name)); +} + +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + FDT_CHECK_HEADER(fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + while (pdepth > depth) { + do { + p--; + } while (buf[p-1] != '/'); + pdepth--; + } + + if (pdepth >= depth) { + name = fdt_get_name(fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) { + memcpy(buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + } + + if (offset == nodeoffset) { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return 0; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + FDT_CHECK_HEADER(fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_depth(const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); + if (err) + return (err < 0) ? err : -FDT_ERR_INTERNAL; + return nodedepth; +} + +int fdt_parent_offset(const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth(fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset(fdt, nodeoffset, + nodedepth - 1, NULL); +} + +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + val = fdt_getprop(fdt, offset, propname, &len); + if (val && (len == proplen) + && (memcmp(val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) +{ + int offset; + + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we + * potentially scan each property of a node in + * fdt_get_phandle(), then if that didn't find what + * we want, we scan over them again making our way to the next + * node. Still it's the easiest to implement approach; + * performance can come later. */ + for (offset = fdt_next_node(fdt, -1, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + if (fdt_get_phandle(fdt, offset) == phandle) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const char *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) +{ + const char *list, *end; + int length, count = 0; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return -length; + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + list += length; + count++; + } + + return count; +} + +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string) +{ + int length, len, idx = 0; + const char *list, *end; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return -length; + + len = strlen(string) + 1; + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + if (length == len && memcmp(list, string, length) == 0) + return idx; + + list += length; + idx++; + } + + return -FDT_ERR_NOTFOUND; +} + +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int idx, + int *lenp) +{ + const char *list, *end; + int length; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) { + if (lenp) + *lenp = length; + + return NULL; + } + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) { + if (lenp) + *lenp = -FDT_ERR_BADVALUE; + + return NULL; + } + + if (idx == 0) { + if (lenp) + *lenp = length - 1; + + return list; + } + + list += length; + idx--; + } + + if (lenp) + *lenp = -FDT_ERR_NOTFOUND; + + return NULL; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + if (fdt_stringlist_contains(prop, len, compatible)) + return 0; + else + return 1; +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/opensrc/helpers/libfdt/fdt_rw.c b/opensrc/helpers/libfdt/fdt_rw.c new file mode 100755 index 0000000..a41d356 --- /dev/null +++ b/opensrc/helpers/libfdt/fdt_rw.c @@ -0,0 +1,494 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int _fdt_blocks_misordered(const void *fdt, + int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) + || (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) + || (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + struct_size)) + || (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); +} + +static int _fdt_rw_check_header(void *fdt) +{ + FDT_CHECK_HEADER(fdt); + + if (fdt_version(fdt) < 17) + return -FDT_ERR_BADVERSION; + if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_BADLAYOUT; + if (fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define FDT_RW_CHECK_HEADER(fdt) \ + { \ + int __err; \ + if ((__err = _fdt_rw_check_header(fdt)) != 0) \ + return __err; \ + } + +static inline int _fdt_data_size(void *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen) +{ + char *p = splicepoint; + char *end = (char *)fdt + _fdt_data_size(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof(*p); + int err; + err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); + if (err) + return err; + fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _fdt_splice_struct(void *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = _fdt_splice(fdt, p, oldlen, newlen)) != 0) + return err; + + fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _fdt_splice_string(void *fdt, int newlen) +{ + void *p = (char *)fdt + + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = _fdt_splice(fdt, p, 0, newlen)) != 0) + return err; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +static int _fdt_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = _fdt_splice_string(fdt, len); + if (err) + return err; + + memcpy(new, s, len); + return (new - strtab); +} + +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt)); + err = _fdt_splice_mem_rsv(fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64(address); + re->size = cpu_to_fdt64(size); + return 0; +} + +int fdt_del_mem_rsv(void *fdt, int n) +{ + struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n); + int err; + + FDT_RW_CHECK_HEADER(fdt); + + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + err = _fdt_splice_mem_rsv(fdt, re, 1, 0); + if (err) + return err; + return 0; +} + +static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (! (*prop)) + return oldlen; + + if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(len))) != 0) + return err; + + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +static int _fdt_add_property(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int proplen; + int nextoffset; + int namestroff; + int err; + + if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = _fdt_find_add_string(fdt, name); + if (namestroff < 0) + return namestroff; + + *prop = _fdt_offset_ptr_w(fdt, nextoffset); + proplen = sizeof(**prop) + FDT_TAGALIGN(len); + + err = _fdt_splice_struct(fdt, *prop, 0, proplen); + if (err) + return err; + + (*prop)->tag = cpu_to_fdt32(FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32(namestroff); + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +int fdt_set_name(void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen(name); + + err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1), + FDT_TAGALIGN(newlen+1)); + if (err) + return err; + + memcpy(namep, name, newlen+1); + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + memcpy(prop->data, val, len); + return 0; +} + +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err, oldlen, newlen; + + FDT_RW_CHECK_HEADER(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (prop) { + newlen = len + oldlen; + err = _fdt_splice_struct(fdt, prop->data, + FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(newlen)); + if (err) + return err; + prop->len = cpu_to_fdt32(newlen); + memcpy(prop->data + oldlen, val, len); + } else { + err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + memcpy(prop->data, val, len); + } + return 0; +} + +int fdt_delprop(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + FDT_RW_CHECK_HEADER(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + proplen = sizeof(*prop) + FDT_TAGALIGN(len); + return _fdt_splice_struct(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + fdt32_t *endtag; + + FDT_RW_CHECK_HEADER(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + } while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = _fdt_offset_ptr_w(fdt, offset); + nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; + + err = _fdt_splice_struct(fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); + memcpy(nh->name, name, namelen); + endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(void *fdt, int nodeoffset) +{ + int endoffset; + + FDT_RW_CHECK_HEADER(fdt); + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void _fdt_packblocks(const char *old, char *new, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); + fdt_set_off_mem_rsvmap(new, mem_rsv_off); + + memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); + fdt_set_off_dt_struct(new, struct_off); + fdt_set_size_dt_struct(new, struct_size); + + memmove(new + strings_off, old + fdt_off_dt_strings(old), + fdt_size_dt_strings(old)); + fdt_set_off_dt_strings(new, strings_off); + fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); +} + +int fdt_open_into(const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + const char *fdtstart = fdt; + const char *fdtend = fdtstart + fdt_totalsize(fdt); + char *tmp; + + FDT_CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + if (struct_size < 0) + return struct_size; + } + + if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) { + /* no further work necessary */ + err = fdt_move(fdt, buf, bufsize); + if (err) + return err; + fdt_set_version(buf, 17); + fdt_set_size_dt_struct(buf, struct_size); + fdt_set_totalsize(buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings(fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + /* First attempt to build converted tree at beginning of buffer */ + tmp = buf; + /* But if that overlaps with the old tree... */ + if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { + /* Try right after the old tree instead */ + tmp = (char *)(uintptr_t)fdtend; + if ((tmp + newsize) > ((char *)buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); + fdt_set_totalsize(buf, bufsize); + fdt_set_version(buf, 17); + fdt_set_last_comp_version(buf, 16); + fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + + return 0; +} + +int fdt_pack(void *fdt) +{ + int mem_rsv_size; + + FDT_RW_CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_set_totalsize(fdt, _fdt_data_size(fdt)); + + return 0; +} diff --git a/opensrc/helpers/libfdt/fdt_strerror.c b/opensrc/helpers/libfdt/fdt_strerror.c new file mode 100755 index 0000000..e6c3cee --- /dev/null +++ b/opensrc/helpers/libfdt/fdt_strerror.c @@ -0,0 +1,96 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +struct fdt_errtabent { + const char *str; +}; + +#define FDT_ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct fdt_errtabent fdt_errtable[] = { + FDT_ERRTABENT(FDT_ERR_NOTFOUND), + FDT_ERRTABENT(FDT_ERR_EXISTS), + FDT_ERRTABENT(FDT_ERR_NOSPACE), + + FDT_ERRTABENT(FDT_ERR_BADOFFSET), + FDT_ERRTABENT(FDT_ERR_BADPATH), + FDT_ERRTABENT(FDT_ERR_BADSTATE), + + FDT_ERRTABENT(FDT_ERR_TRUNCATED), + FDT_ERRTABENT(FDT_ERR_BADMAGIC), + FDT_ERRTABENT(FDT_ERR_BADVERSION), + FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), + FDT_ERRTABENT(FDT_ERR_BADLAYOUT), +}; +#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return ""; + else if (errval == 0) + return ""; + else if (errval > -FDT_ERRTABSIZE) { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return ""; +} diff --git a/opensrc/helpers/libfdt/fdt_sw.c b/opensrc/helpers/libfdt/fdt_sw.c new file mode 100755 index 0000000..ab504ee --- /dev/null +++ b/opensrc/helpers/libfdt/fdt_sw.c @@ -0,0 +1,288 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int _fdt_sw_check_header(void *fdt) +{ + if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + /* FIXME: should check more details about the header state */ + return 0; +} + +#define FDT_SW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_sw_check_header(fdt)) != 0) \ + return err; \ + } + +static void *_fdt_grab_space(void *fdt, size_t len) +{ + int offset = fdt_size_dt_struct(fdt); + int spaceleft; + + spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) + - fdt_size_dt_strings(fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct(fdt, offset + len); + return _fdt_offset_ptr_w(fdt, offset); +} + +int fdt_create(void *buf, int bufsize) +{ + void *fdt = buf; + + if (bufsize < sizeof(struct fdt_header)) + return -FDT_ERR_NOSPACE; + + memset(buf, 0, bufsize); + + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry))); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, bufsize); + + return 0; +} + +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_CHECK_HEADER(fdt); + + headsize = fdt_off_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if ((headsize + tailsize) > bufsize) + return -FDT_ERR_NOSPACE; + + oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; + newtail = (char *)buf + bufsize - tailsize; + + /* Two cases to avoid clobbering data if the old and new + * buffers partially overlap */ + if (buf <= fdt) { + memmove(buf, fdt, headsize); + memmove(newtail, oldtail, tailsize); + } else { + memmove(newtail, oldtail, tailsize); + memmove(buf, fdt, headsize); + } + + fdt_set_off_dt_strings(buf, bufsize); + fdt_set_totalsize(buf, bufsize); + + return 0; +} + +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int offset; + + FDT_SW_CHECK_HEADER(fdt); + + if (fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTATE; + + offset = fdt_off_dt_struct(fdt); + if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *)((char *)fdt + offset); + re->address = cpu_to_fdt64(addr); + re->size = cpu_to_fdt64(size); + + fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); + + return 0; +} + +int fdt_finish_reservemap(void *fdt) +{ + return fdt_add_reservemap_entry(fdt, 0LL, 0LL); +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen = strlen(name) + 1; + + FDT_SW_CHECK_HEADER(fdt); + + nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); + if (! nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memcpy(nh->name, name, namelen); + return 0; +} + +int fdt_end_node(void *fdt) +{ + fdt32_t *en; + + FDT_SW_CHECK_HEADER(fdt); + + en = _fdt_grab_space(fdt, FDT_TAGSIZE); + if (! en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32(FDT_END_NODE); + return 0; +} + +static int _fdt_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + const char *p; + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + p = _fdt_find_string(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + /* Add it */ + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + if (fdt_totalsize(fdt) + offset < struct_top) + return 0; /* no more room :( */ + + memcpy(strtab + offset, s, len); + fdt_set_size_dt_strings(fdt, strtabsize + len); + return offset; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + struct fdt_property *prop; + int nameoff; + + FDT_SW_CHECK_HEADER(fdt); + + nameoff = _fdt_find_add_string(fdt, name); + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) + return -FDT_ERR_NOSPACE; + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + memcpy(prop->data, val, len); + return 0; +} + +int fdt_finish(void *fdt) +{ + char *p = (char *)fdt; + fdt32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + FDT_SW_CHECK_HEADER(fdt); + + /* Add terminator */ + end = _fdt_grab_space(fdt, sizeof(*end)); + if (! end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32(FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); + newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { + if (tag == FDT_PROP) { + struct fdt_property *prop = + _fdt_offset_ptr_w(fdt, offset); + int nameoff; + + nameoff = fdt32_to_cpu(prop->nameoff); + nameoff += fdt_size_dt_strings(fdt); + prop->nameoff = cpu_to_fdt32(nameoff); + } + offset = nextoffset; + } + if (nextoffset < 0) + return nextoffset; + + /* Finally, adjust the header */ + fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + return 0; +} diff --git a/opensrc/helpers/libfdt/fdt_wip.c b/opensrc/helpers/libfdt/fdt_wip.c new file mode 100755 index 0000000..c5bbb68 --- /dev/null +++ b/opensrc/helpers/libfdt/fdt_wip.c @@ -0,0 +1,118 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen); + if (! propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + memcpy(propval, val, len); + return 0; +} + +static void _fdt_nop_region(void *start, int len) +{ + fdt32_t *p; + + for (p = start; (char *)p < ((char *)start + len); p++) + *p = cpu_to_fdt32(FDT_NOP); +} + +int fdt_nop_property(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + _fdt_nop_region(prop, len + sizeof(*prop)); + + return 0; +} + +int _fdt_node_end_offset(void *fdt, int offset) +{ + int depth = 0; + + while ((offset >= 0) && (depth >= 0)) + offset = fdt_next_node(fdt, offset, &depth); + + return offset; +} + +int fdt_nop_node(void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + _fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), + endoffset - nodeoffset); + return 0; +} diff --git a/opensrc/helpers/libfdt/libfdt.h b/opensrc/helpers/libfdt/libfdt.h new file mode 100755 index 0000000..78adb12 --- /dev/null +++ b/opensrc/helpers/libfdt/libfdt.h @@ -0,0 +1,1653 @@ +#ifndef _LIBFDT_H +#define _LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 FDT_FIRST_SUPPORTED_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attemped to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle + * value. phandle values of 0 and -1 are not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: Structure block of the given device tree + * ends without an FDT_END tag. */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +/* Errors in device tree content */ +#define FDT_ERR_BADNCELLS 14 + /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells + * or similar property with a bad format or value */ + +#define FDT_ERR_BADVALUE 15 + /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected + * value. For example: a property expected to contain a string list + * is not NUL-terminated within the length of its value. */ + +#define FDT_ERR_MAX 15 + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/** + * fdt_first_subnode() - get offset of first direct subnode + * + * @fdt: FDT blob + * @offset: Offset of node to check + * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + */ +int fdt_first_subnode(const void *fdt, int offset); + +/** + * fdt_next_subnode() - get offset of next direct subnode + * + * After first calling fdt_first_subnode(), call this function repeatedly to + * get direct subnodes of a parent node. + * + * @fdt: FDT blob + * @offset: Offset of previous subnode + * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes + */ +int fdt_next_subnode(const void *fdt, int offset); + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +#define fdt_get_header(fdt, field) \ + (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define __fdt_set_hdr(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = (struct fdt_header*)fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +__fdt_set_hdr(magic); +__fdt_set_hdr(totalsize); +__fdt_set_hdr(off_dt_struct); +__fdt_set_hdr(off_dt_strings); +__fdt_set_hdr(off_mem_rsvmap); +__fdt_set_hdr(version); +__fdt_set_hdr(last_comp_version); +__fdt_set_hdr(boot_cpuid_phys); +__fdt_set_hdr(size_dt_strings); +__fdt_set_hdr(size_dt_struct); +#undef __fdt_set_hdr + +/** + * fdt_check_header - sanity check a device tree or possible device tree + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree with sane information in its + * header. + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset_namelen - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * @namelen: number of characters of path to consider + * + * Identical to fdt_path_offset(), but only consider the first namelen + * characters of path as the path name. + */ +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_first_property_offset - find the offset of a node's first property + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * + * fdt_first_property_offset() finds the first property of the node at + * the given structure block offset. + * + * returns: + * structure block offset of the property (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested node has no properties + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_first_property_offset(const void *fdt, int nodeoffset); + +/** + * fdt_next_property_offset - step through a node's properties + * @fdt: pointer to the device tree blob + * @offset: structure block offset of a property + * + * fdt_next_property_offset() finds the property immediately after the + * one at the given structure block offset. This will be a property + * of the same node as the given property. + * + * returns: + * structure block offset of the next property (>=0), on success + * -FDT_ERR_NOTFOUND, if the given property is the last in its node + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_next_property_offset(const void *fdt, int offset); + +/** + * fdt_get_property_by_offset - retrieve the property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to retrieve + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property_by_offset() retrieves a pointer to the + * fdt_property structure within the device tree blob at the given + * offset. If lenp is non-NULL, the length of the property value is + * also returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp); + +/** + * fdt_get_property_namelen - find a property based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_get_property(), but only examine the first namelen + * characters of name for matching the property name. + */ +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop_by_offset - retrieve the value of a property at a given offset + * @fdt: pointer to the device tree blob + * @ffset: offset of the property to read + * @namep: pointer to a string variable (will be overwritten) or NULL + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop_by_offset() retrieves a pointer to the value of the + * property at structure block offset 'offset' (this will be a pointer + * to within the device blob itself, not a copy of the value). If + * lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. If namep is non-NULL, + * the property's namne will also be returned in the char * pointed to + * by namep (this will be a pointer to within the device tree's string + * block, not a new copy of the name). + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * if namep is non-NULL *namep contiains a pointer to the property + * name. + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp); + +/** + * fdt_getprop_namelen - get property value based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_getprop(), but only examine the first namelen + * characters of name for matching the property name. + */ +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_alias_namelen - get alias based on substring + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * @namelen: number of characters of name to consider + * + * Identical to fdt_get_alias(), but only examine the first namelen + * characters of name for matching the alias name. + */ +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen); + +/** + * fdt_get_alias - retreive the path referenced by a given alias + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * + * fdt_get_alias() retrieves the value of a given alias. That is, the + * value of the property named 'name' in the node /aliases. + * + * returns: + * a pointer to the expansion of the alias named 'name', if it exists + * NULL, if the given alias or the /aliases node does not exist + */ +const char *fdt_get_alias(const void *fdt, const char *name); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag +* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/** + * fdt_stringlist_contains - check a string list property for a string + * @strlist: Property containing a list of strings to check + * @listlen: Length of property + * @str: String to search for + * + * This is a utility function provided for convenience. The list contains + * one or more strings, each terminated by \0, as is found in a device tree + * "compatible" property. + * + * @return: 1 if the string is found in the list, 0 not found, or invalid list + */ +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); + +/** + * fdt_stringlist_count - count the number of strings in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @return: + * the number of strings in the given property + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); + +/** + * fdt_stringlist_search - find a string in a string list and return its index + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @string: string to look up in the string list + * + * Note that it is possible for this function to succeed on property values + * that are not NUL-terminated. That's because the function will stop after + * finding the first occurrence of @string. This can for example happen with + * small-valued cell properties, such as #address-cells, when searching for + * the empty string. + * + * @return: + * the index of the string in the list of strings + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist or does not contain + * the given string + */ +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string); + +/** + * fdt_stringlist_get() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @index: index of the string to return + * @lenp: return location for the string length or an error code on failure + * + * Note that this will successfully extract strings from properties with + * non-NUL-terminated values. For example on small-valued cell properties + * this function will return the empty string. + * + * If non-NULL, the length of the string (on success) or a negative error-code + * (on failure) will be stored in the integer pointer to by lenp. + * + * @return: + * A pointer to the string at the given index in the string list or NULL on + * failure. On success the length of the string will be stored in the memory + * location pointed to by the lenp parameter, if non-NULL. On failure one of + * the following negative error codes will be returned in the lenp parameter + * (if non-NULL): + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int index, + int *lenp); + +/**********************************************************************/ +/* Read-only functions (addressing related) */ +/**********************************************************************/ + +/** + * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells + * + * This is the maximum value for #address-cells, #size-cells and + * similar properties that will be processed by libfdt. IEE1275 + * requires that OF implementations handle values up to 4. + * Implementations may support larger values, but in practice higher + * values aren't used. + */ +#define FDT_MAX_NCELLS 4 + +/** + * fdt_address_cells - retrieve address size for a bus represented in the tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address size for + * + * When the node has a valid #address-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid #address-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_address_cells(const void *fdt, int nodeoffset); + +/** + * fdt_size_cells - retrieve address range size for a bus represented in the + * tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address range size for + * + * When the node has a valid #size-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid #size-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_size_cells(const void *fdt, int nodeoffset); + + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to replace the property with + * + * fdt_setprop_inplace_u32() replaces the value of a given property + * with the 32-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to replace the property with + * + * fdt_setprop_inplace_u64() replaces the value of a given property + * with the 64-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 8. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * + * This is an alternative name for fdt_setprop_inplace_u32() + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +int fdt_create(void *buf, int bufsize); +int fdt_resize(void *fdt, void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + return fdt_property_u32(fdt, name, val); +} +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_create_empty_tree(void *buf, int bufsize); +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_u32 - set a property to a 32-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_u32() sets the value of the named property in the given + * node to the given 32-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_u64 - set a property to a 64-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value for the property (native endian) + * + * fdt_setprop_u64() sets the value of the named property in the given + * node to the given 64-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, + uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_cell - set a property to a single cell value + * + * This is an alternative name for fdt_setprop_u32() + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + return fdt_setprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_appendprop - append to or create a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to append to + * @val: pointer to data to append to the property value + * @len: length of the data to append to the property value + * + * fdt_appendprop() appends the value to the named property in the + * given node, creating the property if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_appendprop_u32 - append a 32-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u32() appends the given 32-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_u64 - append a 64-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u64() appends the given 64-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_cell - append a single cell value to a property + * + * This is an alternative name for fdt_appendprop_u32() + */ +static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_appendprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_appendprop_string - append a string to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value to append to the property + * + * fdt_appendprop_string() appends the given string to the value of + * the named property in the given node, or creates a new property + * with that value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ + fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#endif /* _LIBFDT_H */ diff --git a/opensrc/helpers/libfdt/libfdt_env.h b/opensrc/helpers/libfdt/libfdt_env.h new file mode 100755 index 0000000..1c966b8 --- /dev/null +++ b/opensrc/helpers/libfdt/libfdt_env.h @@ -0,0 +1,118 @@ +#ifndef _LIBFDT_ENV_H +#define _LIBFDT_ENV_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (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 + +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef uint16_t __bitwise fdt16_t; +typedef uint32_t __bitwise fdt32_t; +typedef uint64_t __bitwise fdt64_t; + +//#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) +// xxx work around a compiler bug... +#define EXTRACT_BYTE(x, n) EXTRACT_BYTE_f(&x, n) +static inline unsigned long long EXTRACT_BYTE_f(void *x, int n) +{ + return ((uint8_t *)x)[n]; +} + +#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) +#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ + (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) +#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ + (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ + (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ + (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) + +static inline uint16_t fdt16_to_cpu(fdt16_t x) +{ + return (__force uint16_t)CPU_TO_FDT16(x); +} +static inline fdt16_t cpu_to_fdt16(uint16_t x) +{ + return (__force fdt16_t)CPU_TO_FDT16(x); +} + +static inline uint32_t fdt32_to_cpu(fdt32_t x) +{ + return (__force uint32_t)CPU_TO_FDT32(x); +} +static inline fdt32_t cpu_to_fdt32(uint32_t x) +{ + return (__force fdt32_t)CPU_TO_FDT32(x); +} + +static inline uint64_t fdt64_to_cpu(fdt64_t x) +{ + return (__force uint64_t)CPU_TO_FDT64(x); +} +static inline fdt64_t cpu_to_fdt64(uint64_t x) +{ + return (__force fdt64_t)CPU_TO_FDT64(x); +} +#undef CPU_TO_FDT64 +#undef CPU_TO_FDT32 +#undef CPU_TO_FDT16 +#undef EXTRACT_BYTE + +#endif /* _LIBFDT_ENV_H */ diff --git a/opensrc/helpers/libfdt/libfdt_internal.h b/opensrc/helpers/libfdt/libfdt_internal.h new file mode 100755 index 0000000..02cfa6f --- /dev/null +++ b/opensrc/helpers/libfdt/libfdt_internal.h @@ -0,0 +1,95 @@ +#ifndef _LIBFDT_INTERNAL_H +#define _LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +#define FDT_CHECK_HEADER(fdt) \ + { \ + int __err; \ + if ((__err = fdt_check_header(fdt)) != 0) \ + return __err; \ + } + +int _fdt_check_node_offset(const void *fdt, int offset); +int _fdt_check_prop_offset(const void *fdt, int offset); +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); +int _fdt_node_end_offset(void *fdt, int nodeoffset); + +static inline const void *_fdt_offset_ptr(const void *fdt, int offset) +{ + return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; +} + +static inline void *_fdt_offset_ptr_w(void *fdt, int offset) +{ + return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); +} + +static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + (const struct fdt_reserve_entry *) + ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); + + return rsv_table + n; +} +static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) +{ + return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +#endif /* _LIBFDT_INTERNAL_H */ diff --git a/packaging/libomxil-vc4.manifest b/packaging/libomxil-vc4.manifest new file mode 100755 index 0000000..017d22d --- /dev/null +++ b/packaging/libomxil-vc4.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec new file mode 100755 index 0000000..c75b975 --- /dev/null +++ b/packaging/libomxil-vc4.spec @@ -0,0 +1,109 @@ +Name: libomxil-vc4 +Version: 0.0.1 +Release: 0.1 +Summary: Libraries for interfacing to Raspberry Pi GPU +Group: System/Libraries +URL: https://github.com/raspberrypi/userland +Source: %{name}-%{version}.tar.gz +License: BSD +BuildRequires: glibc-devel +BuildRequires: cmake +BuildRequires: gcc-c++ +BuildRequires: pkgconfig(libtbm) + +%description +Libraries for interfacing to Raspberry Pi GPU. + +%package -n libomxil-vc4-utils +Group: System/Tools +Summary: System tools for the Raspberry Pi + +%description -n libomxil-vc4-utils +This package contains some system tools for the Raspberry Pi. +Source: https://github.com/libomxil-vc4/userland.git + +%package -n libomxil-vc4-devel +Group: Development/Libraries +Summary: Development files for the Raspberry Pi GPU + +%description -n libomxil-vc4-devel +This package contains libraries and header files for developing applications that use Raspberry Pi GPU. + +%prep +%setup -q + +%build +BUILDTYPE=Release +BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`; +mkdir -p build/armv7l-linux/$BUILDSUBDIR +pushd build/armv7l-linux/$BUILDSUBDIR +cmake -DCMAKE_BUILD_TYPE=Release ../../../ +make +popd + +%install +mkdir -p %{buildroot}/opt/vc/lib/plugins +mkdir %{buildroot}/opt/vc/lib/pkgconfig +mkdir %{buildroot}/opt/vc/bin +mkdir -p %{buildroot}/opt/vc/include/interface +pushd %{buildroot}/opt/vc/lib +cp %{_builddir}/%{name}-%{version}/build/lib/lib*.so ./ +cp %{_builddir}/%{name}-%{version}/build/armv7l-linux/release/bcm_host.pc ./pkgconfig +cp %{_builddir}/%{name}-%{version}/build/armv7l-linux/release/brcmegl.pc ./pkgconfig +cp %{_builddir}/%{name}-%{version}/build/armv7l-linux/release/brcmglesv2.pc ./pkgconfig +cp %{_builddir}/%{name}-%{version}/build/armv7l-linux/release/brcmvg.pc ./pkgconfig +cp %{_builddir}/%{name}-%{version}/build/armv7l-linux/release/mmal.pc ./pkgconfig +cp %{_builddir}/%{name}-%{version}/build/armv7l-linux/release/vcsm.pc ./pkgconfig +cd ./plugins +cp %{_builddir}/%{name}-%{version}/build/lib/reader_*.so ./ +cp %{_builddir}/%{name}-%{version}/build/lib/writer_*.so ./ +cd ../../bin +cp %{_builddir}/%{name}-%{version}/build/bin/* ./ + +cd ../lib +cp %{_builddir}/%{name}-%{version}/build/lib/lib*.a ./ +cd ../include +cp -a %{_builddir}/%{name}-%{version}/interface/khronos/include/EGL ./ +cp -a %{_builddir}/%{name}-%{version}/interface/khronos/include/GLES ./ +cp -a %{_builddir}/%{name}-%{version}/interface/khronos/include/GLES2 ./ +cp -a %{_builddir}/%{name}-%{version}/interface/khronos/include/KHR ./ +cp -a %{_builddir}/%{name}-%{version}/interface/khronos/include/VG ./ +cp -a %{_builddir}/%{name}-%{version}/interface/khronos/include/WF ./ +cp -a %{_builddir}/%{name}-%{version}/interface/vmcs_host/khronos/IL ./ +cp -a %{_builddir}/%{name}-%{version}/interface/mmal ./interface/ +cp -a %{_builddir}/%{name}-%{version}/interface/vchi ./interface/ +cp -a %{_builddir}/%{name}-%{version}/interface/vchiq_arm ./interface/ +cp -a %{_builddir}/%{name}-%{version}/interface/vcos ./interface/ +cp -a %{_builddir}/%{name}-%{version}/interface/vctypes ./interface/ +cp -a %{_builddir}/%{name}-%{version}/interface/vmcs_host ./interface/ +cp -a %{_builddir}/%{name}-%{version}/vcinclude ./ +cp %{_builddir}/%{name}-%{version}/host_applications/linux/libs/bcm_host/include/bcm_host.h ./ +popd + +%clean +[ "%{buildroot}" != / ] && rm -rf "%{buildroot}" + +%files -n libomxil-vc4-utils +/opt/vc/bin/* +%doc LICENCE COPYING + +%files -n libomxil-vc4 +%manifest packaging/%{name}.manifest +%defattr(-,root,root) +%license LICENCE COPYING +/opt/vc/lib/lib*.so +/opt/vc/lib/plugins/*.so + +%files -n libomxil-vc4-devel +/opt/vc/lib/lib*.a +/opt/vc/include/EGL +/opt/vc/include/GLES +/opt/vc/include/GLES2 +/opt/vc/include/IL +/opt/vc/include/KHR +/opt/vc/include/VG +/opt/vc/include/WF +/opt/vc/include/interface +/opt/vc/include/vcinclude +/opt/vc/include/*.h +/opt/vc/lib/pkgconfig/*.pc diff --git a/pkgconfig/bcm_host.pc.in b/pkgconfig/bcm_host.pc.in new file mode 100755 index 0000000..c7237c5 --- /dev/null +++ b/pkgconfig/bcm_host.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: bcm_host +Description: Broadcom VideoCore host API library +Version: 1 +Libs: -L${libdir} -lbcm_host -lvcos -lvchiq_arm -pthread +Cflags: -I${includedir} -I${includedir}/interface/vmcs_host/linux -I${includedir}/interface/vcos/pthreads -DUSE_VCHIQ_ARM diff --git a/pkgconfig/brcmegl.pc.in b/pkgconfig/brcmegl.pc.in new file mode 100755 index 0000000..5dd3d5b --- /dev/null +++ b/pkgconfig/brcmegl.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: brcmEGL +Description: Fake brcmEGL package for RPi +Version: 10 +Requires: bcm_host +Libs: -L${libdir} -lbrcmEGL -lbrcmGLESv2 -lbcm_host -lvchostif +Cflags: -I${includedir} -I${includedir}/interface/vmcs_host/linux \ + -I${includedir}/interface/vcos/pthreads + diff --git a/pkgconfig/brcmglesv2.pc.in b/pkgconfig/brcmglesv2.pc.in new file mode 100755 index 0000000..e0e36f5 --- /dev/null +++ b/pkgconfig/brcmglesv2.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: brcmGLESv2 +Description: Fake brcmGLES2 package for RPi +Version: 10 +Requires: bcm_host +Libs: -L${libdir} -lbrcmGLESv2 +Cflags: -I${includedir} + diff --git a/pkgconfig/brcmvg.pc.in b/pkgconfig/brcmvg.pc.in new file mode 100755 index 0000000..763a44b --- /dev/null +++ b/pkgconfig/brcmvg.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: brcmOpenVG +Description: Fake brcmOpenVG package for RPi +Version: 10 +Requires: bcm_host +Libs: -L${libdir} -lbrcmOpenVG +Cflags: -I${includedir} diff --git a/pkgconfig/egl.pc.in b/pkgconfig/egl.pc.in new file mode 100755 index 0000000..27a6236 --- /dev/null +++ b/pkgconfig/egl.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: EGL +Description: Fake EGL package for RPi +Version: 10 +Requires: bcm_host +Libs: -L${libdir} -lEGL -lGLESv2 -lbcm_host -lvchostif +Cflags: -I${includedir} -I${includedir}/interface/vmcs_host/linux \ + -I${includedir}/interface/vcos/pthreads + diff --git a/pkgconfig/glesv2.pc.in b/pkgconfig/glesv2.pc.in new file mode 100755 index 0000000..5900225 --- /dev/null +++ b/pkgconfig/glesv2.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: GLESv2 +Description: Fake GL ES 2 package for RPi +Version: 10 +Requires: bcm_host +Libs: -L${libdir} -lGLESv2 +Cflags: -I${includedir} + diff --git a/pkgconfig/mmal.pc.in b/pkgconfig/mmal.pc.in new file mode 100755 index 0000000..37d344c --- /dev/null +++ b/pkgconfig/mmal.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: MMAL +Description: Multi-Media Abstraction Layer library for RPi +Version: 1 +Requires: vcsm +Libs: -L${libdir} -lmmal -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host +Cflags: -I${includedir} diff --git a/pkgconfig/vcsm.pc.in b/pkgconfig/vcsm.pc.in new file mode 100755 index 0000000..6780eff --- /dev/null +++ b/pkgconfig/vcsm.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: VCSM +Description: VideoCore Shared Memory library for RPi +Version: 1 +Libs: -L${libdir} -lvcsm +Cflags: -I${includedir} diff --git a/pkgconfig/vg.pc.in b/pkgconfig/vg.pc.in new file mode 100755 index 0000000..8c39c98 --- /dev/null +++ b/pkgconfig/vg.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: OpenVG +Description: Fake OpenVG package for RPi +Version: 10 +Requires: bcm_host +Libs: -L${libdir} -lOpenVG +Cflags: -I${includedir} diff --git a/vcfw/drivers/driver.h b/vcfw/drivers/driver.h new file mode 100755 index 0000000..3e85e7c --- /dev/null +++ b/vcfw/drivers/driver.h @@ -0,0 +1,159 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef DRIVER_H_ +#define DRIVER_H_ + +#include "vcinclude/common.h" + +/****************************************************************************** + Global Macros + *****************************************************************************/ + +// build the driver function table names through macro magic +#define DRIVER_CONCATENATE(a,b) a##b + +//concat the driver name and the _get_func_table extension +#define DRIVER_NAME(x) DRIVER_CONCATENATE( x, _get_func_table ) + +/****************************************************************************** + Global Defines + *****************************************************************************/ + +typedef enum +{ + DRIVER_FLAGS_DUMMY + + // DRIVER_FLAGS_SUPPORTS_CORE_FREQ_CHANGE = 0x1, + // DRIVER_FLAGS_SUPPORTS_RUN_DOMAIN_CHANGE = 0x2, + // DRIVER_FLAGS_SUPPORTS_SUSPEND_RESUME = 0x4 + +} DRIVER_FLAGS_T; + + +/****************************************************************************** + Function defines + *****************************************************************************/ + +//Generic handle passed used by all drivers +typedef struct opaque_driver_handle_t *DRIVER_HANDLE_T; + +//Routine to initialise a driver +typedef int32_t (*DRIVER_INIT_T)( void ); + +//Routine to shutdown a driver +typedef int32_t (*DRIVER_EXIT_T)( void ); + + +//Routine to return a drivers info (name, version etc..) +typedef int32_t (*DRIVER_INFO_T)(const char **driver_name, + uint32_t *version_major, + uint32_t *version_minor, + DRIVER_FLAGS_T *flags ); + +//Routine to open a driver. +typedef int32_t (*DRIVER_OPEN_T)(const void *params, + DRIVER_HANDLE_T *handle ); + +//Routine to close a driver +typedef int32_t (*DRIVER_CLOSE_T)( const DRIVER_HANDLE_T handle ); + + +//Test routine to open a test driver +typedef int32_t (*DRIVER_TEST_INIT_T)( DRIVER_HANDLE_T *handle ); + +//Test routine to close a test driver +typedef int32_t (*DRIVER_TEST_EXIT_T)( const DRIVER_HANDLE_T handle ); + +/****************************************************************************** + Driver struct definition + *****************************************************************************/ + +/* Basic driver structure, as implemented by all device drivers */ +#define COMMON_DRIVER_API(param1) \ + /*Used to be param1... but no drivers were using multiple params and MSVC doesn't like vararg macros*/ \ + /*Function to initialize the driver. This is used at the start of day to //initialize the driver*/ \ + DRIVER_INIT_T init; \ + \ + /*Routine to shutdown a driver*/ \ + DRIVER_EXIT_T exit; \ + \ + /*Function to get the driver name + version*/ \ + DRIVER_INFO_T info; \ + \ + /*Function to open an instance of the driver. Takes in option parameters, //defined per driver.*/ \ + /*Returns a handle to the open driver and a success code*/ \ + int32_t (*open)(param1, \ + DRIVER_HANDLE_T *handle ); \ + \ + /*Function to close a driver instance*/ \ + /*Returns success code*/ \ + DRIVER_CLOSE_T close; + + +typedef struct +{ + //just include the basic driver api + COMMON_DRIVER_API(void const *unused) + +} DRIVER_T; + + +/****************************************************************************** + Test Driver struct definition + *****************************************************************************/ + +/* Test driver structure, implemented by all (optional) test drivers */ + +#define COMMON_DRIVER_TEST_API \ + /*Function used to tell a driver that the core freq is about to change*/ \ + /*Returns success code (0 if its ok to change the clock)*/ \ + DRIVER_TEST_INIT_T test_init;\ + \ + /*Function used to tell a driver if a power domain is about to change*/ \ + /*Returns success code (0 if its ok to change the power domains)*/ \ + DRIVER_TEST_EXIT_T test_exit; + + +typedef struct +{ + //just include the test driver api + COMMON_DRIVER_TEST_API + +} DRIVER_TEST_T; + +#define VCFW_AUTO_REGISTER_DRIVER(type, name) \ + pragma Data(LIT, ".drivers"); \ + static const type * const name##_ptr = &name; \ + pragma Data; + +#define VCFW_AUTO_REGISTER_BASE_DRIVER(type, name) \ + pragma Data(LIT, ".drivers_base"); \ + static const type * const name##_ptr = &name; \ + pragma Data; + +#endif // DRIVER_H_ diff --git a/vcfw/logging/logging.h b/vcfw/logging/logging.h new file mode 100755 index 0000000..ea09201 --- /dev/null +++ b/vcfw/logging/logging.h @@ -0,0 +1,350 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef LOGGING_LOGGING_H +#define LOGGING_LOGGING_H + +#include + +#include "vcinclude/vcore.h" + +/* Bitfield for indicating the category and level of a logging message. */ +#define LOGGING_GENERAL (1<<0) /* for logging general messages */ +#define LOGGING_GENERAL_VERBOSE (2<<0) +#define LOGGING_CODECS (1<<2) /* for codec messages */ +#define LOGGING_CODECS_VERBOSE (2<<2) +#define LOGGING_FILESYSTEM (1<<4) /* filesystem messages */ +#define LOGGING_FILESYSTEM_VERBOSE (2<<4) +#define LOGGING_VMCS (1<<6) /* VMCS related messages */ +#define LOGGING_VMCS_VERBOSE (2<<6) +#define LOGGING_DISPMAN2 (1<<8) /* Dispman2/scalar logs */ +#define LOGGING_DISPMAN2_VERBOSE (2<<8) +#define LOGGING_GCE (1<<8) /* Re-use Dispman2 for GCE logging */ +#define LOGGING_GCE_VERBOSE (2<<8) +#define LOGGING_CAMPLUS (1<<10) /* Camplus logs */ +#define LOGGING_CAMPLUS_VERBOSE (2<<10) +#define LOGGING_APPS (1<<12) /* Application logs */ +#define LOGGING_APPS_VERBOSE (2<<12) +#define LOGGING_CLOCKMAN_POWERMAN (1<<14) /* Clockman + powerman logs */ +#define LOGGING_CLOCKMAN_POWERMAN_VERBOSE (2<<14) +#define LOGGING_VCOS (1<<16) +#define LOGGING_VCOS_VERBOSE (2<<16) +#define LOGGING_IMAGE_POOL (1<<18) /* Image pool messages */ +#define LOGGING_IMAGE_POOL_VERBOSE (2<<18) +#define LOGGING_HDMI (1<<20) /* HDMI and HDCP messages */ +#define LOGGING_HDMI_VERBOSE (2<<20) +#define LOGGING_MINIMAL (1<<22) /* minimal logging for bandwidth measurement, ie all others off. */ +#define LOGGING_MINIMAL_VERBOSE (2<<22) +#define LOGGING_TUNER (1<<24) /* ISP Tuner logs - AGC, AWB etc */ +#define LOGGING_TUNER_VERBOSE (2<<24) +#define LOGGING_VCHI (1<<26) /* For all VCHI based services */ +#define LOGGING_VCHI_VERBOSE (2<<26) +#define LOGGING_FOCUS (1<<28) /* Focus messages */ +#define LOGGING_HANDLERS (1<<29) /* For handler messages */ +#define LOGGING_VOWIFI (1<<28) /* Re-use FOCUS for VOWIFI */ +#define LOGGING_VOWIFI_VERBOSE (2<<28) /* Re-use HANDLERS for VOWIFI */ +// add more here +#define LOGGING_USER (1<<30) /* only for code under development - do not check in! */ +#define LOGGING_USER_VERBOSE (2<<30) + +/* Define some rarely used messages as general messages */ +#define LOGGING_DMB (LOGGING_GENERAL) /* DMB logs */ +#define LOGGING_DMB_VERBOSE (LOGGING_GENERAL_VERBOSE) +#define LOGGING_MEMORY (LOGGING_GENERAL) /* memory task pool logs */ +#define LOGGING_MEMORY_VERBOSE (LOGGING_GENERAL_VERBOSE) + +#define LOGGING_ALL 0x55555555 /* log all messages */ +#define LOGGING_ALL_VERBOSE 0xaaaaaaaa +#define LOGGING_NONE 0 /* log nothing */ + +typedef enum { + LOGGING_FIFO_LOG = 1, + LOGGING_ASSERTION_LOG, + LOGGING_TASK_LOG, +} LOG_FORMAT_T; + +typedef struct { + LOG_FORMAT_T type; + void *log; +} LOG_DESCRIPTOR_T; + +// This header prefixes the logged data on every fifo log entry. +typedef struct { + unsigned long time; + unsigned short seq_num; // if two entries have the same timestamp then this seq num differentiates them. + unsigned short size; // = size of entire log entry = header + data +} logging_fifo_log_msg_header_t; + +// This describes the state of a fifo type log. +typedef struct { + char name[4]; + unsigned char *start; // Start and End are defined when the log is created. + unsigned char *end; + unsigned char *ptr; // Always points to where the next contents will be written. + unsigned char *next_msg; // Points to the first entry in the fifo. Is updated when the fifo gets full and overwrites the oldest entry. + logging_fifo_log_msg_header_t msg_header; // used to keep track of forming a log entry between calls to _start and _end. +} logging_fifo_log_t; + +// This describes the state of an array type log. +typedef struct { + char name[4]; + unsigned short nitems, item_size; + unsigned long available; + unsigned char *data; +} logging_array_log_t; + + +// The header at the start of the log may be one of a number of different types, +// but they all start with a common portion: + +#define LOGGING_SYNC 'VLOG' + +typedef unsigned long LOGGING_LOG_TYPE_T; +#define LOGGING_LOG_TYPE_VCLIB ((LOGGING_LOG_TYPE_T) 0) +#define LOGGING_LOG_TYPE_VMCS ((LOGGING_LOG_TYPE_T) 1) + +typedef struct { + unsigned long sync; + LOGGING_LOG_TYPE_T type; + unsigned long version; + void *self; +} logging_common_header_t; + +#define N_VCLIB_LOGS 3 //Does not include the task switch log + +// This is the top level data structure for the Videocore Logs. It houses several +// logs from different sources as well as extra state info. +typedef struct { + logging_common_header_t common; + unsigned long stc; + int task_switch_log_size; + unsigned char *task_switch_log; + unsigned long n_logs; + LOG_DESCRIPTOR_T assertion_log; + LOG_DESCRIPTOR_T message_log; + LOG_DESCRIPTOR_T task_log; + + //LOG_DESCRIPTOR_T logs[N_VCLIB_LOGS - 3]; + + //logging_fifo_log_t *assertion_log; + //logging_fifo_log_t *message_log; + //logging_array_log_t *task_log; +} logging_header_t; + +typedef void (*logging_notify_callback_fn)( void ); + +#ifdef LOGGING + +#ifndef BLOGGING + +/* Initialise a log for use, supplying the memory where it starts and the + number of bytes available. */ +void logging_fifo_log_init(logging_fifo_log_t *log, const char *name, void *start, int size); + +/* Begin writing a log message. */ +void logging_fifo_log_start(logging_fifo_log_t *log); +/* Write nbytes of message data. */ +void logging_fifo_log_write(logging_fifo_log_t *log, const void *ptr, int nbytes); +/* Finish a log message. */ +void logging_fifo_log_end(logging_fifo_log_t *log); + +/* Read a log message */ +int logging_read( LOG_FORMAT_T logFormat, void *buf, int bufLen ); + +/* Initialise an array log. */ +void logging_array_log_init(logging_array_log_t *log, char *name, int nitems, int item_size, void *data, int total_size); +/* Add an item to the array log. */ +void logging_array_log_add(logging_array_log_t *log, unsigned long key, void *data, int nbytes); +/* Remove an item. */ +void logging_array_log_delete(logging_array_log_t *log, unsigned long key); + +/* Initialise vclib's logging fifos. */ +void logging_init(); + +/* Reset assert log */ +void logging_assert_reset(); + +/* Reset mesage log */ +void logging_message_reset(); + +/* Setup a fifo in the given block of memory. */ +unsigned char *logging_setup_fifo (logging_fifo_log_t **ptr, const char *name, unsigned char *addr, int fifo_size); +/* Set the current logging level. */ +void logging_level(int level); +/* Get the current logging level. */ +int logging_get_level(void); +/* Test if a logging level will actually produce logging messages. */ +int logging_will_log(int level); +/* Log an assertion. */ +void logging_assert(const char *file, const char *func, int line, const char *format, ...); +/* dumps registers and stack which get printed by logging_assert */ +void logging_assert_dump(void); +/* Log a general message. */ +void logging_message(int level, const char *format, ...); +void vlogging_message (int level, const char *format, va_list args); + + +/* Log a simple string */ +void logging_string(int level, const char *string); +/* The log for recording the current tasks. */ +logging_array_log_t *logging_task_log(void); + +void logging_set_notify_callback( logging_notify_callback_fn fn ); + +/* Dump log to sdcard, discarding any logging that happens during the file write */ +int logging_dump_log(char* filename); + +/* Dump log to sdcard. If a sufficiently large temporary buffer is not provided, +logging messages will be discarded during the file write. */ +int logging_dump_log_buffered(char* filename, void *buffer, int buffer_size); + +/* Parse a fifo log entry and put it into a human readable string. */ +int logging_fifo_extract_entry(logging_fifo_log_t *log, const unsigned char *entry_ptr, char *target_buffer, int target_size); + +/* Log a hexadecimal representation of the first num_bytes of byte_array, + formatted to format using logging_message() at logging-level level. */ +int logging_message_hex(int level, const char *format, const void *byte_array, int num_bytes); + +/* Log a hexadecimal representation of the Vector Register File contents.*/ +int logging_message_vrf(int level); + +/* Append logging to a file. If flag set, only output to the file */ +int logging_set_logfile(const char *file, int only_to_file); + +/* Close the file opened above */ +void logging_close_logfile(void); + +#ifdef WANT_LOGGING_UART +/* Enables/disables outputing log messages to the UART. */ +void logging_uart_enable_output(int enable_output); +/* Set the current UART logging level. */ +void logging_level_uart(int level); +/* Get the current UART logging level. */ +int logging_get_level_uart(void); +#endif + +unsigned char* get_message_log_ptr_and_size (int* size); + +#else // BLOGGING + +#include "vcfw/blogging/blogging.h" + +#define logging_fifo_log_init(log, start, size) +#define logging_fifo_log_start(log) +#define logging_fifo_log_write(log, ptr, nbytes) +#define logging_fifo_log_end(log) + +#endif // BLOGGING + +/* Cause the assert macro to log a message if it's defined. Only changed without NDEBUG, though other assert macros still work.*/ +#ifdef __VIDEOCORE__ +#if !defined(NDEBUG) && defined(assert) +#undef assert +#ifndef ARTS +#define assert(cond) \ + ( (cond) ? (void)0 : (_bkpt(),logging_assert_dump(),logging_assert(__FILE__, __func__, __LINE__, #cond))) +#else //ARTS +#define assert(cond) \ + ( (cond) ? (void)0 : (logging_assert_dump(),logging_assert(__FILE__, __func__, __LINE__, #cond))) +#endif //ARTS +#endif // !defined(NDEBUG) && defined(assert) + +/* This will only trigger assert first time it fires */ +#define assert_once(cond,comment) \ + do { \ + static char i_asrt=0; \ + if (!(cond)) { \ + if (i_asrt==0) { \ + _bkpt(); \ + i_asrt++; \ + logging_assert_dump(),logging_assert(__FILE__, __func__, __LINE__, (comment)); \ + } \ + } \ + } while (0) + +#define assert_comment(cond, comment) if (cond) {} else logging_assert_dump(),logging_assert(__FILE__, __func__, __LINE__,(comment)) + +#else + #ifndef ARTS + #include + #else + #undef assert + #define assert(cond) ( (cond) ? (void)0 : (logging_assert_dump(),logging_assert(__FILE__, __FUNCTION__, __LINE__, #cond))) + #endif + +#define assert_once(cond,comment) \ + do { \ + static char i_asrt=0; \ + if (!(cond)) { \ + if (i_asrt==0) { \ + i_asrt++; \ + logging_assert_dump(),logging_assert(__FILE__, __func__, __LINE__, (comment)); \ + } \ + } \ + } while (0) + +#endif // not __VIDEOCORE__ + +#else //LOGGING + +#define logging_fifo_log_init(log, start, size) + +#define logging_fifo_log_start(log) +#define logging_fifo_log_write(log, ptr, nbytes) +#define logging_fifo_log_end(log) + +#define logging_array_log_init(log, nitems, item_size, data, total_size) +#define logging_array_log_add(log, key, data, nbytes) +#define logging_array_log_delete(log, key) + +#define logging_init() +#define logging_level(level) +#define logging_get_level() 0 +#define logging_assert(file, func, line, ...) +#define logging_message_hex(level, format, byte_array, num_bytes) +#define logging_dump_log(filename) -1 +#define logging_will_log(level) 0 + +#ifndef EDID_TOOL //logging_message is mapped to printf for our edid_parser tool +/* Hacky way of getting rid of a var args function with a macro... */ +static __inline void logging_message (int level, const char *format, ...) { } +#endif +#define vlogging_message(level, format, args) +#define logging_string(level, string) + +#define logging_task_log() + +#define assert_periodic(cond,nreps,dt,comment) +#define assert_once(cond,comment) +#define assert_comment(cond, comment) + +#define logging_set_notify_callback(fn) + +#endif // LOGGING + +#endif // LOGGING_LOGGING_H + diff --git a/vcfw/rtos/common/rtos_common_mem.h b/vcfw/rtos/common/rtos_common_mem.h new file mode 100755 index 0000000..33ea2a7 --- /dev/null +++ b/vcfw/rtos/common/rtos_common_mem.h @@ -0,0 +1,1360 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef COMMON_MEM_H +#define COMMON_MEM_H + +#ifndef MEM_DONT_USE_SMALL_ALLOC_POOL +#define MEM_USE_SMALL_ALLOC_POOL +#endif + +#ifndef MEM_CHUNK_SIZE +#define MEM_CHUNK_SIZE 0x40000 +#endif + +#include + +#include "vcinclude/common.h" + +#ifdef _MSC_VER + #define RCM_ALIGNOF(T) __alignof(T) +#else + #define RCM_ALIGNOF(T) (sizeof(struct { T t; char ch; }) - sizeof(T)) +#endif + +#ifdef _MSC_VER + #define RCM_INLINE __inline +#else + #ifdef __LCC__ + #define RCM_INLINE + #else + #define RCM_INLINE inline + #endif +#endif + +/****************************************************************************** +Global initialisation helpers +******************************************************************************/ + +/* + Locate the relocatable heap partition, or malloc sufficient space, then + call mem_init. + */ +int32_t rtos_common_mem_init( void ); + +/****************************************************************************** +Pool management API +******************************************************************************/ + +/* + Options for mem_compact. +*/ + +typedef enum +{ + /* these values are duplicated in rtos_common_mem.inc */ + + MEM_COMPACT_NONE = 0, /* No compaction allowed */ + MEM_COMPACT_NORMAL = 1, /* Move unlocked blocks */ + MEM_COMPACT_DISCARD = 2, /* _NORMAL + discard blocks where possible */ + MEM_COMPACT_AGGRESSIVE = 4, /* _NORMAL + move locked blocks where possible */ + MEM_COMPACT_ALL = MEM_COMPACT_NORMAL | MEM_COMPACT_DISCARD | MEM_COMPACT_AGGRESSIVE, + MEM_COMPACT_NOT_BLOCKING = 8, /* don't retry if initial allocation fails */ + + MEM_COMPACT_SHUFFLE = 0x80 /* Move the lowest unlocked block up to the top + (space permitting) - for testing */ +} mem_compact_mode_t; + +/* + Get default values for memory pool defined in the platform makefile +*/ + +extern void mem_get_default_partition(void **mempool_base, uint32_t *mempool_size, void **mempool_handles_base, uint32_t *mempool_handles_size); + +/* + Initialize the memory subsystem, allocating a pool of a given size and + with space for the given number of handles. +*/ + +extern int mem_init(void *mempool_base, uint32_t mempool_size, void *mempool_handles_base, uint32_t mempool_handles_size); + +/* + Terminate the memory subsystem, releasing the pool. +*/ + +extern void mem_term(void); + +/* + The heap is compacted to the maximum possible extent. If (mode & MEM_COMPACT_DISCARD) + is non-zero, all discardable, unlocked, and unretained MEM_HANDLE_Ts are resized to size 0. + If (mode & MEM_COMPACT_AGGRESSIVE) is non-zero, all long-term block owners (which are + obliged to have registered a callback) are asked to unlock their blocks for the duration + of the compaction. +*/ + +extern void mem_compact(mem_compact_mode_t mode); + +/****************************************************************************** +Movable memory core API +******************************************************************************/ + +/* + A MEM_HANDLE_T refers to a movable block of memory. + + The only way to get a MEM_HANDLE_T is to call mem_alloc. + + The MEM_HANDLE_T you get is immutable and remains valid until its reference + count reaches 0. + + A MEM_HANDLE_T has an initial reference count of 1. This can be incremented + by calling mem_acquire and decremented by calling mem_release. +*/ + +/* + MEM_ZERO_SIZE_HANDLE is a preallocated handle to a zero-size block of memory + MEM_EMPTY_STRING_HANDLE is a preallocated handle to a block of memory containing the empty string + + MEM_HANDLE_INVALID is the equivalent of NULL for MEM_HANDLE_Ts -- no valid + MEM_HANDLE_T will ever equal MEM_HANDLE_INVALID. +*/ + +typedef enum { /* enum to get stricter type checking on msvc */ +#ifdef MEM_USE_SMALL_ALLOC_POOL + MEM_ZERO_SIZE_HANDLE = 0x80000000, + MEM_EMPTY_STRING_HANDLE = 0x80000001, +#else + MEM_ZERO_SIZE_HANDLE = 1, + MEM_EMPTY_STRING_HANDLE = 2, +#endif + + MEM_HANDLE_INVALID = 0, + + MEM_HANDLE_FORCE_32BIT = 0x80000000, + + // deprecated - for backward compatibility + MEM_INVALID_HANDLE = MEM_HANDLE_INVALID +} MEM_HANDLE_T; + + +/* + Flags are set once in mem_alloc -- they do not change over the lifetime of + the MEM_HANDLE_T. +*/ + +typedef enum { + MEM_FLAG_NONE = 0, + + /* + If a MEM_HANDLE_T is discardable, the memory manager may resize it to size + 0 at any time when it is not locked or retained. + */ + + MEM_FLAG_DISCARDABLE = 1 << 0, + + /* + MEM_FLAG_RETAINED should only ever be used when passing flags to + mem_alloc. If it is set, the initial retain count is 1, otherwise it is 0. + */ + + MEM_FLAG_RETAINED = 1 << 9, /* shared with MEM_FLAG_ABANDONED. only used when passing flags to mem_alloc */ + + /* + Block must be kept within bottom 256M region of the relocatable heap. + Specifying this flag means that an allocation will fail if the block + cannot be allocated within that region, and the block will not be moved + out of that range. + (This is to support memory blocks used by the codec cache, which must + have same top 4 bits; see HW-3058) + This flag is ignored on non-VideoCore platforms. + */ + + MEM_FLAG_LOW_256M = 1 << 1, + + /* + Define a mask to extract the memory alias used by the block of memory. + */ + + MEM_FLAG_ALIAS_MASK = 3 << 2, + + /* + If a MEM_HANDLE_T is allocating (or normal), its block of memory will be + accessed in an allocating fashion through the cache. + */ + + MEM_FLAG_NORMAL = 0 << 2, + MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL, + + /* + If a MEM_HANDLE_T is direct, its block of memory will be accessed + directly, bypassing the cache. + */ + + MEM_FLAG_DIRECT = 1 << 2, + + /* + If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a + non-allocating fashion through the cache. + */ + + MEM_FLAG_COHERENT = 2 << 2, + + /* + If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by + the VPU in a fashion which is allocating in L2, but only coherent in L1. + */ + + MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT), + + /* + If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than + MEM_HANDLE_INVALID on allocation and resize up. + */ + + MEM_FLAG_ZERO = 1 << 4, + + /* + If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value + (either zero, or all 1's) on allocation. + */ + + MEM_FLAG_NO_INIT = 1 << 5, + + /* + The INIT flag is a placeholder, designed to make it explicit that + initialisation is required, and to make it possible to change the sense + of this bit at a later date. + */ + + MEM_FLAG_INIT = 0 << 5, + + /* + Hints. + */ + + MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */ + + MEM_FLAG_HINT_ALL = 0xc0, + + MEM_FLAG_USER = 1 << 7, + MEM_FLAG_HINT_GROW = 1 << 7, /* Likely to grow in size over time. If this flag is specified, MEM_FLAG_RESIZEABLE must also be. */ + + MEM_FLAG_UNUSED = 1 << 7, + + /* + If a MEM_HANDLE_T is to be resized with mem_resize, this flag must be + present. This flag prevents things from being allocated out of the small + allocation pool. + */ + MEM_FLAG_RESIZEABLE = 1 << 8, + + /* + If the ABANDONED flag is set, because mem_abandon was called when the lock + count was zero, the contents are undefined. This flag is cleared by + mem_lock. Automatically set for blocks allocated with MEM_FLAG_NO_INIT. + */ + + MEM_FLAG_ABANDONED = 1 << 9, /* shared with MEM_FLAG_RETAINED. never used when passing flags to mem_alloc */ + + /* There is currently room in MEM_HEADER_X_T for 10 flags */ + MEM_FLAG_ALL = 0x3ff +} MEM_FLAG_T; + +/* + A MEM_HANDLE_T may optionally have a terminator. This is a function that will + be called just before the MEM_HANDLE_T becomes invalid: see mem_release. +*/ + +typedef void (*MEM_TERM_T)(void *, uint32_t); + + +/* + A common way of storing a MEM_HANDLE_T together with an offset into it. +*/ + +typedef struct +{ + MEM_HANDLE_T mh_handle; + uint32_t offset; +} MEM_HANDLE_OFFSET_T; + +/* + Attempts to allocate a movable block of memory of the specified size and + alignment. size may be 0. A MEM_HANDLE_T with size 0 is special: see + mem_lock/mem_unlock. mode specifies the types of compaction permitted, + including MEM_COMPACT_NONE. + + Preconditions: + + - align is a power of 2. + - flags only has set bits within the range specified by MEM_FLAG_ALL. + - desc is NULL or a pointer to a null-terminated string. + - the caller of this function is contracted to later call mem_release (or pass such responsibility on) if we don't return MEM_HANDLE_INVALID + + Postconditions: + + If the attempt succeeds: + - A fresh MEM_HANDLE_T referring to the allocated block of memory is + returned. + - The MEM_HANDLE_T is unlocked, without a terminator, and has a reference + count of 1. + - If MEM_FLAG_RETAINED was specified, the MEM_HANDLE_T has a retain count of + 1, otherwise it is unretained. + - If the MEM_FLAG_ZERO flag was specified, the block of memory is set to 0. + Otherwise, each aligned word is set to MEM_HANDLE_INVALID. + + If the attempt fails: + - MEM_HANDLE_INVALID is returned. +*/ + +extern MEM_HANDLE_T mem_alloc_ex( + uint32_t size, + uint32_t align, + MEM_FLAG_T flags, + const char *desc, + mem_compact_mode_t mode); + +#define mem_alloc(s,a,f,d) mem_alloc_ex(s,a,f,d,MEM_COMPACT_ALL) + +#define MEM_WRAP_HACK + +#ifdef MEM_WRAP_HACK +extern MEM_HANDLE_T mem_wrap(void *p, uint32_t size, uint32_t align, MEM_FLAG_T flags, const char *desc); + +typedef void (*MEM_WRAP_TERM_T)(void */*priv*/, MEM_HANDLE_T /*term_handle*/, void */*p*/, int /*size*/); + +/* + int mem_wrap_set_on_term(MEM_HANDLE_T handle, MEM_WRAP_TERM_T term_cb, void *cb_priv); + + Adds an additional release callback for wrapped MEM_HANDLE_T. + This allows the underlying allocator to release the wrapped memory. + The MEM_HANDLE_T must NOT be accessed with mem_lock/mem_acquire etc + from the callback context as the handle is already partially released. +*/ +extern int mem_wrap_set_on_term(MEM_HANDLE_T handle, MEM_WRAP_TERM_T term_cb, void *cb_priv); + +#endif + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The reference count of the MEM_HANDLE_T is incremented. +*/ + +extern void mem_acquire( + MEM_HANDLE_T handle); + +/* + Calls mem_acquire and also calls mem_retain if the handle is discardable. +*/ + +extern void mem_acquire_retain(MEM_HANDLE_T handle); + +/* + If the reference count of the MEM_HANDLE_T is 1 and it has a terminator, the + terminator is called with a pointer to and the size of the MEM_HANDLE_T's + block of memory (or NULL/0 if the size of the MEM_HANDLE_T is 0). The + MEM_HANDLE_T may not be used during the call. + + Preconditions: + + - handle is a valid MEM_HANDLE_T. + - If its reference count is 1, it must not be locked or retained. + + Postconditions: + + If the reference count of the MEM_HANDLE_T was 1: + - The MEM_HANDLE_T is now invalid. The associated block of memory has been + freed. + + Otherwise: + - The reference count of the MEM_HANDLE_T is decremented. +*/ + +extern void mem_release( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + If the reference count of the MEM_HANDLE_T was 1: + - false is returned. + - The reference count of the MEM_HANDLE_T is still 1. + + Otherwise: + - true is returned. + - The reference count of the MEM_HANDLE_T is decremented. +*/ + +extern int mem_try_release( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The total space used by the MEM_HANDLE_T is returned (this includes the + header, the actual block, and padding). + - sum_over_handles(mem_get_space(handle)) + mem_get_free_space() is constant + over the lifetime of the pool. +*/ + +extern uint32_t mem_get_space( + MEM_HANDLE_T handle); + +/* + uint32_t mem_get_size(MEM_HANDLE_T handle); + + The size of the MEM_HANDLE_T's block of memory is returned. + This is consistent with the size requested in a mem_alloc call. + + Implementation notes: + + - + + Preconditions: + + handle is not MEM_HANDLE_INVALID + + Postconditions: + + result <= INT_MAX + + Invariants preserved: + + - +*/ + +extern uint32_t mem_get_size( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The minimum required alignment of the MEM_HANDLE_T's block of memory (as + passed to mem_alloc) is returned. + - +*/ + +extern uint32_t mem_get_align( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's flags (as passed to mem_alloc) are returned. +*/ + +extern MEM_FLAG_T mem_get_flags( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's reference count is returned. +*/ + +extern uint32_t mem_get_ref_count( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's lock count is returned. +*/ + +extern uint32_t mem_get_lock_count( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's retain count is returned. +*/ + +extern uint32_t mem_get_retain_count( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's description string is returned. +*/ + +extern const char *mem_get_desc( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + - desc is NULL or a pointer to a null-terminated string. + + Postconditions: + + - The MEM_HANDLE_T's description is set to desc. +*/ + +extern void mem_set_desc( + MEM_HANDLE_T handle, + const char *desc); + +extern void mem_set_desc_vprintf( + MEM_HANDLE_T handle, + const char *fmt, + va_list ap); + +extern void mem_set_desc_printf( + MEM_HANDLE_T handle, + const char *fmt, + ...); + +/* + void mem_set_term(MEM_HANDLE_T handle, MEM_TERM_T term) + + The MEM_HANDLE_T's terminator is set to term (if term was NULL, the + MEM_HANDLE_T no longer has a terminator). + The MEM_HANDLE_T's terminator is called just before the MEM_HANDLE_T becomes + invalid: see mem_release. + + Preconditions: + + handle is a valid handle to a (possibly uninitialised or partially initialised*) + object of type X + + This implies mem_get_size(handle) == sizeof(type X) + + memory management invariants for handle are satisfied + + term must be NULL or a pointer to a function compatible with the MEM_TERM_T + type: + + void *term(void *v, uint32_t size) + + if term is not NULL, its precondition must be no stronger than the following: + is only called from memory manager internals during destruction of an object of type X + v is a valid pointer to a (possibly uninitialised or partially initialised*) object of type X + memory management invariants for v are satisfied + size == sizeof(type X) + + if term is not NULL, its postcondition must be at least as strong as the following: + Frees any references to resources that are referred to by the object of type X + + + Postconditions: + + - +*/ + +extern void mem_set_term( + MEM_HANDLE_T handle, + MEM_TERM_T term); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's terminator is returned, or NULL if there is none. +*/ + +extern MEM_TERM_T mem_get_term( + MEM_HANDLE_T handle); + +/* + void mem_set_user_flag(MEM_HANDLE_T handle, int flag) + + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + - The MEM_HANDLE_T's user flag is set to 0 if flag is 0, or to 1 otherwise. +*/ + +extern void mem_set_user_flag( + MEM_HANDLE_T handle, int flag); + +/* + Attempts to resize the MEM_HANDLE_T's block of memory. The attempt is + guaranteed to succeed if the new size is less than or equal to the old size. + size may be 0. A MEM_HANDLE_T with size 0 is special: see + mem_lock/mem_unlock. mode specifies the types of compaction permitted, + including MEM_COMPACT_NONE. + + Preconditions: + + - handle is a valid MEM_HANDLE_T. + - It must not be locked. + + Postconditions: + + If the attempt succeeds: + - true is returned. + - The MEM_HANDLE_T's block of memory has been resized. + - The contents in the region [0, min(old size, new size)) are unchanged. If + the MEM_HANDLE_T is zero'd, the region [min(old size, new size), new size) + is set to 0. Otherwise, each aligned word in the region + [min(old size, new size), new size) is set to MEM_HANDLE_INVALID. + + If the attempt fails: + - false is returned. + - The MEM_HANDLE_T is still valid. + - Its block of memory is unchanged. +*/ + +extern int mem_resize_ex( + MEM_HANDLE_T handle, + uint32_t size, + mem_compact_mode_t mode); + +#define mem_resize(h,s) mem_resize_ex(h,s,MEM_COMPACT_ALL) + +/* + A MEM_HANDLE_T with a lock count greater than 0 is considered to be locked + and may not be moved by the memory manager. + + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + If the MEM_HANDLE_T's size is 0: + - NULL is returned. + - The MEM_HANDLE_T is completely unchanged. + + Otherwise: + - A pointer to the MEM_HANDLE_T's block of memory is returned. The pointer is + valid until the MEM_HANDLE_T's lock count reaches 0. + - The MEM_HANDLE_T's lock count is incremented. + - Clears MEM_FLAG_ABANDONED. +*/ + +/*@null@*/ extern void *mem_lock( + MEM_HANDLE_T handle); + +/* + Lock a number of memory handles and store the results in an array of pointers. + May be faster than calling mem_lock repeatedly as we only need to acquire the + memory mutex once. + For convenience you can also pass invalid handles in, and get out either null pointers or + valid pointers (depending on the associated offset field) + + Preconditions: + + pointers is a valid pointer to n elements + handles is a valid pointer to n elements + For all 0 <= i < n + - handles[i].mh_handle is MEM_HANDLE_INVALID or a valid MEM_HANDLE_T. + - If handles[i] != MEM_HANDLE_INVALID then handles[i].offset <= handles[i].size + + Postconditions: + + For all 0 <= i < n + If handles[i] == MEM_HANDLE_INVALID: + - pointers[i] is set to offsets[i] + + Else if handles[i].mh_handle.size == 0: + - pointers[i] is set to 0. + - handles[i].mh_handle is completely unchanged. + + Otherwise: + - pointers[i] is set to a pointer which is valid until handles[i].lockcount reaches 0 + - pointers[i] points to a block of size (handles[i].size - handles[i].offset) + - handles[i].mh_handle.lockcount is incremented. + - MEM_FLAG_ABANDONED is cleared in handles[i].mh_handle.x.flags +*/ + +extern void mem_lock_multiple( + void **pointers, + MEM_HANDLE_OFFSET_T *handles, + uint32_t n); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + - If its size is not 0, it must be locked. + + Postconditions: + + If the MEM_HANDLE_T's size is 0: + - The MEM_HANDLE_T is completely unchanged. + + Otherwise: + - The MEM_HANDLE_T's lock count is decremented. +*/ + +extern void mem_unlock( + MEM_HANDLE_T handle); + +/* + Unlock a number of memory handles. + May be faster than calling mem_unlock repeatedly as we only need to acquire the + memory mutex once. + + Preconditions: + + pointers is a valid pointer to n elements + handles is a valid pointer to n elements + For all 0 <= i < n + - handles[i].mh_handle is a valid MEM_HANDLE_T. + - If handles[i].mh_handle.size is not 0, it must be locked. + + Postconditions: + + For all 0 <= i < n + If handles[i] == MEM_HANDLE_INVALID or handles[i].mh_handle.size == 0: + - handles[i].mh_handle is completely unchanged. + + Otherwise: + - handles[i].mh_handle.lockcount is decremented. +*/ + +extern void mem_unlock_multiple( + MEM_HANDLE_OFFSET_T *handles, + uint32_t n); + +/* + Like mem_unlock_multiple, but will unretain handles if they are discardable. + Also releases handles. +*/ + +extern void mem_unlock_unretain_release_multiple( + MEM_HANDLE_OFFSET_T *handles, + uint32_t n); + +/* + Like mem_unlock_unretain_release_multiple, but without the unlocking. + Also releases handles. +*/ + +extern void mem_unretain_release_multiple( + MEM_HANDLE_OFFSET_T *handles, + uint32_t n); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + + Postconditions: + + If the MEM_HANDLE_T is not a small handle: + - Sets MEM_FLAG_ABANDONED, which causes the data content to become undefined + when the lock count reaches zero. + - Sets MEM_FLAG_NO_INIT. + + Otherwise: + - Does nothing. +*/ + +extern void mem_abandon( + MEM_HANDLE_T handle); + +/* + A discardable MEM_HANDLE_T with a retain count greater than 0 is + considered retained and may not be discarded by the memory manager. + + Preconditions: + + - handle is a valid MEM_HANDLE_T. + - It must be discardable. + + Postconditions: + + - 0 is returned if the size of the MEM_HANDLE_T's block of memory is 0, + otherwise 1 is returned. + - The retain count of the MEM_HANDLE_T is incremented. +*/ + +extern int mem_retain( + MEM_HANDLE_T handle); + +/* + Preconditions: + + - handle is a valid MEM_HANDLE_T. + - It must be retained. + + Postconditions: + + - The retain count of the MEM_HANDLE_T is decremented. +*/ + +extern void mem_unretain( + MEM_HANDLE_T handle); + +/* + A version of mem_lock which adds an indication that an aggressive compaction + should not wait for the block to be unlocked. +*/ + +extern void *mem_lock_perma( + MEM_HANDLE_T handle); + +/* + A version of mem_unlock which removes an indication that an aggressive + compaction should not wait for the block to be unlocked. +*/ + +extern void mem_unlock_perma( + MEM_HANDLE_T handle); + +/****************************************************************************** +Legacy memory blocks API +******************************************************************************/ + +/* + Allocate a fixed-size block. The size of legacy blocks is a constant for the + life of the memory subsystem (and may be a build-time constant). Legacy + blocks are stored in offline chunks, so this is a veneer over mem_offline. + + Preconditions: + + - flags must specify a memory alias + o MEM_FLAG_NORMAL + o MEM_FLAG_COHERENT + o MEM_FLAG_DIRECT + o MEM_FLAG_L1_NONALLOCATING (VC4 only) + - other permitted flags + o MEM_FLAG_LOW_256M + + Postconditions: + + If the attempt succeeds: + - A pointer to the legacy block is returned. + + If the attempt fails: + - NULL (0) is returned. +*/ + +extern void *mem_alloc_legacy_ex(MEM_FLAG_T flags); + +#define mem_alloc_legacy() mem_alloc_legacy_ex(MEM_FLAG_NORMAL) + + +/* + Free a previously-allocated fixed-size block. + + Preconditions: + + - ptr must point to a block previously returned by mem_alloc_legacy. + + Postconditions: + + - None. +*/ + +extern void mem_free_legacy(void *ptr); + + +/* + If count_max is positive then it sets a maximum number of legacy blocks which + can be allocated, otherwise the maximum possible (capped at 32) are allowed. + + Preconditions: + + - align must be a power of two. + - There must be no allocated legacy blocks. + + Postconditions: + + If the attempt succeeds: + - Returns the maximum number of legacy blocks, assuming no other allocations. + + If the attempt fails: + - Returns -1. +*/ + +extern int mem_init_legacy(uint32_t size, uint32_t align, int count_max); + + +/* + Preconditions: + + - None. + + Postconditions: + + - Returns the size of the legacy blocks. +*/ + +extern uint32_t mem_get_legacy_size(void); + + +/* + Preconditions: + + - None. + + Postconditions: + + - Returns the alignment of the legacy blocks. +*/ + +extern uint32_t mem_get_legacy_align(void); + + +/****************************************************************************** +Offline chunks API +******************************************************************************/ + +/* + Mark a contiguous chunk as being "offline". This is similar to a regular + block which is allocated and locked, except that chunks are always a + multiple of MEM_CHUNK_SIZE, and have no headers. Also, the allocator prefers + chunks at higher addresses. + mode specifies the types of compaction permitted, including MEM_COMPACT_NONE. + If no contiguous range of chunks can be found, the heap will be compacted + before retrying. + + Preconditions: + + - size must be an integer multiple of MEM_CHUNK_SIZE + - flags must specify a memory alias + o MEM_FLAG_NORMAL + o MEM_FLAG_COHERENT + o MEM_FLAG_DIRECT + o MEM_FLAG_L1_NONALLOCATING (VC4 only) + - other permitted flags + o MEM_FLAG_LOW_256M + + Postconditions: + + If the attempt succeeds: + - A pointer to the offline block is returned. + + If the attempt fails: + - NULL (0) is returned. +*/ + +extern void *mem_offline(uint32_t size, MEM_FLAG_T flags, + mem_compact_mode_t mode); + +extern int mem_offline_chunks(uint32_t num_chunks, MEM_FLAG_T flags, + mem_compact_mode_t mode); + + +/* + Free a previously-allocated fixed-size block. Note that it is legal + to take a large chunk offline and then bring a portion of it back + online. + + Preconditions: + + - ptr must point to a block previously returned by mem_alloc_legacy, + or a chunk-aligned section thereof. + - size must be an integer multiple of MEM_CHUNK_SIZE. + + Postconditions: + + - None. +*/ + +extern void mem_online(void *ptr, uint32_t size); + +extern void mem_online_chunks(int chunk, int num_chunks); + +/* + Retrieves various statistics about the heap chunks. + Note that _used + _available may not equal _total in the case of + overlapping areas. + */ + +extern void mem_get_chunk_stats(uint32_t *total, + uint32_t *legacy_used, uint32_t *legacy_available, uint32_t *legacy_total, + uint32_t *offline_used, uint32_t *offline_available, uint32_t *offline_total); + + +/****************************************************************************** +Long-term lock owners' API +******************************************************************************/ + +typedef enum +{ + /* An aggressive compaction is beginning. Any long-term locks should be released. */ + MEM_CALLBACK_REASON_UNLOCK, + + /* An aggressive compaction has completed. Any long-term locks can be reclaimed. */ + MEM_CALLBACK_REASON_RELOCK, + + /* The total amount of free memory has fallen below the threshold + * defined by mem_set_low_memory_threshold. + * To avoid repeated callbacks this callback is only invoked for the + * allocation that caused the free memory threshold to be crossed. + * + * Caveats + * The internal overheads of the heap are ignored. + * Small allocs are ignored. + * The available memory may be fragmented. + */ + MEM_CALLBACK_REASON_LOW_MEMORY, + + MEM_CALLBACK_REASON_MAX +} mem_callback_reason_t; + +typedef void (*mem_callback_func_t)(mem_callback_reason_t reason, uintptr_t context); + +/* Returns 1 on success, 0 on failure. */ +extern int mem_register_callback(mem_callback_func_t func, uintptr_t context); + +/* Defines the threshold in bytes at which the + * MEM_CALLBACK_REASON_LOW_MEMORY will be invoked. + */ +extern void mem_set_low_mem_threshold(uint32_t threshold); + +extern void mem_unregister_callback(mem_callback_func_t func, uintptr_t context); + + +/****************************************************************************** +Compaction notification API +******************************************************************************/ + +/** Type of compaction operation. */ +typedef enum +{ + /* A compaction is about to begin. */ + MEM_COMPACT_OP_BEGIN, + + /* A compaction has just ended. */ + MEM_COMPACT_OP_END, + + MEM_COMPACT_OP_MAX +} mem_compact_op_t; + +/** + * Compaction notification function. Called when a relocatable heap compaction is + * about to begin or has just ended. + * This gives the callback the ability to delay the start of compaction. + * Will be invoked independently from those registered via mem_register_callback(). + * @param op Compaction operation. + * @param context Context passed in compaction registration. + * @param retries Number of retries remaining (assuming at least one callback returns non-zero). + * 0 means the last call for this compaction. + * @return non-zero to delay compaction; 0 if ok for compaction to start. + */ +typedef int (*mem_compact_cb_t)(mem_compact_op_t op, uintptr_t context, int retries); + +/** + * Register a callback function to be invoked at the start and end of every relocatable + * heap compaction. + * @return 1 on success; 0 on failure. + * @note Do not call from a compaction notification function. + */ +int mem_register_compact_cb(mem_compact_cb_t func, uintptr_t context); + +/** + * Unregister a callback registered via mem_register_compact_cb(). + * @note Do not call from a compaction notification function. + */ +void mem_unregister_compact_cb(mem_compact_cb_t func, uintptr_t context); + +/****************************************************************************** +Movable memory helpers +******************************************************************************/ + +/* + Enable/disable the memory shuffler. Only has effect if the memory shuffler + has been included in the build (by setting the MEM_SHUFFLE define). The shuffler + will be started and enabled by default. A zero value of enable will disable + compactions. A non-zero value for enable will enable compactions. +*/ +void rtos_common_mem_shuffle_enable(int enable); + +extern MEM_HANDLE_T mem_strdup_ex( + const char *str, + mem_compact_mode_t mode); + +#define mem_strdup(str) mem_strdup_ex((str),MEM_COMPACT_ALL) + +/* + Allocate a new buffer (with a single reference) with the same size and + contents as the supplied buffer. This is intended to be used for data buffers + rather than structures (structures would require adding a reference to each + of their member handles). Hence the returned buffer does not have a + terminator and we require that the supplied buffer doesn't have one either. + + At present, *all* flags are carried across to the new buffer. I'm not sure + if this is wise. TODO: decide which we should allow, and whether any extra + ones should be passed as arguments. + + Alignment is carried across too. + + A valid MEM_HANDLE_T must be passed. It must have no terminator. +*/ + +extern MEM_HANDLE_T mem_dup_ex( + MEM_HANDLE_T handle, + const char *desc, + mem_compact_mode_t mode); + +#define mem_dup(handle,desc) mem_dup_ex((handle),(desc),MEM_COMPACT_ALL) + +extern void mem_print_state(void); +extern void mem_print_small_alloc_pool_state(void); +extern uint32_t mem_debug_get_alloc_count(void); + +/* + Retrieves various statistics about the allocated blocks. + */ + +extern void mem_get_stats(uint32_t *blocks, uint32_t *bytes, uint32_t *locked); + +/* + Returns the size of the pool. +*/ + +extern uint32_t mem_get_total_space(void); + +/* + Returns the size of the largest possible allocation, assuming no fragmentation. + */ + +extern uint32_t mem_get_free_space(void); + +/* + Returns the actual amount of free space. You won't actually be able to + allocate anything this big due to overhead. If the heap is empty, the return + value should equal mem_get_total_space() +*/ + +extern uint32_t mem_get_actual_free_space(void); + +/* + Returns the free space statistics. NULL pointers will not be written to. + */ + +extern void mem_get_free_space_stats(uint32_t *total, uint32_t *max, uint32_t *count); + +/* + Returns the current estimate of available free space. Quicker than mem_get_free_space. + */ + +extern uint32_t mem_get_low_mem_available_space(void); + +/* + Check the internal consistency of the heap. A corruption will result in + a failed assert, which will cause a breakpoint in a debug build. + + If MEM_FILL_FREE_SPACE is defined for the build, then free space is also + checked for corruption; this has a large performance penalty, but can help + to track down random memory corruptions. + + Note that defining MEM_AUTO_VALIDATE will enable the automatic validation + of the heap at key points - allocation, deallocation and compaction. + + Preconditions: + + - None. + + Postconditions: + + - None. +*/ + +extern void mem_validate(void); + +/* + Attempts to allocate a movable block of memory of the same size and alignment + as the specified structure type. + + Implementation Notes: + + The returned object obeys the invariants of the memory subsystem only. Invariants + of the desired structure type may not yet be obeyed. + The memory will be filled such that any handles in the structure would be + interpreted as MEM_HANDLE_INVALID + + Preconditions: + + STRUCT is a structure type + + the caller of this macro is contracted to later call mem_release (or pass such responsibility on) if we don't return MEM_HANDLE_INVALID + + Postconditions: + + If the attempt succeeds: + - A fresh MEM_HANDLE_T referring to the allocated block of memory is + returned. + - The MEM_HANDLE_T is unlocked, unretained, without a terminator, and has a + reference count of 1. + + If the attempt fails: + - MEM_HANDLE_INVALID is returned. +*/ + +#define MEM_ALLOC_STRUCT_EX(STRUCT, mode) mem_alloc_ex(sizeof(STRUCT), RCM_ALIGNOF(STRUCT), MEM_FLAG_NONE, #STRUCT, mode) +#define MEM_ALLOC_STRUCT(STRUCT) mem_alloc(sizeof(STRUCT), RCM_ALIGNOF(STRUCT), MEM_FLAG_NONE, #STRUCT) + +/* + Find out if a memory pointer is within the relocatable pool (excluding legacy blocks). + Returns non-zero if it is. + */ + +extern int mem_is_relocatable(const void *ptr); + +#ifndef VCMODS_LCC +// LCC doesn't support inline so cannot define these functions in a header file + +static RCM_INLINE void mem_assign(MEM_HANDLE_T *x, MEM_HANDLE_T y) +{ + if (y != MEM_HANDLE_INVALID) + mem_acquire(y); + if (*x != MEM_HANDLE_INVALID) + mem_release(*x); + + *x = y; +} + +/* + MEM_ASSIGN(x, y) + + Overwrite a handle with another handle, managing reference counts appropriately + + Implementation notes: + + Always use the macro version rather than the inline function above + + Preconditions: + + each of x and y is MEM_HANDLE_INVALID or a handle to a block with non-zero ref_count + if x is not MEM_HANDLE_INVALID and x != y and x.ref_count is 1, x.lock_count is zero + is y is not MEM_HANDLE_INVALID there must at some point be a MEM_ASSIGN(x, MEM_HANDLE_INVALID) + + Postconditions: + + Invariants preserved: +*/ + +#define MEM_ASSIGN(x, y) mem_assign(&(x), (y)) + +/*@null@*/ static RCM_INLINE void * mem_maybe_lock(MEM_HANDLE_T handle) +{ + if (handle == MEM_HANDLE_INVALID) + return 0; + else + return mem_lock(handle); +} + +static RCM_INLINE void mem_maybe_unlock(MEM_HANDLE_T handle) +{ + if (handle != MEM_HANDLE_INVALID) + mem_unlock(handle); +} + +#endif + +extern void mem_assign_null_multiple( + MEM_HANDLE_OFFSET_T *handles, + uint32_t n); + + +struct MEM_MANAGER_STATS_T; +extern void mem_get_internal_stats(struct MEM_MANAGER_STATS_T *stats); + +extern int mem_handle_acquire_if_valid(MEM_HANDLE_T handle); + +void mem_compact_task_enable(int enable); + +/****************************************************************************** +API of memory access control using sandbox regions + Users take the responsibility of assuring thread-safety +******************************************************************************/ +#undef MEM_ACCESS_CTRL + +#ifdef MEM_ACCESS_CTRL + +# if defined( NDEBUG ) || !defined( __BCM2708__ ) || defined( __BCM2708A0__ ) +# error "BCM2708b0 or better needed" +# endif + +#define MEM_SSSR_PRIV_SECURE (0x18) +#define MEM_SSSR_PRIV_SUPER (0x08) +#define MEM_SSSR_PRIV_USER (0x00) +#define MEM_SSSR_READ (0x04) +#define MEM_SSSR_WRITE (0x02) +#define MEM_SSSR_EXECUTE (0x01) + +#define MEM_ACCESSCTRL_BLOCKS_MAX (7) // Sandbox 0 is reserved for .text + +typedef struct { + uint32_t start; + uint32_t size; + uint32_t flags; +} MEM_ACCESSCTRL_BLOCK; + +void mem_clr_accessctrl(); +void mem_set_accessctrl(MEM_ACCESSCTRL_BLOCK *blocks, uint32_t n); +#define MEM_CLR_ACCESSCTRL mem_clr_accessctrl +#define MEM_SET_ACCESSCTRL mem_set_accessctrl + +#else +#define MEM_CLR_ACCESSCTRL() ((void)0) +#define MEM_SET_ACCESSCTRL(BLOCKS, NUM) ((void)0) +#endif //#ifdef MEM_ACCESS_CTRL + + +#endif diff --git a/vcfw/rtos/rtos.h b/vcfw/rtos/rtos.h new file mode 100755 index 0000000..adee818 --- /dev/null +++ b/vcfw/rtos/rtos.h @@ -0,0 +1,540 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RTOS_H_ +#define RTOS_H_ + +#if !defined(__VIDEOCORE__) && !defined(__vce__) && !defined(VIDEOCORE_CODE_IN_SIMULATION) +#error This code is only for use on VideoCore. If it is being included +#error on the host side, then you have done something wrong. Defining +#error __VIDEOCORE__ is *not* the right answer! +#endif + +#include "vcinclude/common.h" +#include "vcfw/drivers/driver.h" + +#ifdef _VIDEOCORE +#include "host_support/include/vc_debug_sym.h" +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + Global funcs - implementation is specific to which RTOS you are including + *****************************************************************************/ + +//Routine to initialise the RTOS +extern int32_t rtos_init( void ); + +//Routine to start the RTOS +extern int32_t rtos_start( int argc, char *argv[] ); + +/****************************************************************************** + Global data + *****************************************************************************/ + +extern int rtos_argc; +extern char **rtos_argv; + +/****************************************************************************** + RTOS Time functions + *****************************************************************************/ + +//Routine to delay the execution cycle +extern int32_t rtos_delay( const uint32_t usecs, const uint32_t suspend ); + +//Routine used to put the current task (if there is one) to sleep +extern int32_t rtos_sleep( void ); + +/*********************************************************** + * Returns the current hardware microsecond counter value. + * + * @return Microsecond counter value. + * + ***********************************************************/ + +extern unsigned rtos_getmicrosecs(void); +extern uint64_t rtos_getmicrosecs64(void); + +/****************************************************************************** + Driver + module loading / unloading code + *****************************************************************************/ + +//Routine to load a driver +extern int32_t rtos_driver_load( const char *driver_name, DRIVER_T **driver ); + +//Routine to unload a driver +extern int32_t rtos_driver_unload( const DRIVER_T *driver ); + +/****************************************************************************** + ISR functionality + *****************************************************************************/ + +//Definition of a LISR +typedef void (*RTOS_LISR_T)( const uint32_t vector_num ); + +//Definition of an ASM lisr +typedef void (*RTOS_LISR_ASM_T)( void ); + +//Routine to register a isr +extern int32_t rtos_register_lisr ( const uint32_t vector, RTOS_LISR_T new_lisr, RTOS_LISR_T *old_lisr ); + +//Routine to register an assembler based isr +extern int32_t rtos_register_asm_lisr ( const uint32_t vector, RTOS_LISR_ASM_T new_lisr, RTOS_LISR_ASM_T *old_lisr ); + +//Routine to disable interrupts +extern uint32_t rtos_disable_interrupts( void ); + +//Routine to re-enable interrupts +extern void rtos_restore_interrupts( const uint32_t previous_state ); + + +//Routine to query if interrupts are enable or not +#ifdef __VIDEOCORE__ +static inline uint32_t rtos_interrupts_enabled( void ) +{ + return !!(_vasm("mov %D,%sr") & (1<<30)); +} +#endif // ifdef __HIGHC__ + +/****************************************************************************** + Secure functionality + *****************************************************************************/ + +//Definition for a secure function handle +typedef uint32_t RTOS_SECURE_FUNC_HANDLE_T; + +//Definition for a secure function +//NOTE! Whilst no params are shown for this function, the calling function can pass up to 5 registers to it +#if defined( __linux__ ) +// I get compiler warnings when building for linux (MxC) since () isn't a valid prototype. +// Using (...) isn't valid under ANSI C, (you need to have at least one fixed argument). +// So since I don't think that this stuff is actually used from linux, I decided to make it +// use (void) until a better solution can be done. +typedef void (*RTOS_SECURE_FUNC_T)(void); +#else +typedef void (*RTOS_SECURE_FUNC_T)(); +#endif + + +//Function to register secure functions +//NOTE - these functions should be encrypted +extern int32_t rtos_secure_function_register( RTOS_SECURE_FUNC_T secure_func, + RTOS_SECURE_FUNC_HANDLE_T *secure_func_handle ); + +//Routine to call a secure function +extern int32_t rtos_secure_function_call( const RTOS_SECURE_FUNC_HANDLE_T secure_func_handle, + const uint32_t r0, + const uint32_t r1, + const uint32_t r2, + const uint32_t r3, + const uint32_t r4 ); + +/****************************************************************************** + Multithreading functionality + *****************************************************************************/ +// This constant is a thread id which should never be returned by +// rtos_get_thread_id +#define RTOS_INVALID_THREAD_ID (~0) + +// Routine to get the id of the current thread +extern uint32_t rtos_get_thread_id( void ); + +// Routine to migrate the current thread to the other cpu. Does nothing if +// already on the requested cpu. +// In rtos_none, fails if something is already running on the other cpu. +extern void rtos_cpu_migrate( const uint32_t cpu ); + +// Launch the given function asynchronously on the other cpu. Do not wait +// for it to return. +// In rtos_none, fails if the specified cpu is either the one you're calling +// from, or has something else running on it. +typedef void (*RTOS_LAUNCH_FUNC_T)( uint32_t argc, void *argv ); +extern uint32_t rtos_cpu_launch( const uint32_t cpu, RTOS_LAUNCH_FUNC_T launch_func, const uint32_t argc, void *argv, const uint32_t stack_size ); + +// Wait for the specified thread to terminate. This should be called exactly +// once for each thread created. After it is called, thread_id may identify +// a new thread. +extern void rtos_thread_join( const uint32_t thread_id ); + +// Return the amount of free space on the stack (in bytes). Useful for making +// recursive algorithms safe. +extern uint32_t rtos_check_stack( void ); + +// Returns non-zero if the caller is within interrupt context, otherwise +// returns zero. +extern int rtos_in_interrupt( void ); + +/****************************************************************************** + Latch functionality + *****************************************************************************/ + +//typedef of a latch +typedef struct opaque_rtos_latch_t* RTOS_LATCH_T; + +// Latch values: +// 0 = unlocked +// 1 = locked without contention +// Other = locked with contention (one or more threads waiting in rtos_latch_get) + +#define rtos_latch_locked() ((RTOS_LATCH_T)1) // For use in initialisation only + +#define rtos_latch_unlocked() ((RTOS_LATCH_T)0) + +// N.B. a latch can also be a pointer to a queue of waiting threads, so +// comparison to rtos_latch_locked() is rarely wise; inequality to +// rtos_latch_unlocked() is a better test of lockedness. + +// Don't call these functions, use the rtos_latch_get etc macros. +//Routine to get the latch. Will suspend / busy wait dependent on the rtos implementation +extern void rtos_latch_get_real( RTOS_LATCH_T *latch ); +//Routine to put the latch. +extern void rtos_latch_put_real( RTOS_LATCH_T *latch ); + +// Routine to try and get a latch. Can be called from an interrupt +extern int32_t rtos_latch_try_real( RTOS_LATCH_T *latch ); + + +// See vcfw/rtos/common/rtos_latch_logging.c for more about latch logging. +//#define LATCH_LOGGING_ENABLED + +#ifdef NDEBUG +#undef LATCH_LOGGING_ENABLED +#endif + +#ifdef LATCH_LOGGING_ENABLED + +extern void rtos_latch_logging_latch_get( RTOS_LATCH_T *latch, const char * name ); +extern void rtos_latch_logging_latch_put( RTOS_LATCH_T *latch, const char * name ); +extern int32_t rtos_latch_logging_latch_try( RTOS_LATCH_T *latch, const char * name ); + + +#ifndef STRINGISED +#define _STRINGISED(x) #x +#define STRINGISED(x) _STRINGISED(x) +#endif + +#define rtos_latch_get( latch ) rtos_latch_logging_latch_get( (latch) , "Loc:" __FILE__ " " STRINGISED(__LINE__) ) +#define rtos_latch_put( latch ) rtos_latch_logging_latch_put( (latch) , "Loc:" __FILE__ " " STRINGISED(__LINE__) ) +#define rtos_latch_try( latch ) rtos_latch_logging_latch_try( (latch) , "Loc:" __FILE__ " " STRINGISED(__LINE__) ) + +extern void rtos_latch_logging_init(); + +#else + +#define rtos_latch_get( latch ) rtos_latch_get_real( (latch) ) +#define rtos_latch_put( latch ) rtos_latch_put_real( (latch) ) +#define rtos_latch_try( latch ) rtos_latch_try_real( (latch) ) + + +#endif + + +/****************************************************************************** + CPU-related functions + *****************************************************************************/ + +// Returns the number of CPUs controlled by the RTOS +extern uint32_t rtos_get_cpu_count( void ); + +// Returns the number of the current CPU (numbered from 0) +extern uint32_t rtos_get_cpu_number( void ); + +// This constant indicates that the current CPU should be used +#define RTOS_CPU_CURRENT (~0) + +// Returns a count of the usecs spent sleeping on the specified CPU +extern uint32_t rtos_get_sleep_time( const uint32_t cpu ); + +// Zero the sleep time on specified cpu. Used to find out if +// CPU1 has a running thread system after being started: +// zeroing the recorded sleep time is otherwise undesirable. +extern void rtos_reset_sleep_time( const uint32_t cpu ); + + +// Returns a count of the total usecs spent executing LISRs on the +// specified CPU +extern uint32_t rtos_get_lisr_time( const uint32_t cpu ); + +// This constant is used to specify an unlimited execution time +#define RTOS_MAX_TIME_UNLIMITED (~0) + +// Sets the upper limit on the number of usecs an LISR can take. +// Exceeding this limit will trigger a breakpoint on completion. +// Returns the previous limit. +extern uint32_t rtos_set_lisr_max_time( const uint32_t cpu, const uint32_t limit ); + +/****************************************************************************** + Common RTOS functions, stored in vcfw/rtos/common. + Note! There is only 1 copy of these functions and they do not contain platform, + rtos or chip specific functions + *****************************************************************************/ + +/****************************************************************************** + Timer funcs + *****************************************************************************/ + +// forward-declare struct RTOS_TIMER +struct RTOS_TIMER; + +//Callback definition for when the timer has expired +typedef void (*RTOS_TIMER_DONE_OPERATION_T)( struct RTOS_TIMER *timer, void *priv ); + +//The time to delay before calling the callback +typedef uint32_t RTOS_TIMER_TIME_T; /* microseconds */ + +typedef uint32_t RTOS_TIMER_TICKS_T; + +typedef struct RTOS_TIMER +{ + RTOS_TIMER_DONE_OPERATION_T done; + void *priv; + + /* implementation */ + RTOS_TIMER_TICKS_T ticks; + RTOS_TIMER_TICKS_T offset; + struct RTOS_TIMER *next; + +} RTOS_TIMER_T; + +// Initialises TIMER with the given parameters. Returns zero on success +extern int32_t rtos_timer_init( RTOS_TIMER_T *timer, RTOS_TIMER_DONE_OPERATION_T done, void *priv); + +// Returns non-zero if TIMER is in the queue +extern int32_t rtos_timer_is_running( const RTOS_TIMER_T *timer ); + +// Adds TIMER to the queue to run TIMER->done(TIMER->priv) after DELAY microseconds +extern int32_t rtos_timer_set( RTOS_TIMER_T *timer, const RTOS_TIMER_TIME_T delay); + +// TIMER is cancelled and then set. Returns zero if successful +extern int32_t rtos_timer_reset( RTOS_TIMER_T *timer, const RTOS_TIMER_TIME_T delay); + +// TIMER is cancelled if running and the callback is not called. Returns 0 if the timer was running +extern int32_t rtos_timer_cancel( RTOS_TIMER_T *timer ); + + +/****************************************************************************** + Memory- and address space-related functions + *****************************************************************************/ + +#define RTOS_ALIGN_DEFAULT 4 +#define RTOS_ALIGN_128BIT 16 +#define RTOS_ALIGN_256BIT 32 +#define RTOS_ALIGN_AXI RTOS_ALIGN_128BIT +#define RTOS_ALIGN_512BIT 64 +#define RTOS_ALIGN_4KBYTE 4096 +#define RTOS_PRIORITY_INTERNAL 9999 // must be in internal memory +#define RTOS_PRIORITY_EXTERNAL 0 // must be in external memory +#define RTOS_PRIORITY_UNIMPORTANT 1 + +#if defined(__VIDEOCORE5__) + +#define RTOS_PRIORITY_SHIFT 30 +#define RTOS_ALIAS_NORMAL(x) (x) // normal cached data (uses main 128K L2 cache) +#define RTOS_ALIAS_L1_NONALLOCATING(x) (x) // coherent with L1, allocating in L2 +#define RTOS_ALIAS_COHERENT(x) (x) // cache coherent but non-allocating +#define RTOS_ALIAS_DIRECT(x) (x) // uncached +#define RTOS_IS_ALIAS_NOT_L1(x) (0) +#define RTOS_IS_ALIAS_DIRECT(x) (0) + +#define RTOS_PRIORITY_L1_NONALLOCATING (0 << RTOS_PRIORITY_SHIFT) +#define RTOS_PRIORITY_L1L2_NONALLOCATING (0 << RTOS_PRIORITY_SHIFT) +#define RTOS_PRIORITY_COHERENT RTOS_PRIORITY_L1L2_NONALLOCATING +#define RTOS_PRIORITY_DIRECT (0 << RTOS_PRIORITY_SHIFT) + +#elif defined(__VIDEOCORE4__) + +#define RTOS_PRIORITY_SHIFT 30 +#define RTOS_ALIAS_NORMAL(x) ((void*)(((unsigned)(x)&~0xc0000000)|0x00000000)) // normal cached data (uses main 128K L2 cache) +#define RTOS_ALIAS_L1_NONALLOCATING(x) ((void*)(((unsigned)(x)&~0xc0000000)|0x40000000)) // coherent with L1, allocating in L2 +#if defined(__BCM2708A0__) || defined(__BCM2708B0__) +// HW-2827 workaround +#define RTOS_ALIAS_COHERENT(x) RTOS_ALIAS_L1_NONALLOCATING(x) +#else +#define RTOS_ALIAS_COHERENT(x) ((void*)(((unsigned)(x)&~0xc0000000)|0x80000000)) // cache coherent but non-allocating +#endif +#define RTOS_ALIAS_DIRECT(x) ((void*)(((unsigned)(x)&~0xc0000000)|0xc0000000)) // uncached +#define RTOS_IS_ALIAS_NOT_L1(x) (((((unsigned)(x)>>30)&0x3)==1) || (((unsigned)(x)>>29)>=3)) +#define RTOS_IS_ALIAS_DIRECT(x) ((((unsigned)(x)>>30)&0x3)==3) + +// RTOS_PRIORITY_L1_NONALLOCATING will skip the L1 cache, but still allocate in L2 +// this is needed if VPU is communicating with hardware via memory +#define RTOS_PRIORITY_L1_NONALLOCATING (1 << RTOS_PRIORITY_SHIFT) +#define RTOS_PRIORITY_L1L2_NONALLOCATING (2 << RTOS_PRIORITY_SHIFT) +#define RTOS_PRIORITY_COHERENT RTOS_PRIORITY_L1L2_NONALLOCATING +#define RTOS_PRIORITY_DIRECT (3 << RTOS_PRIORITY_SHIFT) + +#else // defined (__VIDEOCORE4__) + +#define RTOS_PRIORITY_SHIFT 28 +#define RTOS_ALIAS_NORMAL(x) ((void*)(((unsigned)(x)&~0xf0000000)|0x00000000)) // normal cached data (uses main 128K L2 cache) +#define RTOS_ALIAS_L1_NONALLOCATING(x) (x) // coherent with L1, allocating in L2 +#define RTOS_ALIAS_COHERENT(x) ((void*)(((unsigned)(x)&~0xf0000000)|0x20000000)) // cache coherent but non-allocating +#define RTOS_ALIAS_DIRECT(x) ((void*)(((unsigned)(x)&~0xf0000000)|0x30000000)) // uncached +#define RTOS_IS_ALIAS_NOT_L1(x) (1) // compatibility with vc4 +#define RTOS_IS_ALIAS_DIRECT(x) ((((unsigned)(x)>>28)&0xf)==3) + +#define RTOS_PRIORITY_L1_NONALLOCATING (0 << RTOS_PRIORITY_SHIFT) +#define RTOS_PRIORITY_L1L2_NONALLOCATING (2 << RTOS_PRIORITY_SHIFT) +#define RTOS_PRIORITY_COHERENT RTOS_PRIORITY_L1L2_NONALLOCATING +#define RTOS_PRIORITY_DIRECT (3 << RTOS_PRIORITY_SHIFT) + +#endif + +#ifdef __VIDEOCORE__ +#define RTOS_WRITE_BARRIER(x) _vasm("ld %D, (%r)\nadd %D, 0", x) +#else +#define RTOS_WRITE_BARRIER(x) (void)0 +#endif + +// Returns a pointer to the the start of the memory allocated to VideoCore +extern void * rtos_get_vc_mem_start(void); + +// Returns the size of the memory allocated to VideoCore +extern uint32_t rtos_get_vc_mem_size(void); + +// Returns a pointer to the the start of the memory addressable by VideoCore +extern void * rtos_get_sys_mem_start(void); + +// Returns the size of the memory addressable by VideoCore +extern uint32_t rtos_get_sys_mem_size(void); + + +/****************************************************************************** + Malloc / free funcs + *****************************************************************************/ + +extern void * const __RTOS_HEAP_END, * const __RTOS_HEAP_START; + +#ifdef _VIDEOCORE + +typedef struct OPAQUE_RTOS_MEMPOOL_T *rtos_mempool_t; + +// Allocates memory using a priority based scheme +extern void *rtos_malloc_priority( const uint32_t size, const uint32_t align, const uint32_t priority, const char *description); + +// Allocates and clears memory using a priority based scheme +extern void *rtos_calloc_priority(const uint32_t size, const uint32_t align, const uint32_t priority, const char *description); + +// Re-allocates some memory (and changes it size) +extern void *rtos_realloc_256bit(void *ret, const uint32_t size); + +//Routine to query how much free memory is available in the pool +extern uint32_t rtos_get_free_mem(const rtos_mempool_t pool); + +//Routine to query the initial malloc heap memory size +extern uint32_t rtos_get_total_mem(const rtos_mempool_t pool); + +/* return a pointer to whatever called us, which can be used as a malloc name + * (works best before first function call in a function). + * This should normally be used only in malloc() wrappers to avoid the + * situation where everything gets named "abstract_malloc" or the like. + */ +#define rtos_default_malloc_name() (char const *)_vasm("bitset %D, %lr, 31") +#define RTOS_MALLOC_NAME_IS_CODE_FLAG (1u << 31) + +VC_DEBUG_EXTERN_UNCACHED_VAR(uint32_t,boot_state); +VC_DEBUG_EXTERN_UNCACHED_VAR(uint32_t,boot_state_info); + +#define BOOT_STATE(c0,c1,c2,c3) VC_DEBUG_ACCESS_UNCACHED_VAR(boot_state) = (c0+(c1<<8)+(c2<<16)+(c3<<24)) +#define BOOT_STATE_INFO(v) VC_DEBUG_ACCESS_UNCACHED_VAR(boot_state_info) = (v) +#define BOOT_STATE_EQUALS(c0,c1,c2,c3) (VC_DEBUG_ACCESS_UNCACHED_VAR(boot_state) == (c0+(c1<<8)+(c2<<16)+(c3<<24))) + +#else //ifdef _VIDEOCORE + +#define rtos_malloc_priority(size, align, priority, description) malloc(size) +#define rtos_calloc_priority(size, align, priority, description) calloc(1,size) +#define rtos_realloc_256bit(ret, size) realloc(ret,size) + +#define rtos_default_malloc_name() (char const *)0 +#define RTOS_MALLOC_NAME_IS_CODE_FLAG 0 + +#endif // ifdef _VIDEOCORE + +// Malloc aliases +#ifdef NDEBUG + #define rtos_prioritymalloc( size, align, priority, description ) rtos_malloc_priority(size, align, priority, (char const *)0) + #define rtos_prioritycalloc( size, align, priority, description ) rtos_calloc_priority(size, align, priority, (char const *)0) +#else + #define rtos_prioritymalloc( size, align, priority, description ) rtos_malloc_priority(size, align, priority, description) + #define rtos_prioritycalloc( size, align, priority, description ) rtos_calloc_priority(size, align, priority, description) +#endif + +#define rtos_priorityfree( buffer ) free(buffer) +#define rtos_malloc_256bit( size ) rtos_prioritymalloc(size, RTOS_ALIGN_256BIT, 100, (char const *)0) +#define rtos_free_256bit( ret ) free(ret) +#define rtos_malloc_external( size ) rtos_prioritymalloc(size, RTOS_ALIGN_DEFAULT, 1, (char const *)0) +#define rtos_malloc_external_256bit( size ) rtos_prioritymalloc(size, RTOS_ALIGN_256BIT, 1, (char const *)0); + +// manipulate priority level for mallocs +extern int rtos_setpriority(const uint32_t priority); +extern int rtos_getpriority(void); + +// Perform basic safety checks on a memory range +extern int rtos_memory_is_valid(void const *base, int length); + +// Scan the heap for evidence of memory corruption -- returns pointer to first corrupt block or NULL if OK +extern void const *rtos_find_heap_corruption(int flags); + +#define rtos_calloc(a, b) calloc( a, b ) +#define rtos_malloc(size) malloc( size ) + +/****************************************************************************** + Power / reset functions +******************************************************************************/ + +// Reset the processor +void rtos_reset( void ); + +//Routine to initialise the relocatable heap. +void rtos_relocatable_heap_init( void ); + +/****************************************************************************** + Testing functions +******************************************************************************/ + +extern int vctest_should_open_fail(char const *description); +extern int vctest_should_alloc_fail(char const *description); +extern int vctest_should_readwrite_fail(char const *description); +extern void vctest_start_failure_testing(void); + +#ifdef _VIDEOCORE +#define VCTEST_DEBUGGER_BKPT_TRAP(name) _vasm(#name ": \n .globl " #name "\n\t nop") +#else +#define VCTEST_DEBUGGER_BKPT_TRAP(name) 0 +#endif + +#ifdef __cplusplus +} +#endif + + +#endif // RTOS_H_ diff --git a/vcfw/vclib/vclib.h b/vcfw/vclib/vclib.h new file mode 100755 index 0000000..024137a --- /dev/null +++ b/vcfw/vclib/vclib.h @@ -0,0 +1,239 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCFW_VCLIB_H +#define VCFW_VCLIB_H + + #ifdef __VIDEOCORE__ + +#include "vcfw/rtos/rtos.h" +#include "vcfw/logging/logging.h" + +int32_t vclib_vcfw_init( void ); +int32_t vclib_vcfw_driver_init( void ); + +typedef enum { + FATAL_RED = 1, + FATAL_GREEN, + FATAL_BLUE, + FATAL_CYAN, + FATAL_MAGENTA, + FATAL_YELLOW, + FATAL_WHITE, + FATAL_BLACK, + + FATAL_SUB_RED = FATAL_RED << 4, + FATAL_SUB_GREEN = FATAL_GREEN << 4, + FATAL_SUB_BLUE = FATAL_BLUE << 4, + FATAL_SUB_CYAN = FATAL_CYAN << 4, + FATAL_SUB_MAGENTA = FATAL_MAGENTA << 4, + FATAL_SUB_YELLOW = FATAL_YELLOW << 4, + FATAL_SUB_WHITE = FATAL_WHITE << 4, + FATAL_SUB_BLACK = FATAL_BLACK << 4, +} FATAL_COLOUR; + +void vclib_fatal_fn(FATAL_COLOUR colour); + #ifdef FATAL_ASSERTS +#define vclib_fatal_assert(x, c) if (x) {} else vclib_fatal_fn(c) + #else +#define vclib_fatal_assert(x, c) assert(x) + #endif + +extern void vclib_init(); +extern int vclib_obtain_VRF(int block); +extern void vclib_release_VRF(void); +extern int vclib_check_VRF(void); + +#define get_free_mem(pool) (rtos_get_free_mem(pool)) + +#define malloc_setpool( external) rtos_malloc_setpool( external) +#define malloc_256bit( size) rtos_malloc_256bit( size) +#define free_256bit(ret) rtos_free_256bit(ret) +#define realloc_256bit(ret, size) rtos_realloc_256bit(ret, size) +#define malloc_external( size) rtos_malloc_external( size) +#define malloc_external_256bit( size) rtos_malloc_external_256bit( size ) + +#define malloc_priority( size, align, priority, description) rtos_prioritymalloc( size, align, priority, description) +#define calloc_priority( size, align, priority, description) rtos_prioritycalloc( size, align, priority, description) + +#define vclib_setpriority rtos_setpriority +#define vclib_getpriority rtos_getpriority + +#define vclib_prioritymalloc(size, align, priority, description) rtos_prioritymalloc(size, align, priority, description) +#define vclib_prioritycalloc(size, align, priority, description) rtos_prioritycalloc(size, align, priority, description) + +#define vclib_priorityfree(buffer) rtos_priorityfree(buffer) +#define VCLIB_ALIGN_DEFAULT RTOS_ALIGN_DEFAULT +#define VCLIB_ALIGN_128BIT RTOS_ALIGN_128BIT +#define VCLIB_ALIGN_256BIT RTOS_ALIGN_256BIT +#define VCLIB_ALIGN_AXI RTOS_ALIGN_AXI +#define VCLIB_ALIGN_512BIT RTOS_ALIGN_512BIT +#define VCLIB_ALIGN_4KBYTE RTOS_ALIGN_4KBYTE +#define VCLIB_PRIORITY_INTERNAL RTOS_PRIORITY_INTERNAL +#define VCLIB_PRIORITY_EXTERNAL RTOS_PRIORITY_EXTERNAL +#define VCLIB_PRIORITY_UNIMPORTANT RTOS_PRIORITY_UNIMPORTANT +#define VCLIB_PRIORITY_COHERENT RTOS_PRIORITY_COHERENT +#define VCLIB_PRIORITY_DIRECT RTOS_PRIORITY_DIRECT + +extern void vclib_save_vrf( unsigned char *vrf_strorage ); +extern void vclib_restore_vrf( unsigned char *vrf_strorage ); +extern void vclib_save_quarter_vrf( unsigned char *vrf_strorage ); +extern void vclib_restore_quarter_vrf( unsigned char *vrf_strorage ); + +extern void vclib_cache_flush(void); +extern void vclib_dcache_flush(void); +extern void vclib_icache_flush(void); +extern void vclib_dcache_flush_range(void *start_addr, int length); +extern void vclib_l1cache_flush_range(void *start_addr, int length); + +/* Use these function to identify the condition where you don't care if the data is + * written back -- if it becomes possible then we save ourselves the bother of + * hunting for places where it should be done that way. + */ +extern void vclib_dcache_invalidate_range(void *start_addr, int length); +extern void vclib_l1cache_invalidate_range(void *start_addr, int length); + +#define vclib_timer_t RTOS_TIMER_T +#define vclib_timer_done_op RTOS_TIMER_DONE_OPERATION_T +#define vclib_timer_time_t RTOS_TIMER_TIME_T +#define vclib_timer_ticks_t RTOS_TIMER_TICKS_T + +#define vclib_timer_init rtos_timer_init +#define vclib_timer_is_running rtos_timer_is_running +#define vclib_timer_set rtos_timer_set +#define vclib_timer_reset rtos_timer_reset +#define vclib_timer_cancel rtos_timer_cancel + +extern int vclib_disableint(void); +extern void vclib_restoreint(int sr); + + +/* Some support for automatic logging. */ + +/* Have a macro that is guaranteed to be a simple "assert" even when logging is on. */ + #ifdef NDEBUG +#define simple_assert(cond) + #else +#define simple_assert(cond) if (cond) {} else _bkpt() + #endif + + +/********* +** HACK!!!!!! Temp function definition (code in cache_flush.s) used to enable the run domain in VCIII +**********/ +extern void vc_enable_run_domain( void ); + +#define vclib_memory_is_valid rtos_memory_is_valid + + +/* Latch events are fast and small event notifications */ +typedef RTOS_LATCH_T latch_event_t; + +#define latch_event_present() ((latch_event_t)rtos_latch_unlocked()) + +#define latch_event_absent() ((latch_event_t)rtos_latch_locked()) + + +#define latch_event_is_present( latch ) (latch_event_present() == *(latch)) + + #if defined(__HIGHC__) /* MetaWare tools */ + + static inline unsigned vclib_status_register(void) + { + /* Would have liked use == syntax for assigning a variable to be a register + but the compiler issues a warning about that */ + return _vasm("mov %D,%sr"); + } + static inline int vclib_interrupts_enabled(void) + { + unsigned sr = vclib_status_register(); + return !!(sr&(1<<30)); + } + + #else + + static inline unsigned vclib_status_register(void) + { + unsigned i; + asm("mov %0, sr" : "=r"(i)); + return i; + } + static inline int vclib_interrupts_enabled(void) + { + unsigned sr = vclib_status_register(); + return !!(sr&(1<<30)); + } + + #endif + + #endif // defined __VIDEOCORE__ + + +// A few helpful things for windows / standalone Linux +# ifndef __VIDEOCORE__ + +# ifdef __GNUC__ + static inline int vclib_obtain_VRF(int block) { return 1; } + static inline void vclib_release_VRF(void) {} + static inline int vclib_check_VRF(void) { return 1; } +# else +# define vclib_obtain_VRF(b) (1) +# define vclib_release_VRF() +# define vclib_check_VRF() (1) +# endif + +# ifdef VIDEOCORE_CODE_IN_SIMULATION + +# undef abs // prior to including stdlib.h +# include // For malloc() et al. + +# define vclib_dcache_flush_range(start_addr, length) + +# define VCLIB_ALIGN_DEFAULT 4 +# define VCLIB_ALIGN_256BIT 32 +# define VCLIB_ALIGN_4KBYTE 4096 + +# define VCLIB_PRIORITY_COHERENT 0 +# define VCLIB_PRIORITY_DIRECT 0 +# define VCLIB_PRIORITY_UNIMPORTANT 0 + +# define vclib_fatal_assert(x, c) assert(x) + +# define vclib_prioritymalloc(size, align, priority, description) \ + rtos_prioritymalloc((size), (align), (priority), (description)) +# define vclib_prioritycalloc(size, align, priority, description) \ + rtos_prioritycalloc((size), (align), (priority), (description)) + +# define vclib_priorityfree(buffer) rtos_priorityfree((buffer)) +# define malloc_256bit(size) rtos_malloc_256bit((size)) +# define free_256bit(ret) rtos_free_256bit((ret)) + +# endif + +# endif // !defined(__VIDEOCORE__) + +#endif // defined VCFW_VCLIB_H diff --git a/vcinclude/common.h b/vcinclude/common.h new file mode 100755 index 0000000..05d1bdd --- /dev/null +++ b/vcinclude/common.h @@ -0,0 +1,140 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __VC_INCLUDE_COMMON_H__ +#define __VC_INCLUDE_COMMON_H__ + +#include "interface/vcos/vcos_stdint.h" +#include "interface/vctypes/vc_image_types.h" + +#if defined(__HIGHC__) && defined(_VIDEOCORE) && !defined(_I386) +// __HIGHC__ is only available with MW +// The scvc plugins are compiled (bizarrely) on an x86 with _VIDEOCORE set! +#include +#endif + +#ifdef __COVERITY__ +#ifndef _Rarely +#define _Rarely(x) (x) +#endif +#ifndef _Usually +#define _Usually(x) (x) +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __SYMBIAN32__ +# ifndef INLINE +# define INLINE __inline +# endif + +/* Align a pointer/integer by rounding up/down */ +#define ALIGN_DOWN(p, n) ((uint32_t)(p) - ( (uint32_t)(p) % (uint32_t)(n) )) +#define ALIGN_UP(p, n) ALIGN_DOWN((uint32_t)(p) + (uint32_t)(n) - 1, (n)) + +#elif defined (VCMODS_LCC) +#include + + +#elif !defined(__KERNEL__) +#include + +#endif + + +/*}}}*/ + +/* Fixed-point types */ +typedef unsigned short uint8p8_t; +typedef signed short sint8p8_t; +typedef unsigned short uint4p12_t; +typedef signed short sint4p12_t; +typedef signed short sint0p16_t; +typedef signed char sint8p0_t; +typedef unsigned char uint0p8_t; +typedef signed long int24p8_t; + +/*{{{ Common typedefs */ + +typedef enum bool_e +{ + VC_FALSE = 0, + VC_TRUE = 1, +} VC_BOOL_T; + +#ifndef bool_t +#define bool_t VC_BOOL_T +#endif + +/*}}}*/ + +/*{{{ Common macros */ + + +/* Align a pointer/integer by rounding up/down */ +#define ALIGN_DOWN(p, n) ((uintptr_t)(p) - ( (uintptr_t)(p) % (uintptr_t)(n) )) +#define ALIGN_UP(p, n) ALIGN_DOWN((uintptr_t)(p) + (uintptr_t)(n) - 1, (n)) + +#define CLIP(lower, n, upper) _min((upper), _max((lower), (n))) + +/*}}}*/ + +/*{{{ Debugging and profiling macros */ + +#if 0 +/* There's already an assert_once in */ +#ifdef DEBUG +#define assert_once(x) \ + { \ + static uint8_t ignore = 0; \ + if(!ignore) \ + { \ + assert(x); \ + ignore++; \ + } \ + } +#else +#define assert_once(x) (void)0 +#endif +#endif /* 0 */ + +#if defined(__HIGHC__) && !defined(NDEBUG) +/* HighC lacks a __FUNCTION__ preproc symbol... :( */ +#define profile_rename(name) _ASM(".global " name "\n" name ":\n") +#else +#define profile_rename(name) (void)0 +#endif + +/*}}}*/ +#ifdef __cplusplus + } +#endif +#endif /* __VCINCLUDE_COMMON_H__ */ + diff --git a/vcinclude/vc_image_types.h b/vcinclude/vc_image_types.h new file mode 100755 index 0000000..58ac333 --- /dev/null +++ b/vcinclude/vc_image_types.h @@ -0,0 +1,34 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __VC_INCLUDE_IMAGE_TYPES_H__ +#define __VC_INCLUDE_IMAGE_TYPES_H__ + +#include "interface/vctypes/vc_image_types.h" +#include "interface/vctypes/vc_image_structs.h" + +#endif /* __VC_INCLUDE_IMAGE_TYPES_H__ */ diff --git a/vcinclude/vcore.h b/vcinclude/vcore.h new file mode 100755 index 0000000..7fd7113 --- /dev/null +++ b/vcinclude/vcore.h @@ -0,0 +1,64 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCORE_H +#define VCORE_H + +#ifdef __VIDEOCORE__ +#include "vc/intrinsics.h" +#undef asm +#define asm(x) _ASM(x) + +#undef min +#define min(x,y) _min(x,y) + +#undef max +#define max(x,y) _max(x,y) + +#ifndef abs +#define abs(x) _abs(x) +#endif +#else +#define _vasm asm +#define _bkpt() do {asm(" bkpt");}while(0) +#define _di() do{asm(" di");}while(0) +#define _ei() do{asm(" ei");}while(0) +#define _nop() do{asm(" nop");}while(0) +#define _sleep() do{asm(" sleep");}while(0) + +#undef min +#define min(x,y) ((x)<(y) ? (x):(y)) + +#undef max +#define max(x,y) ((x)>(y) ? (x):(y)) + +#ifndef abs +#define abs(x) ((x)>=0 ? (x):-(x)) +#endif +#endif + +#endif -- 2.7.4 From c31ecf6ea111d1af5a6de4b647692d0aae9e9e3b Mon Sep 17 00:00:00 2001 From: Sejun Park Date: Thu, 14 Dec 2017 11:55:35 +0900 Subject: [PATCH 03/12] Change to armv7l build only Change-Id: Ibcc20f76cef3e9b6b29943856bfa5aa25358e7a3 --- packaging/libomxil-vc4.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index c75b975..26f2338 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -1,6 +1,6 @@ Name: libomxil-vc4 Version: 0.0.1 -Release: 0.1 +Release: 2 Summary: Libraries for interfacing to Raspberry Pi GPU Group: System/Libraries URL: https://github.com/raspberrypi/userland @@ -10,6 +10,7 @@ BuildRequires: glibc-devel BuildRequires: cmake BuildRequires: gcc-c++ BuildRequires: pkgconfig(libtbm) +ExclusiveArch: %{arm} %description Libraries for interfacing to Raspberry Pi GPU. -- 2.7.4 From edffe7b636f916ddf743913b84891592ebf6195d Mon Sep 17 00:00:00 2001 From: Hackseung Lee Date: Thu, 21 Dec 2017 19:58:24 +0900 Subject: [PATCH 04/12] Added ldconfig after rpm installation The directory of the added library: /opt/vc/lib /opt/vc/lib/plugins Change-Id: I5e122f24bad3fdd59530042c076cb297bfcebfec Signed-off-by: Hackseung Lee --- packaging/libomxil-vc4.conf | 2 ++ packaging/libomxil-vc4.spec | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 packaging/libomxil-vc4.conf diff --git a/packaging/libomxil-vc4.conf b/packaging/libomxil-vc4.conf new file mode 100644 index 0000000..4093030 --- /dev/null +++ b/packaging/libomxil-vc4.conf @@ -0,0 +1,2 @@ +/opt/vc/lib/plugins +/opt/vc/lib diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index 26f2338..7a3023f 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -42,7 +42,15 @@ cmake -DCMAKE_BUILD_TYPE=Release ../../../ make popd +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + %install +mkdir -p %{buildroot}/etc/ld.so.conf.d/ +cp %{_builddir}/%{name}-%{version}/packaging/libomxil-vc4.conf %{buildroot}/etc/ld.so.conf.d mkdir -p %{buildroot}/opt/vc/lib/plugins mkdir %{buildroot}/opt/vc/lib/pkgconfig mkdir %{buildroot}/opt/vc/bin @@ -94,6 +102,7 @@ popd %license LICENCE COPYING /opt/vc/lib/lib*.so /opt/vc/lib/plugins/*.so +/etc/ld.so.conf.d/libomxil-vc4.conf %files -n libomxil-vc4-devel /opt/vc/lib/lib*.a -- 2.7.4 From eed3d6ab21bad1d924324bbdcb21e4453085cbc5 Mon Sep 17 00:00:00 2001 From: Sejun Park Date: Wed, 2 May 2018 10:14:26 +0900 Subject: [PATCH 05/12] changed license based on the result of SOLVe Change-Id: I667f31bda7b8de1864875a15809f53de1facad08 --- COPYING | 21 --------------------- packaging/libomxil-vc4.spec | 4 ++-- 2 files changed, 2 insertions(+), 23 deletions(-) delete mode 100755 COPYING diff --git a/COPYING b/COPYING deleted file mode 100755 index a60e55d..0000000 --- a/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (C) 2017 Samsung Electronics co., Ltd. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sub license, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice (including the -next paragraph) shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. -IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR -ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index 7a3023f..35703da 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -94,12 +94,12 @@ popd %files -n libomxil-vc4-utils /opt/vc/bin/* -%doc LICENCE COPYING +%doc LICENCE %files -n libomxil-vc4 %manifest packaging/%{name}.manifest %defattr(-,root,root) -%license LICENCE COPYING +%license LICENCE /opt/vc/lib/lib*.so /opt/vc/lib/plugins/*.so /etc/ld.so.conf.d/libomxil-vc4.conf -- 2.7.4 From a4ace7501b0ad478553b4b329f79a6d8ad83af0c Mon Sep 17 00:00:00 2001 From: Jeongmo Yang Date: Wed, 13 Nov 2019 16:38:39 +0900 Subject: [PATCH 06/12] Correct license information [Version] 0.0.1-3 [Profile] Common [Issue Type] License Change-Id: Id2365ae2eb3dcc8ba5fe2cbce240c11e1b7c4832 Signed-off-by: Jeongmo Yang --- LICENCE => LICENSE | 0 packaging/libomxil-vc4.spec | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename LICENCE => LICENSE (100%) diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index 35703da..6fbd034 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -1,11 +1,11 @@ Name: libomxil-vc4 Version: 0.0.1 -Release: 2 +Release: 3 Summary: Libraries for interfacing to Raspberry Pi GPU Group: System/Libraries URL: https://github.com/raspberrypi/userland Source: %{name}-%{version}.tar.gz -License: BSD +License: BSD-3-Clause BuildRequires: glibc-devel BuildRequires: cmake BuildRequires: gcc-c++ @@ -94,12 +94,12 @@ popd %files -n libomxil-vc4-utils /opt/vc/bin/* -%doc LICENCE +%doc LICENSE %files -n libomxil-vc4 %manifest packaging/%{name}.manifest %defattr(-,root,root) -%license LICENCE +%license LICENSE /opt/vc/lib/lib*.so /opt/vc/lib/plugins/*.so /etc/ld.so.conf.d/libomxil-vc4.conf -- 2.7.4 From d3dd21229eb08bb3fa540db27967c4e8b5b00db9 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 5 Dec 2019 13:48:21 +0900 Subject: [PATCH 07/12] vidtex: fix to link brcm EGL and GLES There are two sets of EGL and GLES, one is brcm naming and the other is common naming. The vidtex is using brcm EGL and GLES, so fix to link brcm files. Change-Id: I03e9ab63897c0defa6a2b08da33efdb5589bd4b6 Signed-off-by: Seung-Woo Kim --- host_applications/android/apps/vidtex/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host_applications/android/apps/vidtex/CMakeLists.txt b/host_applications/android/apps/vidtex/CMakeLists.txt index 1f705ef..492d3b9 100755 --- a/host_applications/android/apps/vidtex/CMakeLists.txt +++ b/host_applications/android/apps/vidtex/CMakeLists.txt @@ -9,4 +9,4 @@ set (VIDTEX_SOURCES svp.c vidtex.c) add_executable(vidtex ${VIDTEX_SOURCES}) -target_link_libraries(vidtex GLESv2 EGL m bcm_host mmal_core mmal_components mmal_util mmal_vc_client vcos) +target_link_libraries(vidtex brcmGLESv2 brcmEGL m bcm_host mmal_core mmal_components mmal_util mmal_vc_client vcos) -- 2.7.4 From db18f489afb432cd1f5c6be4b456f2dd5925e6dd Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 5 Dec 2019 13:50:01 +0900 Subject: [PATCH 08/12] khronos: not to build and install common EGL and GLES There is conflict for EGL and GLES with mesa. So remove common EGL and GLES from brcm userland. There are still same libraries with brcm naming. Change-Id: Ie9271f5de4f4e761174321a39d555408a143a8d4 Signed-off-by: Seung-Woo Kim --- interface/khronos/CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/khronos/CMakeLists.txt b/interface/khronos/CMakeLists.txt index 9ad615b..4de44d1 100755 --- a/interface/khronos/CMakeLists.txt +++ b/interface/khronos/CMakeLists.txt @@ -55,10 +55,12 @@ set(CLIENT_SOURCE common/khrn_int_hash_asm.s common/khrn_client_cache.c) +if(USE_BRCM_GL) add_library(EGL ${SHARED} ${EGL_SOURCE}) add_library(GLESv2 ${SHARED} ${GLES_SOURCE}) add_library(OpenVG ${SHARED} ${VG_SOURCE}) add_library(WFC ${SHARED} ${WFC_SOURCE}) +endif() add_library(khrn_client ${CLIENT_SOURCE}) # TODO do we need EGL_static and GLESv2_static now that khrn_static exists? @@ -73,12 +75,14 @@ set(VCSM_LIBS vcsm) add_definitions(-DKHRONOS_HAVE_VCSM) endif() +if(USE_BRCM_GL) target_link_libraries(EGL khrn_client vchiq_arm vcos bcm_host ${VCSM_LIBS} -lm) target_link_libraries(GLESv2 EGL khrn_client vcos) target_link_libraries(WFC EGL) target_link_libraries(OpenVG EGL) -install(TARGETS EGL GLESv2 OpenVG WFC khrn_client DESTINATION lib) +install(TARGETS EGL GLESv2 OpenVG WFC DESTINATION lib) +endif() install(TARGETS EGL_static GLESv2_static khrn_static DESTINATION lib) # recommended names to use to avoid conflicts with mesa libs @@ -92,4 +96,4 @@ target_link_libraries(brcmGLESv2 brcmEGL khrn_client vcos) target_link_libraries(brcmWFC brcmEGL) target_link_libraries(brcmOpenVG brcmEGL) -install(TARGETS brcmEGL brcmGLESv2 brcmOpenVG brcmWFC DESTINATION lib) +install(TARGETS brcmEGL brcmGLESv2 brcmOpenVG brcmWFC khrn_client DESTINATION lib) -- 2.7.4 From b7fc864fae9c4d12b26a2a6bf16d255edfddffdd Mon Sep 17 00:00:00 2001 From: Jeongmo Yang Date: Wed, 27 May 2020 15:03:30 +0900 Subject: [PATCH 09/12] Fix ASAN build error - Build error is caused by -Werror=stringop-truncation option Change-Id: Ia869d851825d02920b8b835e2a54419b04b77f04 Signed-off-by: Jeongmo Yang --- containers/mkv/matroska_reader.c | 4 ++-- containers/rtsp/rtsp_reader.c | 12 +++--------- host_applications/linux/apps/raspicam/RaspiCamControl.c | 6 +++--- host_applications/linux/apps/raspicam/RaspiStill.c | 2 +- interface/vcos/pthreads/vcos_pthreads.c | 2 +- interface/vmcs_host/vc_vchi_filesys.c | 4 ++-- interface/vmcs_host/vcilcs_out.c | 2 +- 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/containers/mkv/matroska_reader.c b/containers/mkv/matroska_reader.c index 8625d0c..df91e8b 100755 --- a/containers/mkv/matroska_reader.c +++ b/containers/mkv/matroska_reader.c @@ -352,7 +352,7 @@ typedef struct VC_CONTAINER_TRACK_MODULE_T int64_t timecode_scale; uint32_t duration; int64_t frame_duration; - char codecid[MKV_CODECID_MAX]; + char codecid[MKV_MAX_STRING_SIZE + 2]; union { /* video specific */ @@ -1320,7 +1320,7 @@ static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p LOG_FORMAT(p_ctx, "%s", stringbuf); if(id == MKV_ELEMENT_ID_TRACK_CODEC_ID) - strncpy(track_module->codecid, stringbuf, MKV_CODECID_MAX-1); + strncpy(track_module->codecid, stringbuf, MKV_MAX_STRING_SIZE + 1); return VC_CONTAINER_SUCCESS; } diff --git a/containers/rtsp/rtsp_reader.c b/containers/rtsp/rtsp_reader.c index 2d96234..5df4d25 100755 --- a/containers/rtsp/rtsp_reader.c +++ b/containers/rtsp/rtsp_reader.c @@ -1052,12 +1052,9 @@ static VC_CONTAINER_STATUS_T rtsp_merge_uris( VC_CONTAINER_T *p_ctx, if (vc_uri_scheme(relative_uri) != NULL) { /* URI is absolute, not relative, so return it as the merged URI */ - size_t len = strlen(relative_uri_str); - - *p_merged_uri_str = (char *)malloc(len + 1); + *p_merged_uri_str = strdup(relative_uri_str); if (!*p_merged_uri_str) goto tidy_up; - strncpy(*p_merged_uri_str, relative_uri_str, len); status = VC_CONTAINER_SUCCESS; goto tidy_up; } @@ -1112,15 +1109,12 @@ static VC_CONTAINER_STATUS_T rtsp_parse_control_attribute( VC_CONTAINER_T *p_ctx if (!*attribute || strcmp(attribute, "*") == 0) { - size_t len = strlen(base_uri_str); - - *p_control_uri_str = (char *)malloc(len + 1); + *p_control_uri_str = strdup(base_uri_str); if (!*p_control_uri_str) { LOG_ERROR(p_ctx, "RTSP: Failed to allocate control URI"); return VC_CONTAINER_ERROR_OUT_OF_MEMORY; } - strncpy(*p_control_uri_str, base_uri_str, len); } else { status = rtsp_merge_uris(p_ctx, base_uri_str, attribute, p_control_uri_str); } @@ -1245,7 +1239,7 @@ static VC_CONTAINER_STATUS_T rtsp_open_file_reader( VC_CONTAINER_T *p_ctx, goto tidy_up; } - strncpy(new_path, rtsp_path, len); + strncpy(new_path, rtsp_path, len + 1); extension = strrchr(new_path, '.'); /* Find extension, to replace it */ if (!extension) extension = new_path + strlen(new_path); /* No extension, so append instead */ diff --git a/host_applications/linux/apps/raspicam/RaspiCamControl.c b/host_applications/linux/apps/raspicam/RaspiCamControl.c index a700010..e46128d 100755 --- a/host_applications/linux/apps/raspicam/RaspiCamControl.c +++ b/host_applications/linux/apps/raspicam/RaspiCamControl.c @@ -1615,7 +1615,7 @@ int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, c strftime(annotate.text, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3, string, &tm ); process_datetime = 0; }else{ - strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3); + strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - 1); } annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; } @@ -1627,7 +1627,7 @@ int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, c }else{ strftime(tmp, 32, "%X", &tm ); } - strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text)); } if (process_datetime && (settings & ANNOTATE_DATE_TEXT)) @@ -1637,7 +1637,7 @@ int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, c }else{ strftime(tmp, 32, "%x", &tm ); } - strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text)); } if (settings & ANNOTATE_SHUTTER_SETTINGS) diff --git a/host_applications/linux/apps/raspicam/RaspiStill.c b/host_applications/linux/apps/raspicam/RaspiStill.c index 0c3cf3f..d791200 100755 --- a/host_applications/linux/apps/raspicam/RaspiStill.c +++ b/host_applications/linux/apps/raspicam/RaspiStill.c @@ -294,7 +294,7 @@ static void set_sensor_defaults(RASPISTILL_STATE *state) state->width = param.cameras[state->cameraNum].max_width; if (state->height == 0) state->height = param.cameras[state->cameraNum].max_height; - strncpy(state->camera_name, param.cameras[state->cameraNum].camera_name, MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN); + strncpy(state->camera_name, param.cameras[state->cameraNum].camera_name, MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN - 1); state->camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN-1] = 0; } else diff --git a/interface/vcos/pthreads/vcos_pthreads.c b/interface/vcos/pthreads/vcos_pthreads.c index 77ce58c..3295f7a 100755 --- a/interface/vcos/pthreads/vcos_pthreads.c +++ b/interface/vcos/pthreads/vcos_pthreads.c @@ -205,7 +205,7 @@ VCOS_STATUS_T vcos_thread_create(VCOS_THREAD_T *thread, thread->arg = arg; thread->legacy = local_attrs->legacy; - strncpy(thread->name, name, sizeof(thread->name)); + strncpy(thread->name, name, sizeof(thread->name) - 1); thread->name[sizeof(thread->name)-1] = '\0'; memset(thread->at_exit, 0, sizeof(thread->at_exit)); diff --git a/interface/vmcs_host/vc_vchi_filesys.c b/interface/vmcs_host/vc_vchi_filesys.c index 26a34b9..29c94db 100755 --- a/interface/vmcs_host/vc_vchi_filesys.c +++ b/interface/vmcs_host/vc_vchi_filesys.c @@ -373,8 +373,8 @@ static int vc_filesys_single_string(uint32_t param, const char *str, uint32_t fn { vc_filesys_client.fileserv_msg.params[0] = param; /* coverity[buffer_size_warning] - the length of str has already been checked */ - strncpy((char*)vc_filesys_client.fileserv_msg.data, str, FILESERV_MAX_DATA); - + strncpy((char*)vc_filesys_client.fileserv_msg.data, str, FILESERV_MAX_DATA - 1); + if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, fn, len+1+16) == FILESERV_RESP_OK) { if(return_param) diff --git a/interface/vmcs_host/vcilcs_out.c b/interface/vmcs_host/vcilcs_out.c index 545ddc7..f5d3c11 100755 --- a/interface/vmcs_host/vcilcs_out.c +++ b/interface/vmcs_host/vcilcs_out.c @@ -689,7 +689,7 @@ static OMX_ERRORTYPE vcil_out_GetExtensionIndex(OMX_IN OMX_HANDLETYPE hComponen comp = (VC_PRIVATE_COMPONENT_T *) pComp->pComponentPrivate; exe.reference = comp->reference; - strncpy(exe.name, cParameterName, 128); + strncpy(exe.name, cParameterName, 128 - 1); exe.name[127] = 0; if(ilcs_execute_function(st->ilcs, IL_GET_EXTENSION_INDEX, &exe, sizeof(exe), &resp, &rlen) < 0 || rlen != sizeof(resp)) -- 2.7.4 From 4ec2534e435b530b7f8d14b10a7518b960ecdcfe Mon Sep 17 00:00:00 2001 From: Jeongmo Yang Date: Fri, 29 May 2020 16:58:10 +0900 Subject: [PATCH 10/12] Clean up spec file [Version] 0.0.1-4 [Profile] Common [Issue Type] Clean up Change-Id: I6ed61e2b39579af46513be6c54eef89cbb58ba6a Signed-off-by: Jeongmo Yang --- packaging/libomxil-vc4.spec | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index 6fbd034..06d2384 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -1,12 +1,11 @@ Name: libomxil-vc4 Version: 0.0.1 -Release: 3 +Release: 4 Summary: Libraries for interfacing to Raspberry Pi GPU Group: System/Libraries URL: https://github.com/raspberrypi/userland Source: %{name}-%{version}.tar.gz License: BSD-3-Clause -BuildRequires: glibc-devel BuildRequires: cmake BuildRequires: gcc-c++ BuildRequires: pkgconfig(libtbm) @@ -15,19 +14,20 @@ ExclusiveArch: %{arm} %description Libraries for interfacing to Raspberry Pi GPU. -%package -n libomxil-vc4-utils +%package utils Group: System/Tools Summary: System tools for the Raspberry Pi -%description -n libomxil-vc4-utils +%description utils This package contains some system tools for the Raspberry Pi. Source: https://github.com/libomxil-vc4/userland.git -%package -n libomxil-vc4-devel +%package devel Group: Development/Libraries Summary: Development files for the Raspberry Pi GPU +Requires: %{name} = %{version} -%description -n libomxil-vc4-devel +%description devel This package contains libraries and header files for developing applications that use Raspberry Pi GPU. %prep @@ -92,11 +92,7 @@ popd %clean [ "%{buildroot}" != / ] && rm -rf "%{buildroot}" -%files -n libomxil-vc4-utils -/opt/vc/bin/* -%doc LICENSE - -%files -n libomxil-vc4 +%files %manifest packaging/%{name}.manifest %defattr(-,root,root) %license LICENSE @@ -104,7 +100,11 @@ popd /opt/vc/lib/plugins/*.so /etc/ld.so.conf.d/libomxil-vc4.conf -%files -n libomxil-vc4-devel +%files utils +/opt/vc/bin/* +%doc LICENSE + +%files devel /opt/vc/lib/lib*.a /opt/vc/include/EGL /opt/vc/include/GLES @@ -117,3 +117,4 @@ popd /opt/vc/include/vcinclude /opt/vc/include/*.h /opt/vc/lib/pkgconfig/*.pc + -- 2.7.4 From dcabfec261b9d3422391731211187fd302438e1a Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 5 Mar 2021 13:28:27 +0900 Subject: [PATCH 11/12] packaging: update source url There was wrong base source url. Update with right one. Change-Id: Ifb8a1baf483c4214b85a8d18a4836554723d1e35 Signed-off-by: Seung-Woo Kim --- packaging/libomxil-vc4.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index 06d2384..9b490d1 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -20,7 +20,7 @@ Summary: System tools for the Raspberry Pi %description utils This package contains some system tools for the Raspberry Pi. -Source: https://github.com/libomxil-vc4/userland.git +Source: https://github.com/raspberrypi/userland.git %package devel Group: Development/Libraries -- 2.7.4 From b1e2a631f55e23f7c8db4483bb1cb7352872f1b1 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 5 Mar 2021 16:14:28 +0900 Subject: [PATCH 12/12] packaging: build with _smp_mflags macro for build speed To support build with multiple jobs, add _smp_mflags macro to make command. Change-Id: If2054ed8128b61ee4852d5780d5c33e8f812b3db Signed-off-by: Seung-Woo Kim --- packaging/libomxil-vc4.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libomxil-vc4.spec b/packaging/libomxil-vc4.spec index 9b490d1..05627b0 100755 --- a/packaging/libomxil-vc4.spec +++ b/packaging/libomxil-vc4.spec @@ -39,7 +39,7 @@ BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`; mkdir -p build/armv7l-linux/$BUILDSUBDIR pushd build/armv7l-linux/$BUILDSUBDIR cmake -DCMAKE_BUILD_TYPE=Release ../../../ -make +make %{?_smp_mflags} popd %post -- 2.7.4