├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README-CN.md ├── README.md ├── sample ├── CMakeLists.txt ├── client_sample.c ├── config.c ├── config.h ├── data │ ├── example.aac │ ├── example.h264 │ ├── live │ │ ├── audio │ │ │ ├── 1.aac │ │ │ ├── 2.aac │ │ │ ├── 3.aac │ │ │ ├── 4.aac │ │ │ ├── 5.aac │ │ │ ├── 6.aac │ │ │ ├── 7.aac │ │ │ ├── 8.aac │ │ │ └── 9.aac │ │ └── video │ │ │ ├── 1.h264 │ │ │ ├── 2.h264 │ │ │ ├── 3.h264 │ │ │ ├── 4.h264 │ │ │ ├── 5.h264 │ │ │ ├── 6.h264 │ │ │ ├── 7.h264 │ │ │ ├── 8.h264 │ │ │ └── 9.h264 │ └── slice │ │ ├── audio │ │ ├── 1.aac │ │ ├── 2.aac │ │ └── 3.aac │ │ └── video │ │ ├── 1.h264 │ │ ├── 2.h264 │ │ └── 3.h264 ├── hls_sample.c ├── hls_stream_sample.c ├── sample.h.in └── server_sample.c ├── src ├── CMakeLists.txt ├── oss_media_client.c ├── oss_media_client.h ├── oss_media_define.c ├── oss_media_define.h ├── oss_media_hls.c ├── oss_media_hls.h ├── oss_media_hls_stream.c ├── oss_media_hls_stream.h ├── oss_media_log.c ├── oss_media_server.c ├── oss_media_server.h └── sts │ ├── general.c │ ├── jsmn.c │ ├── jsmn.h │ ├── libsts.h │ ├── request.c │ ├── request.h │ ├── string_buffer.h │ ├── time.c │ ├── token.c │ ├── util.c │ └── util.h └── test ├── CMakeLists.txt ├── CuTest.c ├── CuTest.h ├── config.c ├── config.h ├── test.h.in ├── test_all.c ├── test_client.c ├── test_hls.c ├── test_hls_stream.c ├── test_server.c └── test_sts.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | clean.sh 4 | .vscode 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog - Aliyun OSS Media C SDK 2 | 3 | ## 版本号:2.1.0 日期:2017-09-25 4 | ### 变更内容 5 | - 修改生成的ts文件名格式化参数 6 | - 优化解析H264 frame的逻辑,解决生成的ts文件时间戳不对的问题 7 | - 生成ts文件或者m3u8文件之前会先删除老文件 8 | 9 | ## 版本号:2.0.2 日期:2017-03-02 10 | ### 变更内容 11 | - 优化oss_media_hls_stream_open的参数options深拷贝 12 | 13 | ## 版本号:2.0.1 日期:2016-09-10 14 | ### 变更内容 15 | - 修复STS AssumeRole获取无效ID/KEY/Tonken的Bug 16 | 17 | ## 版本号:2.0.0 日期:2016-06-24 18 | ### 变更内容 19 | - 支持OSS C SDK 3.0.0 20 | 21 | ## 版本号:1.0.0 日期:2016-03-06 22 | ### 变更内容 23 | - 新增H.264,aac转HLS基础接口 24 | - 新增H.264,aac转HLS的录播、直播封装接口 25 | - 优化client,server端接口,提高易用性 26 | - 解决用户无法设置日志级别的问题 27 | 28 | ## 版本号:0.2.0 日期:2016-01-06 29 | ### 变更内容 30 | - 解决头文件多次引用保护无效的问题 31 | - 解决日志开关设置有误的问题 32 | - 解决sts相关sample有误的问题 33 | - 解决部分内存泄露、数组越界等问题 34 | - 优化编译,项目结构等 35 | - 优化、简化示例程序 36 | - 合并client和server两个项目,支持独立编译 37 | - 增加测试用例 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(oss_c_media_sdk) 2 | 3 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 4 | 5 | set(CMAKE_VERSION 2.0.0) 6 | 7 | option(ONLY_BUILD_CLIENT "only build client" OFF) 8 | option(ONLY_BUILD_SERVER "only build server" OFF) 9 | 10 | # default C / CXX flags 11 | set(CMAKE_C_FLAGS " -g -ggdb -O0 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 12 | set(CMAKE_CXX_FLAGS " -g -ggdb -O0 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 13 | 14 | set(CMAKE_C_FLAGS_DEBUG " -g -ggdb -O0 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 15 | set(CMAKE_CXX_FLAGS_DEBUG " -g -ggdb -O0 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 16 | 17 | set(CMAKE_C_FLAGS_RELEASE " -O3 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 18 | set(CMAKE_CXX_FLAGS_RELEASE " -O3 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 19 | 20 | set(CMAKE_C_FLAGS_MINSIZEREF " -Os -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 21 | set(CMAKE_CXX_FLAGS_MINSIZEREF " -Os -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 22 | 23 | set(CMAKE_C_FLAGS_RELWITHDEBINFO " -O2 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 24 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO " -O2 -Werror -fpic -fPIC -D_LARGEFILE64_SOURCE") 25 | 26 | set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/build/${CMAKE_BUILD_TYPE}/lib) 27 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/build/${CMAKE_BUILD_TYPE}/bin) 28 | 29 | FIND_PROGRAM(APR_CONFIG_BIN NAMES apr-config apr-1-config PATHS /usr/bin /usr/local/bin /usr/local/apr/bin/) 30 | 31 | IF (APR_CONFIG_BIN) 32 | EXECUTE_PROCESS( 33 | COMMAND ${APR_CONFIG_BIN} --includedir 34 | OUTPUT_VARIABLE APR_INCLUDEDIRS 35 | OUTPUT_STRIP_TRAILING_WHITESPACE 36 | ) 37 | EXECUTE_PROCESS( 38 | COMMAND ${APR_CONFIG_BIN} --cflags 39 | OUTPUT_VARIABLE APR_C_FLAGS 40 | OUTPUT_STRIP_TRAILING_WHITESPACE 41 | ) 42 | EXECUTE_PROCESS( 43 | COMMAND ${APR_CONFIG_BIN} --link-ld 44 | OUTPUT_VARIABLE APR_LIBRARIES 45 | OUTPUT_STRIP_TRAILING_WHITESPACE 46 | ) 47 | ELSE() 48 | MESSAGE(FATAL_ERROR "Could not find apr-config/apr-1-config") 49 | ENDIF() 50 | 51 | FIND_PROGRAM(APU_CONFIG_BIN NAMES apu-config apu-1-config PATHS /usr/bin /usr/local/bin /usr/local/apr/bin/) 52 | 53 | IF (APU_CONFIG_BIN) 54 | EXECUTE_PROCESS( 55 | COMMAND ${APU_CONFIG_BIN} --includedir 56 | OUTPUT_VARIABLE APU_INCLUDEDIRS 57 | OUTPUT_STRIP_TRAILING_WHITESPACE 58 | ) 59 | EXECUTE_PROCESS( 60 | COMMAND ${APU_CONFIG_BIN} --cflags 61 | OUTPUT_VARIABLE APU_C_FLAGS 62 | OUTPUT_STRIP_TRAILING_WHITESPACE 63 | ) 64 | EXECUTE_PROCESS( 65 | COMMAND ${APU_CONFIG_BIN} --link-ld 66 | OUTPUT_VARIABLE APU_LIBRARIES 67 | OUTPUT_STRIP_TRAILING_WHITESPACE 68 | ) 69 | ELSE() 70 | MESSAGE(FATAL_ERROR "Could not find apu-config/apu-1-config") 71 | ENDIF() 72 | 73 | #curl-config 74 | FIND_PROGRAM(CURL_CONFIG_BIN NAMES curl-config) 75 | 76 | IF (CURL_CONFIG_BIN) 77 | EXECUTE_PROCESS( 78 | COMMAND ${CURL_CONFIG_BIN} --libs 79 | OUTPUT_VARIABLE CURL_LIBRARIES 80 | OUTPUT_STRIP_TRAILING_WHITESPACE 81 | ) 82 | ELSE() 83 | MESSAGE(FATAL_ERROR "Could not find curl-config") 84 | ENDIF() 85 | 86 | # Compile and link lib_oss_c_sdk 87 | include_directories(${APR_INCLUDEDIRS}) 88 | include_directories(${APU_INCLUDEDIRS}) 89 | 90 | find_path(OSS_C_SDK_INCLUDE_DIR NAMES oss_c_sdk/oss_api.h) 91 | include_directories(${OSS_C_SDK_INCLUDE_DIR}) 92 | 93 | add_subdirectory(src) 94 | add_subdirectory(sample) 95 | 96 | IF (NOT ONLY_BUILD_CLIENT AND NOT ONLY_BUILD_SERVER) 97 | add_subdirectory(test) 98 | ENDIF() 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 aliyun.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 6 | permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 9 | Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # Aliyun OSS Media C SDK 2 | 3 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) 4 | [![GitHub version](https://badge.fury.io/gh/aliyun%2Faliyun-media-c-sdk.svg)](https://badge.fury.io/gh/aliyun%2Faliyun-media-c-sdk) 5 | 6 | ## [README of English](https://github.com/aliyun/aliyun-media-c-sdk/blob/master/README.md) 7 | 8 | ## 关于 9 | 不少情况下,我们都需要将摄像头拍摄的视频快速存储到云端(OSS),但是我们也有一些因素要考虑: 10 | 11 | - 设备上不能存储永久access key id和access key secret,因为可能泄露 12 | - 设备上只允许上传或者下载,不允许删除、修改配置等管理权限 13 | - 可以提供网页让用户去管理自己的视频 14 | - 对设备的权限精准控制 15 | - 对设备的权限存在有效期,不能让设备永久持有某种权限` 16 | - 希望摄像机输出的音视频可以通过HLS协议直接被用户观看 17 | 18 | 针对以上考虑,我们推出了OSS MEDIA C SDK,构建于OSS C SDK版本之上,可以方便的解决上述问题,为音视频行业提供更完善易用的解决方案。 19 | 20 | ## 版本 21 | - 当前版本:V2.0.2 22 | 23 | ### 兼容性 24 | - 由于底层C SDK相对于前一版本不兼容导致Media SDK不兼容1.x.x系列SDK,不兼容接口主要涉及list相关 25 | 26 | ## 依赖 27 | - [OSS C SDK 3.4.0](https://github.com/aliyun/aliyun-oss-c-sdk) 28 | 29 | ## 使用 30 | - 示例程序:[参考sample部分](sample/) 31 | - SDK文档:[阿里云OSS MEDIA SDK文档](https://help.aliyun.com/document_detail/oss/sdk/media-c-sdk/preface.html) 32 | 33 | ## 联系我们 34 | - [阿里云OSS官方网站](http://oss.aliyun.com) 35 | - [阿里云OSS官方论坛](http://bbs.aliyun.com) 36 | - [阿里云OSS官方文档中心](http://www.aliyun.com/product/oss#Docs) 37 | - 阿里云官方技术支持:[提交工单](https://workorder.console.aliyun.com/#/ticket/createIndex) 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alibaba Cloud OSS Media C SDK 2 | 3 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) 4 | [![GitHub Version](https://badge.fury.io/gh/aliyun%2Faliyun-media-c-sdk.svg)](https://badge.fury.io/gh/aliyun%2Faliyun-media-c-sdk) 5 | 6 | 7 | ## [README of Chinese](https://github.com/aliyun/aliyun-media-c-sdk/blob/master/README-CN.md) 8 | 9 | ## About 10 | In many cases, we need to store the video captured by the camera to the [OSS](https://www.aliyun.com/product/oss). But we also have some considerations: 11 | 12 | - The `AccessKeyID` and `AccessKeySecret` cannot be stored permanently on the device because it may be leaked. 13 | - The device only allows file uploads and downloads and does not allow administrative permissions such as deletion and configuration modification. 14 | - Webpages can be provided for you to manage your own videos. 15 | - Precise control over the device permission is provided. 16 | - The permission for the device has a validity period. A permission for a device cannot be held permanently. 17 | - The audio and video files output by the camera are expected to be directly watched through the *HLS* protocol. 18 | 19 | Out of the above considerations, we launched the *OSS MEDIA C SDK* built based on the *OSS C SDK*, which can conveniently solve the above mentioned issues and provide more improved and easy-to-use solutions for the audio and video industry. 20 | 21 | ## Version 22 | - Current version: V2.1.0. 23 | 24 | ### Compatibility 25 | - Because the underlying C SDK is not compatible with previous versions, the Media C SDK is not compatible with 1.x.x. Incompatible interfaces mainly involve those related with *list* operations. 26 | 27 | ## Dependency 28 | - [OSS C SDK 3.4.0](https://github.com/aliyun/aliyun-oss-c-sdk) 29 | 30 | ## Usage 31 | - Example project: [Refer to sample section](sample/). 32 | - Documentation: [OSS C MEDIA SDK Documentation](https://help.aliyun.com/document_detail/oss/sdk/media-c-sdk/preface.html). 33 | 34 | ## License 35 | 36 | - MIT 37 | 38 | ## Contact us 39 | - [Alibaba Cloud OSS official website](http://oss.aliyun.com). 40 | - [Alibaba Cloud OSS official forum](http://bbs.aliyun.com). 41 | - [Alibaba Cloud OSS official documentation center](http://www.aliyun.com/product/oss#Docs). 42 | - Alibaba Cloud official technical support: [Submit a ticket](https://workorder.console.aliyun.com/#/ticket/createIndex). 43 | -------------------------------------------------------------------------------- /sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_SOURCE_DIR}) 2 | 3 | configure_file ( 4 | "${PROJECT_SOURCE_DIR}/sample/sample.h.in" 5 | "${PROJECT_SOURCE_DIR}/sample/sample.h" 6 | ) 7 | 8 | find_library(OSS_C_SDK_LIBRARY NAMES oss_c_sdk) 9 | find_library(APR_LIBRARY apr-1 PATHS /usr/local/apr/lib/) 10 | find_library(APR_UTIL_LIBRARY aprutil-1 PATHS /usr/local/apr/lib/) 11 | find_library(MINIXML_LIBRARY mxml) 12 | find_library(CURL_LIBRARY NAMES curl) 13 | find_library(PTHREAD_LIBRARY NAMES pthread) 14 | find_library(RT_LIBRARY NAMES rt) 15 | 16 | 17 | IF (NOT ONLY_BUILD_SERVER) 18 | set(CLIENT_SAMPLE_SRC config.c client_sample.c) 19 | set(TS_SAMPLE_SRC config.c hls_sample.c) 20 | set(TS_STREAM_SAMPLE_SRC config.c hls_stream_sample.c) 21 | 22 | set(CLIENT_SAMPLE_BIN client_sample) 23 | set(TS_SAMPLE_BIN hls_sample) 24 | set(TS_STREAM_SAMPLE_BIN hls_stream_sample) 25 | 26 | add_executable(${CLIENT_SAMPLE_BIN} ${CLIENT_SAMPLE_SRC}) 27 | add_executable(${TS_SAMPLE_BIN} ${TS_SAMPLE_SRC}) 28 | add_executable(${TS_STREAM_SAMPLE_BIN} ${TS_STREAM_SAMPLE_SRC}) 29 | 30 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${CMAKE_PROJECT_NAME}_client) 31 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${OSS_C_SDK_LIBRARY}) 32 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${APR_UTIL_LIBRARY}) 33 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${APR_LIBRARY}) 34 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${MINIXML_LIBRARY}) 35 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${CURL_LIBRARY}) 36 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${PTHREAD_LIBRARY}) 37 | target_link_libraries(${CLIENT_SAMPLE_BIN} ${RT_LIBRARY}) 38 | 39 | target_link_libraries(${TS_SAMPLE_BIN} ${CMAKE_PROJECT_NAME}_client) 40 | target_link_libraries(${TS_SAMPLE_BIN} ${OSS_C_SDK_LIBRARY}) 41 | target_link_libraries(${TS_SAMPLE_BIN} ${APR_UTIL_LIBRARY}) 42 | target_link_libraries(${TS_SAMPLE_BIN} ${APR_LIBRARY}) 43 | target_link_libraries(${TS_SAMPLE_BIN} ${MINIXML_LIBRARY}) 44 | target_link_libraries(${TS_SAMPLE_BIN} ${CURL_LIBRARY}) 45 | target_link_libraries(${TS_SAMPLE_BIN} ${PTHREAD_LIBRARY}) 46 | target_link_libraries(${TS_SAMPLE_BIN} ${RT_LIBRARY}) 47 | 48 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${CMAKE_PROJECT_NAME}_client) 49 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${OSS_C_SDK_LIBRARY}) 50 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${APR_UTIL_LIBRARY}) 51 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${APR_LIBRARY}) 52 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${MINIXML_LIBRARY}) 53 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${CURL_LIBRARY}) 54 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${PTHREAD_LIBRARY}) 55 | target_link_libraries(${TS_STREAM_SAMPLE_BIN} ${RT_LIBRARY}) 56 | ENDIF() 57 | 58 | IF (NOT ONLY_BUILD_CLIENT) 59 | set(SERVER_SAMPLE_SRC config.c server_sample.c) 60 | set(SERVER_SAMPLE_BIN server_sample) 61 | add_executable(${SERVER_SAMPLE_BIN} ${SERVER_SAMPLE_SRC}) 62 | 63 | target_link_libraries(${SERVER_SAMPLE_BIN} ${CMAKE_PROJECT_NAME}_server) 64 | target_link_libraries(${SERVER_SAMPLE_BIN} ${OSS_C_SDK_LIBRARY}) 65 | target_link_libraries(${SERVER_SAMPLE_BIN} ${APR_UTIL_LIBRARY}) 66 | target_link_libraries(${SERVER_SAMPLE_BIN} ${APR_LIBRARY}) 67 | target_link_libraries(${SERVER_SAMPLE_BIN} ${MINIXML_LIBRARY}) 68 | target_link_libraries(${SERVER_SAMPLE_BIN} ${CURL_LIBRARY}) 69 | target_link_libraries(${SERVER_SAMPLE_BIN} ${PTHREAD_LIBRARY}) 70 | target_link_libraries(${SERVER_SAMPLE_BIN} ${RT_LIBRARY}) 71 | ENDIF() 72 | 73 | 74 | -------------------------------------------------------------------------------- /sample/client_sample.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "src/oss_media_client.h" 3 | 4 | char *g_filename="oss_media_file"; 5 | char *g_val = "hello oss media file, this is my first time to write content.\n"; 6 | 7 | extern int oss_media_get_h264_idr_offsets(const void *buf, 8 | int nbyte, int idrs[], int nidrs, int *size); 9 | 10 | static void oss_clean(const char *filename) { 11 | aos_pool_t *pool; 12 | oss_request_options_t *opts; 13 | aos_string_t bucket; 14 | aos_string_t key; 15 | aos_status_t *status; 16 | aos_table_t *resp_headers; 17 | 18 | aos_pool_create(&pool, NULL); 19 | 20 | opts = oss_request_options_create(pool); 21 | opts->config = oss_config_create(pool); 22 | aos_str_set(&opts->config->endpoint, SAMPLE_OSS_ENDPOINT); 23 | aos_str_set(&opts->config->access_key_id, SAMPLE_ACCESS_KEY_ID); 24 | aos_str_set(&opts->config->access_key_secret, SAMPLE_ACCESS_KEY_SECRET); 25 | opts->config->is_cname = 0; 26 | opts->ctl = aos_http_controller_create(pool, 0); 27 | aos_str_set(&bucket, SAMPLE_BUCKET_NAME); 28 | aos_str_set(&key, filename); 29 | 30 | status = oss_delete_object(opts, &bucket, &key, &resp_headers); 31 | if (!aos_status_is_ok(status)) { 32 | aos_error_log("clean oss error. [code=%d, message=%s]", 33 | status->code, status->error_code); 34 | aos_error_log("example exit"); 35 | aos_pool_destroy(pool); 36 | exit(-1); 37 | } 38 | 39 | aos_pool_destroy(pool); 40 | } 41 | 42 | static void auth_func(oss_media_file_t *file) { 43 | file->endpoint = SAMPLE_OSS_ENDPOINT; 44 | file->is_cname = 0; 45 | file->access_key_id = SAMPLE_ACCESS_KEY_ID; 46 | file->access_key_secret = SAMPLE_ACCESS_KEY_SECRET; 47 | file->token = NULL; //set NULL if not use token, otherwise use SAMPLE_STS_TOKEN 48 | 49 | // expiration 300 sec. 50 | file->expiration = time(NULL) + 300; 51 | } 52 | 53 | static void write_file() { 54 | oss_clean(g_filename); 55 | 56 | int64_t write_size; 57 | oss_media_file_t *file; 58 | 59 | // open file 60 | file = oss_media_file_open(SAMPLE_BUCKET_NAME, g_filename, "w", auth_func); 61 | if (!file) { 62 | printf("open media file[%s] failed\n", g_filename); 63 | return; 64 | } 65 | 66 | // write file 67 | write_size = oss_media_file_write(file, g_val, strlen(g_val)); 68 | if (-1 != write_size) { 69 | printf("write %" APR_INT64_T_FMT " bytes succeeded\n", write_size); 70 | } else { 71 | oss_media_file_close(file); 72 | printf("write failed\n"); 73 | return; 74 | } 75 | 76 | write_size = oss_media_file_write(file, g_val, strlen(g_val)); 77 | if (-1 != write_size) { 78 | printf("write %" APR_INT64_T_FMT " bytes succeeded\n", write_size); 79 | } else { 80 | oss_media_file_close(file); 81 | printf("write failed\n"); 82 | return; 83 | } 84 | 85 | // free file 86 | oss_media_file_close(file); 87 | 88 | printf("oss media c sdk write object succeeded\n"); 89 | } 90 | 91 | static void append_file() { 92 | oss_clean(g_filename); 93 | 94 | int i; 95 | int64_t write_size; 96 | oss_media_file_t *file; 97 | 98 | // open file 99 | file = oss_media_file_open(SAMPLE_BUCKET_NAME, g_filename, "a", auth_func); 100 | 101 | // open file 102 | if(!file) { 103 | printf("open media file failed\n"); 104 | return; 105 | } 106 | 107 | // write file 108 | for (i = 0; i < 8; i++) { 109 | write_size = oss_media_file_write(file, g_val, strlen(g_val)); 110 | if (-1 != write_size) { 111 | printf("write %" APR_INT64_T_FMT " bytes succeeded\n", write_size); 112 | } else { 113 | oss_media_file_close(file); 114 | printf("write failed\n"); 115 | return; 116 | } 117 | } 118 | 119 | // close file 120 | oss_media_file_close(file); 121 | printf("oss media c sdk append object succeeded\n"); 122 | } 123 | 124 | static void read_file() { 125 | char *content; 126 | int ntotal, nread, nbuf=16; 127 | char buf[nbuf]; 128 | 129 | oss_media_file_t *file; 130 | 131 | // open file 132 | file = oss_media_file_open(SAMPLE_BUCKET_NAME, g_filename, "r", auth_func); 133 | if (!file) { 134 | printf("open media file failed\n"); 135 | return; 136 | } 137 | 138 | // stat file 139 | oss_media_file_stat_t stat; 140 | if (0 != oss_media_file_stat(file, &stat)) { 141 | oss_media_file_close(file); 142 | printf("stat media file[%s] failed\n", file->object_key); 143 | return; 144 | } 145 | 146 | aos_info_log("file [name=%s, length=%" APR_INT64_T_FMT ", type=%s]", 147 | file->object_key, stat.length, stat.type); 148 | 149 | // read file 150 | content = malloc(stat.length + 1); 151 | ntotal = 0; 152 | while ((nread = oss_media_file_read(file, buf, nbuf)) > 0) { 153 | memcpy(content + ntotal, buf, nread); 154 | ntotal += nread; 155 | } 156 | content[ntotal] = '\0'; 157 | aos_info_log("file content length is: %d", ntotal); 158 | 159 | // close file 160 | oss_media_file_close(file); 161 | free(content); 162 | 163 | printf("oss media c sdk read object succeeded\n"); 164 | } 165 | 166 | static void seek_file() { 167 | char *content; 168 | int ntotal, nread, nbuf=16; 169 | char buf[nbuf]; 170 | 171 | oss_media_file_t *file; 172 | 173 | // open file 174 | file = oss_media_file_open(SAMPLE_BUCKET_NAME, g_filename, "r", auth_func); 175 | if (!file) { 176 | printf("open media file failed\n"); 177 | return; 178 | } 179 | 180 | // stat 181 | oss_media_file_stat_t stat; 182 | if (0 != oss_media_file_stat(file, &stat)) { 183 | oss_media_file_close(file); 184 | printf("stat media file[%s] failed\n", file->object_key); 185 | return; 186 | } 187 | 188 | aos_info_log("file [name=%s, length=%" APR_INT64_T_FMT ", type=%s]", 189 | file->object_key, stat.length, stat.type); 190 | 191 | // tell 192 | aos_info_log("file [position=%" APR_INT64_T_FMT "]", oss_media_file_tell(file)); 193 | 194 | // seek 195 | oss_media_file_seek(file, stat.length / 2); 196 | 197 | // tell 198 | aos_info_log("file [position=%" APR_INT64_T_FMT "] after seek %" APR_INT64_T_FMT, 199 | oss_media_file_tell(file), stat.length / 2); 200 | 201 | // read 202 | content = malloc(stat.length / 2 + 2); 203 | ntotal = 0; 204 | while ((nread = oss_media_file_read(file, buf, nbuf)) > 0) { 205 | memcpy(content + ntotal, buf, nread); 206 | ntotal += nread; 207 | } 208 | content[ntotal] = '\0'; 209 | aos_info_log("file content is: \n%s", content); 210 | 211 | // close file 212 | oss_media_file_close(file); 213 | free(content); 214 | 215 | printf("oss media c sdk seek object succeeded\n"); 216 | } 217 | 218 | static void error_code() { 219 | // prepare 220 | write_file(); 221 | 222 | int64_t write_size; 223 | oss_media_file_t *file; 224 | 225 | // open file 226 | file = oss_media_file_open(SAMPLE_BUCKET_NAME, g_filename, "a", auth_func); 227 | if (!file) { 228 | printf("open media file failed\n"); 229 | return; 230 | } 231 | 232 | // write file 233 | write_size = oss_media_file_write(file, g_val, strlen(g_val)); 234 | if (-1 != write_size) { 235 | printf("write %" APR_INT64_T_FMT " bytes succeeded\n", write_size); 236 | } else { 237 | printf("write [%s] failed\n", file->object_key); 238 | } 239 | 240 | // close file 241 | oss_media_file_close(file); 242 | 243 | printf("oss media c sdk try error code succeeded\n"); 244 | } 245 | 246 | static void idr(char *h264) { 247 | int len, nbyte = 4096; 248 | char buf[nbyte]; 249 | 250 | FILE *file; 251 | file = fopen(h264, "rb"); 252 | 253 | int total = 0; 254 | while((len = fread(buf, 1, nbyte, file)) > 0) { 255 | int offsets[16]; 256 | int nidrs; 257 | int i; 258 | 259 | oss_media_get_h264_idr_offsets(buf, nbyte, offsets, 16, &nidrs); 260 | 261 | for (i = 0; i < nidrs; i++) { 262 | printf("Coded Slice Of An IDR Pic. Offset: [Dec:%.8d Hex:0x%.8X]\n", 263 | total + offsets[i], total + offsets[i]); 264 | } 265 | total += len; 266 | } 267 | 268 | fclose(file); 269 | } 270 | 271 | static void perf(int loop) { 272 | oss_clean(g_filename); 273 | 274 | int i, nbyte = 64*1024; 275 | oss_media_file_t *file; 276 | char buf[nbyte]; 277 | for (i = 0; i < nbyte - 1; i++) { 278 | buf[i] = 'o'; 279 | } 280 | buf[nbyte - 1] = '\n'; 281 | 282 | file = oss_media_file_open(SAMPLE_BUCKET_NAME, g_filename, "a", auth_func); 283 | if (!file) { 284 | printf("open media file failed\n"); 285 | return; 286 | } 287 | 288 | struct timeval start; 289 | struct timeval end; 290 | int64_t duration = 0; 291 | 292 | for (i = 0; i < loop; i++) { 293 | gettimeofday(&start, NULL); 294 | oss_media_file_write(file, buf, nbyte); 295 | gettimeofday(&end, NULL); 296 | 297 | duration = end.tv_sec * 1000 + end.tv_usec / 1000 - 298 | start.tv_sec * 1000 - start.tv_usec / 1000; 299 | printf("perf: [duration=%" APR_INT64_T_FMT ", len=%" APR_INT64_T_FMT "]\n", 300 | duration, file->_stat.length); 301 | } 302 | 303 | oss_media_file_close(file); 304 | } 305 | 306 | static void camera_app(char *h264) { 307 | oss_clean("oss_camera.idx"); 308 | oss_clean("oss_camera.h264"); 309 | 310 | int len, total, nbyte = 128*1024, noffsets = 16; 311 | int i, nidrs, offsets[noffsets]; 312 | char buf[nbyte]; 313 | int idx_i; 314 | char idx_content[2048]; 315 | 316 | oss_media_file_t *idx_file; 317 | oss_media_file_t *h264_file; 318 | 319 | idx_file = oss_media_file_open(SAMPLE_BUCKET_NAME, "oss_camera.idx", "w", 320 | auth_func); 321 | h264_file = oss_media_file_open(SAMPLE_BUCKET_NAME, "oss_camera.h264", "a", 322 | auth_func); 323 | 324 | if (!idx_file || !h264_file) { 325 | printf ("open file failed."); 326 | return; 327 | } 328 | 329 | // get h264 stream from camera, simulated by local h264 file. 330 | // assume: generate idr every 20 seconds. 331 | FILE *file; 332 | file = fopen(h264, "rb"); 333 | total = 0; 334 | idx_i = 0; 335 | memset(idx_content, 0, 1024); 336 | 337 | while((len = fread(buf, 1, nbyte, file)) > 0) { 338 | oss_media_get_h264_idr_offsets(buf, nbyte, offsets, 16, &nidrs); 339 | 340 | for (i = 0; i < nidrs; i++) { 341 | printf("Coded Slice Of An IDR Pic. Offset: [Dec:%.8d Hex:0x%.8X]\n", 342 | total + offsets[i], total + offsets[i]); 343 | char c[64]; 344 | sprintf(c, "offset:%.8d timestamp:%.8d\n", 345 | total + offsets[i], (idx_i ++ ) * 20); 346 | memcpy(idx_content + strlen(idx_content), c, strlen(c)); 347 | } 348 | total += len; 349 | 350 | // write idx 351 | oss_media_file_write(idx_file, idx_content, strlen(idx_content)); 352 | 353 | // append h264 354 | oss_media_file_write(h264_file, buf, len); 355 | } 356 | 357 | fclose(file); 358 | 359 | oss_media_file_close(idx_file); 360 | oss_media_file_close(h264_file); 361 | 362 | // read idx info 363 | // we need the 20seconds's video 364 | idx_file = oss_media_file_open(SAMPLE_BUCKET_NAME, "oss_camera.idx", 365 | "r", auth_func); 366 | h264_file = oss_media_file_open(SAMPLE_BUCKET_NAME, "oss_camera.h264", 367 | "r", auth_func); 368 | if (!idx_file || !h264_file) { 369 | printf ("open file failed."); 370 | return; 371 | } 372 | 373 | total = 0; 374 | memset(idx_content, 0, 1024); 375 | while((len = oss_media_file_read(idx_file, buf, nbyte)) > 0) { 376 | memcpy(idx_content+total, buf, len); 377 | total += len; 378 | } 379 | 380 | char *word, *_word; 381 | int video_offset; 382 | for (word = strtok_r(idx_content, "\n", &_word); 383 | word; 384 | word = strtok_r(NULL, "\n", &_word)) { 385 | 386 | char *info, *_info; 387 | info = strtok_r(word, " :", &_info); // offset 388 | info = strtok_r(NULL, " :", &_info); // offset value 389 | video_offset = atoi(info); 390 | info = strtok_r(NULL, " :", &_info); // timestamp 391 | info = strtok_r(NULL, " :", &_info); // timestamp value 392 | if(atoi(info) == 20) 393 | break; 394 | } 395 | 396 | // seek to the 20seconds' video 397 | oss_media_file_seek(h264_file, video_offset); 398 | while (oss_media_file_read(h264_file, buf, nbyte) > 0) { 399 | // read the video and play. 400 | } 401 | 402 | oss_media_file_close(idx_file); 403 | oss_media_file_close(h264_file); 404 | } 405 | 406 | static void usage() { 407 | printf("Usage: oss_media_client_example type\n" 408 | "type:\n" 409 | " write\n" 410 | " append\n" 411 | " read\n" 412 | " seek\n" 413 | " error_code\n" 414 | " idr h264_file\n" 415 | " perf loop_times\n" 416 | " app h264_file\n"); 417 | } 418 | 419 | int main(int argc, char *argv[]) { 420 | oss_media_init(AOS_LOG_INFO); 421 | 422 | if (argc < 2) { 423 | usage(); 424 | return -1; 425 | } 426 | 427 | // example of oss media file functions 428 | if (strcmp("write", argv[1]) == 0) { 429 | write_file(); 430 | } else if (strcmp("append", argv[1]) == 0) { 431 | append_file(); 432 | } else if (strcmp("read", argv[1]) == 0) { 433 | read_file(); 434 | } else if (strcmp("seek", argv[1]) == 0) { 435 | seek_file(); 436 | } else if (strcmp("error_code", argv[1]) ==0) { 437 | error_code(); 438 | } else if (strcmp("idr", argv[1]) == 0) { 439 | if (argc < 3) { 440 | usage(); 441 | return -1; 442 | } 443 | 444 | idr(argv[2]); 445 | } else if (strcmp("perf", argv[1]) == 0) { 446 | int loop = (argc == 3) ? atoi(argv[2]) : 1000; 447 | perf(loop); 448 | } else if (strcmp("app", argv[1]) == 0) { 449 | if (argc < 3) { 450 | usage(); 451 | return -1; 452 | } 453 | 454 | camera_app(argv[2]); 455 | } else { 456 | printf("Unsupport operation:%s\n", argv[1]); 457 | usage(); 458 | } 459 | 460 | oss_media_destroy(); 461 | return 0; 462 | } 463 | -------------------------------------------------------------------------------- /sample/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | char SAMPLE_OSS_ENDPOINT[] = "oss-cn-hangzhou.aliyuncs.com"; 4 | char SAMPLE_ACCESS_KEY_ID[] = ""; 5 | char SAMPLE_ACCESS_KEY_SECRET[] = ""; 6 | char SAMPLE_STS_TOKEN[] = ""; 7 | char SAMPLE_BUCKET_NAME[] = ""; 8 | char SAMPLE_ROLE_ARN[] = ""; 9 | -------------------------------------------------------------------------------- /sample/config.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_CONFIG_H 2 | #define OSS_MEDIA_CONFIG_H 3 | 4 | #include "src/oss_media_define.h" 5 | 6 | OSS_MEDIA_CPP_START 7 | 8 | extern char SAMPLE_OSS_ENDPOINT[]; 9 | extern char SAMPLE_ACCESS_KEY_ID[]; 10 | extern char SAMPLE_ACCESS_KEY_SECRET[]; 11 | extern char SAMPLE_STS_TOKEN[]; 12 | extern char SAMPLE_BUCKET_NAME[]; 13 | extern char SAMPLE_ROLE_ARN[]; 14 | 15 | OSS_MEDIA_CPP_END 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /sample/data/example.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/example.aac -------------------------------------------------------------------------------- /sample/data/example.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/example.h264 -------------------------------------------------------------------------------- /sample/data/live/audio/1.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/1.aac -------------------------------------------------------------------------------- /sample/data/live/audio/2.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/2.aac -------------------------------------------------------------------------------- /sample/data/live/audio/3.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/3.aac -------------------------------------------------------------------------------- /sample/data/live/audio/4.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/4.aac -------------------------------------------------------------------------------- /sample/data/live/audio/5.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/5.aac -------------------------------------------------------------------------------- /sample/data/live/audio/6.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/6.aac -------------------------------------------------------------------------------- /sample/data/live/audio/7.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/7.aac -------------------------------------------------------------------------------- /sample/data/live/audio/8.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/8.aac -------------------------------------------------------------------------------- /sample/data/live/audio/9.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/audio/9.aac -------------------------------------------------------------------------------- /sample/data/live/video/1.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/1.h264 -------------------------------------------------------------------------------- /sample/data/live/video/2.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/2.h264 -------------------------------------------------------------------------------- /sample/data/live/video/3.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/3.h264 -------------------------------------------------------------------------------- /sample/data/live/video/4.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/4.h264 -------------------------------------------------------------------------------- /sample/data/live/video/5.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/5.h264 -------------------------------------------------------------------------------- /sample/data/live/video/6.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/6.h264 -------------------------------------------------------------------------------- /sample/data/live/video/7.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/7.h264 -------------------------------------------------------------------------------- /sample/data/live/video/8.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/8.h264 -------------------------------------------------------------------------------- /sample/data/live/video/9.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/live/video/9.h264 -------------------------------------------------------------------------------- /sample/data/slice/audio/1.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/slice/audio/1.aac -------------------------------------------------------------------------------- /sample/data/slice/audio/2.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/slice/audio/2.aac -------------------------------------------------------------------------------- /sample/data/slice/audio/3.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/slice/audio/3.aac -------------------------------------------------------------------------------- /sample/data/slice/video/1.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/slice/video/1.h264 -------------------------------------------------------------------------------- /sample/data/slice/video/2.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/slice/video/2.h264 -------------------------------------------------------------------------------- /sample/data/slice/video/3.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-media-c-sdk/1d9586106013e7a002a9ca24fcda73811bd55274/sample/data/slice/video/3.h264 -------------------------------------------------------------------------------- /sample/hls_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "src/oss_media_hls.h" 4 | #include "src/oss_media_client.h" 5 | #include "config.h" 6 | #include "sample.h" 7 | 8 | static void auth_func(oss_media_file_t *file) { 9 | file->endpoint = SAMPLE_OSS_ENDPOINT; 10 | file->is_cname = 0; 11 | file->access_key_id = SAMPLE_ACCESS_KEY_ID; 12 | file->access_key_secret = SAMPLE_ACCESS_KEY_SECRET; 13 | file->token = NULL; //SAMPLE_STS_TOKEN; // if use sts token 14 | 15 | // expiration 300 sec. 16 | file->expiration = time(NULL) + 300; 17 | } 18 | 19 | static void do_write(oss_media_hls_file_t *file) { 20 | oss_media_hls_frame_t frame; 21 | FILE *file_h264; 22 | uint8_t *buf_h264; 23 | int len_h264, i; 24 | int cur_pos = -1; 25 | int last_pos = -1; 26 | int video_frame_rate = 30; 27 | int max_size = 10 * 1024 * 1024; 28 | char *h264_file_name = SAMPLE_DIR"/data/example.h264"; 29 | uint8_t nal_type; 30 | 31 | buf_h264 = calloc(max_size, 1); 32 | file_h264 = fopen(h264_file_name, "r"); 33 | len_h264 = fread(buf_h264, 1, max_size, file_h264); 34 | 35 | frame.stream_type = st_h264; 36 | frame.pts = 0; 37 | frame.continuity_counter = 1; 38 | frame.key = 1; 39 | 40 | for (i = 0; i < len_h264 - 5; i++) { 41 | if ((buf_h264[i] & 0x0F)==0x00 && buf_h264[i+1]==0x00 42 | && buf_h264[i+2]==0x00 && buf_h264[i+3]==0x01) 43 | { 44 | nal_type = buf_h264[i+4] & 0x1f; 45 | if (nal_type == 9) { //Access unit delimiter 46 | cur_pos = i; 47 | } 48 | } 49 | 50 | if (last_pos != -1 && cur_pos > last_pos) { 51 | frame.pts += 90000 / video_frame_rate; 52 | frame.dts = frame.pts; 53 | 54 | frame.pos = buf_h264 + last_pos; 55 | frame.end = buf_h264 + cur_pos; 56 | 57 | oss_media_hls_write_frame(&frame, file); 58 | } 59 | 60 | last_pos = cur_pos; 61 | } 62 | 63 | frame.pts += 90000 / video_frame_rate; 64 | frame.dts = frame.pts; 65 | 66 | frame.pos = buf_h264 + last_pos; 67 | frame.end = buf_h264 + len_h264; 68 | 69 | oss_media_hls_write_frame(&frame, file); 70 | 71 | fclose(file_h264); 72 | free(buf_h264); 73 | } 74 | 75 | static void write_ts() { 76 | oss_media_hls_file_t *file; 77 | 78 | file = oss_media_hls_open(SAMPLE_BUCKET_NAME, "oss_media_hls.ts", 79 | auth_func); 80 | if (file == NULL) { 81 | printf("open ts file failed."); 82 | return; 83 | } 84 | 85 | do_write(file); 86 | 87 | printf("convert H.264 to TS and write to oss file[%s] succeeded\n", 88 | file->file->object_key); 89 | 90 | oss_media_hls_close(file); 91 | } 92 | 93 | static void write_m3u8() { 94 | oss_media_hls_file_t *file; 95 | 96 | file = oss_media_hls_open(SAMPLE_BUCKET_NAME, "oss_media_hls.m3u8", 97 | auth_func); 98 | if (file == NULL) { 99 | printf("open m3u8 file failed."); 100 | return; 101 | } 102 | 103 | oss_media_hls_m3u8_info_t m3u8[3]; 104 | m3u8[0].duration = 9; 105 | memcpy(m3u8[0].url, "video-0.ts", strlen("video-0.ts")); 106 | m3u8[1].duration = 10; 107 | memcpy(m3u8[1].url, "video-1.ts", strlen("video-1.ts")); 108 | 109 | oss_media_hls_begin_m3u8(10, 0, file); 110 | oss_media_hls_write_m3u8(2, m3u8, file); 111 | oss_media_hls_end_m3u8(file); 112 | 113 | oss_media_hls_close(file); 114 | 115 | printf("write m3u8 to oss file succeeded\n"); 116 | } 117 | 118 | static int usage() { 119 | printf("Usage: oss_media_hls_example type\n" 120 | "type:\n" 121 | " ts\n" 122 | " m3u8\n"); 123 | return -1; 124 | } 125 | 126 | int main(int argc, char *argv[]) { 127 | oss_media_init(AOS_LOG_INFO); 128 | 129 | if (argc < 2) { 130 | return usage(); 131 | } 132 | 133 | if (strcmp("ts", argv[1]) == 0) { 134 | write_ts(); 135 | } else if (strcmp("m3u8", argv[1]) == 0) { 136 | write_m3u8(); 137 | } else { 138 | printf("Unsupport operation:%s\n", argv[1]); 139 | usage(); 140 | } 141 | 142 | oss_media_destroy(); 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /sample/hls_stream_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "src/oss_media_hls_stream.h" 5 | #include "config.h" 6 | #include "sample.h" 7 | 8 | static void auth_func(oss_media_file_t *file) { 9 | file->endpoint = SAMPLE_OSS_ENDPOINT; 10 | file->is_cname = 0; 11 | file->access_key_id = SAMPLE_ACCESS_KEY_ID; 12 | file->access_key_secret = SAMPLE_ACCESS_KEY_SECRET; 13 | file->token = NULL; //SAMPLE_STS_TOKEN; // if use sts token 14 | 15 | // expiration 300 sec. 16 | file->expiration = time(NULL) + 300; 17 | } 18 | 19 | static void write_only_video_vod() { 20 | int ret; 21 | int max_size = 10 * 1024 * 1024; 22 | FILE *h264_file; 23 | uint8_t *h264_buf; 24 | int h264_len; 25 | 26 | oss_media_hls_stream_options_t options; 27 | oss_media_hls_stream_t *stream = NULL; 28 | 29 | options.is_live = 0; 30 | options.bucket_name = SAMPLE_BUCKET_NAME; 31 | options.ts_name_prefix = "vod/video/test"; 32 | options.m3u8_name = "vod/video/vod.m3u8"; 33 | options.video_frame_rate = 30; 34 | options.hls_time = 5; 35 | 36 | stream = oss_media_hls_stream_open(auth_func, &options); 37 | if (stream == NULL) { 38 | printf("open hls stream failed.\n"); 39 | return; 40 | } 41 | 42 | h264_buf = malloc(max_size); 43 | 44 | { 45 | h264_file = fopen(SAMPLE_DIR"/data/slice/video/1.h264", "r"); 46 | h264_len = fread(h264_buf, 1, max_size, h264_file); 47 | fclose(h264_file); 48 | 49 | ret = oss_media_hls_stream_write(h264_buf, h264_len, NULL, 0, stream); 50 | if (ret != 0) { 51 | printf("write vod stream failed.\n"); 52 | return; 53 | } 54 | } 55 | 56 | { 57 | h264_file = fopen(SAMPLE_DIR"/data/slice/video/2.h264", "r"); 58 | h264_len = fread(h264_buf, 1, max_size, h264_file); 59 | fclose(h264_file); 60 | 61 | ret = oss_media_hls_stream_write(h264_buf, h264_len, NULL, 0, stream); 62 | if (ret != 0) { 63 | printf("write vod stream failed.\n"); 64 | return; 65 | } 66 | } 67 | 68 | { 69 | h264_file = fopen(SAMPLE_DIR"/data/slice/video/3.h264", "r"); 70 | h264_len = fread(h264_buf, 1, max_size, h264_file); 71 | fclose(h264_file); 72 | 73 | ret = oss_media_hls_stream_write(h264_buf, h264_len, NULL, 0, stream); 74 | if (ret != 0) { 75 | printf("write vod stream failed.\n"); 76 | return; 77 | } 78 | } 79 | 80 | ret = oss_media_hls_stream_close(stream); 81 | if (ret != 0) { 82 | printf("close vod stream failed.\n"); 83 | return; 84 | } 85 | 86 | free(h264_buf); 87 | printf("convert H.264 to HLS vod succeeded\n"); 88 | } 89 | 90 | static void write_only_audio_vod() { 91 | int ret; 92 | int max_size = 10 * 1024 * 1024; 93 | FILE *aac_file; 94 | uint8_t *aac_buf; 95 | int aac_len; 96 | 97 | oss_media_hls_stream_options_t options; 98 | oss_media_hls_stream_t *stream = NULL; 99 | 100 | options.is_live = 0; 101 | options.bucket_name = SAMPLE_BUCKET_NAME; 102 | options.ts_name_prefix = "vod/audio/test"; 103 | options.m3u8_name = "vod/audio/vod.m3u8"; 104 | options.audio_sample_rate = 8000; 105 | options.hls_time = 5; 106 | 107 | stream = oss_media_hls_stream_open(auth_func, &options); 108 | if (stream == NULL) { 109 | printf("open hls stream failed.\n"); 110 | return; 111 | } 112 | 113 | aac_buf = malloc(max_size); 114 | 115 | { 116 | aac_file = fopen(SAMPLE_DIR"/data/slice/audio/1.aac", "r"); 117 | aac_len = fread(aac_buf, 1, max_size, aac_file); 118 | fclose(aac_file); 119 | 120 | ret = oss_media_hls_stream_write(NULL, 0, aac_buf, aac_len, stream); 121 | if (ret != 0) { 122 | printf("write vod stream failed.\n"); 123 | return; 124 | } 125 | } 126 | 127 | { 128 | aac_file = fopen(SAMPLE_DIR"/data/slice/audio/2.aac", "r"); 129 | aac_len = fread(aac_buf, 1, max_size, aac_file); 130 | fclose(aac_file); 131 | 132 | ret = oss_media_hls_stream_write(NULL, 0, aac_buf, aac_len, stream); 133 | if (ret != 0) { 134 | printf("write vod stream failed.\n"); 135 | return; 136 | } 137 | } 138 | 139 | { 140 | aac_file = fopen(SAMPLE_DIR"/data/slice/audio/3.aac", "r"); 141 | aac_len = fread(aac_buf, 1, max_size, aac_file); 142 | fclose(aac_file); 143 | 144 | ret = oss_media_hls_stream_write(NULL, 0, aac_buf, aac_len, stream); 145 | if (ret != 0) { 146 | printf("write vod stream failed.\n"); 147 | return; 148 | } 149 | } 150 | 151 | ret = oss_media_hls_stream_close(stream); 152 | if (ret != 0) { 153 | printf("close vod stream failed.\n"); 154 | return; 155 | } 156 | 157 | free(aac_buf); 158 | printf("convert aac to HLS vod succeeded\n"); 159 | } 160 | 161 | static void write_video_audio_vod() { 162 | int ret; 163 | int max_size = 10 * 1024 * 1024; 164 | FILE *h264_file, *aac_file; 165 | uint8_t *h264_buf, *aac_buf; 166 | int h264_len, aac_len; 167 | 168 | oss_media_hls_stream_options_t options; 169 | oss_media_hls_stream_t *stream = NULL; 170 | 171 | options.is_live = 0; 172 | options.bucket_name = SAMPLE_BUCKET_NAME; 173 | options.ts_name_prefix = "vod/video_audio/test"; 174 | options.m3u8_name = "vod/video_audio/vod.m3u8"; 175 | options.video_frame_rate = 30; 176 | options.audio_sample_rate = 8000; 177 | options.hls_time = 5; 178 | 179 | stream = oss_media_hls_stream_open(auth_func, &options); 180 | if (stream == NULL) { 181 | printf("open hls stream failed.\n"); 182 | return; 183 | } 184 | 185 | h264_buf = malloc(max_size); 186 | aac_buf = malloc(max_size); 187 | { 188 | h264_file = fopen(SAMPLE_DIR"/data/slice/video/1.h264", "r"); 189 | h264_len = fread(h264_buf, 1, max_size, h264_file); 190 | fclose(h264_file); 191 | 192 | aac_file = fopen(SAMPLE_DIR"/data/slice/audio/1.aac", "r"); 193 | aac_len = fread(aac_buf, 1, max_size, aac_file); 194 | fclose(aac_file); 195 | 196 | ret = oss_media_hls_stream_write(h264_buf, h264_len, 197 | aac_buf, aac_len, stream); 198 | if (ret != 0) { 199 | printf("write vod stream failed.\n"); 200 | return; 201 | } 202 | } 203 | 204 | { 205 | h264_file = fopen(SAMPLE_DIR"/data/slice/video/2.h264", "r"); 206 | h264_len = fread(h264_buf, 1, max_size, h264_file); 207 | fclose(h264_file); 208 | 209 | aac_file = fopen(SAMPLE_DIR"/data/slice/audio/1.aac", "r"); 210 | aac_len = fread(aac_buf, 1, max_size, aac_file); 211 | fclose(aac_file); 212 | 213 | ret = oss_media_hls_stream_write(h264_buf, h264_len, 214 | aac_buf, aac_len, stream); 215 | if (ret != 0) { 216 | printf("write vod stream failed.\n"); 217 | return; 218 | } 219 | } 220 | 221 | { 222 | h264_file = fopen(SAMPLE_DIR"/data/slice/video/3.h264", "r"); 223 | h264_len = fread(h264_buf, 1, max_size, h264_file); 224 | fclose(h264_file); 225 | 226 | aac_file = fopen(SAMPLE_DIR"/data/slice/audio/3.aac", "r"); 227 | aac_len = fread(aac_buf, 1, max_size, aac_file); 228 | fclose(aac_file); 229 | 230 | ret = oss_media_hls_stream_write(h264_buf, h264_len, 231 | aac_buf, aac_len, stream); 232 | if (ret != 0) { 233 | printf("write vod stream failed.\n"); 234 | return; 235 | } 236 | } 237 | 238 | ret = oss_media_hls_stream_close(stream); 239 | if (ret != 0) { 240 | printf("close vod stream failed.\n"); 241 | return; 242 | } 243 | 244 | free(h264_buf); 245 | free(aac_buf); 246 | printf("convert H.264 and aac to HLS vod succeeded\n"); 247 | } 248 | 249 | static void write_video_audio_live() { 250 | int ret; 251 | int max_size = 10 * 1024 * 1024; 252 | FILE *h264_file, *aac_file; 253 | uint8_t *h264_buf, *aac_buf; 254 | int h264_len, aac_len; 255 | 256 | oss_media_hls_stream_options_t options; 257 | oss_media_hls_stream_t *stream = NULL; 258 | 259 | options.is_live = 1; 260 | options.bucket_name = SAMPLE_BUCKET_NAME; 261 | options.ts_name_prefix = "live/video_audio/test"; 262 | options.m3u8_name = "live/video_audio/live.m3u8"; 263 | options.video_frame_rate = 30; 264 | options.audio_sample_rate = 8000; 265 | options.hls_time = 5; 266 | options.hls_list_size = 5; 267 | 268 | stream = oss_media_hls_stream_open(auth_func, &options); 269 | if (stream == NULL) { 270 | printf("open hls stream failed.\n"); 271 | return; 272 | } 273 | 274 | int video_index = 1; 275 | int audio_index = 1; 276 | h264_buf = malloc(max_size); 277 | aac_buf = malloc(max_size); 278 | 279 | do { 280 | h264_len = 0; 281 | aac_len = 0; 282 | 283 | char *file_path = (char*)malloc(128); 284 | 285 | sprintf(file_path, SAMPLE_DIR"/data/live/video/%d.h264", 286 | video_index); 287 | h264_file = fopen(file_path, "r"); 288 | h264_len = fread(h264_buf, 1, max_size, h264_file); 289 | fclose(h264_file); 290 | 291 | sprintf(file_path, SAMPLE_DIR"/data/live/audio/%d.aac", 292 | audio_index); 293 | aac_file = fopen(file_path, "r"); 294 | aac_len = fread(aac_buf, 1, max_size, aac_file); 295 | fclose(aac_file); 296 | 297 | ret = oss_media_hls_stream_write(h264_buf, h264_len, 298 | aac_buf, aac_len, stream); 299 | if (ret != 0) { 300 | printf("write live stream failed.\n"); 301 | return; 302 | } 303 | free(file_path); 304 | sleep(8); 305 | } while (video_index++ < 9 && audio_index++ < 9); 306 | 307 | ret = oss_media_hls_stream_close(stream); 308 | if (ret != 0) { 309 | printf("close live stream failed.\n"); 310 | return; 311 | } 312 | 313 | free(h264_buf); 314 | free(aac_buf); 315 | printf("convert H.264 and aac to HLS live succeeded\n"); 316 | } 317 | 318 | static int usage() { 319 | printf("Usage: oss_media_hls_stream_example type\n" 320 | "type:\n" 321 | " only_video_vod\n" 322 | " only_audio_vod\n" 323 | " video_audio_vod\n" 324 | " video_audio_live\n"); 325 | return -1; 326 | } 327 | 328 | int main(int argc, char *argv[]) { 329 | oss_media_init(AOS_LOG_INFO); 330 | 331 | if (argc < 2) { 332 | return usage(); 333 | } 334 | 335 | if (strcmp("only_video_vod", argv[1]) == 0) { 336 | write_only_video_vod(); 337 | } else if (strcmp("only_audio_vod", argv[1]) == 0) { 338 | write_only_audio_vod(); 339 | } else if (strcmp("video_audio_vod", argv[1]) == 0) { 340 | write_video_audio_vod(); 341 | } else if (strcmp("video_audio_live", argv[1]) == 0) { 342 | write_video_audio_live(); 343 | } else { 344 | printf("Unsupport operation:%s\n", argv[1]); 345 | usage(); 346 | } 347 | 348 | oss_media_destroy(); 349 | 350 | return 0; 351 | } 352 | -------------------------------------------------------------------------------- /sample/sample.h.in: -------------------------------------------------------------------------------- 1 | #define SAMPLE_DIR "@PROJECT_SOURCE_DIR@/sample/" 2 | -------------------------------------------------------------------------------- /sample/server_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "src/oss_media_define.h" 4 | #include "src/oss_media_server.h" 5 | #include "src/sts/libsts.h" 6 | #include "config.h" 7 | 8 | static void init_media_config(oss_media_config_t *config) { 9 | config->endpoint = SAMPLE_OSS_ENDPOINT; 10 | config->access_key_id = SAMPLE_ACCESS_KEY_ID; 11 | config->access_key_secret = SAMPLE_ACCESS_KEY_SECRET; 12 | config->role_arn = SAMPLE_ROLE_ARN; 13 | config->is_cname = 0; 14 | } 15 | 16 | void create_bucket() { 17 | int ret; 18 | oss_media_config_t config; 19 | 20 | init_media_config(&config); 21 | 22 | ret = oss_media_create_bucket(&config, SAMPLE_BUCKET_NAME, OSS_ACL_PRIVATE); 23 | 24 | if (0 == ret) { 25 | printf("create bucket[%s] succeeded.\n", SAMPLE_BUCKET_NAME); 26 | } else { 27 | printf("create bucket[%s] failed.\n", SAMPLE_BUCKET_NAME); 28 | } 29 | } 30 | 31 | void delete_bucket() { 32 | int ret; 33 | oss_media_config_t config; 34 | 35 | init_media_config(&config); 36 | 37 | ret = oss_media_delete_bucket(&config, SAMPLE_BUCKET_NAME); 38 | 39 | if (0 == ret) { 40 | printf("delete bucket[%s] succeeded.\n", SAMPLE_BUCKET_NAME); 41 | } else { 42 | printf("delete bucket[%s] failed.\n", SAMPLE_BUCKET_NAME); 43 | } 44 | } 45 | 46 | void create_bucket_lifecycle() { 47 | int ret; 48 | oss_media_lifecycle_rules_t *rules; 49 | oss_media_config_t config; 50 | 51 | init_media_config(&config); 52 | 53 | rules = oss_media_create_lifecycle_rules(2); 54 | oss_media_lifecycle_rule_t rule1; 55 | rule1.name = "example-1"; 56 | rule1.path = "/example/1"; 57 | rule1.status = "Enabled"; 58 | rule1.days = 1; 59 | oss_media_lifecycle_rule_t rule2; 60 | rule2.name = "example-2"; 61 | rule2.path = "/example/2"; 62 | rule2.status = "Disabled"; 63 | rule2.days = 2; 64 | 65 | rules->rules[0] = &rule1; 66 | rules->rules[1] = &rule2; 67 | 68 | ret = oss_media_create_bucket_lifecycle(&config, 69 | SAMPLE_BUCKET_NAME, rules); 70 | 71 | if (0 == ret) { 72 | printf("create bucket[%s] lifecycle succeeded.\n", SAMPLE_BUCKET_NAME); 73 | } else { 74 | printf("create bucket[%s] lifecycle failed.\n", SAMPLE_BUCKET_NAME); 75 | } 76 | 77 | oss_media_free_lifecycle_rules(rules); 78 | } 79 | 80 | void get_bucket_lifecycle() { 81 | int ret, i; 82 | oss_media_lifecycle_rules_t *rules; 83 | oss_media_config_t config; 84 | 85 | init_media_config(&config); 86 | 87 | rules = oss_media_create_lifecycle_rules(0); 88 | 89 | ret = oss_media_get_bucket_lifecycle(&config, SAMPLE_BUCKET_NAME, rules); 90 | 91 | if (0 == ret) { 92 | printf("get bucket[%s] lifecycle succeeded.\n", SAMPLE_BUCKET_NAME); 93 | } else { 94 | printf("get bucket[%s] lifecycle failed.\n", SAMPLE_BUCKET_NAME); 95 | } 96 | 97 | for (i = 0; i < rules->size; i++) { 98 | printf(">>>> rule: [name:%s, path:%s, status=%s, days=%d]\n", 99 | rules->rules[i]->name, rules->rules[i]->path, 100 | rules->rules[i]->status, rules->rules[i]->days); 101 | } 102 | 103 | oss_media_free_lifecycle_rules(rules); 104 | } 105 | 106 | void delete_bucket_lifecycle() 107 | { 108 | int ret; 109 | oss_media_config_t config; 110 | 111 | init_media_config(&config); 112 | 113 | ret = oss_media_delete_bucket_lifecycle(&config, SAMPLE_BUCKET_NAME); 114 | 115 | if (0 == ret) { 116 | printf("delete bucket[%s] lifecycle succeeded.\n", SAMPLE_BUCKET_NAME); 117 | } else { 118 | printf("delete bucket[%s] lifecycle failed.\n", SAMPLE_BUCKET_NAME); 119 | } 120 | } 121 | 122 | void delete_file() { 123 | int ret; 124 | oss_media_config_t config; 125 | char *file; 126 | 127 | init_media_config(&config); 128 | 129 | file = "oss_media_file"; 130 | ret = oss_media_delete_file(&config, SAMPLE_BUCKET_NAME, file); 131 | 132 | if (0 == ret) { 133 | printf("delete file[%s] succeeded.\n", file); 134 | } else { 135 | printf("delete file[%s] lifecycle failed.\n", file); 136 | } 137 | } 138 | 139 | void list_files() { 140 | int ret, i; 141 | oss_media_files_t *files; 142 | oss_media_config_t config; 143 | 144 | init_media_config(&config); 145 | 146 | files = oss_media_create_files(); 147 | files->max_size = 50; 148 | 149 | ret = oss_media_list_files(&config, SAMPLE_BUCKET_NAME, files); 150 | 151 | if (0 == ret) { 152 | printf("list files succeeded.\n"); 153 | } else { 154 | printf("list files lifecycle failed.\n"); 155 | } 156 | 157 | for (i = 0; i < files->size; i++) { 158 | printf(">>>>file name: %s\n", files->file_names[i]); 159 | } 160 | 161 | oss_media_free_files(files); 162 | } 163 | 164 | void get_token() { 165 | int ret; 166 | oss_media_token_t token; 167 | oss_media_config_t config; 168 | 169 | init_media_config(&config); 170 | 171 | ret = oss_media_get_token(&config, SAMPLE_BUCKET_NAME, "/*", "rwa", 172 | 3600, &token); 173 | 174 | if (0 == ret) { 175 | printf("get token succeeded, access_key_id=%s, access_key_secret=%s, token=%s\n", 176 | token.tmpAccessKeyId, token.tmpAccessKeySecret, token.securityToken); 177 | } else { 178 | printf("get token failed.\n"); 179 | } 180 | } 181 | 182 | void get_token_from_policy() { 183 | int ret; 184 | oss_media_token_t token; 185 | char *policy; 186 | oss_media_config_t config; 187 | 188 | init_media_config(&config); 189 | 190 | policy = "{\"Version\":\"1\",\"Statement\":[{\"Effect\":\"Allow\", " 191 | "\"Action\":\"*\", \"Resource\":\"*\"}]}"; 192 | 193 | ret = oss_media_get_token_from_policy(&config, policy, 3600, &token); 194 | 195 | if (0 == ret) { 196 | printf("get token succeeded, access_key_id=%s, access_key_secret=%s, token=%s\n", 197 | token.tmpAccessKeyId, token.tmpAccessKeySecret, token.securityToken); 198 | } else { 199 | printf("get token failed.\n"); 200 | } 201 | } 202 | 203 | static int usage() { 204 | printf("Usage: oss_media_server_example type\n" 205 | "type:\n" 206 | " create_bucket\n" 207 | " delete_bucket\n" 208 | " delete_file\n" 209 | " list_files\n" 210 | " create_bucket_lifecycle\n" 211 | " get_bucket_lifecycle\n" 212 | " delete_bucket_lifecycle\n" 213 | " get_token\n" 214 | " get_token_from_policy\n"); 215 | return -1; 216 | } 217 | 218 | int main(int argc, char *argv[]) { 219 | oss_media_init(AOS_LOG_INFO); 220 | 221 | if (argc < 2) { 222 | usage(); 223 | oss_media_destroy(); 224 | return -1; 225 | } 226 | 227 | if (strcmp("create_bucket", argv[1]) == 0) { 228 | create_bucket(); 229 | } 230 | else if (strcmp("delete_bucket", argv[1]) == 0) { 231 | delete_bucket(); 232 | } 233 | else if (strcmp("delete_file", argv[1]) == 0) { 234 | delete_file(); 235 | } 236 | else if (strcmp("list_files", argv[1]) == 0) { 237 | list_files(); 238 | } 239 | else if (strcmp("create_bucket_lifecycle", argv[1]) == 0) { 240 | create_bucket_lifecycle(); 241 | } 242 | else if (strcmp("get_bucket_lifecycle", argv[1]) == 0) { 243 | get_bucket_lifecycle(); 244 | } 245 | else if (strcmp("delete_bucket_lifecycle", argv[1]) == 0) { 246 | delete_bucket_lifecycle(); 247 | } 248 | else if (strcmp("get_token", argv[1]) == 0) { 249 | get_token(); 250 | } 251 | else if (strcmp("get_token_from_policy", argv[1]) == 0) { 252 | get_token_from_policy(); 253 | } 254 | else { 255 | printf("Unsupport operation: %s\n", argv[1]); 256 | usage(); 257 | } 258 | 259 | oss_media_destroy(); 260 | 261 | return 0; 262 | } 263 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | IF (NOT ONLY_BUILD_SERVER) 2 | set(CLIENT_SRC oss_media_define.c 3 | oss_media_log.c 4 | oss_media_client.c 5 | oss_media_hls.c 6 | oss_media_hls_stream.c 7 | ) 8 | 9 | add_library(${CMAKE_PROJECT_NAME}_client SHARED ${CLIENT_SRC}) 10 | add_library(${CMAKE_PROJECT_NAME}_client_static STATIC ${CLIENT_SRC}) 11 | 12 | set_target_properties(${CMAKE_PROJECT_NAME}_client PROPERTIES VERSION ${CMAKE_VERSION} SOVERSION ${CMAKE_VERSION}) 13 | 14 | INSTALL(TARGETS ${CMAKE_PROJECT_NAME}_client LIBRARY DESTINATION lib) 15 | INSTALL(TARGETS ${CMAKE_PROJECT_NAME}_client_static ARCHIVE DESTINATION lib) 16 | 17 | INSTALL(FILES oss_media_define.h 18 | oss_media_client.h 19 | DESTINATION include/oss_c_media_sdk) 20 | ENDIF() 21 | 22 | IF (NOT ONLY_BUILD_CLIENT) 23 | set(SERVER_SRC oss_media_define.c 24 | oss_media_log.c 25 | oss_media_server.c 26 | sts/token.c 27 | sts/general.c 28 | sts/util.c 29 | sts/request.c 30 | sts/time.c 31 | sts/jsmn.c 32 | ) 33 | 34 | add_library(${CMAKE_PROJECT_NAME}_server SHARED ${SERVER_SRC}) 35 | add_library(${CMAKE_PROJECT_NAME}_server_static STATIC ${SERVER_SRC}) 36 | 37 | set_target_properties(${CMAKE_PROJECT_NAME}_server PROPERTIES VERSION ${CMAKE_VERSION} SOVERSION ${CMAKE_VERSION}) 38 | 39 | INSTALL(TARGETS ${CMAKE_PROJECT_NAME}_server LIBRARY DESTINATION lib) 40 | INSTALL(TARGETS ${CMAKE_PROJECT_NAME}_server_static ARCHIVE DESTINATION lib) 41 | 42 | INSTALL(FILES 43 | oss_media_define.h 44 | oss_media_server.h 45 | DESTINATION include/oss_c_media_sdk) 46 | 47 | INSTALL(FILES 48 | sts/libsts.h 49 | DESTINATION include/oss_c_media_sdk/sts/) 50 | 51 | ENDIF() 52 | -------------------------------------------------------------------------------- /src/oss_media_client.c: -------------------------------------------------------------------------------- 1 | #include "oss_media_client.h" 2 | #include 3 | 4 | static int oss_media_retry_cnt = 1; 5 | static int oss_media_sleep_us = 5000; 6 | #define MAX_RETRY_CNT 30 7 | 8 | extern void oss_op_debug(char *op, 9 | aos_status_t *status, 10 | aos_table_t *req_headers, 11 | aos_table_t *resp_headers); 12 | 13 | static void oss_init_request_opts(aos_pool_t *pool, 14 | oss_media_file_t *file, 15 | oss_request_options_t **options) 16 | { 17 | oss_request_options_t *opts; 18 | 19 | opts = oss_request_options_create(pool); 20 | opts->config = oss_config_create(pool); 21 | aos_str_set(&opts->config->endpoint, file->endpoint); 22 | aos_str_set(&opts->config->access_key_id, file->access_key_id); 23 | aos_str_set(&opts->config->access_key_secret, file->access_key_secret); 24 | if (file->token) 25 | aos_str_set(&opts->config->sts_token, file->token); 26 | opts->config->is_cname = file->is_cname; 27 | opts->ctl = aos_http_controller_create(pool, 0); 28 | 29 | *options = opts; 30 | } 31 | 32 | static int64_t oss_get_content_length(const char *val) 33 | { 34 | return val ? atoll(val) : -1; 35 | } 36 | 37 | static void oss_auth(oss_media_file_t *file, 38 | int force) 39 | { 40 | if (!file || !(file->auth_func)) { 41 | aos_error_log("file is null or file->auth_func is null\n"); 42 | return; 43 | } 44 | 45 | // get authorize info from media server when force 46 | if (force) { 47 | file->auth_func(file); 48 | } 49 | // get authorize info from media server when expired 50 | else { 51 | time_t now = time(NULL); 52 | if (!file->expiration || now >= file->expiration) { 53 | file->auth_func(file); 54 | } 55 | } 56 | } 57 | 58 | static int is_readable(oss_media_file_t *file) { 59 | return (NULL != file->mode && 0 == strcmp("r", file->mode)); 60 | } 61 | 62 | int oss_media_init(aos_log_level_e log_level) { 63 | aos_log_set_level(log_level); 64 | aos_log_set_output(NULL); 65 | return aos_http_io_initialize(OSS_MEDIA_CLIENT_USER_AGENT, 0); 66 | } 67 | 68 | void oss_media_destroy() { 69 | aos_http_io_deinitialize(); 70 | } 71 | 72 | void oss_media_set_retry_config(int retry, int sleep_us) { 73 | oss_media_retry_cnt = retry; 74 | oss_media_sleep_us = sleep_us; 75 | } 76 | 77 | oss_media_file_t* oss_media_file_open(char *bucket_name, 78 | char *object_key, 79 | char *mode, 80 | auth_fn_t auth_func) 81 | { 82 | oss_media_file_t *file = (oss_media_file_t*)malloc(sizeof(oss_media_file_t)); 83 | if (NULL == file) { 84 | aos_error_log("malloc a new file failed.\n"); 85 | return NULL; 86 | } 87 | 88 | if (!(strcmp("r", mode) == 0 || strcmp("w", mode) == 0 89 | || strcmp("a", mode) == 0 || strcmp("aw", mode) == 0)) 90 | { 91 | free(file); 92 | aos_error_log("mode[%s] is wrong\n", mode); 93 | return NULL; 94 | } 95 | 96 | file->auth_func = auth_func; 97 | oss_auth(file, 1); 98 | 99 | file->mode = mode; 100 | file->_stat.pos = 0; 101 | 102 | file->bucket_name = bucket_name; 103 | file->object_key = object_key; 104 | 105 | if (strcmp("aw", mode) == 0) { 106 | if ( 0 != oss_media_file_delete(file)) { 107 | aos_error_log("stat file[%s] failed.\n", file->object_key); 108 | free(file); 109 | file = NULL; 110 | return NULL; 111 | } 112 | file->_stat.length = 0; 113 | file->_stat.type = OSS_MEDIA_FILE_UNKNOWN_TYPE; 114 | return file; 115 | } 116 | 117 | if (0 != oss_media_file_stat(file, &(file->_stat))) { 118 | aos_error_log("stat file[%s] failed.\n", file->object_key); 119 | free(file); 120 | file = NULL; 121 | } 122 | 123 | return file; 124 | } 125 | 126 | void oss_media_file_close(oss_media_file_t *file) { 127 | if (NULL != file) { 128 | free(file); 129 | file = NULL; 130 | } 131 | } 132 | 133 | static int oss_media_file_stat_internal(oss_media_file_t *file, oss_media_file_stat_t *stat, int try_cnt) { 134 | aos_pool_t *pool = NULL; 135 | oss_request_options_t *opts = NULL; 136 | aos_string_t bucket; 137 | aos_string_t key; 138 | aos_status_t *status = NULL; 139 | aos_table_t *req_headers = NULL; 140 | aos_table_t *resp_headers = NULL; 141 | 142 | oss_auth(file, 0); 143 | 144 | aos_pool_create(&pool, NULL); 145 | oss_init_request_opts(pool, file, &opts); 146 | req_headers = aos_table_make(pool, 0); 147 | aos_str_set(&bucket, file->bucket_name); 148 | aos_str_set(&key, file->object_key); 149 | 150 | status = oss_head_object(opts, &bucket, &key, req_headers, &resp_headers); 151 | oss_op_debug("oss_head_object", status, req_headers, resp_headers); 152 | 153 | if (aos_status_is_ok(status)) { 154 | if (stat) { 155 | stat->length = oss_get_content_length( 156 | apr_table_get(resp_headers, "Content-Length")); 157 | stat->type = (char*)apr_table_get(resp_headers, "x-oss-object-type"); 158 | } 159 | aos_pool_destroy(pool); 160 | return 0; 161 | } else if (status->code == OSS_MEDIA_FILE_NOT_FOUND) { 162 | if (stat) { 163 | stat->length = 0; 164 | stat->type = OSS_MEDIA_FILE_UNKNOWN_TYPE; 165 | } 166 | aos_pool_destroy(pool); 167 | return 0; 168 | } 169 | 170 | aos_error_log("head object[%s] failed. request_id:%s, code:%d, " 171 | "error_code:%s, error_message:%s, try_cnt:%d", 172 | file->object_key, status->req_id, status->code, 173 | status->error_code, status->error_msg, try_cnt); 174 | aos_pool_destroy(pool); 175 | return -1; 176 | } 177 | 178 | int oss_media_file_stat(oss_media_file_t *file, oss_media_file_stat_t *stat) { 179 | int try_cnt = 1; 180 | int ret = 0; 181 | do { 182 | if ((ret = oss_media_file_stat_internal(file, stat, try_cnt)) != -1) 183 | break; 184 | 185 | if (++try_cnt > oss_media_retry_cnt) 186 | break; 187 | usleep(oss_media_sleep_us); 188 | } while (try_cnt < MAX_RETRY_CNT); 189 | 190 | return ret; 191 | } 192 | 193 | static int oss_media_file_delete_internal(oss_media_file_t *file, int try_cnt) { 194 | aos_pool_t *pool = NULL; 195 | oss_request_options_t *opts = NULL; 196 | aos_string_t bucket; 197 | aos_string_t key; 198 | aos_status_t *status = NULL; 199 | aos_table_t *req_headers = NULL; 200 | aos_table_t *resp_headers = NULL; 201 | 202 | oss_auth(file, 0); 203 | 204 | aos_pool_create(&pool, NULL); 205 | oss_init_request_opts(pool, file, &opts); 206 | req_headers = aos_table_make(pool, 0); 207 | aos_str_set(&bucket, file->bucket_name); 208 | aos_str_set(&key, file->object_key); 209 | 210 | status = oss_delete_object(opts, &bucket, &key, &resp_headers); 211 | if (!aos_status_is_ok(status)) { 212 | aos_error_log("delete object failed. request_id:%s, code:%d, " 213 | "error_code:%s, error_message:%s, try_cnt:%d", 214 | status->req_id, status->code, status->error_code, 215 | status->error_msg, try_cnt); 216 | aos_pool_destroy(pool); 217 | return -1; 218 | } 219 | 220 | aos_pool_destroy(pool); 221 | return 0; 222 | } 223 | 224 | int oss_media_file_delete(oss_media_file_t *file) { 225 | int try_cnt = 1; 226 | int ret = 0; 227 | do { 228 | if ((ret = oss_media_file_delete_internal(file, try_cnt)) != -1) 229 | break; 230 | 231 | if (++try_cnt > oss_media_retry_cnt) 232 | break; 233 | usleep(oss_media_sleep_us); 234 | } while (try_cnt < MAX_RETRY_CNT); 235 | 236 | return ret; 237 | } 238 | 239 | int64_t oss_media_file_tell(oss_media_file_t *file) { 240 | if (!is_readable(file)) { 241 | aos_error_log("file mode[%s] is not readable\n", file->mode); 242 | return -1; 243 | } 244 | return file->_stat.pos; 245 | } 246 | 247 | int64_t oss_media_file_seek(oss_media_file_t *file, int64_t offset) { 248 | if (!is_readable(file)) { 249 | aos_error_log("file mode[%s] is not readable\n", file->mode); 250 | return -1; 251 | } 252 | if (offset < 0 || offset >= file->_stat.length) { 253 | aos_error_log("offset[%" APR_INT64_T_FMT "] is invalidate, file pos[%" APR_INT64_T_FMT "]\n", 254 | offset, file->_stat.length); 255 | return -1; 256 | } 257 | file->_stat.pos = offset; 258 | return offset; 259 | } 260 | 261 | static int64_t oss_media_file_read_internal(oss_media_file_t *file, void *buf, int64_t nbyte, int try_cnt) { 262 | aos_pool_t *pool = NULL; 263 | oss_request_options_t *opts = NULL; 264 | aos_string_t bucket; 265 | aos_string_t key; 266 | aos_status_t *status = NULL; 267 | aos_table_t *req_headers = NULL; 268 | aos_table_t *req_params = NULL; 269 | aos_table_t *resp_headers = NULL; 270 | aos_list_t buffer; 271 | aos_buf_t *content = NULL; 272 | char *range = NULL; 273 | int64_t end; 274 | int64_t offset = 0; 275 | int64_t size = 0; 276 | int64_t len = 0; 277 | 278 | oss_auth(file, 0); 279 | 280 | if (!is_readable(file)) { 281 | return -1; 282 | } 283 | // EOF 284 | if (file->_stat.pos > file->_stat.length - 1) { 285 | aos_error_log("EOF\n"); 286 | return 0; 287 | } 288 | 289 | aos_pool_create(&pool, NULL); 290 | oss_init_request_opts(pool, file, &opts); 291 | aos_str_set(&bucket, file->bucket_name); 292 | aos_str_set(&key, file->object_key); 293 | aos_list_init(&buffer); 294 | 295 | end = (file->_stat.length > 0 && file->_stat.pos + nbyte > file->_stat.length) ? 296 | file->_stat.length - 1 : file->_stat.pos + nbyte - 1; 297 | range = apr_psprintf(pool, "bytes=%" APR_INT64_T_FMT "-%" APR_INT64_T_FMT, file->_stat.pos, end); 298 | 299 | req_headers = aos_table_make(pool, 1); 300 | apr_table_set(req_headers, "Range", range); 301 | 302 | status = oss_get_object_to_buffer(opts, &bucket, &key, 303 | req_headers, req_params, &buffer, &resp_headers); 304 | 305 | oss_op_debug("oss_get_object_to_buffer", status, req_headers, resp_headers); 306 | 307 | if (!aos_status_is_ok(status)) { 308 | aos_error_log("get object failed. request_id:%s, code:%d, " 309 | "error_code:%s, error_message:%s, try_cnt:%d", 310 | status->req_id, status->code, status->error_code, 311 | status->error_msg, try_cnt); 312 | aos_pool_destroy(pool); 313 | return -1; 314 | } 315 | 316 | aos_list_for_each_entry(aos_buf_t, content, &buffer, node) { 317 | size = aos_buf_size(content); 318 | memcpy(buf + offset, content->pos, size); 319 | offset += size; 320 | } 321 | 322 | len = oss_get_content_length(apr_table_get(resp_headers, "Content-Length")); 323 | file->_stat.pos += len; 324 | 325 | aos_pool_destroy(pool); 326 | return len; 327 | } 328 | 329 | int64_t oss_media_file_read(oss_media_file_t *file, void *buf, int64_t nbyte) { 330 | int try_cnt = 1; 331 | int64_t ret = 0; 332 | 333 | if (!is_readable(file)) { 334 | return -1; 335 | } 336 | 337 | do { 338 | if ((ret = oss_media_file_read_internal(file, buf, nbyte, try_cnt)) != -1) 339 | break; 340 | 341 | if (++try_cnt > oss_media_retry_cnt) 342 | break; 343 | usleep(oss_media_sleep_us); 344 | } while (try_cnt < MAX_RETRY_CNT); 345 | 346 | return ret; 347 | } 348 | 349 | int64_t oss_media_file_write_internal(oss_media_file_t *file, const void *buf, int64_t nbyte, int try_cnt) { 350 | aos_pool_t *pool = NULL; 351 | oss_request_options_t *opts = NULL; 352 | aos_string_t bucket; 353 | aos_string_t key; 354 | aos_status_t *status = NULL; 355 | aos_table_t *req_headers = NULL; 356 | aos_table_t *resp_headers = NULL; 357 | aos_list_t buffer; 358 | aos_buf_t *content = NULL; 359 | 360 | oss_auth(file, 0); 361 | 362 | if (!file->mode || (strcmp("w", file->mode) != 0 && 363 | strcmp("a", file->mode) != 0 && 364 | strcmp("aw", file->mode) != 0)) 365 | { 366 | aos_error_log("file mode[%s] is not [w/a]\n", file->mode); 367 | return -1; 368 | } 369 | 370 | aos_pool_create(&pool, NULL); 371 | oss_init_request_opts(pool, file, &opts); 372 | req_headers = aos_table_make(pool, 0); 373 | aos_str_set(&bucket, file->bucket_name); 374 | aos_str_set(&key, file->object_key); 375 | aos_list_init(&buffer); 376 | content = aos_buf_pack(pool, buf, nbyte); 377 | aos_list_add_tail(&content->node, &buffer); 378 | 379 | if (strcmp("w", file->mode) == 0) { 380 | status = oss_put_object_from_buffer(opts, &bucket, &key, 381 | &buffer, req_headers, &resp_headers); 382 | 383 | oss_op_debug("oss_put_object_from_buffer", status, 384 | req_headers, resp_headers); 385 | 386 | if (!aos_status_is_ok(status)) { 387 | aos_error_log("put object failed. request_id:%s, code:%d, " 388 | "error_code:%s, error_message:%s, try_cnt:%d", 389 | status->req_id, status->code, status->error_code, 390 | status->error_msg, try_cnt); 391 | aos_pool_destroy(pool); 392 | return -1; 393 | } 394 | } else { 395 | status = oss_append_object_from_buffer(opts, &bucket, &key, 396 | file->_stat.length, &buffer, req_headers, &resp_headers); 397 | 398 | oss_op_debug("oss_append_object_from_buffer", status, 399 | req_headers, resp_headers); 400 | 401 | if (!aos_status_is_ok(status)) { 402 | int ret = -1; 403 | //if fail, always update length 404 | oss_media_file_stat_t stat; 405 | memset(&stat, 0, sizeof(stat)); 406 | if (oss_media_file_stat_internal(file, &stat, 0) == 0) { 407 | if (file->_stat.length + nbyte == stat.length) { 408 | ret = nbyte; 409 | } 410 | aos_error_log("append object failed, and reset file length. client length:%ld, server length:%ld, append size:%ld", 411 | file->_stat.length, stat.length, nbyte); 412 | file->_stat.length = stat.length; 413 | } 414 | 415 | if (ret < 0) { 416 | aos_error_log("append object failed. request_id:%s, code:%d, " 417 | "error_code:%s, error_message:%s, try_cnt:%d", 418 | status->req_id, status->code, status->error_code, 419 | status->error_msg, try_cnt); 420 | } else { 421 | aos_error_log("append object failed, but data has been appeded to file, ignore it. request_id:%s, code:%d, " 422 | "error_code:%s, error_message:%s, try_cnt:%d", 423 | status->req_id, status->code, status->error_code, 424 | status->error_msg, try_cnt); 425 | } 426 | 427 | aos_pool_destroy(pool); 428 | return ret; 429 | } 430 | file->_stat.length = oss_get_content_length(apr_table_get(resp_headers, 431 | "x-oss-next-append-position")); 432 | } 433 | 434 | aos_pool_destroy(pool); 435 | return nbyte; 436 | } 437 | 438 | int64_t oss_media_file_write(oss_media_file_t *file, const void *buf, int64_t nbyte) { 439 | int try_cnt = 1; 440 | int64_t ret = 0; 441 | 442 | do { 443 | if ((ret = oss_media_file_write_internal(file, buf, nbyte, try_cnt)) != -1) 444 | break; 445 | 446 | if (++try_cnt > oss_media_retry_cnt) 447 | break; 448 | usleep(oss_media_sleep_us); 449 | } while (try_cnt < MAX_RETRY_CNT); 450 | 451 | return ret; 452 | } 453 | 454 | // 00 00 00 01 65 ==> Coded slice of an IDR picture 455 | int oss_media_get_h264_idr_offsets(const void *buf, 456 | int nbyte, 457 | int idrs[], 458 | int nidrs, 459 | int *size) 460 | { 461 | if (size <= 0) { 462 | return 0; 463 | } 464 | 465 | int i = 0; 466 | int offset = 0; 467 | int stat = 0; 468 | *size = 0; 469 | 470 | while(offset < nbyte) { 471 | unsigned char *pos = (unsigned char *)(buf + (offset++)); 472 | switch (stat) { 473 | case 0: 474 | stat = (*pos == 0x00) ? 1 : 0; 475 | break; 476 | case 1: 477 | stat = (*pos == 0x00) ? 2 : 0; 478 | break; 479 | case 2: 480 | stat = (*pos == 0x00) ? 3 : 0; 481 | break; 482 | case 3: 483 | stat = (*pos == 0x01) ? 4 : 0; 484 | break; 485 | case 4: 486 | if (*pos == 0x65) { 487 | idrs[i++] = offset - stat; 488 | *size = i; 489 | // max idrs 490 | if (*size >= nidrs) { 491 | return offset - stat; 492 | } 493 | } 494 | stat = 0; 495 | break; 496 | default: 497 | stat = 0; 498 | } 499 | } 500 | return offset - stat; 501 | } 502 | -------------------------------------------------------------------------------- /src/oss_media_client.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_CLIENT_H 2 | #define OSS_MEDIA_CLIENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "oss_media_define.h" 10 | 11 | OSS_MEDIA_CPP_START 12 | 13 | /** 14 | * this struct describes the file stat of oss media file 15 | */ 16 | typedef struct { 17 | int64_t length; 18 | int64_t pos; 19 | char *type; 20 | } oss_media_file_stat_t; 21 | 22 | /** 23 | * this typedef define the auth_fn_t. 24 | */ 25 | struct oss_media_file_s; 26 | typedef void (*auth_fn_t)(struct oss_media_file_s *file); 27 | 28 | /** 29 | * this struct describes the properties of oss media file 30 | */ 31 | typedef struct oss_media_file_s { 32 | void *ipc; 33 | char *endpoint; 34 | int8_t is_cname; 35 | char *bucket_name; 36 | char *object_key; 37 | char *access_key_id; 38 | char *access_key_secret; 39 | char *token; 40 | char *mode; 41 | oss_media_file_stat_t _stat; 42 | 43 | time_t expiration; 44 | auth_fn_t auth_func; 45 | } oss_media_file_t; 46 | 47 | /** 48 | * @brief oss media init 49 | * @note this function should be called at first in application 50 | */ 51 | int oss_media_init(aos_log_level_e log_level); 52 | 53 | /** 54 | * @brief oss media destroy 55 | * @note this function should be called at last in application 56 | */ 57 | void oss_media_destroy(); 58 | 59 | /** 60 | * @brief oss media set retry configuration 61 | * @param[in] retry count, default is 1. 62 | * @param[in] sleep time before next try, the unit is us (1/1000000 s), default is 5000 us. 63 | * @note this function should be called if you want to change the default retry configuration 64 | */ 65 | void oss_media_set_retry_config(int retry, int sleep_us); 66 | 67 | /** 68 | * @brief open oss media file, this function opens the oss media file. 69 | * @param[in] bucket_name the bucket name for store file in oss 70 | * @param[in] object_key the object name for oss file 71 | * @param[in] mode: 72 | * 'r': file access mode of read. 73 | * 'w': file access mode of write. 74 | * 'a': file access mode of append. 75 | * notes: combined access mode is not allowd. 76 | * @param[in] auth_func the func to set access_key_id/access_key_secret 77 | * @return: 78 | * upon successful completion 0 is returned. 79 | * otherwise, -1 is returned and code/messaage in struct of file is set to indicate the error. 80 | */ 81 | oss_media_file_t* oss_media_file_open(char *bucket_name, 82 | char *object_key, 83 | char *mode, 84 | auth_fn_t auth_func); 85 | 86 | /** 87 | * @brief close oss media file 88 | */ 89 | void oss_media_file_close(oss_media_file_t *file); 90 | 91 | /** 92 | * @bref stat file, this function obtains information about the file. 93 | * @return: 94 | * upon successful completion 0 is returned. 95 | * otherwise -1 is returned and code/message int struct of file is set to indicate the error. 96 | */ 97 | int oss_media_file_stat(oss_media_file_t *file, oss_media_file_stat_t *stat); 98 | 99 | /** 100 | * @bref delete file, this function delete the oss media file. 101 | * @return: 102 | * upon successful completion 0 is returned. 103 | * otherwise -1 is returned and code/message int struct of file is set to indicate the error. 104 | */ 105 | int oss_media_file_delete(oss_media_file_t *file); 106 | 107 | 108 | /** 109 | * @brief tell the position of the oss media file 110 | * @return: 111 | * upon successful return the position of the oss media file. 112 | * otherwise -1 is returned and code/message int struct of file is set to indicate the error. 113 | */ 114 | int64_t oss_media_file_tell(oss_media_file_t *file); 115 | 116 | /** 117 | * @brief set the position of the oss media file 118 | * @return: 119 | * upon successful return the postition of the oss media file. 120 | * otherwise -1 is returned and code/message int struct of file is set to indicate the error. 121 | */ 122 | int64_t oss_media_file_seek(oss_media_file_t *file, int64_t offset); 123 | 124 | /** 125 | * @brief read from oss media file, this function reads number of bytes from the oss media file. 126 | * @note buf size should longer than nbyte + 1 127 | * @return: 128 | * upon successful return the number of bytes read. 129 | * otherwise -1 is returned and code/message int struct of file is set to indicate the error. 130 | */ 131 | int64_t oss_media_file_read(oss_media_file_t *file, void *buf, int64_t nbyte); 132 | 133 | /** 134 | * @brief write to oss media file, this function write number of bytes to oss media file. 135 | * @return: 136 | * upon successful return the number of bytes write. 137 | * otherwise -1 is returned and code/message int struct of file is set to indicate the error. 138 | */ 139 | int64_t oss_media_file_write(oss_media_file_t *file, const void *buf, int64_t nbyte); 140 | 141 | OSS_MEDIA_CPP_END 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /src/oss_media_define.c: -------------------------------------------------------------------------------- 1 | #include "oss_media_define.h" 2 | 3 | char OSS_MEDIA_CLIENT_USER_AGENT[] = "oss_media_client"; 4 | char OSS_MEDIA_SERVER_USER_AGENT[] = "oss_media_server"; 5 | 6 | int OSS_MEDIA_FILE_NOT_FOUND = 404; 7 | char OSS_MEDIA_FILE_UNKNOWN_TYPE[] = "unknown"; 8 | 9 | int OSS_MEDIA_FILE_NUM_PER_LIST = 1000; 10 | int OSS_MEDIA_INTERNAL_ERROR_CODE = 500; 11 | int OSS_MEDIA_OK = 200; 12 | int OSS_MEDIA_DEFAULT_WRITE_BUFFER = 256 * 1024; // 256K 13 | 14 | int OSS_MEDIA_DEFAULT_VIDEO_PID = 256; 15 | int OSS_MEDIA_DEFAULT_AUDIO_PID = 257; 16 | 17 | int OSS_MEDIA_MP3_SAMPLE_RATE = 1158; 18 | int OSS_MEDIA_AAC_SAMPLE_RATE = 1024; 19 | 20 | int OSS_MEDIA_PAT_INTERVAL_FRAME_COUNT = 19; 21 | 22 | char OSS_MEDIA_TS_FILE_SURFIX[] = ".ts"; 23 | char OSS_MEDIA_M3U8_FILE_SURFIX[] = ".m3u8"; 24 | -------------------------------------------------------------------------------- /src/oss_media_define.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_DEFINE_H 2 | #define OSS_MEDIA_DEFINE_H 3 | 4 | #ifdef __cplusplus 5 | # define OSS_MEDIA_CPP_START extern "C" { 6 | # define OSS_MEDIA_CPP_END } 7 | #else 8 | # define OSS_MEDIA_CPP_START 9 | # define OSS_MEDIA_CPP_END 10 | #endif 11 | 12 | extern char OSS_MEDIA_CLIENT_USER_AGENT[]; 13 | extern char OSS_MEDIA_SERVER_USER_AGENT[]; 14 | 15 | extern int OSS_MEDIA_FILE_NOT_FOUND; 16 | extern char OSS_MEDIA_FILE_UNKNOWN_TYPE[]; 17 | 18 | extern int OSS_MEDIA_FILE_NUM_PER_LIST; 19 | extern int OSS_MEDIA_INTERNAL_ERROR_CODE; 20 | extern int OSS_MEDIA_OK; 21 | extern int OSS_MEDIA_DEFAULT_WRITE_BUFFER; 22 | 23 | extern int OSS_MEDIA_DEFAULT_VIDEO_PID; 24 | extern int OSS_MEDIA_DEFAULT_AUDIO_PID; 25 | extern int OSS_MEDIA_MP3_SAMPLE_RATE; 26 | extern int OSS_MEDIA_AAC_SAMPLE_RATE; 27 | 28 | extern int OSS_MEDIA_PAT_INTERVAL_FRAME_COUNT; 29 | 30 | extern char OSS_MEDIA_TS_FILE_SURFIX[]; 31 | extern char OSS_MEDIA_M3U8_FILE_SURFIX[]; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/oss_media_hls.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_HLS_H 2 | #define OSS_MEDIA_HLS_H 3 | 4 | #include 5 | #include "oss_media_client.h" 6 | 7 | /* packet size: 188 bytes */ 8 | #define OSS_MEDIA_HLS_PACKET_SIZE 188 9 | /* encrypt key size: 32 bytes */ 10 | #define OSS_MEDIA_HLS_ENCRYPT_KEY_SIZE 32 11 | /* encrypt packet length */ 12 | #define OSS_MEDIA_HLS_ENCRYPT_PACKET_LENGTH 16 13 | 14 | #define OSS_MEDIA_M3U8_URL_LENGTH 256 15 | 16 | /** 17 | * this enum describes the stream type. 18 | */ 19 | typedef enum { 20 | st_h264, 21 | st_aac, 22 | st_mp3 23 | } stream_type_t; 24 | 25 | /** 26 | * this enum describes the frame type. 27 | */ 28 | typedef enum { 29 | ft_unspecified = 0, 30 | ft_non_idr = 1, 31 | ft_dpa = 2, 32 | ft_idr = 5, 33 | ft_sei = 6, 34 | ft_sps = 7, 35 | ft_pps = 8, 36 | ft_aud = 9, 37 | } frame_type_t; 38 | 39 | /** 40 | * this struct describes the hls frame infomation. 41 | */ 42 | typedef struct oss_media_hls_frame_s { 43 | stream_type_t stream_type; 44 | frame_type_t frame_type; 45 | uint64_t pts; 46 | uint64_t dts; 47 | uint32_t continuity_counter; 48 | uint8_t key:1; 49 | uint8_t *pos; 50 | uint8_t *end; 51 | } oss_media_hls_frame_t; 52 | 53 | /** 54 | * this typedef define the file_handler_fn_t. 55 | */ 56 | struct oss_media_hls_file_s; 57 | typedef int (*file_handler_fn_t) (struct oss_media_hls_file_s *file); 58 | 59 | /** 60 | * this struct describes the m3u8 infomation. 61 | */ 62 | typedef struct oss_media_hls_m3u8_info_s { 63 | float duration; 64 | char url[OSS_MEDIA_M3U8_URL_LENGTH]; 65 | } oss_media_hls_m3u8_info_t; 66 | 67 | /** 68 | * this struct describes the hls options. 69 | */ 70 | typedef struct oss_media_hls_options_s { 71 | uint16_t video_pid; 72 | uint16_t audio_pid; 73 | uint32_t hls_delay_ms; 74 | uint8_t encrypt:1; 75 | char key[OSS_MEDIA_HLS_ENCRYPT_KEY_SIZE]; 76 | file_handler_fn_t handler_func; 77 | uint16_t pat_interval_frame_count; 78 | } oss_media_hls_options_t; 79 | 80 | /** 81 | * this struct describes the memory buffer. 82 | */ 83 | typedef struct oss_media_hls_buf_s { 84 | uint8_t *buf; 85 | unsigned int pos; 86 | unsigned int start; 87 | unsigned int end; 88 | } oss_media_hls_buf_t; 89 | 90 | /** 91 | * this struct describes the hls file. 92 | */ 93 | typedef struct oss_media_hls_file_s { 94 | oss_media_file_t *file; 95 | oss_media_hls_buf_t *buffer; 96 | oss_media_hls_options_t options; 97 | int64_t frame_count; 98 | } oss_media_hls_file_t; 99 | 100 | /** 101 | * open hls file. 102 | * 103 | * return values: 104 | * On success, a pointer to the oss_media_hls_file_t 105 | * otherwise, a null pointer is returned 106 | */ 107 | oss_media_hls_file_t* oss_media_hls_open(char *bucket_name, 108 | char *object_key, 109 | auth_fn_t auth_func); 110 | 111 | /** 112 | * write hls frame. 113 | * 114 | * return values: 115 | * upon successful completion 0 is returned 116 | * otherwise -1 is returned 117 | */ 118 | int oss_media_hls_write_frame(oss_media_hls_frame_t *frame, 119 | oss_media_hls_file_t *file); 120 | 121 | /** 122 | * write m3u8 head infomation. 123 | * 124 | * return values: 125 | * upon successful completion 0 is returned 126 | * otherwise -1 is returned 127 | */ 128 | void oss_media_hls_begin_m3u8(int32_t max_duration, 129 | int32_t sequence, 130 | oss_media_hls_file_t *file); 131 | 132 | /** 133 | * write m3u8 infomation. 134 | * 135 | * return values: 136 | * upon successful completion 0 is returned 137 | * otherwise -1 is returned 138 | */ 139 | int oss_media_hls_write_m3u8(int size, 140 | oss_media_hls_m3u8_info_t m3u8[], 141 | oss_media_hls_file_t *file); 142 | 143 | /** 144 | * write m3u8 end infomation for vod 145 | * 146 | * return values: 147 | * upon successful completion 0 is returned 148 | * otherwise -1 is returned 149 | */ 150 | void oss_media_hls_end_m3u8(oss_media_hls_file_t *file); 151 | 152 | /** 153 | * flush hls data to oss. 154 | * 155 | * return values: 156 | * upon successful completion 0 is returned 157 | * otherwise -1 is returned 158 | */ 159 | int oss_media_hls_flush(oss_media_hls_file_t *file); 160 | 161 | /** 162 | * close hls file. 163 | * 164 | * return values: 165 | * upon successful completion 0 is returned 166 | * otherwise -1 is returned 167 | */ 168 | int oss_media_hls_close(oss_media_hls_file_t *file); 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /src/oss_media_hls_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_HLS_STREAM_H 2 | #define OSS_MEDIA_HLS_STREAM_H 3 | 4 | #include "oss_media_hls.h" 5 | 6 | /** 7 | * this struct describes the properties of hls stream options 8 | */ 9 | typedef struct oss_media_hls_stream_options_s { 10 | int8_t is_live; 11 | char *bucket_name; 12 | char *ts_name_prefix; 13 | char *m3u8_name; 14 | int32_t video_frame_rate; 15 | int32_t audio_sample_rate; 16 | int32_t hls_time; 17 | int32_t hls_list_size; 18 | } oss_media_hls_stream_options_t; 19 | 20 | /** 21 | * this struct describes the properties of hls stream 22 | */ 23 | typedef struct oss_media_hls_stream_s { 24 | oss_media_hls_stream_options_t *options; 25 | oss_media_hls_file_t *ts_file; 26 | oss_media_hls_file_t *m3u8_file; 27 | oss_media_hls_frame_t *video_frame; 28 | oss_media_hls_frame_t *audio_frame; 29 | oss_media_hls_m3u8_info_t *m3u8_infos; 30 | int64_t ts_file_index; 31 | int64_t current_file_begin_pts; 32 | aos_pool_t *pool; 33 | } oss_media_hls_stream_t; 34 | 35 | /** 36 | * @brief open oss media hls stream, this function opens the oss hls stream. 37 | * @param[in] auth_func the func to set access_key_id/access_key_secret 38 | * @param[in] options the option of describes oss hls stream 39 | * @return: 40 | * On success, a pointer to the oss_media_hls_stream_t 41 | * otherwise, a null pointer is returned 42 | */ 43 | oss_media_hls_stream_t* oss_media_hls_stream_open(auth_fn_t auth_func, 44 | const oss_media_hls_stream_options_t *options); 45 | 46 | /** 47 | * @brief write h.264 and aac data 48 | * @param[in] video_buf the data of h.264 video 49 | * @param[in] video_len the dta lenght of h.264 video 50 | * @param[in] audio_buf the data of aac video 51 | * @param[in] audio_len the dta lenght of aac video 52 | * @param[in] stream the hls stream for store h.264 and aac data 53 | * @return: 54 | * upon successful completion 0 is returned. 55 | * otherwise, -1 is returned and code/messaage in struct of file is set to indicate the error. 56 | */ 57 | int oss_media_hls_stream_write(uint8_t *video_buf, 58 | uint64_t video_len, 59 | uint8_t *audio_buf, 60 | uint64_t audio_len, 61 | oss_media_hls_stream_t *stream); 62 | 63 | /** 64 | * @brief close oss media hls stream 65 | * @return: 66 | * upon successful completion 0 is returned. 67 | * otherwise, -1 is returned and code/messaage in struct of file is set to indicate the error. 68 | */ 69 | int oss_media_hls_stream_close(oss_media_hls_stream_t *stream); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/oss_media_log.c: -------------------------------------------------------------------------------- 1 | #include "oss_c_sdk/aos_status.h" 2 | #include "oss_c_sdk/aos_log.h" 3 | 4 | void oss_op_debug(char *op, 5 | aos_status_t *status, 6 | aos_table_t *req_headers, 7 | aos_table_t *resp_headers) 8 | { 9 | if(aos_log_level >= AOS_LOG_DEBUG) { 10 | int pos; 11 | aos_array_header_t *tarr; 12 | aos_table_entry_t *telts; 13 | 14 | if (NULL != op && NULL != status) { 15 | aos_debug_log("req_id:%s, status:[code=%d, err_code=%s, err_msg=%s]\n", 16 | status->req_id, status->code, 17 | status->error_code, status->error_msg); 18 | } 19 | 20 | if (NULL != req_headers) { 21 | aos_debug_log("request headers:\n"); 22 | tarr = (aos_array_header_t *)aos_table_elts(req_headers); 23 | telts = (aos_table_entry_t*) tarr->elts; 24 | for (pos = 0; pos < tarr->nelts; pos++) { 25 | aos_debug_log(" %s=%s\n", telts[pos].key, telts[pos].val); 26 | } 27 | } 28 | 29 | if (NULL != resp_headers) { 30 | aos_debug_log("response headers:\n"); 31 | tarr = (aos_array_header_t *)aos_table_elts(resp_headers); 32 | telts = (aos_table_entry_t*) tarr->elts; 33 | for (pos = 0; pos < tarr->nelts; pos++) { 34 | aos_debug_log(" %s=%s\n", telts[pos].key, telts[pos].val); 35 | } 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/oss_media_server.c: -------------------------------------------------------------------------------- 1 | #include "oss_media_server.h" 2 | #include "sts/libsts.h" 3 | 4 | extern void oss_op_debug(char *op, 5 | aos_status_t *status, 6 | aos_table_t *req_headers, 7 | aos_table_t *resp_headers); 8 | 9 | static void oss_media_init_request_opts(aos_pool_t *pool, 10 | const oss_media_config_t *config, 11 | oss_request_options_t **options) 12 | { 13 | oss_request_options_t *opts = NULL; 14 | 15 | opts = oss_request_options_create(pool); 16 | opts->config = oss_config_create(pool); 17 | aos_str_set(&opts->config->endpoint, config->endpoint); 18 | aos_str_set(&opts->config->access_key_id, config->access_key_id); 19 | aos_str_set(&opts->config->access_key_secret, config->access_key_secret); 20 | opts->config->is_cname = config->is_cname; 21 | opts->ctl = aos_http_controller_create(pool, 0); 22 | 23 | *options = opts; 24 | } 25 | 26 | int oss_media_init(aos_log_level_e log_level) 27 | { 28 | aos_log_set_level(log_level); 29 | aos_log_set_output(NULL); 30 | return aos_http_io_initialize(OSS_MEDIA_SERVER_USER_AGENT, 0); 31 | } 32 | 33 | void oss_media_destroy() 34 | { 35 | aos_http_io_deinitialize(); 36 | } 37 | 38 | oss_media_files_t* oss_media_create_files() 39 | { 40 | return (oss_media_files_t *) calloc(sizeof(oss_media_files_t), 1); 41 | } 42 | 43 | void oss_media_free_files(oss_media_files_t *files) 44 | { 45 | int i; 46 | 47 | if (!files) 48 | return; 49 | 50 | if (files->file_names) { 51 | for (i = 0 ; i < files->size; i++) { 52 | if (files->file_names[i]) { 53 | free(files->file_names[i]); 54 | files->file_names[i] = NULL; 55 | } 56 | } 57 | free(files->file_names); 58 | files->file_names = NULL; 59 | } 60 | free(files); 61 | files = NULL; 62 | } 63 | 64 | oss_media_lifecycle_rules_t *oss_media_create_lifecycle_rules(int size) 65 | { 66 | aos_pool_t *pool = NULL; 67 | oss_media_lifecycle_rules_t *rules = NULL; 68 | 69 | aos_pool_create(&pool, NULL); 70 | 71 | rules = (oss_media_lifecycle_rules_t *) aos_pcalloc( 72 | pool, sizeof(oss_media_lifecycle_rules_t)); 73 | 74 | rules->size = size > 0 ? size : 0; 75 | rules->rules = size > 0 ? aos_pcalloc(pool, 76 | sizeof(oss_media_lifecycle_rule_t) * rules->size) : NULL; 77 | 78 | rules->_pool = pool; 79 | return rules; 80 | } 81 | 82 | void oss_media_free_lifecycle_rules(oss_media_lifecycle_rules_t *rules) 83 | { 84 | aos_pool_destroy(rules->_pool); 85 | } 86 | 87 | int oss_media_create_bucket(const oss_media_config_t *config, 88 | const char *bucket_name, 89 | oss_media_acl_t acl) 90 | { 91 | aos_pool_t *pool = NULL; 92 | oss_request_options_t *opts = NULL; 93 | aos_string_t bucket; 94 | aos_status_t *status = NULL; 95 | aos_table_t *resp_headers = NULL; 96 | 97 | aos_pool_create(&pool, NULL); 98 | oss_media_init_request_opts(pool, config, &opts); 99 | aos_str_set(&bucket, bucket_name); 100 | 101 | status = oss_create_bucket(opts, &bucket, acl, &resp_headers); 102 | 103 | if (!aos_status_is_ok(status)) { 104 | aos_error_log("create bucket failed. request_id:%s, code:%d, " 105 | "error_code:%s, error_message:%s", 106 | status->req_id, status->code, status->error_code, 107 | status->error_msg); 108 | aos_pool_destroy(pool); 109 | return -1; 110 | } 111 | 112 | aos_pool_destroy(pool); 113 | return 0; 114 | } 115 | 116 | int oss_media_delete_bucket(const oss_media_config_t *config, 117 | const char *bucket_name) 118 | { 119 | aos_pool_t *pool = NULL; 120 | oss_request_options_t *options = NULL; 121 | aos_string_t bucket; 122 | aos_status_t *status = NULL; 123 | aos_table_t *resp_headers = NULL; 124 | 125 | aos_pool_create(&pool, NULL); 126 | options = oss_request_options_create(pool); 127 | oss_media_init_request_opts(pool, config, &options); 128 | aos_str_set(&bucket, bucket_name); 129 | 130 | status = oss_delete_bucket(options, &bucket, &resp_headers); 131 | 132 | if (!aos_status_is_ok(status)) { 133 | aos_error_log("delete bucket failed. request_id:%s, code:%d, " 134 | "error_code:%s, error_message:%s", 135 | status->req_id, status->code, status->error_code, 136 | status->error_msg); 137 | aos_pool_destroy(pool); 138 | return -1; 139 | } 140 | 141 | aos_pool_destroy(pool); 142 | return 0; 143 | } 144 | 145 | int oss_media_create_bucket_lifecycle(const oss_media_config_t *config, 146 | const char *bucket_name, 147 | oss_media_lifecycle_rules_t *rules) 148 | { 149 | aos_pool_t *pool = NULL; 150 | oss_request_options_t *opts = NULL; 151 | aos_string_t bucket; 152 | aos_status_t *status = NULL; 153 | aos_table_t *resp_headers = NULL; 154 | aos_list_t rule_list; 155 | oss_lifecycle_rule_content_t *rule_content = NULL; 156 | int i; 157 | 158 | aos_pool_create(&pool, NULL); 159 | opts = oss_request_options_create(pool); 160 | oss_media_init_request_opts(pool, config, &opts); 161 | aos_str_set(&bucket, bucket_name); 162 | 163 | aos_list_init(&rule_list); 164 | for (i = 0; i < rules->size; i++) { 165 | rule_content = oss_create_lifecycle_rule_content(pool); 166 | aos_str_set(&rule_content->id, rules->rules[i]->name); 167 | aos_str_set(&rule_content->prefix, rules->rules[i]->path); 168 | aos_str_set(&rule_content->status, rules->rules[i]->status); 169 | rule_content->days = rules->rules[i]->days; 170 | 171 | aos_list_add_tail(&rule_content->node, &rule_list); 172 | } 173 | 174 | status = oss_put_bucket_lifecycle(opts, &bucket, &rule_list, &resp_headers); 175 | 176 | oss_op_debug("oss_put_bucket_lifecycle", status, NULL, resp_headers); 177 | 178 | if (!aos_status_is_ok(status)) { 179 | aos_error_log("create bucket lifecycle failed. request_id:%s, code:%d, " 180 | "error_code:%s, error_message:%s", 181 | status->req_id, status->code, status->error_code, 182 | status->error_msg); 183 | aos_pool_destroy(pool); 184 | return -1; 185 | } 186 | 187 | aos_pool_destroy(pool); 188 | return 0; 189 | } 190 | 191 | int oss_media_get_bucket_lifecycle(const oss_media_config_t *config, 192 | const char *bucket_name, 193 | oss_media_lifecycle_rules_t *rules) 194 | { 195 | aos_pool_t *pool = NULL; 196 | oss_request_options_t *opts = NULL; 197 | aos_string_t bucket; 198 | aos_status_t *status = NULL; 199 | aos_table_t *resp_headers = NULL; 200 | aos_list_t rule_list; 201 | oss_lifecycle_rule_content_t *rule_content = NULL; 202 | int i; 203 | 204 | aos_pool_create(&pool, NULL); 205 | opts = oss_request_options_create(pool); 206 | oss_media_init_request_opts(pool, config, &opts); 207 | aos_str_set(&bucket, bucket_name); 208 | aos_list_init(&rule_list); 209 | 210 | status = oss_get_bucket_lifecycle(opts, &bucket, 211 | &rule_list, &resp_headers); 212 | 213 | oss_op_debug("oss_get_bucket_lifecycle", status, NULL, resp_headers); 214 | 215 | if (!aos_status_is_ok(status)) { 216 | aos_error_log("get bucket lifecycle failed. request_id:%s, code:%d, " 217 | "error_code:%s, error_message:%s", 218 | status->req_id, status->code, status->error_code, 219 | status->error_msg); 220 | aos_pool_destroy(pool); 221 | return -1; 222 | } 223 | 224 | i = 0; 225 | aos_list_for_each_entry(oss_lifecycle_rule_content_t, rule_content, &rule_list, node) { 226 | i++; 227 | } 228 | rules->size = i; 229 | rules->rules = aos_pcalloc(rules->_pool, 230 | sizeof(oss_media_lifecycle_rule_t) * rules->size); 231 | 232 | i = 0; 233 | aos_list_for_each_entry(oss_lifecycle_rule_content_t, rule_content, &rule_list, node) { 234 | oss_media_lifecycle_rule_t *rule = 235 | aos_pcalloc(rules->_pool, sizeof(oss_media_lifecycle_rule_t)); 236 | rule->name = aos_pstrdup(rules->_pool, &rule_content->id); 237 | rule->path = aos_pstrdup(rules->_pool, &rule_content->prefix); 238 | rule->status = aos_pstrdup(rules->_pool, &rule_content->status); 239 | rule->days = rule_content->days; 240 | 241 | rules->rules[i++] = rule; 242 | } 243 | 244 | aos_pool_destroy(pool); 245 | return 0; 246 | } 247 | 248 | int oss_media_delete_bucket_lifecycle(const oss_media_config_t *config, 249 | const char *bucket_name) 250 | { 251 | aos_pool_t *pool = NULL; 252 | oss_request_options_t *opts = NULL; 253 | aos_string_t bucket; 254 | aos_status_t *status = NULL; 255 | aos_table_t *resp_headers = NULL; 256 | 257 | aos_pool_create(&pool, NULL); 258 | opts = oss_request_options_create(pool); 259 | oss_media_init_request_opts(pool, config, &opts); 260 | aos_str_set(&bucket, bucket_name); 261 | 262 | status = oss_delete_bucket_lifecycle(opts, &bucket, &resp_headers); 263 | 264 | oss_op_debug("oss_delete_bucket_lifecycle", status, NULL, resp_headers); 265 | 266 | if (!aos_status_is_ok(status)) { 267 | aos_error_log("delete bucket lifecycle failed. request_id:%s, code:%d, " 268 | "error_code:%s, error_message:%s", 269 | status->req_id, status->code, status->error_code, 270 | status->error_msg); 271 | aos_pool_destroy(pool); 272 | return -1; 273 | } 274 | 275 | aos_pool_destroy(pool); 276 | return 0; 277 | } 278 | 279 | int oss_media_delete_file(const oss_media_config_t *config, 280 | const char *bucket_name, 281 | const char *key) 282 | { 283 | aos_pool_t *pool = NULL; 284 | oss_request_options_t *opts = NULL; 285 | aos_string_t bucket; 286 | aos_string_t object; 287 | aos_status_t *status = NULL; 288 | aos_table_t *resp_headers = NULL; 289 | 290 | aos_pool_create(&pool, NULL); 291 | opts = oss_request_options_create(pool); 292 | oss_media_init_request_opts(pool, config, &opts); 293 | aos_str_set(&bucket, bucket_name); 294 | aos_str_set(&object, key); 295 | 296 | status = oss_delete_object(opts, &bucket, &object, &resp_headers); 297 | 298 | if (!aos_status_is_ok(status)) { 299 | aos_error_log("delete file failed. request_id:%s, code:%d, " 300 | "error_code:%s, error_message:%s", 301 | status->req_id, status->code, status->error_code, 302 | status->error_msg); 303 | aos_pool_destroy(pool); 304 | return -1; 305 | } 306 | 307 | aos_pool_destroy(pool); 308 | return 0; 309 | } 310 | 311 | int oss_media_list_files(const oss_media_config_t *config, 312 | const char *bucket_name, 313 | oss_media_files_t *files) 314 | { 315 | aos_pool_t *pool = NULL; 316 | oss_request_options_t *opts = NULL; 317 | aos_string_t bucket; 318 | aos_status_t *status = NULL; 319 | oss_list_object_params_t *params = NULL; 320 | aos_table_t *resp_headers = NULL; 321 | int i; 322 | aos_list_t *head = NULL; 323 | oss_list_object_content_t *content = NULL; 324 | 325 | aos_pool_create(&pool, NULL); 326 | opts = oss_request_options_create(pool); 327 | oss_media_init_request_opts(pool, config, &opts); 328 | aos_str_set(&bucket, bucket_name); 329 | 330 | params = oss_create_list_object_params(pool); 331 | params->max_ret = files->max_size > 0 && 332 | files->max_size <= OSS_MEDIA_FILE_NUM_PER_LIST ? 333 | files->max_size : OSS_MEDIA_FILE_NUM_PER_LIST; 334 | if (files->path) 335 | aos_str_set(¶ms->prefix, files->path); 336 | if (files->marker) 337 | aos_str_set(¶ms->marker, files->marker); 338 | 339 | status = oss_list_object(opts, &bucket, params, &resp_headers); 340 | 341 | if (!aos_status_is_ok(status)) { 342 | aos_error_log("list files failed. request_id:%s, code:%d, " 343 | "error_code:%s, error_message:%s", 344 | status->req_id, status->code, status->error_code, 345 | status->error_msg); 346 | aos_pool_destroy(pool); 347 | return -1; 348 | } 349 | 350 | head = ¶ms->object_list; 351 | aos_list_for_each_entry(oss_list_object_content_t, content, head, node) { 352 | files->size++; 353 | } 354 | files->file_names = calloc(sizeof(char *), files->size); 355 | 356 | i = 0; 357 | aos_list_for_each_entry(oss_list_object_content_t, content, head, node) { 358 | files->file_names[i++] = strdup(content->key.data); 359 | } 360 | 361 | aos_pool_destroy(pool); 362 | return 0; 363 | } 364 | 365 | int oss_media_get_token(const oss_media_config_t *config, 366 | const char *bucket_name, 367 | const char *path, 368 | const char *mode, 369 | int64_t expiration, 370 | oss_media_token_t *token) 371 | { 372 | char policy[1024]; 373 | char *policy_template = NULL; 374 | char *h = NULL; 375 | char *r = NULL; 376 | char *w = NULL; 377 | char *a = NULL; 378 | 379 | policy_template = "{\"Version\":\"1\",\"Statement\":[{\"Effect\":\"Allow\"," 380 | " \"Action\":[\"%s\", \"%s\", \"%s\", \"%s\"], " 381 | "\"Resource\":\"acs:oss:*:*:%s%s\"}]}"; 382 | 383 | h = "oss:HeadObject"; 384 | r = (strchr(mode, 'r') != NULL) ? "oss:GetObject" : ""; 385 | w = (strchr(mode, 'w') != NULL) ? "oss:PutObject" : ""; 386 | a = (strchr(mode, 'a') != NULL) ? "oss:PutObject" : ""; 387 | 388 | sprintf(policy, policy_template, h, r, w, a, bucket_name, path); 389 | 390 | return oss_media_get_token_from_policy(config, 391 | policy, expiration, token); 392 | } 393 | 394 | int oss_media_get_token_from_policy(const oss_media_config_t *config, 395 | const char *policy, 396 | int64_t expiration, 397 | oss_media_token_t *token) 398 | { 399 | char errorMessage[1024]; 400 | int sts_status; 401 | 402 | sts_status = STS_assume_role(config->role_arn, OSS_MEDIA_SERVER_USER_AGENT, 403 | policy, expiration, config->access_key_id, 404 | config->access_key_secret, token, errorMessage); 405 | 406 | if (STSStatusOK != sts_status) { 407 | aos_error_log("get token failed. [role_arn:%s, policy:%s, status:%d, " 408 | "error message:%s]", config->role_arn, policy, sts_status, 409 | errorMessage); 410 | return -1; 411 | } 412 | 413 | return 0; 414 | } 415 | -------------------------------------------------------------------------------- /src/oss_media_server.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_SERVER_H 2 | #define OSS_MEDIA_SERVER_H 3 | 4 | #include "oss_c_sdk/aos_log.h" 5 | #include "oss_c_sdk/aos_status.h" 6 | #include "oss_c_sdk/aos_http_io.h" 7 | #include "oss_c_sdk/oss_define.h" 8 | #include "oss_c_sdk/oss_api.h" 9 | #include "oss_media_define.h" 10 | 11 | OSS_MEDIA_CPP_START 12 | 13 | /** 14 | * this struct describes the oss media configuration. 15 | */ 16 | typedef struct oss_media_config_s { 17 | char *endpoint; 18 | int8_t is_cname; 19 | char *access_key_id; 20 | char *access_key_secret; 21 | char *role_arn; 22 | } oss_media_config_t; 23 | 24 | /** 25 | * oss bucket acl 26 | * acl: 27 | * OSS_ACL_PRIVATE: private write + private read 28 | * OSS_ACL_PUBLIC_READ: private write + public read 29 | * OSS_ACL_PUBLIC_READ_WRITE: public write + public read 30 | */ 31 | typedef oss_acl_e oss_media_acl_t; 32 | 33 | /** 34 | * @brief oss bucket lifecycle rule. 35 | * oss file will be deleted automatic in specified days. 36 | */ 37 | typedef struct oss_media_lifecycle_rule_s { 38 | char *name; // rule name 39 | char *path; // oss media path 40 | char *status; // Enabled or Disabled 41 | int days; 42 | } oss_media_lifecycle_rule_t; 43 | 44 | /** 45 | * @brief oss bucket lifecycle rule sets. 46 | * this struct describes a collection of oss bucket lifecycle rules. 47 | */ 48 | typedef struct oss_media_lifecycle_rules_s { 49 | int size; // rule array size 50 | oss_media_lifecycle_rule_t **rules; // rule array 51 | aos_pool_t *_pool; 52 | } oss_media_lifecycle_rules_t; 53 | 54 | 55 | /** 56 | * @bried oss media files 57 | * this struct describes a collection of oss media files. 58 | */ 59 | typedef struct oss_media_files_s { 60 | char *path; // file path 61 | char *marker; // paging identifier 62 | int max_size; // max items per page 63 | 64 | char *next_marker; // paging identifier 65 | int size; // iterms per page 66 | char **file_names; // file name array 67 | } oss_media_files_t; 68 | 69 | /** 70 | * @brief oss media token 71 | * this struct describes the sts security token for accessing oss media file. 72 | */ 73 | typedef struct STSData oss_media_token_t; 74 | 75 | /** 76 | * @brief oss media init 77 | * @note this function should be called at first in application 78 | */ 79 | int oss_media_init(aos_log_level_e log_level); 80 | 81 | /* 82 | * @brief oss media destroy 83 | * @note this function should be called at last in application 84 | */ 85 | void oss_media_destroy(); 86 | 87 | /** 88 | * @brief create the data struct of oss_media_files_t 89 | */ 90 | oss_media_files_t* oss_media_create_files(); 91 | 92 | /** 93 | * @brief free the data struct of oss_media_files_t 94 | */ 95 | void oss_media_free_files(oss_media_files_t *files); 96 | 97 | /** 98 | * @brief create the data struct of oss_media_lifecycle_rules_t 99 | */ 100 | oss_media_lifecycle_rules_t* oss_media_create_lifecycle_rules(int size); 101 | 102 | /** 103 | * @brief free the data struct of oss_media_lifecycle_rules_t 104 | */ 105 | void oss_media_free_lifecycle_rules(oss_media_lifecycle_rules_t *rules); 106 | 107 | 108 | /** 109 | * @brief create oss bucket. this function creates the oss bucket. 110 | * @return: 111 | * upon sucessful 0 is returned. 112 | * otherwise, -1 is returned and code/message in data status is set to indicate the error. 113 | */ 114 | int oss_media_create_bucket(const oss_media_config_t *config, 115 | const char *bucket_name, 116 | oss_media_acl_t acl); 117 | 118 | /* 119 | * @brief delete oss bucket. this function deletes the oss bucket. 120 | * @return: 121 | * upon successful 0 is returned. 122 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 123 | */ 124 | int oss_media_delete_bucket(const oss_media_config_t *config, 125 | const char *bucket_name); 126 | 127 | /** 128 | * @brief create bucket lifecycle. this function creates the bucket lifecycle. 129 | * @note under the specified rule, the oss file will be deleted in specified days. 130 | * @return: 131 | * upon successful 0 is returned. 132 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 133 | */ 134 | int oss_media_create_bucket_lifecycle(const oss_media_config_t *config, 135 | const char *bucket_name, 136 | oss_media_lifecycle_rules_t *rules); 137 | 138 | /** 139 | * @brief get bucket lifecycle. this function obtains the bucket lifecycle. 140 | * @return: 141 | * upon successful 0 is returned. 142 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 143 | */ 144 | int oss_media_get_bucket_lifecycle(const oss_media_config_t *config, 145 | const char *bucket_name, 146 | oss_media_lifecycle_rules_t *rules); 147 | 148 | /** 149 | * @brief delete bucket lifecycle. this function delete all lifecycles for the bucket. 150 | * @return: 151 | * upon successful 0 is returned. 152 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 153 | */ 154 | int oss_media_delete_bucket_lifecycle(const oss_media_config_t *config, 155 | const char *bucket_name); 156 | 157 | /** 158 | * @brief delete oss file. this function delete the oss file. 159 | * @return: 160 | * upon successful 0 is returned. 161 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 162 | */ 163 | int oss_media_delete_file(const oss_media_config_t *config, 164 | const char *bucket_name, 165 | const char *object_key); 166 | 167 | /** 168 | * @brief list oss files. this function obtains the oss files. 169 | * @return: 170 | * upon successful 0 is returned. 171 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 172 | */ 173 | int oss_media_list_files(const oss_media_config_t *config, 174 | const char *bucket_name, 175 | oss_media_files_t *files); 176 | 177 | /* 178 | * @brief get sts token. this function obtains the access token. 179 | * @param[in]: 180 | mode: 181 | * 'r': file access mode of read. 182 | * 'w': file access mode of write. 183 | * 'a': file access mode of append. 184 | * expiration: 185 | * access token expiration, in seconds, max value is 3600 (1 hour) 186 | * @return: 187 | * upon successful 0 is returned. 188 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 189 | */ 190 | int oss_media_get_token(const oss_media_config_t *config, 191 | const char *bucket_name, 192 | const char *path, 193 | const char *mode, 194 | int64_t expiration, 195 | oss_media_token_t *token); 196 | 197 | /** 198 | * @brief get token from policy. this function obtains the access token, with sts policy. 199 | * @param[in]: 200 | * expiration: access token expiration, in seconds, max value is 3600 (1 hour) 201 | * @return: 202 | * upon successful 0 is returned. 203 | * otherwise, -1 is returned and code/message in struct status is set to indicate the error. 204 | */ 205 | int oss_media_get_token_from_policy(const oss_media_config_t *config, 206 | const char *policy, 207 | int64_t expiration, 208 | oss_media_token_t *token); 209 | 210 | OSS_MEDIA_CPP_END 211 | #endif 212 | -------------------------------------------------------------------------------- /src/sts/general.c: -------------------------------------------------------------------------------- 1 | #include "libsts.h" 2 | 3 | 4 | STSStatus STS_initialize(const char *userAgentInfo, int flags, 5 | const char *defaultSTSHostName) 6 | { 7 | return STSStatusOK; 8 | } 9 | 10 | void STS_deinitialize() 11 | { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/sts/jsmn.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "jsmn.h" 4 | 5 | /** 6 | * Allocates a fresh unused token from the token pull. 7 | */ 8 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 9 | jsmntok_t *tokens, size_t num_tokens) { 10 | jsmntok_t *tok; 11 | if (parser->toknext >= num_tokens) { 12 | return NULL; 13 | } 14 | tok = &tokens[parser->toknext++]; 15 | tok->start = tok->end = -1; 16 | tok->size = 0; 17 | #ifdef JSMN_PARENT_LINKS 18 | tok->parent = -1; 19 | #endif 20 | return tok; 21 | } 22 | 23 | /** 24 | * Fills token type and boundaries. 25 | */ 26 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 27 | int start, int end) { 28 | token->type = type; 29 | token->start = start; 30 | token->end = end; 31 | token->size = 0; 32 | } 33 | 34 | /** 35 | * Fills next available token with JSON primitive. 36 | */ 37 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, 38 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 39 | jsmntok_t *token; 40 | int start; 41 | 42 | start = parser->pos; 43 | 44 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 45 | switch (js[parser->pos]) { 46 | #ifndef JSMN_STRICT 47 | /* In strict mode primitive must be followed by "," or "}" or "]" */ 48 | case ':': 49 | #endif 50 | case '\t' : case '\r' : case '\n' : case ' ' : 51 | case ',' : case ']' : case '}' : 52 | goto found; 53 | } 54 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 55 | parser->pos = start; 56 | return JSMN_ERROR_INVAL; 57 | } 58 | } 59 | #ifdef JSMN_STRICT 60 | /* In strict mode primitive must be followed by a comma/object/array */ 61 | parser->pos = start; 62 | return JSMN_ERROR_PART; 63 | #endif 64 | 65 | found: 66 | if (tokens == NULL) { 67 | parser->pos--; 68 | return 0; 69 | } 70 | token = jsmn_alloc_token(parser, tokens, num_tokens); 71 | if (token == NULL) { 72 | parser->pos = start; 73 | return JSMN_ERROR_NOMEM; 74 | } 75 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 76 | #ifdef JSMN_PARENT_LINKS 77 | token->parent = parser->toksuper; 78 | #endif 79 | parser->pos--; 80 | return 0; 81 | } 82 | 83 | /** 84 | * Filsl next token with JSON string. 85 | */ 86 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 87 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 88 | jsmntok_t *token; 89 | 90 | int start = parser->pos; 91 | 92 | parser->pos++; 93 | 94 | /* Skip starting quote */ 95 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 96 | char c = js[parser->pos]; 97 | 98 | /* Quote: end of string */ 99 | if (c == '\"') { 100 | if (tokens == NULL) { 101 | return 0; 102 | } 103 | token = jsmn_alloc_token(parser, tokens, num_tokens); 104 | if (token == NULL) { 105 | parser->pos = start; 106 | return JSMN_ERROR_NOMEM; 107 | } 108 | jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); 109 | #ifdef JSMN_PARENT_LINKS 110 | token->parent = parser->toksuper; 111 | #endif 112 | return 0; 113 | } 114 | 115 | /* Backslash: Quoted symbol expected */ 116 | if (c == '\\' && parser->pos + 1 < len) { 117 | int i; 118 | parser->pos++; 119 | switch (js[parser->pos]) { 120 | /* Allowed escaped symbols */ 121 | case '\"': case '/' : case '\\' : case 'b' : 122 | case 'f' : case 'r' : case 'n' : case 't' : 123 | break; 124 | /* Allows escaped symbol \uXXXX */ 125 | case 'u': 126 | parser->pos++; 127 | for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { 128 | /* If it isn't a hex character we have an error */ 129 | if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 130 | (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 131 | (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 132 | parser->pos = start; 133 | return JSMN_ERROR_INVAL; 134 | } 135 | parser->pos++; 136 | } 137 | parser->pos--; 138 | break; 139 | /* Unexpected symbol */ 140 | default: 141 | parser->pos = start; 142 | return JSMN_ERROR_INVAL; 143 | } 144 | } 145 | } 146 | parser->pos = start; 147 | return JSMN_ERROR_PART; 148 | } 149 | 150 | /** 151 | * Parse JSON string and fill tokens. 152 | */ 153 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 154 | jsmntok_t *tokens, unsigned int num_tokens) { 155 | jsmnerr_t r; 156 | int i; 157 | jsmntok_t *token; 158 | int count = 0; 159 | 160 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 161 | char c; 162 | jsmntype_t type; 163 | 164 | c = js[parser->pos]; 165 | switch (c) { 166 | case '{': case '[': 167 | count++; 168 | if (tokens == NULL) { 169 | break; 170 | } 171 | token = jsmn_alloc_token(parser, tokens, num_tokens); 172 | if (token == NULL) 173 | return JSMN_ERROR_NOMEM; 174 | if (parser->toksuper != -1) { 175 | tokens[parser->toksuper].size++; 176 | #ifdef JSMN_PARENT_LINKS 177 | token->parent = parser->toksuper; 178 | #endif 179 | } 180 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 181 | token->start = parser->pos; 182 | parser->toksuper = parser->toknext - 1; 183 | break; 184 | case '}': case ']': 185 | if (tokens == NULL) 186 | break; 187 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 188 | #ifdef JSMN_PARENT_LINKS 189 | if (parser->toknext < 1) { 190 | return JSMN_ERROR_INVAL; 191 | } 192 | token = &tokens[parser->toknext - 1]; 193 | for (;;) { 194 | if (token->start != -1 && token->end == -1) { 195 | if (token->type != type) { 196 | return JSMN_ERROR_INVAL; 197 | } 198 | token->end = parser->pos + 1; 199 | parser->toksuper = token->parent; 200 | break; 201 | } 202 | if (token->parent == -1) { 203 | break; 204 | } 205 | token = &tokens[token->parent]; 206 | } 207 | #else 208 | for (i = parser->toknext - 1; i >= 0; i--) { 209 | token = &tokens[i]; 210 | if (token->start != -1 && token->end == -1) { 211 | if (token->type != type) { 212 | return JSMN_ERROR_INVAL; 213 | } 214 | parser->toksuper = -1; 215 | token->end = parser->pos + 1; 216 | break; 217 | } 218 | } 219 | /* Error if unmatched closing bracket */ 220 | if (i == -1) return JSMN_ERROR_INVAL; 221 | for (; i >= 0; i--) { 222 | token = &tokens[i]; 223 | if (token->start != -1 && token->end == -1) { 224 | parser->toksuper = i; 225 | break; 226 | } 227 | } 228 | #endif 229 | break; 230 | case '\"': 231 | r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 232 | if (r < 0) return r; 233 | count++; 234 | if (parser->toksuper != -1 && tokens != NULL) 235 | tokens[parser->toksuper].size++; 236 | break; 237 | case '\t' : case '\r' : case '\n' : case ' ': 238 | break; 239 | case ':': 240 | parser->toksuper = parser->toknext - 1; 241 | break; 242 | case ',': 243 | if (tokens != NULL && 244 | tokens[parser->toksuper].type != JSMN_ARRAY && 245 | tokens[parser->toksuper].type != JSMN_OBJECT) { 246 | #ifdef JSMN_PARENT_LINKS 247 | parser->toksuper = tokens[parser->toksuper].parent; 248 | #else 249 | for (i = parser->toknext - 1; i >= 0; i--) { 250 | if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 251 | if (tokens[i].start != -1 && tokens[i].end == -1) { 252 | parser->toksuper = i; 253 | break; 254 | } 255 | } 256 | } 257 | #endif 258 | } 259 | break; 260 | #ifdef JSMN_STRICT 261 | /* In strict mode primitives are: numbers and booleans */ 262 | case '-': case '0': case '1' : case '2': case '3' : case '4': 263 | case '5': case '6': case '7' : case '8': case '9': 264 | case 't': case 'f': case 'n' : 265 | /* And they must not be keys of the object */ 266 | if (tokens != NULL) { 267 | jsmntok_t *t = &tokens[parser->toksuper]; 268 | if (t->type == JSMN_OBJECT || 269 | (t->type == JSMN_STRING && t->size != 0)) { 270 | return JSMN_ERROR_INVAL; 271 | } 272 | } 273 | #else 274 | /* In non-strict mode every unquoted value is a primitive */ 275 | default: 276 | #endif 277 | r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 278 | if (r < 0) return r; 279 | count++; 280 | if (parser->toksuper != -1 && tokens != NULL) 281 | tokens[parser->toksuper].size++; 282 | break; 283 | 284 | #ifdef JSMN_STRICT 285 | /* Unexpected char in strict mode */ 286 | default: 287 | return JSMN_ERROR_INVAL; 288 | #endif 289 | } 290 | } 291 | 292 | for (i = parser->toknext - 1; i >= 0; i--) { 293 | /* Unmatched opened object or array */ 294 | if (tokens[i].start != -1 && tokens[i].end == -1) { 295 | return JSMN_ERROR_PART; 296 | } 297 | } 298 | 299 | return count; 300 | } 301 | 302 | /** 303 | * Creates a new parser based over a given buffer with an array of tokens 304 | * available. 305 | */ 306 | void jsmn_init(jsmn_parser *parser) { 307 | parser->pos = 0; 308 | parser->toknext = 0; 309 | parser->toksuper = -1; 310 | } 311 | 312 | -------------------------------------------------------------------------------- /src/sts/jsmn.h: -------------------------------------------------------------------------------- 1 | #ifndef __JSMN_H_ 2 | #define __JSMN_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * JSON type identifier. Basic types are: 12 | * o Object 13 | * o Array 14 | * o String 15 | * o Other primitive: number, boolean (true/false) or null 16 | */ 17 | typedef enum { 18 | JSMN_PRIMITIVE = 0, 19 | JSMN_OBJECT = 1, 20 | JSMN_ARRAY = 2, 21 | JSMN_STRING = 3 22 | } jsmntype_t; 23 | 24 | typedef enum { 25 | /* Not enough tokens were provided */ 26 | JSMN_ERROR_NOMEM = -1, 27 | /* Invalid character inside JSON string */ 28 | JSMN_ERROR_INVAL = -2, 29 | /* The string is not a full JSON packet, more bytes expected */ 30 | JSMN_ERROR_PART = -3 31 | } jsmnerr_t; 32 | 33 | /** 34 | * JSON token description. 35 | * @param type type (object, array, string etc.) 36 | * @param start start position in JSON data string 37 | * @param end end position in JSON data string 38 | */ 39 | typedef struct { 40 | jsmntype_t type; 41 | int start; 42 | int end; 43 | int size; 44 | #ifdef JSMN_PARENT_LINKS 45 | int parent; 46 | #endif 47 | } jsmntok_t; 48 | 49 | /** 50 | * JSON parser. Contains an array of token blocks available. Also stores 51 | * the string being parsed now and current position in that string 52 | */ 53 | typedef struct { 54 | unsigned int pos; /* offset in the JSON string */ 55 | unsigned int toknext; /* next token to allocate */ 56 | int toksuper; /* superior token node, e.g parent object or array */ 57 | } jsmn_parser; 58 | 59 | /** 60 | * Create JSON parser over an array of tokens 61 | */ 62 | void jsmn_init(jsmn_parser *parser); 63 | 64 | /** 65 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing 66 | * a single JSON object. 67 | */ 68 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 69 | jsmntok_t *tokens, unsigned int num_tokens); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif /* __JSMN_H_ */ 76 | -------------------------------------------------------------------------------- /src/sts/libsts.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_STS_H_ 2 | #define _LIB_STS_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus__ 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * * This is the default hostname that is being used for the STS requests 12 | * 13 | **/ 14 | #define STS_DEFAULT_HOSTNAME "sts.aliyuncs.com" 15 | 16 | #define MAX_URI_SIZE 1024 17 | 18 | #define MAX_RESPONSE_SIZE 1024*10 19 | 20 | #define STS_HTTP_TIMEOUT 5 21 | 22 | 23 | 24 | typedef enum 25 | { 26 | STSStatusOK, 27 | 28 | STSStatusOutOfMemory, 29 | 30 | STSStatusFailedToInitRequest, 31 | 32 | STSStatusUriTooLong, 33 | 34 | STSStatusResponseToLarge, 35 | 36 | STSStatusWaiting, 37 | 38 | STSStatusInternalError, 39 | 40 | STSStatusHttpErrorBadRequest, 41 | 42 | STSStatusNameLookupError, 43 | 44 | STSStatusFailedToConnect, 45 | 46 | STSStatusConnectionFailed, 47 | 48 | STSStatusServerFailedVerification, 49 | 50 | STSStatusHttpErrorInvalidParameter, 51 | 52 | STSStatusHttpErrorNoPermission, 53 | 54 | STSStatusHttpErrorInternalError, 55 | 56 | STSStatusHttpErrorUnknown, 57 | 58 | STSStatusInvalidParameter, 59 | 60 | STSStatusNoPermission, 61 | 62 | STSStatusInvalidAcccessKeyId 63 | 64 | } STSStatus; 65 | 66 | typedef enum 67 | { 68 | STSProtocolHTTPS = 0, 69 | 70 | STSProtocolHTTP = 1 71 | } STSProtocol; 72 | 73 | typedef struct CommonRequestContext 74 | { 75 | STSProtocol protocol; 76 | 77 | const char* hostname; 78 | 79 | const char* accessKeyId; 80 | 81 | const char* accessKeySecret; 82 | 83 | const char* action; 84 | 85 | } CommonRequestContext; 86 | 87 | typedef struct AssumeRoleRequestContext 88 | { 89 | const char* RoleArn; 90 | 91 | const char* RoleSessionName; 92 | 93 | const char* policy; 94 | 95 | uint32_t durationSeconds; 96 | 97 | } AssumeRoleRequestContext; 98 | 99 | typedef struct STSData 100 | { 101 | char tmpAccessKeyId[64]; 102 | char tmpAccessKeySecret[64]; 103 | char securityToken[2048]; 104 | } STSData; 105 | 106 | typedef STSStatus (STSGetResultCallback)(int bufferSize, const char* buffer, 107 | void* callbackData); 108 | 109 | STSStatus STS_assume_role(const char* roleArn, const char* roleSessionName, 110 | const char* policy, const uint32_t durationSeconds, 111 | const char* accessKeyId, const char* accessKeySecret, 112 | STSData* stsData, char* errorMessage); 113 | 114 | 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/sts/request.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "request.h" 5 | #include "util.h" 6 | 7 | 8 | static STSStatus compose_uri(char* buffer, int bufferSize, const CommonRequestContext* commonContext) 9 | { 10 | int len = 0; 11 | 12 | #define uri_append(fmt, ...) \ 13 | do { \ 14 | len += snprintf(&(buffer[len]), bufferSize - len, fmt, __VA_ARGS__); \ 15 | if (len >= bufferSize) { \ 16 | return STSStatusUriTooLong; \ 17 | } \ 18 | } while (0) 19 | 20 | uri_append("http%s://", (STSProtocolHTTP == commonContext->protocol) ? "" : "s"); 21 | 22 | const char* hostName = commonContext->hostname ? commonContext->hostname : STS_DEFAULT_HOSTNAME; 23 | 24 | uri_append("%s", hostName); 25 | 26 | return STSStatusOK; 27 | } 28 | 29 | static void request_headers_done(Request *request) 30 | { 31 | // Get the http response code 32 | long httpResponseCode; 33 | request->httpResponseCode = 0; 34 | if (curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE, 35 | &httpResponseCode) != CURLE_OK) { 36 | // Not able to get the HTTP response code - error 37 | request->status = STSStatusInternalError; 38 | return; 39 | } 40 | else { 41 | request->httpResponseCode = httpResponseCode; 42 | } 43 | } 44 | 45 | static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, 46 | void *data) 47 | { 48 | Request *request = (Request *) data; 49 | 50 | int len = size * nmemb; 51 | 52 | request_headers_done(request); 53 | 54 | //if (request->status != STSStatusOK) { 55 | // return 0; 56 | //} 57 | 58 | // On HTTP error, we expect to parse an HTTP error response 59 | //if ((request->httpResponseCode < 200) || 60 | // (request->httpResponseCode > 299)) { 61 | //request->status = error_parser_add(&(request->errorParser), (char *) ptr, len); 62 | //} 63 | // If there was a callback registered, make it 64 | //else 65 | 66 | if (request->getResultCallback) { 67 | request->status = (*(request->getResultCallback))(len, (char *) ptr, request->callbackData); 68 | } 69 | // Else, consider this an error - S3 has sent back data when it was not 70 | // expected 71 | else { 72 | request->status = STSStatusInternalError; 73 | } 74 | 75 | return ((request->status == STSStatusOK) ? len : 0); 76 | } 77 | 78 | 79 | 80 | static STSStatus setup_curl(Request* request, RequestParams* params) 81 | { 82 | STSStatus status; 83 | 84 | #define curl_easy_setopt_safe(opt, val) \ 85 | if ((status = curl_easy_setopt \ 86 | (request->curl, opt, val)) != CURLE_OK) { \ 87 | return STSStatusFailedToInitRequest; \ 88 | } 89 | 90 | curl_easy_setopt_safe(CURLOPT_PRIVATE, request); 91 | curl_easy_setopt_safe(CURLOPT_URL, request->uri); 92 | //curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, 0); 93 | //curl_easy_setopt_safe(CURLOPT_SSL_VERIFYHOST, 0); 94 | //curl_easy_setopt_safe(CURLOPT_VERBOSE, 1); 95 | curl_easy_setopt_safe(CURLOPT_TIMEOUT, STS_HTTP_TIMEOUT); 96 | curl_easy_setopt_safe(CURLOPT_POST, 1); 97 | curl_easy_setopt_safe(CURLOPT_POSTFIELDS, params->postData); 98 | curl_easy_setopt_safe(CURLOPT_POSTFIELDSIZE, strlen(params->postData)); 99 | 100 | // Set write callback and data 101 | curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, &curl_write_func); 102 | curl_easy_setopt_safe(CURLOPT_WRITEDATA, request); 103 | 104 | request->headers = curl_slist_append(request->headers, "Content-Type: application/x-www-form-urlencoded"); 105 | curl_easy_setopt(request->curl, CURLOPT_HTTPHEADER, request->headers); 106 | 107 | return status; 108 | } 109 | 110 | STSStatus request_get(RequestParams* params, Request** requestReturn) 111 | { 112 | Request* request; 113 | STSStatus status; 114 | 115 | if (!(request = (Request*)malloc(sizeof(Request)))) { 116 | return STSStatusOutOfMemory; 117 | } 118 | 119 | request->headers = 0; 120 | 121 | if (!(request->curl = curl_easy_init())) { 122 | return STSStatusFailedToInitRequest; 123 | } 124 | 125 | if (STSStatusOK != (status = compose_uri(request->uri, sizeof(request->uri), &(params->commonContext)))) { 126 | curl_easy_cleanup(request->curl); 127 | free(request); 128 | return status; 129 | } 130 | 131 | if (STSStatusOK != (status = setup_curl(request, params))) { 132 | curl_easy_cleanup(request->curl); 133 | free(request); 134 | return status; 135 | } 136 | 137 | *requestReturn = request; 138 | 139 | return status; 140 | } 141 | 142 | static void request_deinitialize(Request *request) 143 | { 144 | if (request->headers) { 145 | curl_slist_free_all(request->headers); 146 | } 147 | 148 | //error_parser_deinitialize(&(request->errorParser)); 149 | 150 | } 151 | 152 | static void request_destroy(Request *request) 153 | { 154 | request_deinitialize(request); 155 | curl_easy_cleanup(request->curl); 156 | free(request); 157 | } 158 | 159 | void request_finish(Request *request) 160 | { 161 | request_destroy(request); 162 | } 163 | 164 | static STSStatus responseCallback(int bufferSize, const char* buffer, 165 | void* callbackData) 166 | { 167 | char* response = (char*)callbackData; 168 | 169 | if (bufferSize > MAX_RESPONSE_SIZE) { 170 | return STSStatusResponseToLarge; 171 | } 172 | else { 173 | snprintf(response+strlen(response), bufferSize+1, "%s", buffer); 174 | } 175 | 176 | return STSStatusOK; 177 | } 178 | 179 | STSStatus request_curl_code_to_status(CURLcode code) 180 | { 181 | switch (code) { 182 | case CURLE_OUT_OF_MEMORY: 183 | return STSStatusOutOfMemory; 184 | 185 | case CURLE_COULDNT_RESOLVE_PROXY: 186 | case CURLE_COULDNT_RESOLVE_HOST: 187 | return STSStatusNameLookupError; 188 | 189 | case CURLE_COULDNT_CONNECT: 190 | return STSStatusFailedToConnect; 191 | 192 | case CURLE_WRITE_ERROR: 193 | case CURLE_OPERATION_TIMEDOUT: 194 | return STSStatusConnectionFailed; 195 | 196 | case CURLE_PARTIAL_FILE: 197 | return STSStatusOK; 198 | 199 | case CURLE_SSL_CACERT: 200 | return STSStatusServerFailedVerification; 201 | 202 | default: 203 | return STSStatusInternalError; 204 | } 205 | } 206 | 207 | STSStatus request_perform(RequestParams* params, int* httpResponseCode, char* response) 208 | { 209 | Request* request = 0; 210 | STSStatus status; 211 | 212 | 213 | if (STSStatusOK != (status = request_get(params, &request))) { 214 | fprintf(stderr, "Request Get ERR\n"); 215 | return status; 216 | } 217 | 218 | request->getResultCallback = responseCallback; 219 | request->callbackData = response; 220 | request->status = STSStatusWaiting; 221 | 222 | CURLcode code = curl_easy_perform(request->curl); 223 | 224 | if (CURLE_OK != code) { 225 | request->httpResponseCode = 0; 226 | *httpResponseCode = request->httpResponseCode; 227 | request->status = request_curl_code_to_status(code); 228 | return request->status; 229 | } 230 | else { 231 | *httpResponseCode = request->httpResponseCode; 232 | request_finish(request); 233 | return request->status; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/sts/request.h: -------------------------------------------------------------------------------- 1 | #ifndef _STS_REQEUST_H_ 2 | #define _STS_REQEUST_H_ 3 | 4 | #include "libsts.h" 5 | #include "util.h" 6 | 7 | typedef enum 8 | { 9 | HttpRequestTypeGET, 10 | HttpRequestTypePOST, 11 | HttpRequestTypeHEAD, 12 | HttpRequestTypePUT, 13 | HttpRequestTypeCOPY, 14 | HttpRequestTypeDELETE 15 | } HttpRequestType; 16 | 17 | typedef struct RequestParams 18 | { 19 | HttpRequestType httpRequestType; 20 | 21 | CommonRequestContext commonContext; 22 | 23 | AssumeRoleRequestContext assumeRoleContext; 24 | 25 | const char* postData; 26 | 27 | } RequestParams; 28 | 29 | typedef struct Request 30 | { 31 | CURL* curl; 32 | 33 | struct curl_slist *headers; 34 | 35 | char uri[MAX_URI_SIZE + 1]; 36 | 37 | int httpResponseCode; 38 | 39 | STSStatus status; 40 | 41 | STSGetResultCallback* getResultCallback; 42 | 43 | void* callbackData; 44 | 45 | } Request; 46 | 47 | STSStatus request_perform(RequestParams* params, int* httpResponseCode, char* response); 48 | 49 | 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/sts/string_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_BUFFER_H 2 | #define STRING_BUFFER_H 3 | 4 | #include 5 | 6 | 7 | // Declare a string_buffer with the given name of the given maximum length 8 | #define string_buffer(name, len) \ 9 | char name[len + 1]; \ 10 | int name##Len 11 | 12 | 13 | // Initialize a string_buffer 14 | #define string_buffer_initialize(sb) \ 15 | do { \ 16 | sb[0] = 0; \ 17 | sb##Len = 0; \ 18 | } while (0) 19 | 20 | 21 | // Append [len] bytes of [str] to [sb], setting [all_fit] to 1 if it fit, and 22 | // 0 if it did not 23 | #define string_buffer_append(sb, str, len, all_fit) \ 24 | do { \ 25 | sb##Len += snprintf(&(sb[sb##Len]), sizeof(sb) - sb##Len - 1, \ 26 | "%.*s", (int) (len), str); \ 27 | if (sb##Len > (int) (sizeof(sb) - 1)) { \ 28 | sb##Len = sizeof(sb) - 1; \ 29 | all_fit = 0; \ 30 | } \ 31 | else { \ 32 | all_fit = 1; \ 33 | } \ 34 | } while (0) 35 | 36 | 37 | // Declare a string multibuffer with the given name of the given maximum size 38 | #define string_multibuffer(name, size) \ 39 | char name[size]; \ 40 | int name##Size 41 | 42 | 43 | // Initialize a string_multibuffer 44 | #define string_multibuffer_initialize(smb) \ 45 | do { \ 46 | smb##Size = 0; \ 47 | } while (0) 48 | 49 | 50 | // Evaluates to the current string within the string_multibuffer 51 | #define string_multibuffer_current(smb) \ 52 | &(smb[smb##Size]) 53 | 54 | 55 | // Adds a new string to the string_multibuffer 56 | #define string_multibuffer_add(smb, str, len, all_fit) \ 57 | do { \ 58 | smb##Size += (snprintf(&(smb[smb##Size]), \ 59 | sizeof(smb) - smb##Size, \ 60 | "%.*s", (int) (len), str) + 1); \ 61 | if (smb##Size > (int) sizeof(smb)) { \ 62 | smb##Size = sizeof(smb); \ 63 | all_fit = 0; \ 64 | } \ 65 | else { \ 66 | all_fit = 1; \ 67 | } \ 68 | } while (0) 69 | 70 | 71 | // Appends to the current string in the string_multibuffer. There must be a 72 | // current string, meaning that string_multibuffer_add must have been called 73 | // at least once for this string_multibuffer. 74 | #define string_multibuffer_append(smb, str, len, all_fit) \ 75 | do { \ 76 | smb##Size--; \ 77 | string_multibuffer_add(smb, str, len, all_fit); \ 78 | } while (0) 79 | 80 | 81 | #endif /* STRING_BUFFER_H */ 82 | -------------------------------------------------------------------------------- /src/sts/time.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: David Robert Nadeau 3 | * Site: http://NadeauSoftware.com/ 4 | * License: Creative Commons Attribution 3.0 Unported License 5 | * http://creativecommons.org/licenses/by/3.0/deed.en_US 6 | */ 7 | 8 | #if defined(_WIN32) 9 | #include 10 | 11 | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) 12 | #include /* POSIX flags */ 13 | #include /* clock_gettime(), time() */ 14 | #include /* gethrtime(), gettimeofday() */ 15 | 16 | #if defined(__MACH__) && defined(__APPLE__) 17 | #include 18 | #include 19 | #endif 20 | 21 | #else 22 | #error "Unable to define getRealTime( ) for an unknown OS." 23 | #endif 24 | 25 | #include 26 | #include "util.h" 27 | 28 | /** 29 | * Returns the real time, in seconds, or -1.0 if an error occurred. 30 | * 31 | * Time is measured since an arbitrary and OS-dependent start time. 32 | * The returned real time is only useful for computing an elapsed time 33 | * between two calls to this function. 34 | */ 35 | uint64_t getRealTime( ) 36 | { 37 | #if defined(_WIN32) 38 | FILETIME tm; 39 | ULONGLONG t; 40 | #if defined(NTDDI_WIN8) && NTDDI_VERSION >= NTDDI_WIN8 41 | /* Windows 8, Windows Server 2012 and later. ---------------- */ 42 | GetSystemTimePreciseAsFileTime( &tm ); 43 | #else 44 | /* Windows 2000 and later. ---------------------------------- */ 45 | GetSystemTimeAsFileTime( &tm ); 46 | #endif 47 | t = ((ULONGLONG)tm.dwHighDateTime << 32) | (ULONGLONG)tm.dwLowDateTime; 48 | return (uint64_t)t; 49 | 50 | #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) 51 | /* HP-UX, Solaris. ------------------------------------------ */ 52 | return (uint64_t)gethrtime(); 53 | 54 | #elif defined(__MACH__) && defined(__APPLE__) 55 | /* OSX. ----------------------------------------------------- */ 56 | static double timeConvert = 0.0; 57 | if ( timeConvert == 0.0 ) 58 | { 59 | mach_timebase_info_data_t timeBase; 60 | (void)mach_timebase_info( &timeBase ); 61 | timeConvert = (double)timeBase.numer / 62 | (double)timeBase.denom / 63 | 1000000000.0; 64 | } 65 | return (uint64_t)mach_absolute_time( ); 66 | 67 | #elif defined(_POSIX_VERSION) 68 | /* POSIX. --------------------------------------------------- */ 69 | #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) 70 | { 71 | struct timespec ts; 72 | #if defined(CLOCK_MONOTONIC_PRECISE) 73 | /* BSD. --------------------------------------------- */ 74 | const clockid_t id = CLOCK_MONOTONIC_PRECISE; 75 | #elif defined(CLOCK_MONOTONIC_RAW) 76 | /* Linux. ------------------------------------------- */ 77 | const clockid_t id = CLOCK_MONOTONIC_RAW; 78 | #elif defined(CLOCK_HIGHRES) 79 | /* Solaris. ----------------------------------------- */ 80 | const clockid_t id = CLOCK_HIGHRES; 81 | #elif defined(CLOCK_MONOTONIC) 82 | /* AIX, BSD, Linux, POSIX, Solaris. ----------------- */ 83 | const clockid_t id = CLOCK_MONOTONIC; 84 | #elif defined(CLOCK_REALTIME) 85 | /* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */ 86 | const clockid_t id = CLOCK_REALTIME; 87 | #else 88 | const clockid_t id = (clockid_t)-1; /* Unknown. */ 89 | #endif /* CLOCK_* */ 90 | if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) 91 | return ts.tv_sec * 1000000000 + ts.tv_nsec; 92 | /* Fall thru. */ 93 | } 94 | #endif /* _POSIX_TIMERS */ 95 | 96 | /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */ 97 | struct timeval tm; 98 | gettimeofday( &tm, NULL ); 99 | return tm.tv_sec * 1000000 + tm.tv_usec; 100 | #else 101 | return -1.0; /* Failed. */ 102 | #endif 103 | } 104 | -------------------------------------------------------------------------------- /src/sts/token.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "libsts.h" 7 | #include "util.h" 8 | #include "request.h" 9 | #include "string_buffer.h" 10 | #include "jsmn.h" 11 | 12 | // sort 13 | static void params_array_sort(char* paramsArray[], int size) 14 | { 15 | int i = 0, j = 0; 16 | 17 | for (i = 0; i < size; ++i) { 18 | for (j = i + 1; j < size; ++j) { 19 | if (strcmp(paramsArray[i], paramsArray[j]) > 0) { 20 | char *tmp = paramsArray[i]; 21 | paramsArray[i] = paramsArray[j]; 22 | paramsArray[j] = tmp; 23 | } 24 | } 25 | 26 | } 27 | } 28 | static STSStatus compose_canonicalized_query_string(char* buffer, uint32_t bufferSize, uint32_t* len, 29 | char* paramsArray[], uint32_t paramsCount) 30 | { 31 | int i = 0; 32 | *len = 0; 33 | 34 | char* pBuffer = &(buffer[0]); 35 | 36 | for (i = 0; i < paramsCount; ++i) { 37 | char* begin = paramsArray[i]; 38 | char* middle = strchr(begin, '='); 39 | char* end = strchr(begin, '&'); 40 | size_t size = 0; 41 | 42 | if (NULL != end) { 43 | size = end - begin + 1; 44 | } 45 | else { 46 | size = strlen(begin) + 1; 47 | } 48 | 49 | size_t keyLen = middle - begin; 50 | char* key = begin; 51 | 52 | size_t valueLen = size - keyLen - 2; 53 | char* value = middle + 1; 54 | 55 | char* encodedKey = calloc(keyLen*3, 1); 56 | char* encodedValue = calloc(valueLen*3, 1); 57 | 58 | percentEncode(encodedKey, key, keyLen); 59 | percentEncode(encodedValue, value, valueLen); 60 | 61 | snprintf(pBuffer, strlen(encodedKey)+2, "%s=", encodedKey); 62 | *len += strlen(encodedKey) + 1; 63 | pBuffer = buffer + *len; 64 | 65 | snprintf(pBuffer, strlen(encodedValue)+2, "%s&", encodedValue); 66 | *len += strlen(encodedValue) + 1; 67 | pBuffer = buffer + *len; 68 | 69 | free(encodedKey); 70 | free(encodedValue); 71 | } 72 | 73 | *(--pBuffer) = '\0'; 74 | *len -= 1; 75 | 76 | return STSStatusOK; 77 | } 78 | 79 | static STSStatus compose_url_encoded_post_data(char* buffer, uint32_t bufferSize, 80 | char* queryParams, const uint32_t queryLen, 81 | const char* signature) 82 | { 83 | char* pParams = queryParams; 84 | char* pBuffer = buffer; 85 | int len = 0; 86 | int index = 0; 87 | 88 | while (index < queryLen) { 89 | char* begin = pParams; 90 | char* middle = strchr(begin, '='); 91 | char* end = strchr(begin, '&'); 92 | 93 | size_t size = 0; 94 | if (NULL != end) { 95 | size = end - begin + 1; 96 | } 97 | else { 98 | size = strlen(begin) + 1; 99 | } 100 | index += size; 101 | 102 | size_t keyLen = middle - begin; 103 | char* key = begin; 104 | size_t valueLen = size - keyLen - 2; 105 | char* value = middle + 1; 106 | 107 | char* encodedKey = calloc(keyLen*3, 1); 108 | char* encodedValue = calloc(valueLen*3, 1); 109 | 110 | urlEncode(encodedKey, key, keyLen); 111 | urlEncode(encodedValue, value, valueLen); 112 | 113 | snprintf(pBuffer, strlen(encodedKey)+2, "%s=", encodedKey); 114 | len += strlen(encodedKey) + 1; 115 | pBuffer = buffer + len; 116 | 117 | snprintf(pBuffer, strlen(encodedValue)+2, "%s&", encodedValue); 118 | len += strlen(encodedValue) + 1; 119 | pBuffer = buffer + len; 120 | 121 | pParams = end + 1; 122 | free(encodedKey); 123 | free(encodedValue); 124 | } 125 | 126 | char* signatureKey = "Signature="; 127 | size_t len2 = strlen(signatureKey); 128 | snprintf(pBuffer, strlen(signature)+len2+1, "Signature=%s", signature); 129 | 130 | return STSStatusOK; 131 | } 132 | 133 | static STSStatus compute_signature(char* buffer, uint32_t bufferSize, 134 | char* paramsArray[], const uint32_t paramsCount, 135 | const char* accessKeySecret) 136 | { 137 | 138 | ////////////////////////////////////////////////////////////////////////// 139 | // sign 140 | uint32_t canonLen = 0; 141 | 142 | char canonicalizedQueryString[2048 * 3]; 143 | compose_canonicalized_query_string(canonicalizedQueryString, 2048 * 3, &canonLen, 144 | paramsArray, paramsCount); 145 | 146 | string_buffer(strToSign, 2048 * 3); 147 | string_buffer_initialize(strToSign); 148 | int fit; 149 | string_buffer_append(strToSign, "POST&%2F&", 9, fit); 150 | if (!fit) { 151 | return STSStatusUriTooLong; 152 | } 153 | 154 | string_buffer(percentTwice, 2048 * 3); 155 | string_buffer_initialize(percentTwice); 156 | percentEncode(percentTwice, canonicalizedQueryString, canonLen); 157 | string_buffer_append(strToSign, percentTwice, strlen(percentTwice), fit); 158 | 159 | //fprintf(stdout, "strToSign(%lu): %s\n", strlen(strToSign), strToSign); 160 | 161 | // Generate an HMAC-SHA-1 of the strToSign 162 | size_t akSecretLen = strlen(accessKeySecret); 163 | char newAccessKeySecret[akSecretLen + 1]; 164 | snprintf(newAccessKeySecret, akSecretLen+2, "%s&", accessKeySecret); 165 | 166 | unsigned char hmac[20]; 167 | STS_HMAC_SHA1(hmac, (unsigned char *) newAccessKeySecret, strlen(newAccessKeySecret), 168 | (unsigned char *) strToSign, strlen(strToSign)); 169 | 170 | // Now base-64 encode the results 171 | char b64[((20 + 1) * 4) / 3]; 172 | int b64Len = base64Encode(hmac, 20, b64); 173 | 174 | char b64Encoded[256]; 175 | if (!urlEncode(b64Encoded, b64, b64Len)) { 176 | return STSStatusUriTooLong; 177 | } 178 | 179 | snprintf(buffer, strlen(b64Encoded)+1, "%s", b64Encoded); 180 | 181 | return STSStatusOK; 182 | 183 | } 184 | 185 | static STSStatus compose_post_data(char* buffer, uint32_t bufferSize, 186 | CommonRequestContext* commonContext, 187 | AssumeRoleRequestContext* assumeRoleContext) 188 | { 189 | string_buffer(queryParams, 2048*3); 190 | string_buffer_initialize(queryParams); 191 | char* queryParamsArray[32]; 192 | int paramsCount = 0; 193 | int len = 0; 194 | int start = 0; 195 | int amp = 0; 196 | 197 | #define safe_append(isNewParam, name, value) \ 198 | do { \ 199 | int fit; \ 200 | start = len; \ 201 | if (amp) { start++; } \ 202 | if (isNewParam) { \ 203 | queryParamsArray[paramsCount++] = &(queryParams[start]); \ 204 | } \ 205 | if (amp) { \ 206 | string_buffer_append(queryParams, "&", 1, fit); \ 207 | if (!fit) { \ 208 | return STSStatusUriTooLong; \ 209 | } \ 210 | len++; \ 211 | } \ 212 | string_buffer_append(queryParams, name "=", \ 213 | sizeof(name "=") - 1, fit); \ 214 | if (!fit) { \ 215 | return STSStatusUriTooLong; \ 216 | } \ 217 | len += strlen(name) + 1; \ 218 | amp = 1; \ 219 | string_buffer_append(queryParams, value, strlen(value), fit); \ 220 | len += strlen(value); \ 221 | } while (0) 222 | 223 | time_t now = time(NULL); 224 | char timebuf[256]; 225 | strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); 226 | 227 | STSUUID uuid; 228 | char* uuidString; 229 | uuid_create(&uuid); 230 | uuid_to_string(&uuid, &uuidString); 231 | 232 | char durationSeconds[10]; 233 | snprintf(durationSeconds, 10, "%d", assumeRoleContext->durationSeconds); 234 | 235 | safe_append(1, "Format", "JSON"); 236 | safe_append(1, "Version", "2015-04-01"); 237 | safe_append(1, "SignatureVersion", "1.0"); 238 | safe_append(1, "SignatureMethod", "HMAC-SHA1"); 239 | safe_append(1, "SignatureNonce", uuidString); 240 | safe_append(1, "Timestamp", timebuf); 241 | safe_append(1, "Action", commonContext->action); 242 | safe_append(1, "AccessKeyId", commonContext->accessKeyId); 243 | safe_append(1, "RoleArn", assumeRoleContext->RoleArn); 244 | safe_append(1, "RoleSessionName", assumeRoleContext->RoleSessionName); 245 | safe_append(1, "Policy", assumeRoleContext->policy); 246 | safe_append(1, "DurationSeconds", durationSeconds); 247 | 248 | params_array_sort(queryParamsArray, paramsCount); 249 | 250 | string_buffer(signature, 256); 251 | string_buffer_initialize(signature); 252 | compute_signature(signature, 256, queryParamsArray, paramsCount, commonContext->accessKeySecret); 253 | 254 | compose_url_encoded_post_data(buffer, bufferSize, queryParams, strlen(queryParams), signature); 255 | 256 | free(uuidString); 257 | return STSStatusOK; 258 | } 259 | 260 | static STSStatus parse_sts_result(int bufferSize, const char* buffer, STSData* result) 261 | { 262 | STSData* stsData = (STSData*)result; 263 | jsmntok_t json[1024]; 264 | jsmn_parser parser; 265 | jsmnerr_t r; 266 | int i = 0; 267 | int keyLen = 0; 268 | int valueLen = 0; 269 | const char* pKey; 270 | const char* pValue; 271 | 272 | jsmn_init(&parser); 273 | r = jsmn_parse(&parser, buffer, bufferSize, json, 1024); 274 | 275 | if (r >= 0) { 276 | for (i = 0; i < r; ++i) { 277 | if (JSMN_STRING == json[i].type) { 278 | keyLen = json[i].end - json[i].start; 279 | valueLen = json[i+1].end - json[i+1].start; 280 | pKey = &buffer[json[i].start]; 281 | pValue = &buffer[json[i+1].start]; 282 | 283 | if (0 == strncmp("AccessKeyId", pKey, keyLen)) { 284 | snprintf(stsData->tmpAccessKeyId, valueLen+1, "%s", pValue); 285 | } 286 | else if (0 == strncmp("AccessKeySecret", pKey, keyLen)) { 287 | snprintf(stsData->tmpAccessKeySecret, valueLen+1, "%s", pValue); 288 | } 289 | else if (0 == strncmp("SecurityToken", pKey, keyLen)) { 290 | snprintf(stsData->securityToken, valueLen+1, "%s", pValue); 291 | } 292 | } 293 | } 294 | } 295 | 296 | return STSStatusOK; 297 | } 298 | 299 | STSStatus STS_assume_role(const char* roleArn, const char* roleSessionName, 300 | const char* policy, const uint32_t durationSeconds, 301 | const char* accessKeyId, const char* accessKeySecret, 302 | STSData* assumeRoleData, char* errorMessage) 303 | { 304 | int status; 305 | int httpResponseCode; 306 | CommonRequestContext commonContext = {STSProtocolHTTPS, STS_DEFAULT_HOSTNAME, 307 | accessKeyId, accessKeySecret, "AssumeRole"}; 308 | 309 | AssumeRoleRequestContext assumeRoleContext = {roleArn, roleSessionName, policy, durationSeconds}; 310 | 311 | string_buffer(response, MAX_RESPONSE_SIZE); 312 | string_buffer_initialize(response); 313 | 314 | string_buffer(postData, 2048*3); 315 | string_buffer_initialize(postData); 316 | 317 | compose_post_data(postData, 2048*3, &commonContext, &assumeRoleContext); 318 | 319 | RequestParams params = 320 | { 321 | HttpRequestTypePOST, // httpRequestType 322 | commonContext, // commonContext 323 | assumeRoleContext, // assumeRoleContext 324 | postData // postData 325 | }; 326 | 327 | status = request_perform(¶ms, &httpResponseCode, response); 328 | 329 | if (200 == httpResponseCode) { 330 | parse_sts_result(strlen(response), response, assumeRoleData); 331 | } 332 | else if( 0 != httpResponseCode) 333 | { 334 | if (400 == httpResponseCode) { 335 | status = STSStatusInvalidParameter; 336 | } 337 | else if (403 == httpResponseCode) { 338 | status = STSStatusNoPermission; 339 | } 340 | else if (500 == httpResponseCode) { 341 | status = STSStatusInternalError; 342 | } 343 | else if (404 == httpResponseCode) { 344 | status = STSStatusInvalidAcccessKeyId; 345 | } 346 | snprintf(errorMessage, strlen(response)+1, "%s", response); 347 | } 348 | else if (0 == httpResponseCode) { 349 | switch (status) { 350 | case STSStatusOutOfMemory: 351 | snprintf(errorMessage, 256, "%s.", "Out of memory"); 352 | break; 353 | 354 | case STSStatusNameLookupError: 355 | snprintf(errorMessage, 256, "%s %s.", 356 | "Failed to lookup STS server:", 357 | commonContext.hostname); 358 | break; 359 | 360 | case STSStatusFailedToConnect: 361 | snprintf(errorMessage, 256, "%s %s.", 362 | "Failed to connect to STS server:", 363 | commonContext.hostname); 364 | break; 365 | 366 | case STSStatusConnectionFailed: 367 | snprintf(errorMessage, 256, "%s.", 368 | "Write Error or Operation Timeout"); 369 | break; 370 | 371 | case STSStatusServerFailedVerification: 372 | snprintf(errorMessage, 256, "%s.", 373 | "SSL verification failed"); 374 | break; 375 | 376 | case STSStatusInternalError: 377 | snprintf(errorMessage, 256, "%s.", "Internal Error"); 378 | break; 379 | 380 | default: 381 | break; 382 | } 383 | } 384 | return status; 385 | } 386 | -------------------------------------------------------------------------------- /src/sts/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _STS_UTIL_H_ 2 | #define _STS_UTIL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "libsts.h" 10 | 11 | /* Length of a node address (an IEEE 802 address). */ 12 | #define UUID_NODE_LEN 6 13 | 14 | typedef struct STSUUID { 15 | uint32_t time_low; 16 | uint16_t time_mid; 17 | uint16_t time_hi_and_version; 18 | uint8_t clock_seq_hi_and_reserved; 19 | uint8_t clock_seq_low; 20 | uint8_t node[UUID_NODE_LEN]; 21 | } STSUUID; 22 | 23 | void uuid_create(STSUUID* uuid); 24 | 25 | void uuid_to_string(STSUUID* uuid, char** str); 26 | 27 | uint64_t getRealTime(); 28 | 29 | // Utilities ----------------------------------------------------------------- 30 | 31 | int percentEncode(char* dest, const char *src, int maxSrcSize); 32 | 33 | // URL-encodes a string from [src] into [dest]. [dest] must have at least 34 | // 3x the number of characters that [source] has. At most [maxSrcSize] bytes 35 | // from [src] are encoded; if more are present in [src], 0 is returned from 36 | // urlEncode, else nonzero is returned. 37 | int urlEncode(char *dest, const char *src, int maxSrcSize); 38 | 39 | // Returns < 0 on failure >= 0 on success 40 | int64_t parseIso8601Time(const char *str); 41 | 42 | uint64_t parseUnsignedInt(const char *str); 43 | 44 | // base64 encode bytes. The output buffer must have at least 45 | // ((4 * (inLen + 1)) / 3) bytes in it. Returns the number of bytes written 46 | // to [out]. 47 | int base64Encode(const unsigned char *in, int inLen, char *out); 48 | 49 | // Compute HMAC-SHA-1 with key [key] and message [message], storing result 50 | // in [hmac] 51 | void HMAC_SHA1(unsigned char hmac[20], const unsigned char *key, int key_len, 52 | const unsigned char *message, int message_len); 53 | 54 | void STS_HMAC_SHA1(unsigned char hmac[20], const unsigned char *key, int key_len, 55 | const unsigned char *message, int message_len); 56 | 57 | // Compute a 64-bit hash values given a set of bytes 58 | uint64_t hash(const unsigned char *k, int length); 59 | 60 | // Because Windows seems to be missing isblank(), use our own; it's a very 61 | // easy function to write in any case 62 | int is_blank(char c); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_SOURCE_DIR}) 2 | 3 | configure_file ( 4 | "${PROJECT_SOURCE_DIR}/test/test.h.in" 5 | "${PROJECT_SOURCE_DIR}/test/test.h" 6 | ) 7 | 8 | set(TEST_BIN_NAME ${CMAKE_PROJECT_NAME}_test) 9 | 10 | set(TEST_SOURCE_FILES CuTest.c 11 | config.c 12 | test_client.c 13 | test_server.c 14 | test_sts.c 15 | test_hls.c 16 | test_hls_stream.c 17 | test_all.c) 18 | 19 | include_directories (${APR_INCLUDE_DIR}) 20 | include_directories (${APR_UTIL_INCLUDE_DIR}) 21 | include_directories (${MINIXML_INCLUDE_DIR}) 22 | include_directories (${CURL_INCLUDE_DIR}) 23 | include_directories (${WOLFSSL_INCLUDE_DIR}) 24 | include_directories ("${CMAKE_SOURCE_DIR}/src") 25 | 26 | find_library(APR_LIBRARY apr-1 PATHS /usr/local/apr/lib/) 27 | find_library(APR_UTIL_LIBRARY aprutil-1 PATHS /usr/local/apr/lib/) 28 | find_library(MINIXML_LIBRARY mxml) 29 | find_library(CURL_LIBRARY curl) 30 | find_library(PTHREAD_LIBRARY pthread) 31 | find_library(RT_LIBRARY rt) 32 | find_library(OSS_C_SDK_LIBRARY oss_c_sdk) 33 | 34 | add_executable(${TEST_BIN_NAME} ${TEST_SOURCE_FILES}) 35 | 36 | target_link_libraries(${TEST_BIN_NAME} ${OSS_C_SDK_LIBRARY}) 37 | target_link_libraries(${TEST_BIN_NAME} ${APR_UTIL_LIBRARY}) 38 | target_link_libraries(${TEST_BIN_NAME} ${APR_LIBRARY}) 39 | target_link_libraries(${TEST_BIN_NAME} ${MINIXML_LIBRARY}) 40 | target_link_libraries(${TEST_BIN_NAME} ${CURL_LIBRARY}) 41 | target_link_libraries(${TEST_BIN_NAME} ${PTHREAD_LIBRARY}) 42 | target_link_libraries(${TEST_BIN_NAME} ${WOLFSSL_LIBRARY}) 43 | target_link_libraries(${TEST_BIN_NAME} ${RT_LIBRARY}) 44 | target_link_libraries(${TEST_BIN_NAME} ${CMAKE_PROJECT_NAME}_client) 45 | target_link_libraries(${TEST_BIN_NAME} ${CMAKE_PROJECT_NAME}_server) 46 | -------------------------------------------------------------------------------- /test/CuTest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003 Asim Jalis 3 | * 4 | * This software is provided 'as-is', without any express or implied 5 | * warranty. In no event will the authors be held liable for any 6 | * damages arising from the use of this software. 7 | * 8 | * Permission is granted to anyone to use this software for any 9 | * purpose, including commercial applications, and to alter it and 10 | * redistribute it freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you 13 | * must not claim that you wrote the original software. If you use 14 | * this software in a product, an acknowledgment in the product 15 | * documentation would be appreciated but is not required. 16 | * 17 | * 2. Altered source versions must be plainly marked as such, and 18 | * must not be misrepresented as being the original software. 19 | * 20 | * 3. This notice may not be removed or altered from any source 21 | * distribution. 22 | *-------------------------------------------------------------------------* 23 | * 24 | * Originally obtained from "http://cutest.sourceforge.net/" version 1.4. 25 | * 26 | * See CuTest.h for a list of serf-specific modifications. 27 | */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "CuTest.h" 36 | 37 | /*-------------------------------------------------------------------------* 38 | * CuStr 39 | *-------------------------------------------------------------------------*/ 40 | 41 | char* CuStrAlloc(int size) 42 | { 43 | char* newStr = (char*) malloc( sizeof(char) * (size) ); 44 | return newStr; 45 | } 46 | 47 | char* CuStrCopy(const char* old) 48 | { 49 | int len = strlen(old); 50 | char* newStr = CuStrAlloc(len + 1); 51 | strcpy(newStr, old); 52 | return newStr; 53 | } 54 | 55 | /*-------------------------------------------------------------------------* 56 | * CuString 57 | *-------------------------------------------------------------------------*/ 58 | 59 | void CuStringInit(CuString* str) 60 | { 61 | str->length = 0; 62 | str->size = STRING_MAX; 63 | str->buffer = (char*) malloc(sizeof(char) * str->size); 64 | str->buffer[0] = '\0'; 65 | } 66 | 67 | CuString* CuStringNew(void) 68 | { 69 | CuString* str = (CuString*) malloc(sizeof(CuString)); 70 | str->length = 0; 71 | str->size = STRING_MAX; 72 | str->buffer = (char*) malloc(sizeof(char) * str->size); 73 | str->buffer[0] = '\0'; 74 | return str; 75 | } 76 | 77 | void CuStringFree(CuString *str) 78 | { 79 | free(str->buffer); 80 | free(str); 81 | } 82 | 83 | void CuStringResize(CuString* str, int newSize) 84 | { 85 | str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize); 86 | str->size = newSize; 87 | } 88 | 89 | void CuStringAppend(CuString* str, const char* text) 90 | { 91 | int length; 92 | 93 | if (text == NULL) { 94 | text = "NULL"; 95 | } 96 | 97 | length = strlen(text); 98 | if (str->length + length + 1 >= str->size) 99 | CuStringResize(str, str->length + length + 1 + STRING_INC); 100 | str->length += length; 101 | strcat(str->buffer, text); 102 | } 103 | 104 | void CuStringAppendChar(CuString* str, char ch) 105 | { 106 | char text[2]; 107 | text[0] = ch; 108 | text[1] = '\0'; 109 | CuStringAppend(str, text); 110 | } 111 | 112 | void CuStringAppendFormat(CuString* str, const char* format, ...) 113 | { 114 | va_list argp; 115 | char buf[HUGE_STRING_LEN]; 116 | va_start(argp, format); 117 | vsprintf(buf, format, argp); 118 | va_end(argp); 119 | CuStringAppend(str, buf); 120 | } 121 | 122 | void CuStringInsert(CuString* str, const char* text, int pos) 123 | { 124 | int length = strlen(text); 125 | if (pos > str->length) 126 | pos = str->length; 127 | if (str->length + length + 1 >= str->size) 128 | CuStringResize(str, str->length + length + 1 + STRING_INC); 129 | memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1); 130 | str->length += length; 131 | memcpy(str->buffer + pos, text, length); 132 | } 133 | 134 | /*-------------------------------------------------------------------------* 135 | * CuTest 136 | *-------------------------------------------------------------------------*/ 137 | 138 | void CuTestInit(CuTest* t, const char* name, TestFunction function) 139 | { 140 | t->name = CuStrCopy(name); 141 | t->failed = 0; 142 | t->ran = 0; 143 | t->message = NULL; 144 | t->function = function; 145 | t->jumpBuf = NULL; 146 | t->setup = NULL; 147 | t->teardown = NULL; 148 | t->testBaton = NULL; 149 | } 150 | 151 | CuTest* CuTestNew(const char* name, TestFunction function) 152 | { 153 | CuTest* tc = CU_ALLOC(CuTest); 154 | CuTestInit(tc, name, function); 155 | return tc; 156 | } 157 | 158 | void CuTestFree(CuTest* tc) 159 | { 160 | free(tc->name); 161 | free(tc); 162 | } 163 | 164 | void CuTestRun(CuTest* tc) 165 | { 166 | jmp_buf buf; 167 | tc->jumpBuf = &buf; 168 | if (tc->setup) 169 | tc->testBaton = tc->setup(tc); 170 | if (setjmp(buf) == 0) 171 | { 172 | tc->ran = 1; 173 | (tc->function)(tc); 174 | } 175 | if (tc->teardown) 176 | tc->teardown(tc->testBaton); 177 | 178 | tc->jumpBuf = 0; 179 | } 180 | 181 | static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) 182 | { 183 | char buf[HUGE_STRING_LEN]; 184 | 185 | sprintf(buf, "%s:%d: ", file, line); 186 | CuStringInsert(string, buf, 0); 187 | 188 | tc->failed = 1; 189 | tc->message = string->buffer; 190 | if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); 191 | } 192 | 193 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) 194 | { 195 | CuString string; 196 | 197 | CuStringInit(&string); 198 | if (message2 != NULL) 199 | { 200 | CuStringAppend(&string, message2); 201 | CuStringAppend(&string, ": "); 202 | } 203 | CuStringAppend(&string, message); 204 | CuFailInternal(tc, file, line, &string); 205 | } 206 | 207 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition) 208 | { 209 | if (condition) return; 210 | CuFail_Line(tc, file, line, NULL, message); 211 | } 212 | 213 | void CuAssertStrnEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 214 | const char* expected, size_t explen, 215 | const char* actual) 216 | { 217 | CuString string; 218 | if ((explen == 0) || 219 | (expected == NULL && actual == NULL) || 220 | (expected != NULL && actual != NULL && 221 | strncmp(expected, actual, explen) == 0)) 222 | { 223 | return; 224 | } 225 | 226 | CuStringInit(&string); 227 | if (message != NULL) 228 | { 229 | CuStringAppend(&string, message); 230 | CuStringAppend(&string, ": "); 231 | } 232 | CuStringAppend(&string, "expected <"); 233 | CuStringAppend(&string, expected); 234 | CuStringAppend(&string, "> but was <"); 235 | CuStringAppend(&string, actual); 236 | CuStringAppend(&string, ">"); 237 | CuFailInternal(tc, file, line, &string); 238 | } 239 | 240 | void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 241 | const char* expected, const char* actual) 242 | { 243 | CuString string; 244 | if ((expected == NULL && actual == NULL) || 245 | (expected != NULL && actual != NULL && 246 | strcmp(expected, actual) == 0)) 247 | { 248 | return; 249 | } 250 | 251 | CuStringInit(&string); 252 | if (message != NULL) 253 | { 254 | CuStringAppend(&string, message); 255 | CuStringAppend(&string, ": "); 256 | } 257 | CuStringAppend(&string, "expected <"); 258 | CuStringAppend(&string, expected); 259 | CuStringAppend(&string, "> but was <"); 260 | CuStringAppend(&string, actual); 261 | CuStringAppend(&string, ">"); 262 | CuFailInternal(tc, file, line, &string);} 263 | 264 | void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 265 | int expected, int actual) 266 | { 267 | char buf[STRING_MAX]; 268 | if (expected == actual) return; 269 | sprintf(buf, "expected <%d> but was <%d>", expected, actual); 270 | CuFail_Line(tc, file, line, message, buf); 271 | } 272 | 273 | void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 274 | double expected, double actual, double delta) 275 | { 276 | char buf[STRING_MAX]; 277 | if (fabs(expected - actual) <= delta) return; 278 | sprintf(buf, "expected <%lf> but was <%lf>", expected, actual); 279 | CuFail_Line(tc, file, line, message, buf); 280 | } 281 | 282 | void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 283 | void* expected, void* actual) 284 | { 285 | char buf[STRING_MAX]; 286 | if (expected == actual) return; 287 | sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual); 288 | CuFail_Line(tc, file, line, message, buf); 289 | } 290 | 291 | 292 | /*-------------------------------------------------------------------------* 293 | * CuSuite 294 | *-------------------------------------------------------------------------*/ 295 | 296 | void CuSuiteInit(CuSuite* testSuite) 297 | { 298 | testSuite->count = 0; 299 | testSuite->failCount = 0; 300 | testSuite->setup = NULL; 301 | testSuite->teardown = NULL; 302 | } 303 | 304 | CuSuite* CuSuiteNew(void) 305 | { 306 | CuSuite* testSuite = CU_ALLOC(CuSuite); 307 | CuSuiteInit(testSuite); 308 | return testSuite; 309 | } 310 | 311 | void CuSuiteFree(CuSuite *testSuite) 312 | { 313 | free(testSuite); 314 | } 315 | 316 | void CuSuiteFreeDeep(CuSuite *testSuite) 317 | { 318 | int i; 319 | for (i = 0 ; i < testSuite->count ; ++i) 320 | { 321 | CuTest* testCase = testSuite->list[i]; 322 | CuTestFree(testCase); 323 | } 324 | free(testSuite); 325 | } 326 | 327 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) 328 | { 329 | assert(testSuite->count < MAX_TEST_CASES); 330 | testSuite->list[testSuite->count] = testCase; 331 | testSuite->count++; 332 | 333 | /* CuSuiteAdd is called twice per test, don't reset the callbacks if 334 | already set. */ 335 | if (!testCase->setup) 336 | testCase->setup = testSuite->setup; 337 | if (!testCase->teardown) 338 | testCase->teardown = testSuite->teardown; 339 | } 340 | 341 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2) 342 | { 343 | int i; 344 | for (i = 0 ; i < testSuite2->count ; ++i) 345 | { 346 | CuTest* testCase = testSuite2->list[i]; 347 | CuSuiteAdd(testSuite, testCase); 348 | } 349 | } 350 | 351 | void CuSuiteRun(CuSuite* testSuite) 352 | { 353 | int i; 354 | for (i = 0 ; i < testSuite->count ; ++i) 355 | { 356 | CuTest* testCase = testSuite->list[i]; 357 | CuTestRun(testCase); 358 | if (testCase->failed) { testSuite->failCount += 1; } 359 | } 360 | } 361 | 362 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary) 363 | { 364 | int i; 365 | for (i = 0 ; i < testSuite->count ; ++i) 366 | { 367 | CuTest* testCase = testSuite->list[i]; 368 | CuStringAppend(summary, testCase->failed ? "F" : "."); 369 | } 370 | CuStringAppend(summary, "\n\n"); 371 | } 372 | 373 | void CuSuiteDetails(CuSuite* testSuite, CuString* details) 374 | { 375 | int i; 376 | int failCount = 0; 377 | 378 | if (testSuite->failCount == 0) 379 | { 380 | int passCount = testSuite->count - testSuite->failCount; 381 | const char* testWord = passCount == 1 ? "test" : "tests"; 382 | CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord); 383 | } 384 | else 385 | { 386 | if (testSuite->failCount == 1) 387 | CuStringAppend(details, "There was 1 failure:\n"); 388 | else 389 | CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount); 390 | 391 | for (i = 0 ; i < testSuite->count ; ++i) 392 | { 393 | CuTest* testCase = testSuite->list[i]; 394 | if (testCase->failed) 395 | { 396 | failCount++; 397 | CuStringAppendFormat(details, "%d) %s: %s\n", 398 | failCount, testCase->name, testCase->message); 399 | } 400 | } 401 | CuStringAppend(details, "\n!!!FAILURES!!!\n"); 402 | 403 | CuStringAppendFormat(details, "Runs: %d ", testSuite->count); 404 | CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount); 405 | CuStringAppendFormat(details, "Fails: %d\n", testSuite->failCount); 406 | } 407 | } 408 | 409 | void CuSuiteSetSetupTeardownCallbacks(CuSuite* testSuite, TestCallback setup, 410 | TestCallback teardown) 411 | { 412 | testSuite->setup = setup; 413 | testSuite->teardown = teardown; 414 | } 415 | -------------------------------------------------------------------------------- /test/CuTest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003 Asim Jalis 3 | * 4 | * This software is provided 'as-is', without any express or implied 5 | * warranty. In no event will the authors be held liable for any 6 | * damages arising from the use of this software. 7 | * 8 | * Permission is granted to anyone to use this software for any 9 | * purpose, including commercial applications, and to alter it and 10 | * redistribute it freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you 13 | * must not claim that you wrote the original software. If you use 14 | * this software in a product, an acknowledgment in the product 15 | * documentation would be appreciated but is not required. 16 | * 17 | * 2. Altered source versions must be plainly marked as such, and 18 | * must not be misrepresented as being the original software. 19 | * 20 | * 3. This notice may not be removed or altered from any source 21 | * distribution. 22 | *-------------------------------------------------------------------------* 23 | * 24 | * Originally obtained from "http://cutest.sourceforge.net/" version 1.4. 25 | * 26 | * Modified for serf as follows 27 | * 4) added CuSuiteSetSetupTeardownCallbacks to set a constructor and 28 | * destructor per test suite, run for each test. 29 | * 3) added CuAssertStrnEquals(), CuAssertStrnEquals_Msg() and 30 | * CuAssertStrnEquals_LineMsg() 31 | * 2) removed const from struct CuTest.name 32 | * 1) added CuStringFree(), CuTestFree(), CuSuiteFree(), and 33 | * CuSuiteFreeDeep() 34 | * 0) reformatted the whitespace (doh!) 35 | */ 36 | #ifndef CU_TEST_H 37 | #define CU_TEST_H 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | /* CuString */ 45 | 46 | char* CuStrAlloc(int size); 47 | char* CuStrCopy(const char* old); 48 | 49 | #define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE))) 50 | 51 | #define HUGE_STRING_LEN 8192 52 | #define STRING_MAX 256 53 | #define STRING_INC 256 54 | 55 | typedef struct 56 | { 57 | int length; 58 | int size; 59 | char* buffer; 60 | } CuString; 61 | 62 | void CuStringInit(CuString* str); 63 | CuString* CuStringNew(void); 64 | void CuStringFree(CuString *str); 65 | void CuStringRead(CuString* str, const char* path); 66 | void CuStringAppend(CuString* str, const char* text); 67 | void CuStringAppendChar(CuString* str, char ch); 68 | void CuStringAppendFormat(CuString* str, const char* format, ...); 69 | void CuStringInsert(CuString* str, const char* text, int pos); 70 | void CuStringResize(CuString* str, int newSize); 71 | 72 | /* CuTest */ 73 | 74 | typedef struct CuTest CuTest; 75 | 76 | typedef void (*TestFunction)(CuTest *); 77 | 78 | typedef void *(*TestCallback)(void *baton); 79 | 80 | struct CuTest 81 | { 82 | char* name; 83 | TestFunction function; 84 | int failed; 85 | int ran; 86 | const char* message; 87 | jmp_buf *jumpBuf; 88 | 89 | TestCallback setup; 90 | TestCallback teardown; 91 | void *testBaton; 92 | }; 93 | 94 | void CuTestInit(CuTest* t, const char* name, TestFunction function); 95 | CuTest* CuTestNew(const char* name, TestFunction function); 96 | void CuTestFree(CuTest* tc); 97 | void CuTestRun(CuTest* tc); 98 | 99 | /* Internal versions of assert functions -- use the public versions */ 100 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message); 101 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition); 102 | void CuAssertStrEquals_LineMsg(CuTest* tc, 103 | const char* file, int line, const char* message, 104 | const char* expected, const char* actual); 105 | void CuAssertStrnEquals_LineMsg(CuTest* tc, 106 | const char* file, int line, const char* message, 107 | const char* expected, size_t explen, const char* actual); 108 | void CuAssertIntEquals_LineMsg(CuTest* tc, 109 | const char* file, int line, const char* message, 110 | int expected, int actual); 111 | void CuAssertDblEquals_LineMsg(CuTest* tc, 112 | const char* file, int line, const char* message, 113 | double expected, double actual, double delta); 114 | void CuAssertPtrEquals_LineMsg(CuTest* tc, 115 | const char* file, int line, const char* message, 116 | void* expected, void* actual); 117 | 118 | /* public assert functions */ 119 | 120 | #define CuFail(tc, ms) CuFail_Line( (tc), __FILE__, __LINE__, NULL, (ms)) 121 | #define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond)) 122 | #define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond)) 123 | 124 | #define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 125 | #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 126 | #define CuAssertStrnEquals(tc,ex,exlen,ac) CuAssertStrnEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(exlen),(ac)) 127 | #define CuAssertStrnEquals_Msg(tc,ms,ex,exlen,ac) CuAssertStrnEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(exlen),(ac)) 128 | #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 129 | #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 130 | #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) 131 | #define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl)) 132 | #define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 133 | #define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 134 | 135 | #define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL)) 136 | #define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL)) 137 | 138 | /* CuSuite */ 139 | 140 | #define MAX_TEST_CASES 1024 141 | 142 | #define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) 143 | 144 | typedef struct 145 | { 146 | int count; 147 | CuTest* list[MAX_TEST_CASES]; 148 | int failCount; 149 | 150 | TestCallback setup; 151 | TestCallback teardown; 152 | void *testBaton; 153 | } CuSuite; 154 | 155 | 156 | void CuSuiteInit(CuSuite* testSuite); 157 | CuSuite* CuSuiteNew(void); 158 | void CuSuiteFree(CuSuite *testSuite); 159 | void CuSuiteFreeDeep(CuSuite *testSuite); 160 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase); 161 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); 162 | void CuSuiteRun(CuSuite* testSuite); 163 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary); 164 | void CuSuiteDetails(CuSuite* testSuite, CuString* details); 165 | 166 | void CuSuiteSetSetupTeardownCallbacks(CuSuite* testSuite, TestCallback setup, 167 | TestCallback teardown); 168 | 169 | #endif /* CU_TEST_H */ 170 | -------------------------------------------------------------------------------- /test/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | char TEST_OSS_ENDPOINT[] = "oss-cn-hangzhou.aliyuncs.com"; 4 | char TEST_ACCESS_KEY_ID[] = ""; 5 | char TEST_ACCESS_KEY_SECRET[] = ""; 6 | char TEST_BUCKET_NAME[] = ""; 7 | char TEST_ROLE_ARN[] = ""; 8 | -------------------------------------------------------------------------------- /test/config.h: -------------------------------------------------------------------------------- 1 | #ifndef OSS_MEDIA_CONFIG_H 2 | #define OSS_MEDIA_CONFIG_H 3 | 4 | #include "src/oss_media_define.h" 5 | 6 | OSS_MEDIA_CPP_START 7 | 8 | extern char TEST_OSS_ENDPOINT[]; 9 | extern char TEST_ACCESS_KEY_ID[]; 10 | extern char TEST_ACCESS_KEY_SECRET[]; 11 | extern char TEST_BUCKET_NAME[]; 12 | extern char TEST_ROLE_ARN[]; 13 | 14 | OSS_MEDIA_CPP_END 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /test/test.h.in: -------------------------------------------------------------------------------- 1 | #define TEST_DIR "@PROJECT_SOURCE_DIR@/test/" 2 | -------------------------------------------------------------------------------- /test/test_all.c: -------------------------------------------------------------------------------- 1 | #include "CuTest.h" 2 | #include "oss_c_sdk/aos_log.h" 3 | #include "oss_c_sdk/aos_http_io.h" 4 | #include "src/oss_media_define.h" 5 | #include "src/oss_media_client.h" 6 | 7 | extern CuSuite *test_client(); 8 | extern CuSuite *test_server(); 9 | extern CuSuite *test_sts(); 10 | extern CuSuite *test_hls(); 11 | extern CuSuite *test_hls_stream(); 12 | 13 | static const struct testlist { 14 | const char *testname; 15 | CuSuite *(*func)(void); 16 | } tests[] = { 17 | {"test_client", test_client}, 18 | {"test_server", test_server}, 19 | {"test_sts", test_sts}, 20 | {"test_hls", test_hls}, 21 | {"test_hls_stream", test_hls_stream}, 22 | {"LastTest", NULL} 23 | }; 24 | 25 | int run_all_tests(int argc, char *argv[]) 26 | { 27 | int i; 28 | int exit_code; 29 | int list_provided = 0; 30 | CuSuite* suite = NULL; 31 | int j; 32 | int found; 33 | CuSuite *st = NULL; 34 | CuString *output = NULL; 35 | 36 | for (i = 1; i < argc; i++) { 37 | if (!strcmp(argv[i], "-v")) { 38 | continue; 39 | } 40 | if (!strcmp(argv[i], "-l")) { 41 | for (i = 0; tests[i].func != NULL; i++) { 42 | printf("%s\n", tests[i].testname); 43 | } 44 | exit(0); 45 | } 46 | if (argv[i][0] == '-') { 47 | fprintf(stderr, "invalid option: `%s'\n", argv[i]); 48 | exit(1); 49 | } 50 | list_provided = 1; 51 | } 52 | 53 | suite = CuSuiteNew(); 54 | 55 | if (!list_provided) { 56 | /* add everything */ 57 | for (i = 0; tests[i].func != NULL; i++) { 58 | st = tests[i].func(); 59 | CuSuiteAddSuite(suite, st); 60 | CuSuiteFree(st); 61 | } 62 | } else { 63 | /* add only the tests listed */ 64 | for (i = 1; i < argc; i++) { 65 | found = 0; 66 | if (argv[i][0] == '-') { 67 | continue; 68 | } 69 | for (j = 0; tests[j].func != NULL; j++) { 70 | if (!strcmp(argv[i], tests[j].testname)) { 71 | CuSuiteAddSuite(suite, tests[j].func()); 72 | found = 1; 73 | } 74 | } 75 | if (!found) { 76 | fprintf(stderr, "invalid test name: `%s'\n", argv[i]); 77 | exit(1); 78 | } 79 | } 80 | } 81 | 82 | output = CuStringNew(); 83 | CuSuiteRun(suite); 84 | CuSuiteSummary(suite, output); 85 | CuSuiteDetails(suite, output); 86 | printf("%s\n", output->buffer); 87 | 88 | exit_code = suite->failCount > 0 ? 1 : 0; 89 | 90 | CuSuiteFreeDeep(suite); 91 | CuStringFree(output); 92 | 93 | return exit_code; 94 | } 95 | 96 | int main(int argc, char *argv[]) 97 | { 98 | int exit_code; 99 | if (oss_media_init(AOS_LOG_OFF) != AOSE_OK) { 100 | exit(1); 101 | } 102 | 103 | exit_code = run_all_tests(argc, argv); 104 | 105 | oss_media_destroy(); 106 | 107 | return exit_code; 108 | } 109 | -------------------------------------------------------------------------------- /test/test_server.c: -------------------------------------------------------------------------------- 1 | #include "CuTest.h" 2 | #include "test.h" 3 | #include "config.h" 4 | #include "src/oss_media_server.h" 5 | #include 6 | #include 7 | #include 8 | 9 | static void init_media_config(oss_media_config_t *config); 10 | static void write_file(const char* key, oss_media_config_t *config); 11 | static void clear_bucket(oss_media_config_t *config); 12 | 13 | void test_server_setup(CuTest *tc) { 14 | aos_pool_t *p = NULL; 15 | aos_pool_create(&p, NULL); 16 | 17 | apr_dir_make(TEST_DIR"/data/", APR_OS_DEFAULT, p); 18 | 19 | aos_pool_destroy(p); 20 | } 21 | 22 | void test_server_teardown(CuTest *tc) { 23 | aos_pool_t *p = NULL; 24 | aos_pool_create(&p, NULL); 25 | apr_dir_remove(TEST_DIR"/data/", p); 26 | aos_pool_destroy(p); 27 | } 28 | 29 | void test_create_bucket_succeeded(CuTest *tc) { 30 | int ret; 31 | oss_media_config_t config; 32 | char* bucket_name = "test-bucket9"; 33 | 34 | init_media_config(&config); 35 | 36 | ret = oss_media_create_bucket(&config, bucket_name, 37 | OSS_ACL_PRIVATE); 38 | 39 | CuAssertIntEquals(tc, 0, ret); 40 | 41 | ret = oss_media_create_bucket(&config, bucket_name, 42 | OSS_ACL_PUBLIC_READ); 43 | 44 | CuAssertIntEquals(tc, 0, ret); 45 | } 46 | 47 | void test_create_bucket_failed(CuTest *tc) { 48 | int ret; 49 | oss_media_config_t config; 50 | 51 | init_media_config(&config); 52 | 53 | ret = oss_media_create_bucket(&config, "//test", 54 | OSS_ACL_PRIVATE); 55 | 56 | CuAssertIntEquals(tc, -1, ret); 57 | } 58 | 59 | void test_delete_bucket_succeeded(CuTest *tc) { 60 | int ret; 61 | oss_media_config_t config; 62 | char* bucket_name = "test-bucket9"; 63 | 64 | init_media_config(&config); 65 | 66 | ret = oss_media_delete_bucket(&config, bucket_name); 67 | 68 | CuAssertIntEquals(tc, 0, ret); 69 | } 70 | 71 | void test_delete_bucket_failed(CuTest *tc) { 72 | int ret; 73 | oss_media_config_t config; 74 | char* bucket_name = "test-bucket9"; 75 | 76 | init_media_config(&config); 77 | 78 | ret = oss_media_delete_bucket(&config, bucket_name); 79 | 80 | ret = oss_media_delete_bucket(&config, bucket_name); 81 | 82 | CuAssertIntEquals(tc, -1, ret); 83 | } 84 | 85 | void test_create_bucket_lifecycle_succeeded(CuTest *tc) { 86 | int ret; 87 | oss_media_lifecycle_rules_t *rules = NULL; 88 | oss_media_config_t config; 89 | 90 | init_media_config(&config); 91 | 92 | rules = oss_media_create_lifecycle_rules(2); 93 | oss_media_lifecycle_rule_t rule1; 94 | rule1.name = "example-1"; 95 | rule1.path = "/example/1"; 96 | rule1.status = "Enabled"; 97 | rule1.days = 1; 98 | 99 | oss_media_lifecycle_rule_t rule2; 100 | rule2.name = "example-2"; 101 | rule2.path = "/example/2"; 102 | rule2.status = "Disabled"; 103 | rule2.days = 2; 104 | 105 | rules->rules[0] = &rule1; 106 | rules->rules[1] = &rule2; 107 | 108 | ret = oss_media_create_bucket_lifecycle(&config, 109 | TEST_BUCKET_NAME, rules); 110 | 111 | CuAssertIntEquals(tc, 0, ret); 112 | 113 | oss_media_free_lifecycle_rules(rules); 114 | } 115 | 116 | void test_create_bucket_lifecycle_failed(CuTest *tc) { 117 | int ret; 118 | oss_media_lifecycle_rules_t *rules = NULL; 119 | oss_media_config_t config; 120 | 121 | init_media_config(&config); 122 | 123 | rules = oss_media_create_lifecycle_rules(2); 124 | oss_media_lifecycle_rule_t rule1; 125 | rule1.name = "example-1"; 126 | rule1.path = "/example/1"; 127 | rule1.status = "Enabled"; 128 | rule1.days = 1; 129 | 130 | oss_media_lifecycle_rule_t rule2; 131 | rule2.name = "example-2"; 132 | rule2.path = "/example/2"; 133 | rule2.status = "Disabled"; 134 | rule2.days = 2; 135 | 136 | rules->rules[0] = &rule1; 137 | rules->rules[1] = &rule2; 138 | 139 | ret = oss_media_create_bucket_lifecycle(&config, 140 | "bucket-not-exist", rules); 141 | 142 | CuAssertIntEquals(tc, -1, ret); 143 | 144 | oss_media_free_lifecycle_rules(rules); 145 | } 146 | 147 | void test_get_bucket_lifecycle_succeeded(CuTest *tc) { 148 | int ret; 149 | oss_media_lifecycle_rules_t *rules = NULL; 150 | oss_media_config_t config; 151 | 152 | init_media_config(&config); 153 | 154 | rules = oss_media_create_lifecycle_rules(0); 155 | 156 | sleep(1); 157 | ret = oss_media_get_bucket_lifecycle(&config, TEST_BUCKET_NAME, rules); 158 | CuAssertIntEquals(tc, 0, ret); 159 | 160 | CuAssertIntEquals(tc, 2, rules->size); 161 | CuAssertStrEquals(tc, "example-1", rules->rules[0]->name); 162 | CuAssertStrEquals(tc, "/example/1", rules->rules[0]->path); 163 | CuAssertStrEquals(tc, "Enabled", rules->rules[0]->status); 164 | CuAssertIntEquals(tc, 1, rules->rules[0]->days); 165 | CuAssertStrEquals(tc, "example-2", rules->rules[1]->name); 166 | CuAssertStrEquals(tc, "/example/2", rules->rules[1]->path); 167 | CuAssertStrEquals(tc, "Disabled", rules->rules[1]->status); 168 | CuAssertIntEquals(tc, 2, rules->rules[1]->days); 169 | 170 | oss_media_free_lifecycle_rules(rules); 171 | } 172 | 173 | void test_get_bucket_lifecycle_failed(CuTest *tc) { 174 | int ret; 175 | oss_media_lifecycle_rules_t *rules = NULL; 176 | oss_media_config_t config; 177 | 178 | init_media_config(&config); 179 | 180 | rules = oss_media_create_lifecycle_rules(0); 181 | 182 | ret = oss_media_get_bucket_lifecycle(&config, 183 | "bucket-not-exist", rules); 184 | CuAssertIntEquals(tc, -1, ret); 185 | 186 | oss_media_free_lifecycle_rules(rules); 187 | } 188 | 189 | void test_delete_bucket_lifecycle_succeeded(CuTest *tc) { 190 | int ret; 191 | oss_media_config_t config; 192 | 193 | init_media_config(&config); 194 | 195 | ret = oss_media_delete_bucket_lifecycle(&config, TEST_BUCKET_NAME); 196 | 197 | CuAssertIntEquals(tc, 0, ret); 198 | } 199 | 200 | void test_delete_bucket_lifecycle_failed(CuTest *tc) { 201 | int ret; 202 | oss_media_config_t config; 203 | 204 | init_media_config(&config); 205 | 206 | ret = oss_media_delete_bucket_lifecycle(&config, 207 | "bucket-not-exist"); 208 | 209 | CuAssertIntEquals(tc, -1, ret); 210 | } 211 | 212 | void test_delete_file_succeeded(CuTest *tc) { 213 | int ret; 214 | oss_media_config_t config; 215 | char *file = NULL; 216 | 217 | init_media_config(&config); 218 | 219 | file = "oss_media_file"; 220 | write_file(file, &config); 221 | 222 | ret = oss_media_delete_file(&config, TEST_BUCKET_NAME, file); 223 | 224 | CuAssertIntEquals(tc, 0, ret); 225 | 226 | ret = oss_media_delete_file(&config, TEST_BUCKET_NAME, file); 227 | CuAssertIntEquals(tc, 0, ret); 228 | } 229 | 230 | void test_delete_file_failed_with_no_object(CuTest *tc) { 231 | int ret; 232 | oss_media_config_t config; 233 | char *file = NULL; 234 | 235 | init_media_config(&config); 236 | 237 | file = ".not_exist_file"; 238 | ret = oss_media_delete_file(&config, TEST_BUCKET_NAME, file); 239 | 240 | CuAssertIntEquals(tc, 0, ret); 241 | } 242 | 243 | void test_delete_file_failed_with_no_bucket(CuTest *tc) { 244 | int ret; 245 | oss_media_config_t config; 246 | char *file = NULL; 247 | 248 | init_media_config(&config); 249 | 250 | file = "oss_media_file"; 251 | ret = oss_media_delete_file(&config, "bucket-not-exist", file); 252 | 253 | CuAssertIntEquals(tc, -1, ret); 254 | } 255 | 256 | void test_list_files_succeeded(CuTest *tc) { 257 | int ret; 258 | oss_media_config_t config; 259 | oss_media_files_t *file_list = NULL; 260 | char *file1 = NULL; 261 | char *file2 = NULL; 262 | 263 | init_media_config(&config); 264 | clear_bucket(&config); 265 | 266 | file1 = "oss_media_file.1"; 267 | write_file(file1, &config); 268 | 269 | file2 = "oss_media_file.2"; 270 | write_file(file2, &config); 271 | 272 | file_list = oss_media_create_files(); 273 | 274 | ret = oss_media_list_files(&config, TEST_BUCKET_NAME, file_list); 275 | 276 | CuAssertIntEquals(tc, 0, ret); 277 | 278 | ret = oss_media_delete_file(&config, TEST_BUCKET_NAME, file1); 279 | CuAssertIntEquals(tc, 0, ret); 280 | 281 | ret = oss_media_delete_file(&config, TEST_BUCKET_NAME, file2); 282 | CuAssertIntEquals(tc, 0, ret); 283 | 284 | CuAssertStrEquals(tc, NULL, file_list->path); 285 | CuAssertStrEquals(tc, NULL, file_list->marker); 286 | CuAssertIntEquals(tc, 0, file_list->max_size); 287 | CuAssertStrEquals(tc, NULL, file_list->next_marker); 288 | CuAssertIntEquals(tc, 2, file_list->size); 289 | CuAssertStrEquals(tc, file1, file_list->file_names[0]); 290 | CuAssertStrEquals(tc, file2, file_list->file_names[1]); 291 | 292 | oss_media_free_files(file_list); 293 | } 294 | 295 | void test_list_files_succeeded_with_no_file(CuTest *tc) { 296 | int ret; 297 | oss_media_config_t config; 298 | oss_media_files_t *file_list = NULL; 299 | 300 | init_media_config(&config); 301 | 302 | file_list = oss_media_create_files(); 303 | 304 | ret = oss_media_list_files(&config, TEST_BUCKET_NAME, file_list); 305 | 306 | CuAssertIntEquals(tc, 0, ret); 307 | 308 | CuAssertStrEquals(tc, NULL, file_list->path); 309 | CuAssertStrEquals(tc, NULL, file_list->marker); 310 | CuAssertIntEquals(tc, 0, file_list->max_size); 311 | CuAssertStrEquals(tc, NULL, file_list->next_marker); 312 | CuAssertIntEquals(tc, 0, file_list->size); 313 | 314 | oss_media_free_files(file_list); 315 | } 316 | 317 | void test_list_files_failed(CuTest *tc) { 318 | int ret; 319 | oss_media_config_t config; 320 | oss_media_files_t *file_list = NULL; 321 | 322 | init_media_config(&config); 323 | 324 | file_list = oss_media_create_files(); 325 | 326 | ret = oss_media_list_files(&config, "bucket-not-exist", 327 | file_list); 328 | 329 | CuAssertIntEquals(tc, -1, ret); 330 | 331 | oss_media_free_files(file_list); 332 | } 333 | 334 | static void clear_bucket(oss_media_config_t *config) { 335 | int i; 336 | 337 | oss_media_files_t *file_list = oss_media_create_files(); 338 | 339 | oss_media_list_files(config, TEST_BUCKET_NAME, file_list); 340 | for (i = 0; i < file_list->size; i++) { 341 | oss_media_delete_file(config, TEST_BUCKET_NAME, file_list->file_names[i]); 342 | } 343 | oss_media_free_files(file_list); 344 | } 345 | 346 | static void write_file(const char* key, oss_media_config_t *config) { 347 | aos_pool_t *p = NULL; 348 | aos_string_t bucket; 349 | aos_string_t object; 350 | aos_table_t *headers = NULL; 351 | aos_table_t *resp_headers = NULL; 352 | oss_request_options_t *options = NULL; 353 | aos_list_t buffer; 354 | aos_buf_t *content = NULL; 355 | char *str = "test oss c sdk"; 356 | 357 | aos_pool_create(&p, NULL); 358 | options = oss_request_options_create(p); 359 | options->config = oss_config_create(options->pool); 360 | aos_str_set(&options->config->endpoint, config->endpoint); 361 | aos_str_set(&options->config->access_key_id, config->access_key_id); 362 | aos_str_set(&options->config->access_key_secret, config->access_key_secret); 363 | options->config->is_cname = config->is_cname; 364 | options->ctl = aos_http_controller_create(options->pool, 0); 365 | aos_str_set(&bucket, TEST_BUCKET_NAME); 366 | aos_str_set(&object, key); 367 | 368 | aos_list_init(&buffer); 369 | content = aos_buf_pack(options->pool, str, strlen(str)); 370 | aos_list_add_tail(&content->node, &buffer); 371 | 372 | oss_put_object_from_buffer(options, &bucket, &object, 373 | &buffer, headers, &resp_headers); 374 | 375 | aos_pool_destroy(p); 376 | } 377 | 378 | static void init_media_config(oss_media_config_t *config) { 379 | config->endpoint = TEST_OSS_ENDPOINT; 380 | config->access_key_id = TEST_ACCESS_KEY_ID; 381 | config->access_key_secret = TEST_ACCESS_KEY_SECRET; 382 | config->role_arn = TEST_ROLE_ARN; 383 | config->is_cname = 0; 384 | } 385 | 386 | CuSuite *test_server() 387 | { 388 | CuSuite* suite = CuSuiteNew(); 389 | 390 | SUITE_ADD_TEST(suite, test_server_setup); 391 | 392 | SUITE_ADD_TEST(suite, test_create_bucket_succeeded); 393 | SUITE_ADD_TEST(suite, test_create_bucket_failed); 394 | SUITE_ADD_TEST(suite, test_delete_bucket_succeeded); 395 | SUITE_ADD_TEST(suite, test_delete_bucket_failed); 396 | 397 | SUITE_ADD_TEST(suite, test_create_bucket_lifecycle_succeeded); 398 | SUITE_ADD_TEST(suite, test_create_bucket_lifecycle_failed); 399 | SUITE_ADD_TEST(suite, test_get_bucket_lifecycle_succeeded); 400 | SUITE_ADD_TEST(suite, test_get_bucket_lifecycle_failed); 401 | SUITE_ADD_TEST(suite, test_delete_bucket_lifecycle_succeeded); 402 | SUITE_ADD_TEST(suite, test_delete_bucket_lifecycle_failed); 403 | 404 | SUITE_ADD_TEST(suite, test_delete_file_succeeded); 405 | SUITE_ADD_TEST(suite, test_delete_file_failed_with_no_object); 406 | SUITE_ADD_TEST(suite, test_delete_file_failed_with_no_bucket); 407 | 408 | SUITE_ADD_TEST(suite, test_list_files_succeeded); 409 | SUITE_ADD_TEST(suite, test_list_files_succeeded_with_no_file); 410 | SUITE_ADD_TEST(suite, test_list_files_failed); 411 | 412 | SUITE_ADD_TEST(suite, test_server_teardown); 413 | 414 | return suite; 415 | } 416 | -------------------------------------------------------------------------------- /test/test_sts.c: -------------------------------------------------------------------------------- 1 | #include "CuTest.h" 2 | #include "test.h" 3 | #include "config.h" 4 | #include "src/oss_media_server.h" 5 | #include "src/oss_media_client.h" 6 | #include "src/sts/libsts.h" 7 | #include 8 | #include 9 | #include 10 | 11 | static void init_media_config(oss_media_config_t *config); 12 | static void delete_file(oss_media_file_t *file); 13 | static void send_token_to_client(oss_media_token_t token); 14 | static void clear_client_token(); 15 | static void auth_func(oss_media_file_t *file); 16 | 17 | char* global_temp_access_key_id = NULL; 18 | char* global_temp_access_key_secret = NULL; 19 | char* global_temp_token = NULL; 20 | 21 | void test_get_and_use_token_succeeded(CuTest *tc) { 22 | // server get token and send to client 23 | { 24 | int ret; 25 | oss_media_token_t token; 26 | oss_media_config_t config; 27 | 28 | init_media_config(&config); 29 | 30 | ret = oss_media_get_token(&config, TEST_BUCKET_NAME, "/*", "rwa", 31 | 15 * 60, &token); 32 | 33 | CuAssertIntEquals(tc, 0, ret); 34 | 35 | send_token_to_client(token); 36 | } 37 | 38 | // client operation 39 | { 40 | int ret; 41 | int64_t write_size = 0; 42 | oss_media_file_t *file = NULL; 43 | char *content = NULL; 44 | oss_media_file_stat_t stat; 45 | 46 | content = "hello oss media file\n"; 47 | 48 | // open file 49 | file = oss_media_file_open(TEST_BUCKET_NAME, "oss_media_file", 50 | "w", auth_func); 51 | CuAssertTrue(tc, NULL != file); 52 | 53 | // write file 54 | write_size = oss_media_file_write(file, content, strlen(content)); 55 | CuAssertIntEquals(tc, strlen(content), write_size); 56 | 57 | ret = oss_media_file_stat(file, &stat); 58 | CuAssertIntEquals(tc, 0, ret); 59 | 60 | CuAssertStrEquals(tc, "Normal", stat.type); 61 | CuAssertIntEquals(tc, write_size, stat.length); 62 | 63 | delete_file(file); 64 | 65 | // close file 66 | clear_client_token(); 67 | oss_media_file_close(file); 68 | } 69 | } 70 | 71 | void test_get_token_failed_with_expiration_less_than_15m(CuTest *tc) { 72 | int ret; 73 | oss_media_token_t token; 74 | oss_media_config_t config; 75 | 76 | init_media_config(&config); 77 | 78 | ret = oss_media_get_token(&config, TEST_BUCKET_NAME, "/*", "rwa", 79 | 15 * 60 - 1, &token); 80 | 81 | CuAssertIntEquals(tc, -1, ret); 82 | } 83 | 84 | void test_get_token_failed_with_expiration_more_than_1h(CuTest *tc) { 85 | int ret; 86 | oss_media_token_t token; 87 | oss_media_config_t config; 88 | 89 | init_media_config(&config); 90 | 91 | ret = oss_media_get_token(&config, TEST_BUCKET_NAME, "/*", "rwa", 92 | 60 * 60 + 1, &token); 93 | 94 | CuAssertIntEquals(tc, -1, ret); 95 | } 96 | 97 | void test_get_and_use_token_from_policy_succeeded(CuTest *tc) { 98 | oss_media_token_t *token = NULL; 99 | // server get token and send to client 100 | { 101 | int ret; 102 | char *policy = NULL; 103 | oss_media_config_t config; 104 | 105 | policy = "{\n" 106 | "\"Statement\": [\n" 107 | "{" 108 | "\"Action\": \"oss:*\",\n" 109 | "\"Effect\": \"Allow\",\n" 110 | "\"Resource\": \"*\"\n" 111 | "}\n" 112 | "],\n" 113 | "\"Version\": \"1\"\n" 114 | "}\n"; 115 | 116 | init_media_config(&config); 117 | 118 | token = (STSData*) malloc(sizeof(STSData)); 119 | 120 | ret = oss_media_get_token_from_policy(&config, policy, 121 | 15 * 60, token); 122 | 123 | CuAssertIntEquals(tc, 0, ret); 124 | 125 | send_token_to_client(*token); 126 | } 127 | 128 | // client operation 129 | { 130 | int ret; 131 | int64_t write_size = 0; 132 | oss_media_file_t *file = NULL; 133 | char *content = NULL; 134 | oss_media_file_stat_t stat; 135 | 136 | content = "hello oss media file\n"; 137 | 138 | // open file 139 | file = oss_media_file_open(TEST_BUCKET_NAME, "oss_media_file", 140 | "w", auth_func); 141 | CuAssertTrue(tc, NULL != file); 142 | 143 | // write file 144 | write_size = oss_media_file_write(file, content, strlen(content)); 145 | CuAssertIntEquals(tc, strlen(content), write_size); 146 | 147 | ret = oss_media_file_stat(file, &stat); 148 | CuAssertIntEquals(tc, 0, ret); 149 | 150 | CuAssertStrEquals(tc, "Normal", stat.type); 151 | CuAssertIntEquals(tc, write_size, stat.length); 152 | 153 | delete_file(file); 154 | 155 | // close file 156 | clear_client_token(); 157 | free(token); 158 | oss_media_file_close(file); 159 | } 160 | } 161 | 162 | void test_get_and_use_token_from_policy_failed_with_policy_disallow(CuTest *tc) { 163 | oss_media_token_t *token = NULL; 164 | // server get token and send to client 165 | { 166 | int ret; 167 | char *policy = NULL; 168 | oss_media_config_t config; 169 | 170 | policy = "{\n" 171 | "\"Statement\": [\n" 172 | "{" 173 | "\"Action\": \"oss:GetObject\",\n" 174 | "\"Effect\": \"Allow\",\n" 175 | "\"Resource\": \"*\"\n" 176 | "}\n" 177 | "],\n" 178 | "\"Version\": \"1\"\n" 179 | "}\n"; 180 | 181 | init_media_config(&config); 182 | 183 | token = (STSData*) malloc(sizeof(STSData)); 184 | 185 | ret = oss_media_get_token_from_policy(&config, policy, 186 | 15 * 60, token); 187 | 188 | CuAssertIntEquals(tc, 0, ret); 189 | 190 | send_token_to_client(*token); 191 | } 192 | 193 | // client operation 194 | { 195 | int ret; 196 | int64_t write_size = 0; 197 | oss_media_file_t *file = NULL; 198 | char *content = NULL; 199 | oss_media_file_stat_t stat; 200 | 201 | content = "hello oss media file\n"; 202 | 203 | // open file 204 | file = oss_media_file_open(TEST_BUCKET_NAME, "oss_media_file", 205 | "w", auth_func); 206 | CuAssertTrue(tc, NULL != file); 207 | 208 | //write file 209 | write_size = oss_media_file_write(file, content, strlen(content)); 210 | CuAssertIntEquals(tc, -1, write_size); 211 | 212 | sleep(1); 213 | ret = oss_media_file_stat(file, &stat); 214 | CuAssertIntEquals(tc, 0, ret); 215 | 216 | CuAssertStrEquals(tc, "unknown", stat.type); 217 | CuAssertIntEquals(tc, 0, stat.length); 218 | 219 | delete_file(file); 220 | 221 | // close file and free 222 | clear_client_token(); 223 | free(token); 224 | oss_media_file_close(file); 225 | } 226 | } 227 | 228 | static void auth_func(oss_media_file_t *file) { 229 | file->endpoint = TEST_OSS_ENDPOINT; 230 | file->is_cname = 0; 231 | file->access_key_id = global_temp_access_key_id; 232 | file->access_key_secret = global_temp_access_key_secret; 233 | file->token = global_temp_token; 234 | 235 | // expiration 300 sec. 236 | file->expiration = time(NULL) + 300; 237 | } 238 | 239 | static void init_media_config(oss_media_config_t *config) { 240 | config->endpoint = TEST_OSS_ENDPOINT; 241 | config->access_key_id = TEST_ACCESS_KEY_ID; 242 | config->access_key_secret = TEST_ACCESS_KEY_SECRET; 243 | config->role_arn = TEST_ROLE_ARN; 244 | config->is_cname = 0; 245 | } 246 | 247 | static void delete_file(oss_media_file_t *file) { 248 | aos_pool_t *p; 249 | aos_string_t bucket; 250 | aos_string_t object; 251 | oss_request_options_t *options; 252 | aos_table_t *resp_headers; 253 | 254 | aos_pool_create(&p, NULL); 255 | options = oss_request_options_create(p); 256 | options->config = oss_config_create(options->pool); 257 | aos_str_set(&options->config->endpoint, file->endpoint); 258 | aos_str_set(&options->config->access_key_id, TEST_ACCESS_KEY_ID); 259 | aos_str_set(&options->config->access_key_secret, TEST_ACCESS_KEY_SECRET); 260 | options->config->is_cname = file->is_cname; 261 | options->ctl = aos_http_controller_create(options->pool, 0); 262 | aos_str_set(&bucket, file->bucket_name); 263 | aos_str_set(&object, file->object_key); 264 | 265 | oss_delete_object(options, &bucket, &object, &resp_headers); 266 | } 267 | 268 | static void send_token_to_client(oss_media_token_t token) { 269 | global_temp_access_key_id = token.tmpAccessKeyId; 270 | global_temp_access_key_secret = token.tmpAccessKeySecret; 271 | global_temp_token = token.securityToken; 272 | } 273 | 274 | static void clear_client_token() { 275 | global_temp_access_key_id = NULL; 276 | global_temp_access_key_secret = NULL; 277 | global_temp_token = NULL; 278 | } 279 | 280 | CuSuite *test_sts() 281 | { 282 | CuSuite* suite = CuSuiteNew(); 283 | 284 | SUITE_ADD_TEST(suite, test_get_and_use_token_succeeded); 285 | SUITE_ADD_TEST(suite, test_get_token_failed_with_expiration_less_than_15m); 286 | SUITE_ADD_TEST(suite, test_get_token_failed_with_expiration_more_than_1h); 287 | SUITE_ADD_TEST(suite, test_get_and_use_token_from_policy_succeeded); 288 | SUITE_ADD_TEST(suite, test_get_and_use_token_from_policy_failed_with_policy_disallow); 289 | 290 | return suite; 291 | } 292 | --------------------------------------------------------------------------------