diff --git a/.gitignore b/.gitignore index c1970b1..922b8cf 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ release/ *.data *.len + +*.pcap diff --git a/.vscode/launch.json b/.vscode/launch.json index 3576f42..8c8115f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,7 +22,29 @@ "ignoreFailures": true } ], - "preLaunchTask": "C/C++: g++ 生成活动文件", + "miDebuggerPath": "/usr/bin/gdb" + }, + { + "name": "pcapsender", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/release/out/PcapSender", + "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapSender/hw28181.pcap", + "127.0.0.1","30001","udp src port 20180"], + // "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapSender/signhw28181.pcap", + // "127.0.0.1","30001","udp src port 55763"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "为 gdb 启用整齐打印", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], "miDebuggerPath": "/usr/bin/gdb" } ] diff --git a/CMakeLists.txt b/CMakeLists.txt index d8e3ac7..4021aeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Release") message(STATUS "Release版本") set(BuildType "Release") add_definitions(-DNDEBUG) - add_definitions(-DDEBUG_LOG) elseif(${CMAKE_BUILD_TYPE} MATCHES "MinSizeRel") message(STATUS "MinSizeRel版本") set(BuildType "MinSizeRel") @@ -18,23 +17,38 @@ else() add_definitions(-DDUMP_FILE) endif() #加载自定义模块 +add_definitions(-DSIGN_ENABLE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/release/lib) +SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/release/lib/${BuildType}) SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/release/out) set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_C_FLAGS$ "${CMAKE_C_FLAGS} -fvisibility = hidden") -set(CMAKE_CXX_FLAGS$ "${CMAKE_CXX_FLAGS} -fvisibility = hidden") +# set(CMAKE_C_FLAGS$ "${CMAKE_C_FLAGS} -fvisibility = hidden") +# set(CMAKE_CXX_FLAGS$ "${CMAKE_CXX_FLAGS} -fvisibility = hidden") set(SecMedia_Root ${CMAKE_CURRENT_SOURCE_DIR}/src) add_compile_options(-fPIC) -add_compile_options(-fvisibility=hidden) -add_compile_options(-std=c++11) +# add_compile_options(-fvisibility=hidden) +# add_compile_options(-std=c++11) #add_compile_options(-mcpu=cortex-a7 -mfloat-abi=softfp -mfpu=neon-vfpv4 -mno-unaligned-access -fno-aggressive-loop-optimizations -Wcast-align) include(GNUInstallDirs) include(GenerateExportHeader) +#media-server ps dec enc +set(MediaServer_Root ${SecMedia_Root}/3rdpart/media-server) +include_directories(${MediaServer_Root}/librtp/include) +include_directories(${MediaServer_Root}/libmpeg/include) +include_directories(${MediaServer_Root}/libmpeg/source) + +aux_source_directory(${MediaServer_Root}/libmpeg/include src_rtp) +aux_source_directory(${MediaServer_Root}/libmpeg/source src_rtp) +aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) +aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) +aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp) +add_library(rtp STATIC ${src_rtp}) + + include_directories(${SecMedia_Root}/SVAC/src/svac_src) #添加svac解密 @@ -68,7 +82,7 @@ file(GLOB SecMedia_api_list ${CMAKE_CURRENT_SOURCE_DIR}/include/common.h ) add_library(${PROJECT_NAME} SHARED ${SecMedia_src_list}) # add_library(${PROJECT_NAME} STATIC ${SecMedia_src_list}) -target_link_libraries(${PROJECT_NAME} ${LINK_LIB_SVAC_LIST}) +target_link_libraries(${PROJECT_NAME} ${LINK_LIB_SVAC_LIST} rtp) target_include_directories(${PROJECT_NAME} PRIVATE ${SecMedia_Root}/.) # set_target_properties(${PROJECT_NAME} PROPERTIES @@ -107,4 +121,5 @@ endif() -add_subdirectory(test) \ No newline at end of file +add_subdirectory(test) +add_subdirectory(PcapSender) \ No newline at end of file diff --git a/PcapSender/CMakeLists.txt b/PcapSender/CMakeLists.txt new file mode 100644 index 0000000..cd857ee --- /dev/null +++ b/PcapSender/CMakeLists.txt @@ -0,0 +1,16 @@ +include_directories(../include) + +file(GLOB sender_src_list ./*.cpp ./*.h ../include/*.h) +MESSAGE(STATUS ${sender_src_list}) + +find_package(PcapPlusPlus CONFIG REQUIRED) +find_package(DPDK REQUIRED) +message(STATUS "Using Pcap++ ${PcapPlusPlus_VERSION}") +message(STATUS "Include dir: ${PcapPlusPlus_INCLUDE_DIR}") + + +add_executable(PcapSender ${sender_src_list}) + +target_link_libraries(PcapSender PUBLIC ${LINK_LIB_LIST} PUBLIC PcapPlusPlus::Pcap++ PUBLIC DPDK::DPDK) +# target_link_libraries(PcapSender PUBLIC PcapPlusPlus::Pcap++) + diff --git a/PcapSender/main.cpp b/PcapSender/main.cpp new file mode 100644 index 0000000..634e257 --- /dev/null +++ b/PcapSender/main.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HuaWei/HWsign.h" +#include "HuaWei/HWsec.h" + +using namespace std; +using namespace pcpp; +sec_set_info sign_info={ + 2, + "34020000001320000003", + "2021-07-06T17:27:19.000", + 32, + 64, + { + 0x24,0x88,0xc8,0xdc,0x7f,0xd7,0xe0,0x91,0x30,0x1b,0x5c,0x58,0x2f,0xe7,0x44,0x7d, + 0x2f,0x43,0xe4,0xee,0xc8,0x7d,0xc0,0xfb,0xa4,0xb8,0x7d,0x4b,0x8a,0x69,0x7c,0x4e + }, + { + 0xaa,0xb1,0x3f,0xd7,0x66,0xe2,0x75,0x97,0xc0,0x03,0xe6,0xe4,0x1d,0x77,0x54,0x78, + 0xc8,0x29,0xb2,0x0b,0x9e,0xd1,0xff,0xa3,0x6a,0x6f,0xd2,0x7f,0xd6,0x2d,0xaa,0x3f, + 0xc9,0x24,0xec,0x6c,0x96,0x0a,0x7b,0x73,0xf6,0xe6,0xfc,0xda,0x3a,0x08,0xfd,0x92, + 0xfc,0x00,0x08,0x97,0x78,0x2c,0x71,0x6b,0xe1,0x26,0xf5,0x1e,0xba,0x31,0xf5,0xb2, + } + }; +void * EncrypInit(){ + // auto Verify_handle=HWVerify_init(); + + auto sign_handle=GB28181_stream_init(&sign_info); //HWSign_init(&sign_info); + return sign_handle; +} +std::string getProtocolTypeAsString(pcpp::ProtocolType protocolType) +{ + switch (protocolType) + { + case pcpp::Ethernet: + return "Ethernet"; + case pcpp::IPv4: + return "IPv4"; + case pcpp::TCP: + return "TCP"; + case pcpp::HTTPRequest: + case pcpp::HTTPResponse: + return "HTTP"; + case pcpp::UDP: + return "UDP"; + default: + return "Unknown"; + } + +} + +inline void sleep(timeval & delta){ + // delta.tv_sec + select(0,NULL,NULL,NULL,&delta); +} +template +timespec TimeDiff(T && minu,U && sub){ + timespec deltatime; + deltatime.tv_nsec=minu.tv_nsec-sub.tv_nsec; + deltatime.tv_sec=minu.tv_sec-sub.tv_sec; + if(deltatime.tv_nsec<0 && deltatime.tv_sec>0){ + deltatime.tv_nsec+=1000000000; + deltatime.tv_sec--; + } + return move(deltatime); +} + +int ReadPcapAndSend(int socket,sockaddr_in & addr,const string & filename,const string & filter,void * sign_handle){ + auto reader=pcpp::IFileReaderDevice::getReader(filename); + // verify that a reader interface was indeed created + if (reader == NULL) + { + std::cerr << "Cannot determine reader for file type" << std::endl; + return 1; + } + // open the reader for reading + if (!reader->open()) + { + std::cerr << "Cannot open input.pcap for reading" << std::endl; + return 1; + } + + if (!reader->setFilter(filter)) + { + std::cerr << "Cannot set filter for file reader" << std::endl; + return 1; + } + + uint8_t * payload; + size_t payload_len; + pcpp::RawPacket rawPacket; + bool first_flag=true; + timespec nowtime,gaptime; + timespec real_now; + timespec inital_time,current_time; + static char sign_out_buf[3096]; + unsigned int sign_out_len; + void *param=nullptr; + uint16_t offset_len,append_len; + while (reader->getNextPacket(rawPacket)) + { + pcpp::Packet parsedPacket(&rawPacket,OsiModelTransportLayer); + auto layer = parsedPacket.getLayerOfType(false); + + if(layer){ + payload=layer->getLayerPayload(); + payload_len=layer->getLayerPayloadSize(); + // cout<<" payload_len:" << payload_len<=0 && gaptime.tv_sec>=0) + { + nanosleep(&gaptime,NULL); + }else{ + // cout<<" s:" << gaptime.tv_sec<<" ns:" << gaptime.tv_nsec <getNextPacket(rawPacket); + + // for (pcpp::Layer* curLayer = parsedPacket.getFirstLayer(); curLayer != NULL; curLayer = curLayer->getNextLayer()) + // { + // std::cout + // << "Layer type: " <getProtocol()) << "; " // get layer type + // << "Total data: " << curLayer->getDataLen() << " [bytes]; " // get total length of the layer + // << "Layer data: " << curLayer->getHeaderLen() << " [bytes]; " // get the header length of the layer + // << "Layer payload: " << curLayer->getLayerPayloadSize() << " [bytes]" // get the payload length of the layer (equals total length minus header length) + // << std::endl; + // } + // create the stats object + pcpp::IPcapDevice::PcapStats stats; + reader->getStatistics(stats); + std::cout << "Read " << stats.packetsRecv << " packets successfully and " << stats.packetsDrop << " packets could not be read" << std::flush; + return 0; +} +int main(int argc, char *argv[]){ + + char ip[16] = {0}; + int sockfd, port; + if (argc>4) + { + string filename(argv[1]); + strcpy(ip, argv[2]); + port = atoi(argv[3]); + string filter(argv[4]); + printf("filename:%s\nip:%s\nport:%d\nfilter:%s\n", filename.data(), ip,port,filter.data()); + + sockfd = socket(AF_INET, SOCK_DGRAM , IPPROTO_UDP);//SOCK_STREAM + if (sockfd == -1) { + printf("create socket failed : %s\n", strerror(errno)); + return -1; + } + int rcvBufSize = 2097152; + int rcvlen = sizeof(rcvBufSize); + setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&rcvBufSize, sizeof(rcvBufSize)); + if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&rcvBufSize, (socklen_t *)&rcvlen) >= 0) + { + printf("set udp socket send buff size to : %d\n", rcvBufSize); + if (rcvBufSize < 4194304) + { + printf("socket send buff too small, please set up to 2097152(2M)"); + } + }else { + printf("socket failed : %s\n", strerror(errno)); + return -1; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr =inet_addr(ip); + // if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr))==-1){ + // printf("connection error : %s\n", strerror(errno)); + // return -1; + // } + // bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); + // while (1) + // { + cout.flush(); + auto sign_h=EncrypInit(); + ReadPcapAndSend(sockfd,addr,filename,filter,sign_h); + // } + + } + else + { + printf("CMD as: filename.pcap ip port\n"); + system("pause"); + return 0; + } + return 0; + +} \ No newline at end of file diff --git a/include/HWsign.h b/include/HWsign.h index ccc8724..42004ff 100644 --- a/include/HWsign.h +++ b/include/HWsign.h @@ -10,10 +10,19 @@ API_EXPORT int SDF_Device_open(); API_EXPORT int SDF_Device_close(); +struct sec_set_info{ + uint8_t camera_id[20]; + uint8_t vkek_version[32]; // 秘钥的加密秘钥的版本号 + uint8_t prikey_size; + uint8_t pubkey_size; + uint8_t prikey[64]; + uint8_t pubkey[128]; +}; -API_EXPORT void * HWSign_init(); + +API_EXPORT void * HWSign_init(struct sec_set_info * sign_info); API_EXPORT void HWSign_release(void* Handle); -API_EXPORT int HWSign_rtp_input(void* Handle, const char * buf, const uint32_t len,int tcp, void * param); +API_EXPORT int HWSign_rtp_input(void* Handle, const char * buf, const uint32_t len, void * param); API_EXPORT int HWSign_rtp_out(void* Handle, char * buf, uint32_t * len, void ** param); API_EXPORT void * HWSign_tcp_init(); diff --git a/src/3rdpart/media-server/.DS_Store b/src/3rdpart/media-server/.DS_Store new file mode 100644 index 0000000..60e5a77 Binary files /dev/null and b/src/3rdpart/media-server/.DS_Store differ diff --git a/src/3rdpart/media-server/.travis.yml b/src/3rdpart/media-server/.travis.yml new file mode 100644 index 0000000..1401bd6 --- /dev/null +++ b/src/3rdpart/media-server/.travis.yml @@ -0,0 +1,28 @@ +language: c +install: true +before_script: + - wget https://github.com/ireader/sdk/archive/master.zip -O /tmp/sdk.zip + - unzip /tmp/sdk.zip + - echo $PWD + - mv sdk-master ../sdk + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "I/9S/L5PVzty9kcCcWKPnF3rlZ1FqVIBMCqZkjf2zx6Pl+FHQoAe/9KSdRFtKfzM+GxRu1ttxvmNignghgO60QGS7tvU79N/u/5o4xZ1UdCotGmFmwytEZ+szuTrH2PuSlA3p1opcvuWsrcIpUFXBU/qp9VYZl8v8L6qpir9vtAeMGjMGSJO4pC5ePnv4oxtrLLY1u+uPVMdqMmK2d1sHOYZy166hn89IgM1hU7kqyYAeyhU6bXEWHKtEEApIwvWSciSFFl0J+EiWDynjnPtIWaNXrIG5Wf1uJ9gQYU/u76NQhxbBouSrGyps4hrmuF2gmGAhPGIIlQlzU/DDnLqRT2fKgy4yhfMylTy+zXMMi0jBDyFGjPYjnDYjWEUNkG79AUJ6t/fe8QPUc5w88p5Hbq3ljom+yflshPshlVdWyqCxBB5aw/oOnslgnEVNkNBEivXcpHhELbYu79NwmrkRSUaBzb77nuue5MsfMSkFISiMvHRPF9FzOO+2NnycGQrLpj14bNIJoNbwtQO40BoeSVYPjBlKNf62vhM2Xf2pm7SpGuezH2IvS0ulLB4Di4ap5vL+MIJer6mQ8xsRT7CQO7r1d1dFp2yqB3dOEERqywd5xl+tfgNy76TwXyRM4u5RbOMJSgwLJE59rHg3RQ8ApEB3P1Bvot838P7Pp+vv5I=" + +before_install: + - echo -n | openssl s_client -connect https://scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- + +addons: + coverity_scan: + project: + name: "ireader/media-server" + description: "media-server" + notification_email: tao3@outlook.com + build_command_prepend: "make clean" + build_command: "make" + branch_pattern: coverity_scan + +script: make diff --git a/src/3rdpart/media-server/Android.mk b/src/3rdpart/media-server/Android.mk new file mode 100644 index 0000000..c89d386 --- /dev/null +++ b/src/3rdpart/media-server/Android.mk @@ -0,0 +1,72 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := dash +LOCAL_SRC_FILES := libdash/obj/local/$(TARGET_ARCH_ABI)/libdash.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := flv +LOCAL_SRC_FILES := libflv/obj/local/$(TARGET_ARCH_ABI)/libflv.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := hls +LOCAL_SRC_FILES := libhls/obj/local/$(TARGET_ARCH_ABI)/libhls.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := mov +LOCAL_SRC_FILES := libmov/obj/local/$(TARGET_ARCH_ABI)/libmov.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := mpeg +LOCAL_SRC_FILES := libmpeg/obj/local/$(TARGET_ARCH_ABI)/libmpeg.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := rtmp +LOCAL_SRC_FILES := librtmp/obj/local/$(TARGET_ARCH_ABI)/librtmp.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := rtp +LOCAL_SRC_FILES := librtp/obj/local/$(TARGET_ARCH_ABI)/librtp.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := rtsp +LOCAL_SRC_FILES := librtsp/obj/local/$(TARGET_ARCH_ABI)/librtsp.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_MODULE := sip +LOCAL_SRC_FILES := libsip/obj/local/$(TARGET_ARCH_ABI)/libsip.a +include $(PREBUILT_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_CFLAGS += -DOS_ANDROID -DOS_LINUX +LOCAL_LDLIBS += -llog -landroid + +LOCAL_C_INCLUDES := . +LOCAL_C_INCLUDES += $(LOCAL_PATH) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../sdk/include + +LOCAL_SRC_FILES := test/test.cpp + +LOCAL_SHARED_LIBRARIES := +LOCAL_STATIC_LIBRARIES := + +LOCAL_MODULE := test +include $(BUILD_SHARED_LIBRARY) diff --git a/src/3rdpart/media-server/Application.mk b/src/3rdpart/media-server/Application.mk new file mode 100644 index 0000000..08c5b6f --- /dev/null +++ b/src/3rdpart/media-server/Application.mk @@ -0,0 +1,4 @@ +APP_BUILD_SCRIPT := ./Android.mk +APP_STL := gnustl_static +APP_ABI := armeabi-v7a #arm64-v8a +#APP_PLATFORM := android-21 #SLES SLAndroidDataFormat_PCM_EX diff --git a/src/3rdpart/media-server/LICENSE b/src/3rdpart/media-server/LICENSE new file mode 100644 index 0000000..8ae5344 --- /dev/null +++ b/src/3rdpart/media-server/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 chen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/3rdpart/media-server/Makefile b/src/3rdpart/media-server/Makefile new file mode 100644 index 0000000..687b0f4 --- /dev/null +++ b/src/3rdpart/media-server/Makefile @@ -0,0 +1,41 @@ +ifdef PLATFORM + CROSS:=$(PLATFORM)- +else + CROSS:= + PLATFORM:=linux +endif + +ifeq ($(RELEASE),1) + BUILD:=release +else + BUILD:=debug +endif + +all: + $(MAKE) -C libflv + $(MAKE) -C librtmp + $(MAKE) -C libmpeg + $(MAKE) -C libhls + $(MAKE) -C librtp + $(MAKE) -C librtsp + $(MAKE) -C libmov + $(MAKE) -C libdash + $(MAKE) -C libsip + +clean: + $(MAKE) -C libflv clean + $(MAKE) -C librtmp clean + $(MAKE) -C libmpeg clean + $(MAKE) -C libhls clean + $(MAKE) -C librtp clean + $(MAKE) -C librtsp clean + $(MAKE) -C libmov clean + $(MAKE) -C libdash clean + $(MAKE) -C libsip clean + $(MAKE) -C test clean + +.PHONY : test +test: + $(MAKE) -C ../sdk + $(MAKE) -C test + ln -sf ../sdk/libaio/$(BUILD).$(PLATFORM)/libaio.so . && ./test/$(BUILD).$(PLATFORM)/test diff --git a/src/3rdpart/media-server/README.md b/src/3rdpart/media-server/README.md new file mode 100644 index 0000000..f3f4acf --- /dev/null +++ b/src/3rdpart/media-server/README.md @@ -0,0 +1,64 @@ +* Build status: [![Build Status](https://travis-ci.org/ireader/media-server.svg?branch=master)](https://travis-ci.org/ireader/media-server) Coverity Scan Build Status +* Build Dependence: https://github.com/ireader/sdk + +# libflv +1. Adobe FLV muxer/demuxer +2. MPEG-4 AVCDecoderConfigurationRecord/HEVCDecoderConfigurationRecord/AV1CodecConfigurationRecord/VPCodecConfigurationRecord/AudioSpecificConfig +3. H.264/H.265 AnnexB to/from MP4 stream +4. AAC ADTS to/from ASC/MUX +5. FLV with H.264/H.264/AV1/VPX(vp8/vp9/vp10) +6. FLV with AAC/mp3/G.711/Opus + +# librtmp +1. rtmp-client: RTMP publish/play +2. rtmp-server: RTMP Server live/vod streaming + +# libmpeg +1. MPEG-2 PS packer/unpacker +2. MPEG-2 TS packer/unpacker +3. ps/ts with H.264/H.265/AAC/MP3/G.711/Opus + +# librtp +1. RFC3550 RTP/RTCP +2. RTP with H.264/H.265/MPEG-2/MPEG-4/VP8/VP9/AV1 +2. RTP with G.711/G.726/G.729/MP3/AAC/Opus +3. RTP with MPEG-2 PS/TS + +# librtsp +1. RFC 2326 RTSP client +2. RFC 2326 RTSP Server +3. RTSP parser +4. RFC 4566 SDP parser +5. SDP with H.264/H.265/AAC/Opus/G.711 fmtp + +# libhls +1. HLS Media: TS segmenter +2. HLS M3U8: generate m3u8 file +3. HLS fmp4 segmenter +4. HLS Master/Playlist m3u8 parser + +# libdash +1. ISO/IEC 23009-1 MPEG-DASH static(vod) +2. ISO/IEC 23009-1 MPEG-DASH dynamic(live) +3. DASH MPD v3/v4 parser + +# libmov +1. MP4 File reader/writer +2. MP4 faststart(moov box before mdat) +3. Fragment MP4 writer +4. MP4 with H.264/H.265/AV1/VP9 +5. MP4 with AAC/Opus/MP3/G.711 + +# libsip +1. sip user-agent (UAC/UAS) +2. sip with ICE + +# libhttp(https://github.com/ireader/sdk) +1. HTTP Server(base AIO) +2. HTTP Client +3. HTTP Cookie + +### Make +1. make clean && make +2. make RELEASE=1 (make release library, default debug) +3. make PLATFORM=arm-hisiv100nptl-linux (cross compile) diff --git a/src/3rdpart/media-server/gcc.mk b/src/3rdpart/media-server/gcc.mk new file mode 100644 index 0000000..756d40a --- /dev/null +++ b/src/3rdpart/media-server/gcc.mk @@ -0,0 +1,96 @@ +RELEASE ?= 0 # default debug +UNICODE ?= 0 # default ansi + +ifdef PLATFORM + CROSS:=$(PLATFORM)- +else + CROSS:= + PLATFORM:=linux +endif + +ifeq ($(RELEASE),1) + BUILD:=release +else + BUILD:=debug +endif + +KERNEL := $(shell uname -s) +ifeq ($(KERNEL),Linux) + DEFINES += OS_LINUX +endif +ifeq ($(KERNEL),Darwin) + DEFINES += OS_MAC +endif + +#--------------------------------Compile----------------------------- +# +#-------------------------------------------------------------------- +AR := $(CROSS)ar +CC := $(CROSS)gcc +CXX := $(CROSS)g++ +CFLAGS += -Wall -fPIC +CXXFLAGS += -Wall +DEPFLAGS = -MMD -MP -MF $(OUTPATH)/$(*F).d + +ifeq ($(RELEASE),1) + CFLAGS += -Wall -O2 + CXXFLAGS += $(CFLAGS) + DEFINES += NDEBUG +else + CFLAGS += -g -Wall +# CFLAGS += -fsanitize=address + CXXFLAGS += $(CFLAGS) + DEFINES += DEBUG _DEBUG +endif + +# default don't export anything +CFLAGS += -fvisibility=hidden + +COMPILE.CC = $(CC) $(addprefix -I,$(INCLUDES)) $(addprefix -D,$(DEFINES)) $(CFLAGS) +COMPILE.CXX = $(CXX) $(addprefix -I,$(INCLUDES)) $(addprefix -D,$(DEFINES)) $(CXXFLAGS) + +#-------------------------Compile Output--------------------------- +# +#-------------------------------------------------------------------- +ifeq ($(UNICODE),1) + OUTPATH += unicode.$(BUILD).$(PLATFORM) +else + OUTPATH += $(BUILD).$(PLATFORM) +endif + +# make output dir +$(shell mkdir -p $(OUTPATH) > /dev/null) + +OBJECT_FILES := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE_FILES))) +DEPENDENCE_FILES := $(OBJECT_FILES:%.o=%.d) +DEPENDENCE_FILES := $(foreach file,$(DEPENDENCE_FILES),$(OUTPATH)/$(notdir $(file))) + +#--------------------------Makefile Rules---------------------------- +# +#-------------------------------------------------------------------- +$(OUTPATH)/$(OUTFILE): $(OBJECT_FILES) $(STATIC_LIBS) +ifeq ($(OUTTYPE),0) + $(CXX) -o $@ -Wl,-rpath . $(LDFLAGS) $^ $(addprefix -L,$(LIBPATHS)) $(addprefix -l,$(LIBS)) +else +ifeq ($(OUTTYPE),1) + $(CXX) -o $@ -shared -fpic -rdynamic -Wl,-rpath . $(LDFLAGS) $^ $(addprefix -L,$(LIBPATHS)) $(addprefix -l,$(LIBS)) +else + $(AR) -rc $@ $^ +endif +endif + @echo make ok, output: $(OUTPATH)/$(OUTFILE) + +%.o : %.c + $(COMPILE.CC) -c $(DEPFLAGS) -o $@ $< + +%.o : %.cpp + $(COMPILE.CXX) -c $(DEPFLAGS) -o $@ $< + +-include $(DEPENDENCE_FILES) + +version.h : version.ver + $(ROOT)/svnver.sh version.ver version.h + +.PHONY: clean +clean: + rm -f $(OBJECT_FILES) $(OUTPATH)/$(OUTFILE) $(DEPENDENCE_FILES) diff --git a/src/3rdpart/media-server/libdash/Android.mk b/src/3rdpart/media-server/libdash/Android.mk new file mode 100644 index 0000000..1a351c2 --- /dev/null +++ b/src/3rdpart/media-server/libdash/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS += -DOS_LINUX -DOS_ANDROID +LOCAL_LDLIBS += + +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libmov/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libhls/include + +LOCAL_SRC_FILES := $(wildcard src/*.c) +LOCAL_SRC_FILES += $(wildcard src/*.cpp) + +LOCAL_MODULE := dash +include $(BUILD_STATIC_LIBRARY) diff --git a/src/3rdpart/media-server/libdash/Makefile b/src/3rdpart/media-server/libdash/Makefile new file mode 100644 index 0000000..2c08511 --- /dev/null +++ b/src/3rdpart/media-server/libdash/Makefile @@ -0,0 +1,45 @@ +#--------------------------------Output------------------------------ +# OUTTYPE: 0-exe, 1-dll, 2-static +#-------------------------------------------------------------------- +OUTTYPE = 2 +OUTFILE = libdash.a + +#-------------------------------Include------------------------------ +# +# INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix +#-------------------------------------------------------------------- +INCLUDES = . \ + ./include \ + ../libmov/include \ + ../libhls/include + +#-------------------------------Source------------------------------- +# +#-------------------------------------------------------------------- +SOURCE_PATHS = src +SOURCE_FILES = $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.cpp)) +SOURCE_FILES += $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.c)) + +#-----------------------------Library-------------------------------- +# +# LIBPATHS = $(addprefix -L,$(LIBPATHS)) # add -L prefix +#-------------------------------------------------------------------- +LIBPATHS = +ifdef RELEASE +# relase library path +LIBPATHS += +else +LIBPATHS += +endif + +LIBS = + +STATIC_LIBS = + +#-----------------------------DEFINES-------------------------------- +# +# DEFINES := $(addprefix -D,$(DEFINES)) # add -L prefix +#-------------------------------------------------------------------- +DEFINES = + +include ../gcc.mk diff --git a/src/3rdpart/media-server/libdash/include/dash-mpd.h b/src/3rdpart/media-server/libdash/include/dash-mpd.h new file mode 100644 index 0000000..a3d4634 --- /dev/null +++ b/src/3rdpart/media-server/libdash/include/dash-mpd.h @@ -0,0 +1,31 @@ +#ifndef _dash_mpd_h_ +#define _dash_mpd_h_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dash_mpd_t dash_mpd_t; + +typedef int (*dash_mpd_segment)(void* param, int adapation, const void* data, size_t bytes, int64_t pts, int64_t dts, int64_t duration, const char* name); + +dash_mpd_t* dash_mpd_create(int flags, dash_mpd_segment handler, void* param); +void dash_mpd_destroy(dash_mpd_t* mpd); + +/// @param[in] prefix dash adapation set name prefix +/// @return >=0-adapation id, <0-error +int dash_mpd_add_video_adaptation_set(dash_mpd_t* mpd, const char* prefix, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size); +int dash_mpd_add_audio_adaptation_set(dash_mpd_t* mpd, const char* prefix, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size); + +/// @param[in] adapation create by dash_mpd_add_video_adapation_set/dash_mpd_add_audio_adapation_set +int dash_mpd_input(dash_mpd_t* mpd, int adapation, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags); + +size_t dash_mpd_playlist(dash_mpd_t* mpd, char* playlist, size_t bytes); + +#ifdef __cplusplus +} +#endif +#endif /* !_dash_mpd_h_ */ diff --git a/src/3rdpart/media-server/libdash/include/dash-parser.h b/src/3rdpart/media-server/libdash/include/dash-parser.h new file mode 100644 index 0000000..0e08ff9 --- /dev/null +++ b/src/3rdpart/media-server/libdash/include/dash-parser.h @@ -0,0 +1,483 @@ +#ifndef _dash_parser_h_ +#define _dash_parser_h_ + +#include "dash-proto.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + DASH_SEGMENT_NONE = 0, + DASH_SEGMENT_BASE, + DASH_SEGMENT_LIST, + DASH_SEGMENT_TEMPLATE, +}; + +// https://tools.ietf.org/html/rfc6838 +enum +{ + DASH_MEDIA_UNKNOWN, + DASH_MEDIA_FONT, // font + DASH_MEDIA_TEXT, // text + DASH_MEDIA_IMAGE, // image + DASH_MEDIA_AUDIO, // audio + DASH_MEDIA_VIDEO, // video + DASH_MEDIA_APPLICATION, // application +}; + +// 5.3.9.4.4 Template-based Segment URL construction +// @media, @index, @initialization, @bitstreamSwitching +// 1. Either $Number$ or $Time$ may be used but not both at the same time. +// 2. $SubNumber$ shall only be present if either $Number$ or $Time$ are present as well +enum +{ + DASH_TEMPLATE_UNKNOWN, + DASH_TEMPLATE_REPRESENTATIONID, // Representation@id + DASH_TEMPLATE_NUMBER, + DASH_TEMPLATE_BANDWIDTH, // Representation@bandwidth + DASH_TEMPLATE_TIME, // SegmentTimeline@t + DASH_TEMPLATE_SUBNUMBER, +}; + +enum { DASH_SCAN_UNKNOWN = 0, DASH_SCAN_PROGRESSIVE, DASH_SCAN_INTERLACED, }; + +struct dash_urltype_t +{ + char* source_url; + char* range; +}; + +// BaseURL +struct dash_url_t +{ + size_t count; + struct + { + char* uri; + char* service_location; + char* byte_range; + double availability_time_offset; + int availability_time_complete; // boolean + } *urls; +}; + +struct dash_event_t +{ + uint64_t presentation_time; + uint64_t duration; + unsigned int id; + char* message_data; +}; + +struct dash_event_stream_t +{ + char* href; + char* actuate; // default: onRequest + char* scheme_id_uri; // SchemeIdUri + char* value; + unsigned int timescale; + + size_t event_count; + struct dash_event_t* events; +}; + +struct dash_label_t +{ + size_t count; + struct + { + char* label; + unsigned int id; + char* lang; + }*labels; +}; + +struct dash_program_information_t +{ + char* lang; + char* more_information; + + char* title; + char* source; + char* copyright; +}; + +struct dash_descriptor_t +{ + size_t count; + struct + { + char* scheme_uri; + char* value; + char* id; + }* descs; +}; + +struct dash_metric_t +{ + char* metrics; + + size_t range_count; + struct + { + int64_t time; + int64_t duration; + }* ranges; + + struct dash_descriptor_t reportings; +}; + +struct dash_segment_url_t +{ + char* media; + char* media_range; + char* index; + char* index_range; +}; + +struct dash_segment_timeline_t +{ + size_t count; + + struct + { + uint64_t t; + uint64_t n; // specifies the Segment number of the first Segment in the series. + uint64_t d; // Any @d value shall not exceed the value of MPD@maxSegmentDuration + uint64_t k; // default 1 + int r; // default 0 + }* S; +}; + +struct dash_segment_t +{ + int type; // DASH_SEGMENT_BASE/DASH_SEGMENT_LIST/DASH_SEGMENT_TEMPLATE + + // segment base + unsigned int timescale; + uint64_t presentation_time_offset; + uint64_t presentation_duration; + int64_t time_shift_buffer_depth; + char* index_range; + int index_range_exact; // 0-false, 1-true + double availability_time_offset; + int availability_time_complete; + struct dash_urltype_t initialization; // Initialization + struct dash_urltype_t representation_index; // RepresentationIndex + + // Multiple segment base + uint64_t start_number; + // All Segments within this Representation element have the same duration + // unless it is the last Segment within the Period, which can be significantly shorter. + unsigned int duration; // specifies the constant approximate Segment duration. + struct dash_segment_timeline_t segment_timeline; // SegmentTimeline + struct dash_urltype_t bitstream_switching; // BitstreamSwitching + + // segment list + char* href; + char* actuate; // default: onRequest + size_t segment_url_count; + struct dash_segment_url_t* segment_urls; // SegmentURL + + // segment template + char* media; + char* index; + char* initialization_url; // initialization + char* bitstream_switching_url; +}; + +struct dash_content_component_t +{ + unsigned int id; + char* lang; + char* content_type; + char* par; + char* tag; + + struct dash_descriptor_t accessibilities; // Accessibility + struct dash_descriptor_t roles; // Role + struct dash_descriptor_t ratings; // Rating + struct dash_descriptor_t viewpoints; // Viewpoint +}; + +struct dash_representation_base_t +{ + char* profiles; + unsigned int width; + unsigned int height; + char* sar; + char* frame_rate; + char* audio_sampling_rate; + char* mime_type; + char* segment_profiles; + char* codecs; + double maxmum_sap_period; + char* start_with_sap; + double max_playout_rate; + int coding_dependency; + int scan_type; // progressive, interlaced + unsigned int selection_priority; // default 1 + int tag; + + struct dash_descriptor_t frame_packings; // FramePacking + struct dash_descriptor_t audio_channel_configurations; // AudioChannelConfiguration + struct dash_descriptor_t content_protections; // ContentProtection + struct dash_descriptor_t essentials; // EssentialProperty + struct dash_descriptor_t supplementals; // SupplementalProperty + + size_t inband_event_stream_count; + struct dash_event_stream_t* inband_event_streams; // InbandEventStream + + size_t switching_count; + struct + { + unsigned int interval; + int type; // media, bitstream + } *switchings; // Switching + + size_t random_access_count; + struct + { + unsigned int interval; + int type; // closed, open, gradual + int64_t min_buffer_time; + unsigned int bandwidth; + } *random_accesses; // RandomAccess + + struct dash_label_t group_labels; // GroupLabel + struct dash_label_t labels; // Label +}; + +struct dash_preselection_t +{ + char* id; + char* preselection_compoents; + char* lang; + + struct dash_representation_base_t base; + + struct dash_descriptor_t accessibilities; // Accessibility + struct dash_descriptor_t roles; // Role + struct dash_descriptor_t ratings; // Rating + struct dash_descriptor_t viewpoints; // Viewpoint +}; + +struct dash_subrepresentation_t +{ + unsigned int level; + unsigned int dependency_level; + unsigned int bandwidth; + char* content_component; + + struct dash_representation_t* parent; + struct dash_representation_base_t base; +}; + +struct dash_representation_t +{ + char* id; + unsigned int bandwidth; + unsigned int quality_ranking; + char* dependncy_id; + char* association_id; + char* association_type; + char* media_stream_structure_id; + + struct dash_adaptation_set_t* parent; + struct dash_representation_base_t base; + + struct dash_url_t base_urls; // BaseURL + + size_t subrepresentation_count; + struct dash_subrepresentation_t* subrepresentations; // SubRepresentation + + struct dash_segment_t segment; +}; + +struct dash_adaptation_set_t +{ + char* href; + char* actuate; // default: onRequest + unsigned int id; + unsigned int group; + char* lang; // und + char* content_type; // text/image/audio/video/application/font + char* par; // n:m 16:9 + unsigned int min_bandwidth; + unsigned int max_bandwidth; + unsigned int min_width; + unsigned int max_width; + unsigned int min_height; + unsigned int max_height; + char* min_framerate; // n/m + char* max_framerate; + int segment_alignment; // 0-false, 1-true + int subsegment_aligment; // 0-false, 1-true + int subsegment_start_with_sap; // default 0 + int bitstream_switching; // 0-false, 1-true + + struct dash_period_t* parent; + struct dash_representation_base_t base; + + struct dash_descriptor_t accessibilities; // Accessibility + struct dash_descriptor_t roles; // Role + struct dash_descriptor_t ratings; // Rating + struct dash_descriptor_t viewpoints; // Viewpoint + + size_t content_component_count; + struct dash_content_component_t* content_components; // ContentComponent + + struct dash_url_t base_urls; // BaseURL + + struct dash_segment_t segment; + + size_t representation_count; + struct dash_representation_t* representations; // Representation +}; + +struct dash_period_t +{ + char* href; + char* actuate; // default: onRequest + char* id; + int64_t start; // start + int64_t duration; // duration + int bitstream_switching; // 0-false + + struct dash_mpd_t* parent; + struct dash_url_t base_urls; // BaseURL + + struct dash_segment_t segment; // SegmentBase/SegmentList/SegmentTemplate + + struct dash_descriptor_t asset_identifier; // AssetIdentifier + + size_t event_stream_count; + size_t event_stream_capacity; + struct dash_event_stream_t* event_streams; // EventStream + + size_t adaptation_set_count; + struct dash_adaptation_set_t* adaptation_sets; // AdaptationSet + + size_t subset_count; + struct + { + char* contains; + char* id; + } *subsets; // SubSet + + struct dash_descriptor_t supplementals; // SupplementalProperty + + size_t empty_adaptation_set_count; + struct dash_adaptation_set_t* empty_adaptation_sets; // EmptyAdaptationSet + + struct dash_label_t group_labels; // GroupLabel + + size_t preselection_count; + struct dash_preselection_t* preselections; // Preselection +}; + +struct dash_mpd_t +{ + int type; // presentation type: 0-static, 1-dynamic + char* id; + char* profiles; // profiles + char* availability_start_time; + char* availability_end_time; + char* publish_time; + int64_t media_presentation_duration; // mediaPresentationDuration, MPD total duration + int64_t minimum_update_period; // minimumUpdatePeriod + int64_t min_buffer_time; // minBufferTime + int64_t time_shift_buffer_depth; // timeShiftBufferDepth + int64_t suggested_presentation_delay; // suggestedPresentationDelay + int64_t max_segment_duration; // maxSegmentDuration + int64_t max_subsegment_duration; // maxSubsegmentDuration + char* xsi; // xmlns:xsi + char* xmlns; // xmlns + char* schema_location; // xsi:schemaLocation + + size_t info_count; + struct dash_program_information_t* infos; // ProgramInfomation + + struct dash_url_t urls; // BaseURL + + size_t location_count; + char** locations; // Location + + size_t period_count; + struct dash_period_t* periods; // Period + + size_t metric_count; + struct dash_metric_t* metrics; // Metrics + + struct dash_descriptor_t essentials; // EssentialProperty + struct dash_descriptor_t supplementals; // SupplementalProperty + struct dash_descriptor_t timings; // UTCTming +}; + +/// Parse MPD(Media Presentation Description for MPEG-DASH ) manifest file +/// @param[out] mpd mpd object(free by dash_mpd_free) +/// @return 0-ok, other-error +int dash_mpd_parse(struct dash_mpd_t** mpd, const char* data, size_t bytes); + +int dash_mpd_free(struct dash_mpd_t** mpd); + +/// @return -1-live, >=0-duration(ms), other-error +int64_t dash_get_duration(const struct dash_mpd_t* mpd); + +/// Period + +/// location period by time +/// @param[in] time from previous period end to current perid end, range: (period-m-end, period-n-end] +/// @return >=0-period index, -1-if don't find, other-undefined +int dash_period_find(const struct dash_mpd_t* mpd, int64_t time); + +/// @param[in] media DASH_MEDIA_AUDIO/DASH_MEDIA_VIDEO/xxx +/// @param[in] id adaptation set id, 0 if unknown +/// @param[in] group adaptation set group id, 0 if unknown +/// @param[in] lang adaptation set lang, such as 'en'/'fr', NULL if don't care +/// @param[in] codecs adaptation set codec type, such as 'avc1', NULL if don't care +/// @return NULL if don't find any adaptation set +const struct dash_adaptation_set_t* dash_period_select(const struct dash_period_t* period, int media, unsigned int id, unsigned int group, const char* lang, const char* codecs); + +/// Adaptation Set + +/// @return adaptation set media type DASH_MEDIA_AUDIO/DASH_MEDIA_VIDEO, DASH_MEDIA_UNKNOWN if unknown +int dash_adaptation_set_media_type(const struct dash_adaptation_set_t* set); + +/// Priority: 1. selectionPriority; 2. qualityRanking; 3. bandwidth +/// @return >=0-representation index, -1-error +int dash_adaptation_set_best_representation(const struct dash_adaptation_set_t* set); + +/// Representation + +const struct dash_url_t* dash_representation_get_base_url(const struct dash_representation_t* representation); + +/// Get initialization segment url(MUST use with dash_representation_get_base_url) +/// @return >0-ok with url length, =0-don't have initialization segment, <0-error +int dash_representation_get_initialization(const struct dash_representation_t* representation, char* url, size_t size); + +/// Find segment by start time +/// @param[in] time segment time, range: (previous segment end, segment start + duration) +/// @return -1-not found, >=0-segment item index(index is not the startnumber) +int dash_representation_find_segment(const struct dash_representation_t* representation, int64_t time); + +/// @return <0-error, >=0-segment count(INT_MAX for segment template) +int dash_representation_segment_count(const struct dash_representation_t* representation); + +/// Get segment url(MUST use with dash_representation_get_base_url) +/// @param[in] index segment index(!= start number) +/// @param[out] number start number of representation +/// @param[out] start start time of representation(MUST add period.start) +/// @param[out] duration segment duration, 0 if unknown +/// @param[out] range url range, NULL if don't have range +/// @return >=0-ok with url length, <0-error +int dash_representation_segment_url(const struct dash_representation_t* representation, int index, int64_t* number, int64_t* start, int64_t* duration, const char** range, char* url, size_t size); + +#ifdef __cplusplus +} +#endif +#endif /* !_dash_parser_h_ */ diff --git a/src/3rdpart/media-server/libdash/include/dash-proto.h b/src/3rdpart/media-server/libdash/include/dash-proto.h new file mode 100644 index 0000000..4243a61 --- /dev/null +++ b/src/3rdpart/media-server/libdash/include/dash-proto.h @@ -0,0 +1,10 @@ +#ifndef _dash_proto_h_ +#define _dash_proto_h_ + +enum +{ + DASH_STATIC = 0, + DASH_DYNAMIC, +}; + +#endif /* !_dash_proto_h_ */ diff --git a/src/3rdpart/media-server/libdash/include/xs-datatype.h b/src/3rdpart/media-server/libdash/include/xs-datatype.h new file mode 100644 index 0000000..d51e23c --- /dev/null +++ b/src/3rdpart/media-server/libdash/include/xs-datatype.h @@ -0,0 +1,19 @@ +#ifndef _xs_datatype_h_ +#define _xs_datatype_h_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @param[in] duration millisecond duration +/// @param[out] data ISO8601 duration: P[n]Y[n]M[n]DT[n]H[n]M[n]S +int xs_duration_write(int64_t duration, char* data, int size); +int xs_duration_read(int64_t* duration, const char* data, int size); + +#ifdef __cplusplus +} +#endif +#endif /* !_xs_datatype_h_ */ diff --git a/src/3rdpart/media-server/libdash/libdash.vcxproj b/src/3rdpart/media-server/libdash/libdash.vcxproj new file mode 100644 index 0000000..2d74b44 --- /dev/null +++ b/src/3rdpart/media-server/libdash/libdash.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + {A5DA231F-6146-43B5-8BBE-891F7E552357} + Win32Proj + libdash + 10.0 + + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + Level4 + Disabled + WIN32;_DEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions) + .;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories) + + + Windows + + + + + + + Level4 + Disabled + _DEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions) + .;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories) + + + Windows + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions) + .;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + Level4 + + + MaxSpeed + true + true + NDEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions) + .;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + + \ No newline at end of file diff --git a/src/3rdpart/media-server/libdash/libdash.vcxproj.filters b/src/3rdpart/media-server/libdash/libdash.vcxproj.filters new file mode 100644 index 0000000..04cdbbb --- /dev/null +++ b/src/3rdpart/media-server/libdash/libdash.vcxproj.filters @@ -0,0 +1,54 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/3rdpart/media-server/libdash/libdash.xcodeproj/project.pbxproj b/src/3rdpart/media-server/libdash/libdash.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6b74427 --- /dev/null +++ b/src/3rdpart/media-server/libdash/libdash.xcodeproj/project.pbxproj @@ -0,0 +1,328 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 46C5B2AF2183ED6100419E57 /* list.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C5B2AC2183ED6100419E57 /* list.h */; }; + 46C5B2B02183ED6100419E57 /* dash-mpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2AD2183ED6100419E57 /* dash-mpd.c */; }; + 46E55E6824A7395F00D8BDBA /* xs-duration.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6224A7395F00D8BDBA /* xs-duration.c */; }; + 46E55E6924A7395F00D8BDBA /* dash-period.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6324A7395F00D8BDBA /* dash-period.c */; }; + 46E55E6A24A7395F00D8BDBA /* dash-adaptation.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6424A7395F00D8BDBA /* dash-adaptation.c */; }; + 46E55E6B24A7395F00D8BDBA /* dash-representation.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6524A7395F00D8BDBA /* dash-representation.c */; }; + 46E55E6C24A7395F00D8BDBA /* dash-parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6624A7395F00D8BDBA /* dash-parser.c */; }; + 46E55E6D24A7395F00D8BDBA /* dash-segment.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6724A7395F00D8BDBA /* dash-segment.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 46C5B22C2183EAAD00419E57 /* libdash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdash.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 46C5B2A92183ED5400419E57 /* include */ = {isa = PBXFileReference; lastKnownFileType = folder; path = include; sourceTree = ""; }; + 46C5B2AC2183ED6100419E57 /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = ""; }; + 46C5B2AD2183ED6100419E57 /* dash-mpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-mpd.c"; sourceTree = ""; }; + 46E55E6224A7395F00D8BDBA /* xs-duration.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "xs-duration.c"; sourceTree = ""; }; + 46E55E6324A7395F00D8BDBA /* dash-period.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-period.c"; sourceTree = ""; }; + 46E55E6424A7395F00D8BDBA /* dash-adaptation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-adaptation.c"; sourceTree = ""; }; + 46E55E6524A7395F00D8BDBA /* dash-representation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-representation.c"; sourceTree = ""; }; + 46E55E6624A7395F00D8BDBA /* dash-parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-parser.c"; sourceTree = ""; }; + 46E55E6724A7395F00D8BDBA /* dash-segment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-segment.c"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 46C5B22A2183EAAD00419E57 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 46C5B2232183EAAD00419E57 = { + isa = PBXGroup; + children = ( + 46C5B2AA2183ED6100419E57 /* src */, + 46C5B2A92183ED5400419E57 /* include */, + 46C5B22D2183EAAD00419E57 /* Products */, + ); + sourceTree = ""; + }; + 46C5B22D2183EAAD00419E57 /* Products */ = { + isa = PBXGroup; + children = ( + 46C5B22C2183EAAD00419E57 /* libdash.a */, + ); + name = Products; + sourceTree = ""; + }; + 46C5B2AA2183ED6100419E57 /* src */ = { + isa = PBXGroup; + children = ( + 46C5B2AC2183ED6100419E57 /* list.h */, + 46E55E6424A7395F00D8BDBA /* dash-adaptation.c */, + 46E55E6624A7395F00D8BDBA /* dash-parser.c */, + 46E55E6324A7395F00D8BDBA /* dash-period.c */, + 46E55E6524A7395F00D8BDBA /* dash-representation.c */, + 46E55E6724A7395F00D8BDBA /* dash-segment.c */, + 46E55E6224A7395F00D8BDBA /* xs-duration.c */, + 46C5B2AD2183ED6100419E57 /* dash-mpd.c */, + ); + path = src; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 46C5B2282183EAAD00419E57 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 46C5B2AF2183ED6100419E57 /* list.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 46C5B22B2183EAAD00419E57 /* libdash */ = { + isa = PBXNativeTarget; + buildConfigurationList = 46C5B2302183EAAD00419E57 /* Build configuration list for PBXNativeTarget "libdash" */; + buildPhases = ( + 46C5B2282183EAAD00419E57 /* Headers */, + 46C5B2292183EAAD00419E57 /* Sources */, + 46C5B22A2183EAAD00419E57 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdash; + productName = libdash; + productReference = 46C5B22C2183EAAD00419E57 /* libdash.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 46C5B2242183EAAD00419E57 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1000; + ORGANIZATIONNAME = ireader; + TargetAttributes = { + 46C5B22B2183EAAD00419E57 = { + CreatedOnToolsVersion = 10.0; + }; + }; + }; + buildConfigurationList = 46C5B2272183EAAD00419E57 /* Build configuration list for PBXProject "libdash" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 46C5B2232183EAAD00419E57; + productRefGroup = 46C5B22D2183EAAD00419E57 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 46C5B22B2183EAAD00419E57 /* libdash */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 46C5B2292183EAAD00419E57 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 46E55E6B24A7395F00D8BDBA /* dash-representation.c in Sources */, + 46E55E6A24A7395F00D8BDBA /* dash-adaptation.c in Sources */, + 46E55E6C24A7395F00D8BDBA /* dash-parser.c in Sources */, + 46E55E6824A7395F00D8BDBA /* xs-duration.c in Sources */, + 46C5B2B02183ED6100419E57 /* dash-mpd.c in Sources */, + 46E55E6924A7395F00D8BDBA /* dash-period.c in Sources */, + 46E55E6D24A7395F00D8BDBA /* dash-segment.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 46C5B22E2183EAAD00419E57 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = ( + ., + ./include, + ../libmov/include, + ../libhls/include, + ); + }; + name = Debug; + }; + 46C5B22F2183EAAD00419E57 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = ( + ., + ./include, + ../libmov/include, + ../libhls/include, + ); + }; + name = Release; + }; + 46C5B2312183EAAD00419E57 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + EXECUTABLE_PREFIX = ""; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "$(inherited)"; + }; + name = Debug; + }; + 46C5B2322183EAAD00419E57 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + EXECUTABLE_PREFIX = ""; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "$(inherited)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 46C5B2272183EAAD00419E57 /* Build configuration list for PBXProject "libdash" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 46C5B22E2183EAAD00419E57 /* Debug */, + 46C5B22F2183EAAD00419E57 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 46C5B2302183EAAD00419E57 /* Build configuration list for PBXNativeTarget "libdash" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 46C5B2312183EAAD00419E57 /* Debug */, + 46C5B2322183EAAD00419E57 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 46C5B2242183EAAD00419E57 /* Project object */; +} diff --git a/src/3rdpart/media-server/libdash/src/dash-adaptation.c b/src/3rdpart/media-server/libdash/src/dash-adaptation.c new file mode 100644 index 0000000..0ab558f --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/dash-adaptation.c @@ -0,0 +1,77 @@ +#include "dash-parser.h" +#include +#include +#include + +static int dash_mime_type_to_media_type(const char* mime) +{ + switch (mime[0]) + { + case 't': + return 0 == strncmp(mime, "text/", 5) ? DASH_MEDIA_TEXT : DASH_MEDIA_UNKNOWN; + case 'i': + return 0 == strncmp(mime, "image/", 6) ? DASH_MEDIA_IMAGE : DASH_MEDIA_UNKNOWN; + case 'a': + if (0 == strncmp(mime, "audio/", 6)) + return DASH_MEDIA_AUDIO; + else if (0 == strncmp(mime, "application/", 12)) + return DASH_MEDIA_APPLICATION; + else + return DASH_MEDIA_UNKNOWN; + case 'v': + return 0 == strncmp(mime, "video/", 6) ? DASH_MEDIA_VIDEO : DASH_MEDIA_UNKNOWN; + case 'f': + return 0 == strncmp(mime, "font/", 5) ? DASH_MEDIA_FONT : DASH_MEDIA_UNKNOWN; + default: + return DASH_MEDIA_UNKNOWN; + } +} + +int dash_adaptation_set_media_type(const struct dash_adaptation_set_t* set) +{ + const struct dash_representation_base_t* base; + + base = &set->base; + if (base->mime_type && *base->mime_type) + return dash_mime_type_to_media_type(base->mime_type); + else if (set->content_type && *set->content_type) + return dash_mime_type_to_media_type(set->content_type); + + if (set->representation_count > 0) + { + base = &set->representations[0].base; + if (base->mime_type && *base->mime_type) + return dash_mime_type_to_media_type(base->mime_type); + + if (set->representations[0].subrepresentation_count > 0) + { + base = &set->representations[0].subrepresentations[0].base; + return dash_mime_type_to_media_type(base->mime_type); + } + } + + return DASH_MEDIA_UNKNOWN; +} + +int dash_adaptation_set_best_representation(const struct dash_adaptation_set_t* set) +{ + int best; + size_t i; + const struct dash_representation_t* v; + + best = -1; + for (i = 0; i < set->representation_count; i++) + { + v = &set->representations[i]; + // qualityRanking: specifies a quality ranking of the Representation + // relative to other Representations in the same Adaptation Set. + // Lower values represent higher quality content. + if (-1 == best + || v->base.selection_priority > set->representations[best].base.selection_priority + || v->quality_ranking < set->representations[best].quality_ranking + || v->bandwidth > set->representations[best].bandwidth) + best = (int)i; + } + + return best; +} diff --git a/src/3rdpart/media-server/libdash/src/dash-mpd.c b/src/3rdpart/media-server/libdash/src/dash-mpd.c new file mode 100644 index 0000000..494310f --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/dash-mpd.c @@ -0,0 +1,520 @@ +#include "dash-mpd.h" +#include "dash-proto.h" +#include "mov-format.h" +#include "fmp4-writer.h" +#include "list.h" +#include +#include +#include +#include +#include +#include +#include + +#define N_TRACK 8 +#define N_NAME 128 +#define N_COUNT 5 + +#define N_SEGMENT (1 * 1024 * 1024) +#define N_FILESIZE (100 * 1024 * 1024) // 100M + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +struct dash_segment_t +{ + struct list_head link; + int64_t timestamp; + int64_t duration; +}; + +struct dash_adaptation_set_t +{ + fmp4_writer_t* fmp4; + char prefix[N_NAME]; + + uint8_t* ptr; + size_t bytes; + size_t capacity; + size_t offset; + size_t maxsize; // max bytes per mp4 file + + int64_t pts; + int64_t dts; + int64_t dts_last; + int64_t raw_bytes; + int bitrate; + int track; // MP4 track id + int setid; // dash adapation set id + + int seq; + uint8_t object; + + union + { + struct + { + int width; + int height; + int frame_rate; + struct + { + uint8_t profile; + uint8_t compatibility; + uint8_t level; + } avc; + } video; + + struct + { + uint8_t profile; // AAC profile + int channel; + int sample_bit; + int sample_rate; + } audio; + } u; + + size_t count; + struct list_head root; // segments +}; + +struct dash_mpd_t +{ + int flags; + time_t time; + int64_t duration; + int64_t max_segment_duration; + + dash_mpd_segment handler; + void* param; + + int count; // adaptation set count + struct dash_adaptation_set_t tracks[N_TRACK]; +}; + +static int mov_buffer_read(void* param, void* data, uint64_t bytes) +{ + struct dash_adaptation_set_t* dash; + dash = (struct dash_adaptation_set_t*)param; + if (dash->offset + bytes > dash->bytes) + return E2BIG; + memcpy(data, dash->ptr + dash->offset, (size_t)bytes); + return 0; +} + +static int mov_buffer_write(void* param, const void* data, uint64_t bytes) +{ + void* ptr; + size_t capacity; + struct dash_adaptation_set_t* dash; + dash = (struct dash_adaptation_set_t*)param; + if (dash->offset + bytes > dash->maxsize) + return E2BIG; + + if (dash->offset + (size_t)bytes > dash->capacity) + { + capacity = dash->offset + (size_t)bytes + N_SEGMENT; + capacity = capacity > dash->maxsize ? dash->maxsize : capacity; + ptr = realloc(dash->ptr, capacity); + if (NULL == ptr) + return ENOMEM; + dash->ptr = ptr; + dash->capacity = capacity; + } + + memcpy(dash->ptr + dash->offset, data, (size_t)bytes); + dash->offset += (size_t)bytes; + if (dash->offset > dash->bytes) + dash->bytes = dash->offset; + return 0; +} + +static int mov_buffer_seek(void* param, uint64_t offset) +{ + struct dash_adaptation_set_t* dash; + dash = (struct dash_adaptation_set_t*)param; + if (offset >= dash->maxsize) + return E2BIG; + dash->offset = (size_t)offset; + return 0; +} + +static uint64_t mov_buffer_tell(void* param) +{ + return ((struct dash_adaptation_set_t*)param)->offset; +} + +static struct mov_buffer_t s_io = { + mov_buffer_read, + mov_buffer_write, + mov_buffer_seek, + mov_buffer_tell, +}; + +static int dash_adaptation_set_segment(struct dash_mpd_t* mpd, struct dash_adaptation_set_t* track) +{ + int r; + char name[N_NAME + 32]; + struct list_head *link; + struct dash_segment_t* seg; + + r = fmp4_writer_save_segment(track->fmp4); + if (0 != r) + return r; + + seg = (struct dash_segment_t*)calloc(1, sizeof(*seg)); + if(!seg) + return -1; // ENOMEM + seg->timestamp = track->dts; + seg->duration = track->dts_last - track->dts; + + if(MOV_OBJECT_AAC == track->object) + snprintf(name, sizeof(name), "%s-%" PRId64 ".m4a", track->prefix, seg->timestamp); + else + snprintf(name, sizeof(name), "%s-%" PRId64 ".m4v", track->prefix, seg->timestamp); + r = mpd->handler(mpd->param, track->setid, track->ptr, track->bytes, track->pts, track->dts, seg->duration, name); + if (0 != r) + { + free(seg); + return r; + } + + // link + list_insert_after(&seg->link, track->root.prev); + + track->count += 1; + if (DASH_DYNAMIC == mpd->flags && track->count > N_COUNT) + { + link = track->root.next; + list_remove(link); + seg = list_entry(link, struct dash_segment_t, link); + free(seg); + --track->count; + } + return 0; +} + +static int dash_mpd_flush(struct dash_mpd_t* mpd) +{ + int i, r; + struct dash_adaptation_set_t* track; + + for (r = i = 0; i < mpd->count && 0 == r; i++) + { + track = mpd->tracks + i; + if (track->raw_bytes) + { + r = dash_adaptation_set_segment(mpd, track); + + // update maximum segment duration + mpd->max_segment_duration = MAX(track->dts_last - track->dts, mpd->max_segment_duration); + if(track->dts_last > track->dts) + track->bitrate = MAX(track->bitrate, (int)(track->raw_bytes * 1000 / (track->dts_last - track->dts) * 8)); + } + + track->pts = INT64_MIN; + track->dts = INT64_MIN; + track->raw_bytes = 0; + + // reset track buffer + track->offset = 0; + track->bytes = 0; + } + + return r; +} + +struct dash_mpd_t* dash_mpd_create(int flags, dash_mpd_segment segment, void* param) +{ + struct dash_mpd_t* mpd; + mpd = (struct dash_mpd_t*)calloc(1, sizeof(*mpd)); + if (mpd) + { + mpd->flags = flags; + mpd->handler = segment; + mpd->param = param; + mpd->time = time(NULL); + } + return mpd; +} + +void dash_mpd_destroy(struct dash_mpd_t* mpd) +{ + int i; + struct list_head *p, *n; + struct dash_segment_t *seg; + struct dash_adaptation_set_t* track; + + dash_mpd_flush(mpd); + + for (i = 0; i < mpd->count; i++) + { + track = &mpd->tracks[i]; + + if (track->ptr) + { + free(track->ptr); + track->ptr = NULL; + } + + list_for_each_safe(p, n, &track->root) + { + seg = list_entry(p, struct dash_segment_t, link); + free(seg); + } + } + + free(mpd); +} + +int dash_mpd_add_video_adaptation_set(struct dash_mpd_t* mpd, const char* prefix, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size) +{ + int r; + char name[N_NAME + 16]; + struct dash_adaptation_set_t* track; + + r = (int)strlen(prefix); + if (mpd->count + 1 >= N_TRACK || extra_data_size < 4 || r >= N_NAME) + return -1; + + assert(MOV_OBJECT_H264 == object); + track = &mpd->tracks[mpd->count]; + memcpy(track->prefix, prefix, r); + LIST_INIT_HEAD(&track->root); + track->setid = mpd->count++; + track->object = object; + track->bitrate = 0; + track->u.video.width = width; + track->u.video.height = height; + track->u.video.frame_rate = 25; + assert(((const uint8_t*)extra_data)[0] == 1); // configurationVersion + if (MOV_OBJECT_H264 == object) + { + track->u.video.avc.profile = ((const uint8_t*)extra_data)[1]; + track->u.video.avc.compatibility = ((const uint8_t*)extra_data)[2]; + track->u.video.avc.level = ((const uint8_t*)extra_data)[3]; + } + + track->seq = 1; + track->maxsize = N_FILESIZE; + track->fmp4 = fmp4_writer_create(&s_io, track, MOV_FLAG_SEGMENT); + if (!track->fmp4) + return -1; + track->track = fmp4_writer_add_video(track->fmp4, object, width, height, extra_data, extra_data_size); + + // save init segment file + r = fmp4_writer_init_segment(track->fmp4); + if (0 == r) + { + snprintf(name, sizeof(name), "%s-init.m4v", prefix); + r = mpd->handler(mpd->param, mpd->count, track->ptr, track->bytes, 0, 0, 0, name); + } + + track->bytes = 0; + track->offset = 0; + return 0 == r ? track->setid : r; +} + +int dash_mpd_add_audio_adaptation_set(struct dash_mpd_t* mpd, const char* prefix, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size) +{ + int r; + char name[N_NAME + 16]; + struct dash_adaptation_set_t* track; + + r = (int)strlen(prefix); + if (mpd->count + 1 >= N_TRACK || extra_data_size < 2 || r >= N_NAME) + return -1; + + assert(MOV_OBJECT_AAC == object); + track = &mpd->tracks[mpd->count]; + memcpy(track->prefix, prefix, r); + LIST_INIT_HEAD(&track->root); + track->setid = mpd->count++; + track->object = object; + track->bitrate = 0; + track->u.audio.channel = channel_count; + track->u.audio.sample_bit = bits_per_sample; + track->u.audio.sample_rate = sample_rate; + track->u.audio.profile = ((const uint8_t*)extra_data)[0] >> 3; + if(MOV_OBJECT_AAC == object && 31 == track->u.audio.profile) + track->u.audio.profile = 32 + (((((const uint8_t*)extra_data)[0] & 0x07) << 3) | ((((const uint8_t*)extra_data)[1] >> 5) & 0x07)); + + track->seq = 1; + track->maxsize = N_FILESIZE; + track->fmp4 = fmp4_writer_create(&s_io, track, MOV_FLAG_SEGMENT); + if (!track->fmp4) + return -1; + track->track = fmp4_writer_add_audio(track->fmp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); + + r = fmp4_writer_init_segment(track->fmp4); + if (0 == r) + { + snprintf(name, sizeof(name), "%s-init.m4a", prefix); + r = mpd->handler(mpd->param, mpd->count, track->ptr, track->bytes, 0, 0, 0, name); + } + + track->bytes = 0; + track->offset = 0; + return 0 == r ? track->setid : r; +} + +int dash_mpd_input(struct dash_mpd_t* mpd, int adapation, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags) +{ + int r = 0; + struct dash_adaptation_set_t* track; + if (adapation >= mpd->count || adapation < 0) + return -1; + + track = &mpd->tracks[adapation]; + if (NULL == data || 0 == bytes // flash fragment + || ((MOV_AV_FLAG_KEYFREAME & flags) && (MOV_OBJECT_H264 == track->object || MOV_OBJECT_HEVC == track->object))) + { + r = dash_mpd_flush(mpd); + + // FIXME: live duration + mpd->duration += mpd->max_segment_duration; + } + + if (NULL == data || 0 == bytes) + return r; + + if (0 == track->raw_bytes) + { + track->pts = pts; + track->dts = dts; + } + track->dts_last = dts; + track->raw_bytes += bytes; + return fmp4_writer_write(track->fmp4, track->track, data, bytes, pts, dts, flags); +} + +// ISO/IEC 23009-1:2014(E) 5.4 Media Presentation Description updates (p67) +// 1. the value of MPD@id, if present, shall be the same in the original and the updated MPD; +// 2. the values of any Period@id attributes shall be the same in the original and the updated MPD, unless the containing Period element has been removed; +// 3. the values of any AdaptationSet@id attributes shall be the same in the original and the updated MPD unless the containing Period element has been removed; +size_t dash_mpd_playlist(struct dash_mpd_t* mpd, char* playlist, size_t bytes) +{ + // ISO/IEC 23009-1:2014(E) + // G.2 Example for ISO Base media file format Live profile (141) + static const char* s_mpd_dynamic = + "\n" + "\n"; + + // ISO/IEC 23009-1:2014(E) + // G.1 Example MPD for ISO Base media file format On Demand profile + static const char* s_mpd_static = + "\n" + "\n"; + + static const char* s_h264 = + " \n" + " \n" + " \n" + " \n"; + + static const char* s_h265 = + " \n" + " \n" + " \n" + " \n"; + + static const char* s_aac = + " \n" + " \n" + " \n" + " \n" + " \n"; + + static const char* s_footer = + " \n" + " \n" + " \n" + " \n"; + + int i; + size_t n; + time_t now; + char publishTime[32]; + char availabilityStartTime[32]; + unsigned int minimumUpdatePeriod; + unsigned int timeShiftBufferDepth; + struct dash_adaptation_set_t* track; + struct dash_segment_t *seg; + struct list_head *link; + + now = time(NULL); + strftime(availabilityStartTime, sizeof(availabilityStartTime), "%Y-%m-%dT%H:%M:%SZ", gmtime(&mpd->time)); + strftime(publishTime, sizeof(publishTime), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + + minimumUpdatePeriod = (unsigned int)MAX(mpd->max_segment_duration / 1000, 1); + + if (mpd->flags == DASH_DYNAMIC) + { + timeShiftBufferDepth = minimumUpdatePeriod * N_COUNT + 1; + n = snprintf(playlist, bytes, s_mpd_dynamic, minimumUpdatePeriod, timeShiftBufferDepth, availabilityStartTime, minimumUpdatePeriod, publishTime); + n += snprintf(playlist + n, bytes - n, " \n"); + } + else + { + n = snprintf(playlist, bytes, s_mpd_static, (unsigned int)(mpd->duration / 1000), minimumUpdatePeriod); + n += snprintf(playlist + n, bytes - n, " \n"); + } + + for (i = 0; i < mpd->count; i++) + { + track = &mpd->tracks[i]; + if (MOV_OBJECT_H264 == track->object) + { + n += snprintf(playlist + n, bytes - n, s_h264, (unsigned int)track->u.video.avc.profile, (unsigned int)track->u.video.avc.compatibility, (unsigned int)track->u.video.avc.level, track->u.video.width, track->u.video.height, track->u.video.frame_rate, track->bitrate, track->prefix, track->prefix); + list_for_each(link, &track->root) + { + seg = list_entry(link, struct dash_segment_t, link); + n += snprintf(playlist + n, bytes - n, " \n", seg->timestamp, (unsigned int)seg->duration); + } + n += snprintf(playlist + n, bytes - n, "%s", s_footer); + } + else if (MOV_OBJECT_HEVC == track->object) + { + n += snprintf(playlist + n, bytes - n, s_h265, (unsigned int)track->u.video.avc.profile, (unsigned int)track->u.video.avc.compatibility, (unsigned int)track->u.video.avc.level, track->u.video.width, track->u.video.height, track->u.video.frame_rate, track->bitrate, track->prefix, track->prefix); + list_for_each(link, &track->root) + { + seg = list_entry(link, struct dash_segment_t, link); + n += snprintf(playlist + n, bytes - n, " \n", seg->timestamp, (unsigned int)seg->duration); + } + n += snprintf(playlist + n, bytes - n, "%s", s_footer); + } + else if (MOV_OBJECT_AAC == track->object) + { + n += snprintf(playlist + n, bytes - n, s_aac, (unsigned int)track->u.audio.profile, track->u.audio.sample_rate, track->bitrate, track->u.audio.channel, track->prefix, track->prefix); + list_for_each(link, &track->root) + { + seg = list_entry(link, struct dash_segment_t, link); + n += snprintf(playlist + n, bytes - n, " \n", seg->timestamp, (unsigned int)seg->duration); + } + n += snprintf(playlist + n, bytes - n, "%s", s_footer); + } + else + { + assert(0); + } + } + + n += snprintf(playlist + n, bytes - n, " \n\n"); + return n; +} diff --git a/src/3rdpart/media-server/libdash/src/dash-parser.c b/src/3rdpart/media-server/libdash/src/dash-parser.c new file mode 100644 index 0000000..55eb928 --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/dash-parser.c @@ -0,0 +1,1537 @@ +// https://standards.iso.org/iso-iec/23009/-1/ed-3/en/DASH-MPD.xsd + +#include "dash-parser.h" +#include "xs-datatype.h" +#include "hls-string.h" +#include +#include +#include +#include +#include + +#ifndef offsetof +#define offsetof(s, m) (size_t)&(((s*)0)->m) +#endif + +#define PRESELECTION_INCR 4 +#define PRESELECTION_CAPACITY(count) ((count+PRESELECTION_INCR-1)/PRESELECTION_INCR*PRESELECTION_INCR) + +#define ADAPTATION_SET_INCR 4 +#define ADAPTATION_SET_CAPACITY(count) ((count+ADAPTATION_SET_INCR-1)/ADAPTATION_SET_INCR*ADAPTATION_SET_INCR) + +#define REPRESENTATION_INCR 4 +#define REPRESENTATION_CAPACITY(count) ((count+REPRESENTATION_INCR-1)/REPRESENTATION_INCR*REPRESENTATION_INCR) + +#define SUBREPRESENTATION_INCR 4 +#define SUBREPRESENTATION_CAPACITY(count) ((count+SUBREPRESENTATION_INCR-1)/SUBREPRESENTATION_INCR*SUBREPRESENTATION_INCR) + +#define EVENT_CAPACITY_INCR 4 +#define EVENT_CAPACITY(count) ((count+EVENT_CAPACITY_INCR-1)/EVENT_CAPACITY_INCR*EVENT_CAPACITY_INCR) + +#define CONTENT_COMPONENT_CAPACITY_INCR 4 +#define CONTENT_COMPONENT_CAPACITY(count) ((count+CONTENT_COMPONENT_CAPACITY_INCR-1)/CONTENT_COMPONENT_CAPACITY_INCR*CONTENT_COMPONENT_CAPACITY_INCR) + +#define SUBSET_INCR 4 +#define SUBSET_CAPACITY(count) ((count+SUBSET_INCR-1)/SUBSET_INCR*SUBSET_INCR) + +#define SEGMENT_URL_INCR 8 +#define SEGMENT_URL_CAPACITY(count) ((count+SEGMENT_URL_INCR-1)/SEGMENT_URL_INCR*SEGMENT_URL_INCR) + +enum +{ + DASH_TAG_ELEMENT, + DASH_TAG_DECLARATION, + DASH_TAG_COMMENT, + DASH_TAG_CDATA, +}; + +enum +{ + DASH_TAG_FLAG_START = 0x01, // start tag
+ DASH_TAG_FLAG_END = 0x02, // 1-end tag
+ DASH_TAG_FLAG_LINBREAK = 0x04, // empty-element tag +}; + +struct dash_tag_t +{ + int type; + int flags; + const char* ptr; // < + const char* end; // > + + const char* name; // tag name + size_t nlen; // tag name length in byte + + const char* attr; + size_t nattr; +}; + +struct dash_parser_t +{ + size_t period_capacity; + struct dash_mpd_t* mpd; + char* content; + void* tag; // dash tag object + + struct + { + const char* tag; + void* ptr; // e.g. struct dash_mpd_t/struct dash_period_t/struct dash_adaptation_set_t + size_t off; + } stack[16]; + int level; +}; + +enum +{ + ATTR_VALUE_TYPE_UINT32, + ATTR_VALUE_TYPE_UINT64, + ATTR_VALUE_TYPE_FLOAT64, + ATTR_VALUE_TYPE_STRING, + ATTR_VALUE_TYPE_STRING_BOOL, + ATTR_VALUE_TYPE_DATETIME, + ATTR_VALUE_TYPE_DURATION, + //ATTR_VALUE_TYPE_FRAME_RATE, + //ATTR_VALUE_TYPE_RATIO, +}; + +struct dash_tag_attr_t +{ + int cls; + const char* name; + void* ptr; +}; + +#define DASH_TAG_ATTR_VALUE(a, cls0, name0, ptr0) {a.cls=cls0; a.name=name0; a.ptr=ptr0; } + +static int dash_attr_read(const char* value, size_t n, int cls, void* ptr) +{ + switch (cls) + { + case ATTR_VALUE_TYPE_STRING_BOOL: + *(int*)ptr = (4 == n && 0 == strncasecmp(value, "true", 4)) ? 1 : 0; + return 0; + + case ATTR_VALUE_TYPE_UINT32: + *(uint32_t*)ptr = (uint32_t)strtol(value, NULL, 10); + break; + + case ATTR_VALUE_TYPE_UINT64: + *(uint64_t*)ptr = (uint64_t)strtoll(value, NULL, 10); + break; + + case ATTR_VALUE_TYPE_FLOAT64: + *(double*)ptr = strtod(value, NULL); + break; + + case ATTR_VALUE_TYPE_STRING: + *((char**)ptr) = (char*)value; + ((char*)value)[n] = 0; + break; + + case ATTR_VALUE_TYPE_DATETIME: + *((char**)ptr) = (char*)value; + ((char*)value)[n] = 0; + break; + + case ATTR_VALUE_TYPE_DURATION: + return xs_duration_read((int64_t*)ptr, value, (int)n); + + //case ATTR_VALUE_TYPE_FRAME_RATE: + // // [0-9]*[0-9](/[0-9]*[0-9])? + // break; + + //case ATTR_VALUE_TYPE_RATIO: + // // [0-9]*:[0-9]* + // sscanf(value, "%d:%d", &num, &den); + // break; + + default: + assert(0); + return -1; + } + + return 0; +} + +static int dash_parse_attrs(const char* data, size_t bytes, struct dash_tag_attr_t* attrs, size_t nattrs) +{ + int r; + size_t i, n, nn, nv; + const char* ptr, * next; + const char* name, * value; + + r = 0; + for (ptr = data; ptr && ptr < data + bytes && 0 == r; ptr = next) + { + n = hls_strsplit(ptr, data + bytes, " \r\n", "\"", &next); + + nn = hls_strsplit(ptr, ptr + n, "=", "", &value); + name = hls_strtrim(ptr, &nn, " \t\r\n", " \t\r\n"); // trim SP/HTAB + nv = ptr + n - value; + value = hls_strtrim(value, &nv, " \t\r\n'\"", " \t\r\n'\""); // trim SP/HTAB/'/" + + for (i = 0; i < nattrs; i++) + { + if (nn == strlen(attrs[i].name) && 0 == strncasecmp(attrs[i].name, name, nn)) + { + r = dash_attr_read(value, nv, attrs[i].cls, attrs[i].ptr); + break; + } + } + } + + return r; +} + +static int dash_parser_realloc(void** ptr, size_t* capacity, size_t len, size_t incr, size_t size) +{ + void* ptr1; + if (len >= *capacity) + { + assert(incr > 0); + ptr1 = realloc(*ptr, (len + incr) * size); + if (!ptr1) + return -ENOMEM; + + memset((uint8_t*)ptr1 + len * size, 0, incr * size); + *capacity = len + incr; + *ptr = ptr1; + } + return 0; +} + +static int dash_tag_mpd(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + struct dash_tag_attr_t attrs[16]; + struct dash_mpd_t* mpd; + char* type; + + (void)ptr; + mpd = parser->mpd; + parser->tag = mpd; // save + type = NULL; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "id", &mpd->id); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "profiles", &mpd->profiles); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "type", &type); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_DATETIME, "availabilityStartTime", &mpd->availability_start_time); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_DATETIME, "availabilityEndTime", &mpd->availability_end_time); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_DATETIME, "publishTime", &mpd->publish_time); + DASH_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_DURATION, "mediaPresentationDuration", &mpd->media_presentation_duration); + DASH_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_DURATION, "minimumUpdatePeriod", &mpd->minimum_update_period); + DASH_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_DURATION, "minBufferTime", &mpd->min_buffer_time); + DASH_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_DURATION, "timeShiftBufferDepath", &mpd->time_shift_buffer_depth); + DASH_TAG_ATTR_VALUE(attrs[10], ATTR_VALUE_TYPE_DURATION, "suggestedPresentationDelay", &mpd->suggested_presentation_delay); + DASH_TAG_ATTR_VALUE(attrs[11], ATTR_VALUE_TYPE_DURATION, "maxSegmentDuration", &mpd->max_segment_duration); + DASH_TAG_ATTR_VALUE(attrs[12], ATTR_VALUE_TYPE_DURATION, "maxSubsegmentDuration", &mpd->max_subsegment_duration); + DASH_TAG_ATTR_VALUE(attrs[13], ATTR_VALUE_TYPE_STRING, "xmlns:xsi", &mpd->xsi); + DASH_TAG_ATTR_VALUE(attrs[14], ATTR_VALUE_TYPE_STRING, "xmlns", &mpd->xmlns); + DASH_TAG_ATTR_VALUE(attrs[15], ATTR_VALUE_TYPE_STRING, "xsi:schemaLocation", &mpd->schema_location); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + if (type && 0 == strcasecmp(type, "dynamic")) + mpd->type = DASH_DYNAMIC; + else + mpd->type = DASH_STATIC; + return 0; +} + +static int dash_tag_period(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + struct dash_tag_attr_t attrs[6]; + struct dash_period_t* period; + struct dash_mpd_t* mpd; + + mpd = (struct dash_mpd_t*)ptr; + if (0 != dash_parser_realloc((void**)&mpd->periods, &parser->period_capacity, mpd->period_count, 2, sizeof(mpd->periods[0]))) + return -ENOMEM; + + period = &mpd->periods[mpd->period_count]; + period->parent = mpd; + period->actuate = "onRequest"; // default + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "id", &period->id); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "xlink:href", &period->href); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "xlink:actuate", &period->actuate); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_DURATION, "start", &period->start); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_DURATION, "duration", &period->duration); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING_BOOL, "bitstreamSwitching", &period->bitstream_switching); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = period; // save + mpd->period_count++; + return 0; +} + +static int dash_tag_representation_base(struct dash_representation_base_t* base, const char* attr, size_t len) +{ + struct dash_tag_attr_t attrs[16]; + base->selection_priority = 1; // default 1 + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "profiles", &base->profiles); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT32, "width", &base->width); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "height", &base->height); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "sar", &base->sar); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "frameRate", &base->frame_rate); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "audioSamplingRate", &base->audio_sampling_rate); + DASH_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING, "mimeType", &base->mime_type); + DASH_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_STRING, "segmentProfiles", &base->segment_profiles); + DASH_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_STRING, "codecs", &base->codecs); + DASH_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_FLOAT64, "maximumSAPPeriod", &base->maxmum_sap_period); + DASH_TAG_ATTR_VALUE(attrs[10], ATTR_VALUE_TYPE_STRING, "startWithSAP", &base->start_with_sap); + DASH_TAG_ATTR_VALUE(attrs[11], ATTR_VALUE_TYPE_FLOAT64, "maxPlayoutRate", &base->max_playout_rate); + DASH_TAG_ATTR_VALUE(attrs[12], ATTR_VALUE_TYPE_STRING_BOOL, "codingDependency", &base->coding_dependency); + DASH_TAG_ATTR_VALUE(attrs[13], ATTR_VALUE_TYPE_STRING, "scanType", &base->scan_type); + DASH_TAG_ATTR_VALUE(attrs[14], ATTR_VALUE_TYPE_UINT32, "selectionPriority", &base->selection_priority); + DASH_TAG_ATTR_VALUE(attrs[15], ATTR_VALUE_TYPE_STRING, "tag", &base->tag); + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_preselection(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[3]; + struct dash_period_t* period; + struct dash_preselection_t* preselection; + + period = (struct dash_period_t*)ptr; + capacity = PRESELECTION_CAPACITY(period->preselection_count); + if (0 != dash_parser_realloc((void**)&period->preselections, &capacity, period->preselection_count, PRESELECTION_INCR, sizeof(period->preselections[0]))) + return -ENOMEM; + + preselection = &period->preselections[period->preselection_count]; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "id", &preselection->id); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "preselectionComponents", &preselection->preselection_compoents); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "lang", &preselection->lang); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 == r) + r = dash_tag_representation_base(&preselection->base, attr, len); + if (0 != r) + return r; + + parser->tag = preselection; // save + period->preselection_count++; + return 0; +} + +static int dash_tag_adaptation_set(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[19]; + struct dash_period_t* period; + struct dash_adaptation_set_t* adaptation_set; + + period = (struct dash_period_t*)ptr; + capacity = ADAPTATION_SET_CAPACITY(period->adaptation_set_count); + if (0 != dash_parser_realloc((void**)&period->adaptation_sets, &capacity, period->adaptation_set_count, ADAPTATION_SET_INCR, sizeof(period->adaptation_sets[0]))) + return -ENOMEM; + + adaptation_set = &period->adaptation_sets[period->adaptation_set_count]; + adaptation_set->parent = period; + adaptation_set->actuate = "onRequest"; // default + adaptation_set->segment_alignment = 0; // default + adaptation_set->subsegment_aligment = 0; // default + adaptation_set->subsegment_start_with_sap = 0; // default + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "xlink:href", &adaptation_set->href); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "xlink:actuate", &adaptation_set->actuate); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "id", &adaptation_set->id); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_UINT32, "group", &adaptation_set->group); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "lang", &adaptation_set->lang); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "contentType", &adaptation_set->content_type); + DASH_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING, "par", &adaptation_set->par); + DASH_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_UINT32, "minBandwidth", &adaptation_set->min_bandwidth); + DASH_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_UINT32, "maxBandwidth", &adaptation_set->max_bandwidth); + DASH_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_UINT32, "minWidth", &adaptation_set->min_width); + DASH_TAG_ATTR_VALUE(attrs[10], ATTR_VALUE_TYPE_UINT32, "maxWidth", &adaptation_set->max_width); + DASH_TAG_ATTR_VALUE(attrs[11], ATTR_VALUE_TYPE_UINT32, "minHeight", &adaptation_set->min_height); + DASH_TAG_ATTR_VALUE(attrs[12], ATTR_VALUE_TYPE_UINT32, "maxHeight", &adaptation_set->max_height); + DASH_TAG_ATTR_VALUE(attrs[13], ATTR_VALUE_TYPE_STRING, "minFrameRate", &adaptation_set->min_framerate); + DASH_TAG_ATTR_VALUE(attrs[14], ATTR_VALUE_TYPE_STRING, "maxFrameRate", &adaptation_set->max_framerate); + DASH_TAG_ATTR_VALUE(attrs[15], ATTR_VALUE_TYPE_STRING_BOOL, "segmentAlignment", &adaptation_set->segment_alignment); + DASH_TAG_ATTR_VALUE(attrs[16], ATTR_VALUE_TYPE_STRING_BOOL, "subsegmentAlignment", &adaptation_set->subsegment_aligment); + DASH_TAG_ATTR_VALUE(attrs[17], ATTR_VALUE_TYPE_UINT32, "subsegmentStartsWithSAP", &adaptation_set->subsegment_start_with_sap); + DASH_TAG_ATTR_VALUE(attrs[18], ATTR_VALUE_TYPE_STRING_BOOL, "bitstreamSwitching", &adaptation_set->bitstream_switching); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 == r) + r = dash_tag_representation_base(&adaptation_set->base, attr, len); + if (0 != r) + return r; + + parser->tag = adaptation_set; // save + period->adaptation_set_count++; + return 0; +} + +static int dash_tag_empty_adaptation_set(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[19]; + struct dash_period_t* period; + struct dash_adaptation_set_t* adaptation_set; + + period = (struct dash_period_t*)ptr; + capacity = ADAPTATION_SET_CAPACITY(period->empty_adaptation_set_count); + if (0 != dash_parser_realloc((void**)&period->empty_adaptation_sets, &capacity, period->empty_adaptation_set_count, ADAPTATION_SET_INCR, sizeof(period->empty_adaptation_sets[0]))) + return -ENOMEM; + + adaptation_set = &period->empty_adaptation_sets[period->empty_adaptation_set_count]; + adaptation_set->actuate = "onRequest"; // default + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "xlink:href", &adaptation_set->href); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "xlink:actuate", &adaptation_set->actuate); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "id", &adaptation_set->id); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_UINT32, "group", &adaptation_set->group); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "lang", &adaptation_set->lang); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "contentType", &adaptation_set->content_type); + DASH_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING, "par", &adaptation_set->par); + DASH_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_UINT32, "minBandwidth", &adaptation_set->min_bandwidth); + DASH_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_UINT32, "maxBandwidth", &adaptation_set->max_bandwidth); + DASH_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_UINT32, "minWidth", &adaptation_set->min_width); + DASH_TAG_ATTR_VALUE(attrs[10], ATTR_VALUE_TYPE_UINT32, "maxWidth", &adaptation_set->max_width); + DASH_TAG_ATTR_VALUE(attrs[11], ATTR_VALUE_TYPE_UINT32, "minHeight", &adaptation_set->min_height); + DASH_TAG_ATTR_VALUE(attrs[12], ATTR_VALUE_TYPE_UINT32, "maxHeight", &adaptation_set->max_height); + DASH_TAG_ATTR_VALUE(attrs[13], ATTR_VALUE_TYPE_STRING, "minFrameRate", &adaptation_set->min_framerate); + DASH_TAG_ATTR_VALUE(attrs[14], ATTR_VALUE_TYPE_STRING, "maxFrameRate", &adaptation_set->max_framerate); + DASH_TAG_ATTR_VALUE(attrs[15], ATTR_VALUE_TYPE_STRING_BOOL, "segmentAlignment", &adaptation_set->segment_alignment); + DASH_TAG_ATTR_VALUE(attrs[16], ATTR_VALUE_TYPE_STRING_BOOL, "sugsegmentAlignment", &adaptation_set->subsegment_aligment); + DASH_TAG_ATTR_VALUE(attrs[17], ATTR_VALUE_TYPE_STRING, "subsegmentStartsWithSAP", &adaptation_set->subsegment_start_with_sap); + DASH_TAG_ATTR_VALUE(attrs[18], ATTR_VALUE_TYPE_STRING_BOOL, "bitstreamSwitching", &adaptation_set->bitstream_switching); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 == r) + r = dash_tag_representation_base(&adaptation_set->base, attr, len); + if (0 != r) + return r; + + parser->tag = adaptation_set; // save + period->empty_adaptation_set_count++; + return 0; +} + +static int dash_tag_representation(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[7]; + struct dash_adaptation_set_t* adaptation_set; + struct dash_representation_t* representation; + + adaptation_set = (struct dash_adaptation_set_t*)ptr; + capacity = REPRESENTATION_CAPACITY(adaptation_set->representation_count); + if (0 != dash_parser_realloc((void**)&adaptation_set->representations, &capacity, adaptation_set->representation_count, REPRESENTATION_INCR, sizeof(adaptation_set->representations[0]))) + return -ENOMEM; + + representation = &adaptation_set->representations[adaptation_set->representation_count]; + representation->parent = adaptation_set; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "id", &representation->id); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT32, "bandwidth", &representation->bandwidth); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "qualityRanking", &representation->quality_ranking); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "dependencyId", &representation->dependncy_id); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "associationId", &representation->association_id); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "associationType", &representation->association_type); + DASH_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING, "mediaStreamStructureId", &representation->media_stream_structure_id); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 == r) + r = dash_tag_representation_base(&representation->base, attr, len); + if (0 != r) + return r; + + parser->tag = representation; // save + adaptation_set->representation_count++; + return 0; +} + +static int dash_tag_subrepresentation(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[4]; + struct dash_representation_t* representation; + struct dash_subrepresentation_t* subrepresentation; + + representation = (struct dash_representation_t*)ptr; + capacity = SUBREPRESENTATION_CAPACITY(representation->subrepresentation_count); + if (0 != dash_parser_realloc((void**)&representation->subrepresentations, &capacity, representation->subrepresentation_count, SUBREPRESENTATION_INCR, sizeof(representation->subrepresentations[0]))) + return -ENOMEM; + + subrepresentation = &representation->subrepresentations[representation->subrepresentation_count]; + subrepresentation->parent = representation; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "level", &subrepresentation->level); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "dependencyLevel", &subrepresentation->dependency_level); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "bandwidth", &subrepresentation->bandwidth); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "contentComponent", &subrepresentation->content_component); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 == r) + r = dash_tag_representation_base(&subrepresentation->base, attr, len); + if (0 != r) + return r; + + parser->tag = subrepresentation; // save + representation->subrepresentation_count++; + return 0; +} + +static int dash_tag_segment(struct dash_parser_t* parser, void* ptr, int type, const char* attr, size_t len) +{ + //const char* tags[] = { "SegmentBase", "SegmentList", "SegmentTemplate" }; + struct dash_tag_attr_t attrs[16]; + struct dash_segment_t* segment; + + assert(type > 0 && type <= 3); + segment = (struct dash_segment_t*)ptr; + segment->type = type; + segment->actuate = "onRequest"; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "timescale", &segment->timescale); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT64, "presentationTimeOffset", &segment->presentation_time_offset); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT64, "presentationDuration", &segment->presentation_duration); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_DURATION, "timeShiftBufferDepath", &segment->time_shift_buffer_depth); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "indexRange", &segment->index_range); + DASH_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING_BOOL, "indexRangeExact", &segment->index_range_exact); + DASH_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_FLOAT64, "availabiltiyTimeOffset", &segment->availability_time_offset); + DASH_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_STRING_BOOL, "availabilityTimeComplete", &segment->availability_time_complete); + // MultipleSegmentBaseType + DASH_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_UINT32, "duration", &segment->duration); + DASH_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_UINT64, "startNumber", &segment->start_number); + // SegmentListType + DASH_TAG_ATTR_VALUE(attrs[10], ATTR_VALUE_TYPE_STRING, "xlink:href", &segment->href); + DASH_TAG_ATTR_VALUE(attrs[11], ATTR_VALUE_TYPE_STRING, "xlink:actuate", &segment->actuate); + // SegmentTemplateType + DASH_TAG_ATTR_VALUE(attrs[12], ATTR_VALUE_TYPE_STRING, "media", &segment->media); + DASH_TAG_ATTR_VALUE(attrs[13], ATTR_VALUE_TYPE_STRING, "index", &segment->index); + DASH_TAG_ATTR_VALUE(attrs[14], ATTR_VALUE_TYPE_STRING, "initialization", &segment->initialization_url); + DASH_TAG_ATTR_VALUE(attrs[15], ATTR_VALUE_TYPE_STRING, "bitstreamSwitching", &segment->bitstream_switching_url); + + parser->tag = segment; // save + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_segment_base(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + return dash_tag_segment(parser, ptr, DASH_SEGMENT_BASE, attr, len); +} + +static int dash_tag_segment_list(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + return dash_tag_segment(parser, ptr, DASH_SEGMENT_LIST, attr, len); +} + +static int dash_tag_segment_template(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + return dash_tag_segment(parser, ptr, DASH_SEGMENT_TEMPLATE, attr, len); +} + +static int dash_tag_segment_timeline(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + struct dash_segment_t* segment; + struct dash_segment_timeline_t* timeline; + segment = (struct dash_segment_t*)ptr; + timeline = &segment->segment_timeline; + parser->tag = timeline; // save + (void)attr, (void)len; + return 0; +} + +static int dash_tag_s(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[5]; + struct dash_segment_timeline_t* timeline; + + timeline = (struct dash_segment_timeline_t*)ptr; + capacity = SEGMENT_URL_CAPACITY(timeline->count); + if (0 != dash_parser_realloc((void**)&timeline->S, &capacity, timeline->count, SEGMENT_URL_INCR, sizeof(timeline->S[0]))) + return -ENOMEM; + + timeline->S[timeline->count].k = 1; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT64, "t", &timeline->S[timeline->count].t); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT64, "n", &timeline->S[timeline->count].n); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT64, "d", &timeline->S[timeline->count].d); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_UINT64, "k", &timeline->S[timeline->count].k); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_UINT32, "r", &timeline->S[timeline->count].r); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = &timeline->S[timeline->count]; // save + timeline->count++; + return 0; +} + +static int dash_tag_segment_url(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[4]; + struct dash_segment_url_t* url; + struct dash_segment_t* segment; + + segment = (struct dash_segment_t*)ptr; + capacity = SEGMENT_URL_CAPACITY(segment->segment_url_count); + if (0 != dash_parser_realloc((void**)&segment->segment_urls, &capacity, segment->segment_url_count, SEGMENT_URL_INCR, sizeof(segment->segment_urls[0]))) + return -ENOMEM; + + url = &segment->segment_urls[segment->segment_url_count]; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "media", &url->media); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "mediaRange", &url->media_range); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_DURATION, "index", &url->index); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_UINT32, "indexRange", &url->index_range); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = url; // save + segment->segment_url_count++; + return 0; +} + +static int dash_tag_baseurl(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + size_t capacity; + struct dash_tag_attr_t attrs[4]; + struct dash_url_t* url; + + url = (struct dash_url_t*)ptr; + capacity = url->count; + if (0 != dash_parser_realloc((void**)&url->urls, &capacity, url->count, 1, sizeof(url->urls[0]))) + return -ENOMEM; + + url->urls[url->count].uri = parser->content; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "serviceLocation", &url->urls[url->count].service_location); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "byteRange", &url->urls[url->count].byte_range); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_FLOAT64, "availabilityTimeOffset", &url->urls[url->count].availability_time_offset); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING_BOOL, "availabilityTimeComplete", &url->urls[url->count].availability_time_complete); + + url->count++; + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_urltype(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + struct dash_tag_attr_t attrs[2]; + struct dash_urltype_t* url; + url = (struct dash_urltype_t*)ptr; + parser->tag = url; // save + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "sourceURL", &url->source_url); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "range", &url->range); + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_event_stream_parse(struct dash_event_stream_t* stream, const char* attr, size_t len) +{ + struct dash_tag_attr_t attrs[5]; + stream->actuate = "onRequest"; // default + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "xlink:href", &stream->href); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "xlink:actuate", &stream->actuate); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "schemeIdUri", &stream->scheme_id_uri); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "value", &stream->value); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_UINT32, "timescale", &stream->timescale); + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_event_stream(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + struct dash_event_stream_t* stream; + struct dash_period_t* period; + + period = (struct dash_period_t*)ptr; + if (0 != dash_parser_realloc((void**)&period->event_streams, &period->event_stream_capacity, period->event_stream_count, 2, sizeof(period->event_streams[0]))) + return -ENOMEM; + + stream = &period->event_streams[period->event_stream_count]; + r = dash_tag_event_stream_parse(stream, attr, len); + if (0 != r) + return r; + + parser->tag = stream; // save + period->event_stream_count++; + return 0; +} + +static int dash_tag_event(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[4]; + struct dash_event_stream_t* stream; + struct dash_event_t* event; + + stream = (struct dash_event_stream_t*)ptr; + capacity = EVENT_CAPACITY(stream->event_count); + if (0 != dash_parser_realloc((void**)&stream->events, &capacity, stream->event_count, EVENT_CAPACITY_INCR, sizeof(stream->events[0]))) + return -ENOMEM; + + event = &stream->events[stream->event_count]; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT64, "presentationTime", &event->presentation_time); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT64, "duration", &event->duration); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "id", &event->id); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "messageData", &event->message_data); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = event; // save + stream->event_count++; + return 0; +} + +static int dash_tag_content_component(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[5]; + struct dash_content_component_t* component; + struct dash_adaptation_set_t* adaptation_set; + + adaptation_set = (struct dash_adaptation_set_t*)ptr; + capacity = CONTENT_COMPONENT_CAPACITY(adaptation_set->content_component_count); + if (0 != dash_parser_realloc((void**)&adaptation_set->content_components, &capacity, adaptation_set->content_component_count, CONTENT_COMPONENT_CAPACITY_INCR, sizeof(adaptation_set->content_components[0]))) + return -ENOMEM; + + component = &adaptation_set->content_components[adaptation_set->content_component_count]; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "id", &component->id); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "lang", &component->lang); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "contentType", &component->content_type); + DASH_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "par", &component->par); + DASH_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "tag", &component->tag); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = component; // save + adaptation_set->content_component_count++; + return 0; +} + +static int dash_tag_subset(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[2]; + struct dash_period_t* period; + + period = (struct dash_period_t*)ptr; + capacity = SUBSET_CAPACITY(period->preselection_count); + if (0 != dash_parser_realloc((void**)&period->subsets, &capacity, period->subset_count, SUBSET_INCR, sizeof(period->subsets[0]))) + return -ENOMEM; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "contains", &period->subsets[period->subset_count].contains); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "id", &period->subsets[period->subset_count].id); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = &period->subsets[period->subset_count]; // save + period->subset_count++; + return 0; +} + +static int dash_tag_switching(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[2]; + struct dash_representation_base_t* base; + + base = (struct dash_representation_base_t*)ptr; + capacity = base->switching_count; + if (0 != dash_parser_realloc((void**)&base->switchings, &capacity, base->switching_count, 1, sizeof(base->switchings[0]))) + return -ENOMEM; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "interval", &base->switchings[base->switching_count].interval); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "type", &base->switchings[base->switching_count].type); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = &base->switchings[base->switching_count]; // save + base->switching_count++; + return 0; +} + +static int dash_tag_random_access(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[4]; + struct dash_representation_base_t* base; + + base = (struct dash_representation_base_t*)ptr; + capacity = base->random_access_count; + if (0 != dash_parser_realloc((void**)&base->random_accesses, &capacity, base->random_access_count, 1, sizeof(base->random_accesses[0]))) + return -ENOMEM; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "interval", &base->random_accesses[base->random_access_count].interval); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "type", &base->random_accesses[base->random_access_count].type); + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_DURATION, "miniBufferTime", &base->random_accesses[base->random_access_count].min_buffer_time); + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "bandwidth", &base->random_accesses[base->random_access_count].bandwidth); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = &base->random_accesses[base->random_access_count]; // save + base->random_access_count++; + return 0; +} + +static int dash_tag_program_information(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[2]; + struct dash_mpd_t* mpd; + struct dash_program_information_t* info; + + mpd = (struct dash_mpd_t*)ptr; + capacity = mpd->info_count; + if (0 != dash_parser_realloc((void**)&mpd->infos, &capacity, mpd->info_count, 1, sizeof(mpd->infos[0]))) + return -ENOMEM; + + info = &mpd->infos[mpd->info_count]; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "lang", &info->lang); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "moreInformationURL", &info->more_information); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = info; // save + mpd->info_count++; + return 0; +} + +static int dash_tag_title(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + struct dash_program_information_t* info; + info = (struct dash_program_information_t*)ptr; + info->title = parser->content; + (void)attr, (void)len; + return 0; +} + +static int dash_tag_source(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + struct dash_program_information_t* info; + info = (struct dash_program_information_t*)ptr; + info->source = parser->content; + (void)attr, (void)len; + return 0; +} + +static int dash_tag_copyright(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + struct dash_program_information_t* info; + info = (struct dash_program_information_t*)ptr; + info->copyright = parser->content; + (void)attr, (void)len; + return 0; +} + +static int dash_tag_location(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + size_t capacity; + struct dash_mpd_t* mpd; + mpd = (struct dash_mpd_t*)ptr; + capacity = mpd->location_count; + if (0 != dash_parser_realloc((void**)&mpd->locations, &capacity, mpd->location_count, 1, sizeof(mpd->locations[0]))) + return -ENOMEM; + + parser->tag = &mpd->locations[mpd->location_count]; // save + mpd->locations[mpd->location_count++] = parser->content; + (void)attr, (void)len; + return 0; +} + +static int dash_tag_descritor(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + size_t capacity; + struct dash_tag_attr_t attrs[3]; + struct dash_descriptor_t* desc; + + desc = (struct dash_descriptor_t*)ptr; + capacity = desc->count; + if (0 != dash_parser_realloc((void**)&desc->descs, &capacity, desc->count, 1, sizeof(desc->descs[0]))) + return -ENOMEM; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "schemeIdUri", &desc->descs[desc->count].scheme_uri); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "value", &desc->descs[desc->count].value); + DASH_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "id", &desc->descs[desc->count].id); + + parser->tag = &desc->descs[desc->count]; // save + desc->count++; + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_metrics(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[2]; + struct dash_mpd_t* mpd; + struct dash_metric_t* metric; + + mpd = (struct dash_mpd_t*)ptr; + capacity = mpd->metric_count; + if (0 != dash_parser_realloc((void**)&mpd->metrics, &capacity, mpd->metric_count, 1, sizeof(mpd->metrics[0]))) + return -ENOMEM; + + metric = &mpd->metrics[mpd->metric_count]; + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "metrics", &metric->metrics); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = metric; // save + mpd->metric_count++; + return 0; +} + +static int dash_tag_metric_range(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_tag_attr_t attrs[2]; + struct dash_metric_t* metric; + + metric = (struct dash_metric_t*)ptr; + capacity = metric->range_count; + if (0 != dash_parser_realloc((void**)&metric->ranges, &capacity, metric->range_count, 1, sizeof(metric->ranges[0]))) + return -ENOMEM; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_DURATION, "time", &metric->ranges[metric->range_count].time); + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_DURATION, "duration", &metric->ranges[metric->range_count].duration); + + r = dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); + if (0 != r) + return r; + + parser->tag = &metric->ranges[metric->range_count]; // save + metric->range_count++; + return 0; +} + +static int dash_tag_label(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + size_t capacity; + struct dash_tag_attr_t attrs[2]; + struct dash_label_t* label; + + label = (struct dash_label_t*)ptr; + capacity = label->count; + if (0 != dash_parser_realloc((void**)&label->labels, &capacity, label->count, 1, sizeof(label->labels[0]))) + return -ENOMEM; + + DASH_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "id", &label->labels[label->count].id); + DASH_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "lang", &label->labels[label->count].lang); + + parser->tag = &label->labels[label->count++]; // save + return dash_parse_attrs(attr, len, attrs, sizeof(attrs) / sizeof(attrs[0])); +} + +static int dash_tag_inband_event_stream(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len) +{ + int r; + size_t capacity; + struct dash_event_stream_t* stream; + struct dash_representation_base_t* base; + + base = (struct dash_representation_base_t*)ptr; + capacity = base->inband_event_stream_count; + if (0 != dash_parser_realloc((void**)&base->inband_event_streams, &capacity, base->inband_event_stream_count, 1, sizeof(base->inband_event_streams[0]))) + return -ENOMEM; + + stream = &base->inband_event_streams[base->inband_event_stream_count]; + r = dash_tag_event_stream_parse(stream, attr, len); + if (0 != r) + return r; + + parser->tag = stream; // save + base->inband_event_stream_count++; + return 0; +} + +static const struct +{ + const char* name; + const char* parent; + int (*parser)(struct dash_parser_t* parser, void* ptr, const char* attr, size_t len); + size_t offset; +} s_tags[] = { + // MPD + { "MPD", "", dash_tag_mpd, 0 }, + { "ProgramInformation", "MPD", dash_tag_program_information, 0 }, + { "BaseURL", "MPD", dash_tag_baseurl, offsetof(struct dash_mpd_t, urls) }, + { "Location", "MPD", dash_tag_location, 0 }, + { "Period", "MPD", dash_tag_period, 0 }, + { "Metrics", "MPD", dash_tag_metrics, 0 }, + { "EssentialProperty", "MPD", dash_tag_descritor, offsetof(struct dash_mpd_t, essentials) }, + { "SupplementalProperty", "MPD", dash_tag_descritor, offsetof(struct dash_mpd_t, supplementals) }, + { "UTCTiming", "MPD", dash_tag_descritor, offsetof(struct dash_mpd_t, timings) }, + + // Period + { "BaseURL", "Period", dash_tag_baseurl, offsetof(struct dash_period_t, base_urls) }, + { "SegmentBase", "Period", dash_tag_segment_base, offsetof(struct dash_period_t, segment) }, + { "SegmentList", "Period", dash_tag_segment_list, offsetof(struct dash_period_t, segment) }, + { "SegmentTemplate", "Period", dash_tag_segment_template, offsetof(struct dash_period_t, segment) }, + { "AssetIdentifier", "Period", dash_tag_descritor, offsetof(struct dash_period_t, asset_identifier) }, + { "EventStream", "Period", dash_tag_event_stream, 0 }, + { "AdaptationSet", "Period", dash_tag_adaptation_set, 0 }, + { "Subset", "Period", dash_tag_subset, 0 }, + { "SupplementalProperty", "Period", dash_tag_descritor, offsetof(struct dash_period_t, supplementals) }, + { "EmptyAdaptationSet", "Period", dash_tag_empty_adaptation_set, 0 }, + { "GroupLabel", "Period", dash_tag_label, offsetof(struct dash_period_t, group_labels) }, + { "Preselection", "Period", dash_tag_preselection, 0 }, + + // EventStream + { "Event", "EventStream", dash_tag_event, 0 }, + + // Adaptation Set + { "FramePacking", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, frame_packings) }, + { "AudioChannelConfiguration", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, audio_channel_configurations) }, + { "ContentProtection", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, content_protections) }, + { "EssentialProperty", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, essentials) }, + { "SupplementalProperty", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, supplementals) }, + { "InbandEventSteram", "AdaptationSet", dash_tag_inband_event_stream, offsetof(struct dash_adaptation_set_t, base) }, + { "Switching", "AdaptationSet", dash_tag_switching, offsetof(struct dash_adaptation_set_t, base) }, + { "RandomAccess", "AdaptationSet", dash_tag_random_access, offsetof(struct dash_adaptation_set_t, base) }, + { "GroupLabel", "AdaptationSet", dash_tag_label, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, group_labels) }, + { "Label", "AdaptationSet", dash_tag_label, offsetof(struct dash_adaptation_set_t, base) + offsetof(struct dash_representation_base_t, labels) }, + { "Accessibility", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, accessibilities) }, + { "Role", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, roles) }, + { "Rating", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, ratings) }, + { "Viewpoint", "AdaptationSet", dash_tag_descritor, offsetof(struct dash_adaptation_set_t, viewpoints) }, + { "ContentComponent", "AdaptationSet", dash_tag_content_component, 0 }, + { "BaseURL", "AdaptationSet", dash_tag_baseurl, offsetof(struct dash_adaptation_set_t, base_urls) }, + { "SegmentBase", "AdaptationSet", dash_tag_segment_base, offsetof(struct dash_adaptation_set_t, segment) }, + { "SegmentList", "AdaptationSet", dash_tag_segment_list, offsetof(struct dash_adaptation_set_t, segment) }, + { "SegmentTemplate", "AdaptationSet", dash_tag_segment_template, offsetof(struct dash_adaptation_set_t, segment) }, + { "Representation", "AdaptationSet", dash_tag_representation, 0 }, + + // Content Component + { "Accessibility", "ContentComponent", dash_tag_descritor, offsetof(struct dash_content_component_t, accessibilities) }, + { "Role", "ContentComponent", dash_tag_descritor, offsetof(struct dash_content_component_t, roles) }, + { "Rating", "ContentComponent", dash_tag_descritor, offsetof(struct dash_content_component_t, ratings) }, + { "Viewpoint", "ContentComponent", dash_tag_descritor, offsetof(struct dash_content_component_t, viewpoints) }, + + // Representation + { "FramePacking", "Representation", dash_tag_descritor, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, frame_packings) }, + { "AudioChannelConfiguration", "Representation", dash_tag_descritor, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, audio_channel_configurations) }, + { "ContentProtection", "Representation", dash_tag_descritor, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, content_protections) }, + { "EssentialProperty", "Representation", dash_tag_descritor, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, essentials) }, + { "SupplementalProperty", "Representation", dash_tag_descritor, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, supplementals) }, + { "InbandEventSteram", "Representation", dash_tag_inband_event_stream, offsetof(struct dash_representation_t, base) }, + { "Switching", "Representation", dash_tag_switching, offsetof(struct dash_representation_t, base) }, + { "RandomAccess", "Representation", dash_tag_random_access, offsetof(struct dash_representation_t, base) }, + { "GroupLabel", "Representation", dash_tag_label, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, group_labels) }, + { "Label", "Representation", dash_tag_label, offsetof(struct dash_representation_t, base) + offsetof(struct dash_representation_base_t, labels) }, + { "BaseURL", "Representation", dash_tag_baseurl, offsetof(struct dash_adaptation_set_t, base_urls) }, + { "SubRepresentation", "Representation", dash_tag_subrepresentation, 0 }, + { "SegmentBase", "Representation", dash_tag_segment_base, offsetof(struct dash_representation_t, segment) }, + { "SegmentList", "Representation", dash_tag_segment_list, offsetof(struct dash_representation_t, segment) }, + { "SegmentTemplate", "Representation", dash_tag_segment_template, offsetof(struct dash_representation_t, segment) }, + + // Subresentation + { "FramePacking", "SubRepresentation", dash_tag_descritor, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, frame_packings) }, + { "AudioChannelConfiguration", "SubRepresentation", dash_tag_descritor, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, audio_channel_configurations) }, + { "ContentProtection", "SubRepresentation", dash_tag_descritor, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, content_protections) }, + { "EssentialProperty", "SubRepresentation", dash_tag_descritor, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, essentials) }, + { "SupplementalProperty", "SubRepresentation", dash_tag_descritor, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, supplementals) }, + { "InbandEventSteram", "SubRepresentation", dash_tag_inband_event_stream, offsetof(struct dash_subrepresentation_t, base) }, + { "Switching", "SubRepresentation", dash_tag_switching, offsetof(struct dash_subrepresentation_t, base) }, + { "RandomAccess", "SubRepresentation", dash_tag_random_access, offsetof(struct dash_subrepresentation_t, base) }, + { "GroupLabel", "SubRepresentation", dash_tag_label, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, group_labels) }, + { "Label", "SubRepresentation", dash_tag_label, offsetof(struct dash_subrepresentation_t, base) + offsetof(struct dash_representation_base_t, labels) }, + + // Preselection + { "FramePacking", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, frame_packings) }, + { "AudioChannelConfiguration", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, audio_channel_configurations) }, + { "ContentProtection", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, content_protections) }, + { "EssentialProperty", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, essentials) }, + { "SupplementalProperty", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, supplementals) }, + { "InbandEventSteram", "Preselection", dash_tag_inband_event_stream, offsetof(struct dash_preselection_t, base) }, + { "Switching", "Preselection", dash_tag_switching, offsetof(struct dash_preselection_t, base) }, + { "RandomAccess", "Preselection", dash_tag_random_access, offsetof(struct dash_preselection_t, base) }, + { "GroupLabel", "Preselection", dash_tag_label, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, group_labels) }, + { "Label", "Preselection", dash_tag_label, offsetof(struct dash_preselection_t, base) + offsetof(struct dash_representation_base_t, labels) }, + { "Accessibility", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, accessibilities) }, + { "Role", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, roles) }, + { "Rating", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, ratings) }, + { "Viewpoint", "Preselection", dash_tag_descritor, offsetof(struct dash_preselection_t, viewpoints) }, + + // SegmentBase + { "Initialization", "SegmentBase", dash_tag_urltype, offsetof(struct dash_segment_t, initialization) }, + { "RepresentationIndex", "SegmentBase", dash_tag_urltype, offsetof(struct dash_segment_t, representation_index) }, + + // SegmentList + { "Initialization", "SegmentList", dash_tag_urltype, offsetof(struct dash_segment_t, initialization) }, + { "RepresentationIndex", "SegmentList", dash_tag_urltype, offsetof(struct dash_segment_t, representation_index) }, + { "SegmentTimeline", "SegmentList", dash_tag_segment_timeline, 0 }, + { "BitstreamSwitching", "SegmentList", dash_tag_urltype, offsetof(struct dash_segment_t, bitstream_switching) }, + { "SegmentUrl", "SegmentList", dash_tag_segment_url, 0 }, + + // SegmentTemplate + { "Initialization", "SegmentTemplate", dash_tag_urltype, offsetof(struct dash_segment_t, initialization) }, + { "RepresentationIndex", "SegmentTemplate", dash_tag_urltype, offsetof(struct dash_segment_t, representation_index) }, + { "SegmentTimeline", "SegmentTemplate", dash_tag_segment_timeline, 0 }, + { "BitstreamSwitching", "SegmentTemplate", dash_tag_urltype, offsetof(struct dash_segment_t, bitstream_switching) }, + + // SegmentTimeline + { "S", "SegmentTimeline", dash_tag_s, 0 }, + + // Program Information + { "Title", "ProgramInformation", dash_tag_title, 0 }, + { "Source", "ProgramInformation", dash_tag_source, 0 }, + { "CopyRight", "ProgramInformation", dash_tag_copyright, 0 }, + + // Metrics + { "Range", "Metrics", dash_tag_metric_range, 0 }, + { "Reporting", "Metrics", dash_tag_descritor, offsetof(struct dash_metric_t, reportings) }, +}; + +static int dash_parser_ontag(struct dash_parser_t* parser, const char* tag, const char* attrs, size_t bytes) +{ + int r; + size_t i; + void* ptr; + const char* parent; + + r = 0; + parent = parser->level > 0 ? parser->stack[parser->level - 1].tag : ""; + //printf("%.*s: %.*s %.*s\n", (int)len, tag, (int)bytes, attrs, (int)ncontent, content); + for (i = 0; i < sizeof(s_tags) / sizeof(s_tags[0]) && 0 == r; i++) + { + if (0 == strcasecmp(s_tags[i].name, tag) && 0 == strcasecmp(s_tags[i].parent, parent)) + { + ptr = parser->level > 0 ? parser->stack[parser->level - 1].ptr : NULL; + ptr = (uint8_t*)ptr + s_tags[i].offset; + r = s_tags[i].parser(parser, ptr, attrs, bytes); + break; + } + } + + return r; +} + +static const char* dash_parser_tag(struct dash_tag_t* tag, const char* data, const char* end) +{ + const char* next; + + memset(tag, 0, sizeof(*tag)); + tag->ptr = strchr(data, '<'); + if (!tag->ptr || tag->ptr >= end) + return end; // all done + + tag->nlen = end - tag->ptr - 1; + tag->name = hls_strtrim(tag->ptr + 1 /* skip '<' */, &tag->nlen, " \t\r\n", NULL); // trim SP/HTAB + switch (*tag->name) + { + case '?': + // XML declaration + // + tag->type = DASH_TAG_DECLARATION; + tag->name += 1; // skip '?' + tag->nlen -= 1; + break; + + case '/': + tag->flags |= DASH_TAG_FLAG_END; + tag->type = DASH_TAG_ELEMENT; + tag->name += 1; // skip '/' + tag->nlen -= 1; + break; + + case '!': + if (tag->nlen >= 3 && 0 == strncmp("!--", tag->name, 3)) + { + tag->type = DASH_TAG_COMMENT; + next = strstr(tag->name + 3, "-->"); + tag->end = next ? next + 2 : end; + return next ? next + 3 : end; + } + else if (tag->nlen >= 8 && 0 == strncmp("![CDATA[", tag->name, 8)) + { + tag->type = DASH_TAG_CDATA; + next = strstr(tag->name + 8, "]]>"); + tag->end = next ? next + 2 : end; + return next ? next + 3 : end; + } + else + { + // unknown + assert(0); + return NULL; + } + break; + + default: + tag->flags |= DASH_TAG_FLAG_START; + tag->type = DASH_TAG_ELEMENT; + break; + } + + tag->nlen = hls_strsplit(tag->name, end, ">", "\'\"", &next); + tag->end = tag->name + tag->nlen; + tag->name = hls_strtrim(tag->name, &tag->nlen, " \t\r\n", " \t\r\n"); // trim SP/HTAB + if (tag->nlen > 0 && '/' == tag->name[tag->nlen - 1]) + { + // line-break + assert(0 == (tag->flags & DASH_TAG_FLAG_END)); + tag->flags |= DASH_TAG_FLAG_LINBREAK; + tag->nlen -= 1; + } + + // attributes + tag->attr = strpbrk(tag->name, "/> \t\r\n"); + assert(tag->attr <= tag->name + tag->nlen); + tag->nattr = tag->nlen - (tag->attr - tag->name); + tag->nlen = tag->attr ? tag->attr - tag->name : tag->nlen; + tag->attr = hls_strtrim(tag->attr, &tag->nattr, " \t\r\n", " \t\r\n"); // trim SP/HTAB + + if (tag->nlen < 1) + { + assert(0); + return NULL; + } + return next; +} + +static int dash_parser_input(struct dash_parser_t* parser, const char* data, size_t bytes) +{ + int r; + size_t ncontent; + const char* ptr, *next; + struct dash_tag_t tag, tag2; + + r = -1; + assert(data && bytes > 0); + next = dash_parser_tag(&tag, data, data + bytes); + for (ptr = next; ptr && ptr < data + bytes; ptr = next) + { + next = dash_parser_tag(&tag2, ptr, data + bytes); + + if (DASH_TAG_ELEMENT != tag.type) + { + memcpy(&tag, &tag2, sizeof(tag)); + continue; // ignore declaration/comments/cdata + } + + assert( /*'<' == *tag.ptr &&*/ '>' == *tag.end); + assert(tag.name && tag.nlen > 0); + ((char*)tag.name)[tag.nlen] = '\0'; + + if (DASH_TAG_FLAG_END & tag.flags) + { + assert(0 == ((DASH_TAG_FLAG_LINBREAK | DASH_TAG_FLAG_START) & tag.flags)); + if (parser->level < 1 || 0 != strcasecmp(parser->stack[parser->level - 1].tag, tag.name)) + { + assert(0); + return -1; // tag don't match + } + parser->level -= 1; + } + else + { + assert(DASH_TAG_FLAG_START & tag.flags); + if (0 == (DASH_TAG_FLAG_LINBREAK & tag.flags)) + { + ncontent = tag2.ptr - ptr; + parser->content = (char*)hls_strtrim(ptr, &ncontent, " \t\r\n", " \t\r\n"); // trim SP/HTAB + parser->content[ncontent] = '\0'; + } + else + { + parser->content = ""; + } + + r = dash_parser_ontag(parser, tag.name, tag.attr, tag.nattr); + + assert(parser->level < sizeof(parser->stack) / sizeof(parser->stack[0])); + if (0 == (DASH_TAG_FLAG_LINBREAK & tag.flags) && parser->level < sizeof(parser->stack) / sizeof(parser->stack[0])) + { + parser->stack[parser->level].tag = tag.name; + parser->stack[parser->level].ptr = parser->tag; + parser->level += 1; + } + } + + memcpy(&tag, &tag2, sizeof(tag)); + } + + if (!ptr) + return -1; + return r; +} + +int dash_mpd_parse(struct dash_mpd_t** mpd, const char* data, size_t bytes) +{ + int r; + char* ptr; + struct dash_parser_t parser; + + if (!data || bytes < 6 /* */) + return -1; + + memset(&parser, 0, sizeof(parser)); + parser.mpd = (struct dash_mpd_t*)calloc(1, sizeof(struct dash_mpd_t) + bytes + 1); + if (!parser.mpd) + return -ENOMEM; + + ptr = (char*)(parser.mpd + 1); + memcpy(ptr, data, bytes); + + r = dash_parser_input(&parser, ptr, bytes); + if (0 != r) + { + dash_mpd_free(&parser.mpd); + return r; + } + + *mpd = parser.mpd; + return 0; +} + +static void dash_segment_free(struct dash_segment_t* segment) +{ + if (segment->segment_timeline.S) + free(segment->segment_timeline.S); + if (segment->segment_urls) + free(segment->segment_urls); +} + +static void dash_representation_base_free(struct dash_representation_base_t* base) +{ + size_t i; + + if (base->frame_packings.descs) + free(base->frame_packings.descs); + if (base->audio_channel_configurations.descs) + free(base->audio_channel_configurations.descs); + if (base->content_protections.descs) + free(base->content_protections.descs); + if (base->essentials.descs) + free(base->essentials.descs); + if (base->supplementals.descs) + free(base->supplementals.descs); + + for (i = 0; i < base->inband_event_stream_count; i++) + { + if (base->inband_event_streams[i].events) + free(base->inband_event_streams[i].events); + } + if (base->inband_event_streams) + free(base->inband_event_streams); + + if (base->switchings) + free(base->switchings); + if (base->random_accesses) + free(base->random_accesses); + if (base->group_labels.labels) + free(base->group_labels.labels); + if (base->labels.labels) + free(base->labels.labels); +} + +static void dash_representation_free(struct dash_representation_t* representation) +{ + size_t i; + + dash_representation_base_free(&representation->base); + + if (representation->base_urls.urls) + free(representation->base_urls.urls); + + for (i = 0; i < representation->subrepresentation_count; i++) + { + dash_representation_base_free(&representation->subrepresentations[i].base); + } + if (representation->subrepresentations) + free(representation->subrepresentations); + + dash_segment_free(&representation->segment); +} + +static void dash_adaptation_set_free(struct dash_adaptation_set_t* set) +{ + size_t k; + + dash_representation_base_free(&set->base); + if (set->accessibilities.descs) + free(set->accessibilities.descs); + if (set->roles.descs) + free(set->roles.descs); + if (set->ratings.descs) + free(set->ratings.descs); + if (set->viewpoints.descs) + free(set->viewpoints.descs); + + for (k = 0; k < set->content_component_count; k++) + { + if (set->content_components[k].accessibilities.descs) + free(set->content_components[k].accessibilities.descs); + if (set->content_components[k].roles.descs) + free(set->content_components[k].roles.descs); + if (set->content_components[k].ratings.descs) + free(set->content_components[k].ratings.descs); + if (set->content_components[k].viewpoints.descs) + free(set->content_components[k].viewpoints.descs); + } + if (set->content_components) + free(set->content_components); + + if (set->base_urls.urls) + free(set->base_urls.urls); + + dash_segment_free(&set->segment); + + for (k = 0; k < set->content_component_count; k++) + { + dash_representation_free(&set->representations[k]); + } + if (set->representations) + free(set->representations); +} + +static void dash_period_free(struct dash_period_t* period) +{ + size_t j; + + dash_segment_free(&period->segment); + if (period->base_urls.urls) + free(period->base_urls.urls); + if (period->asset_identifier.descs) + free(period->asset_identifier.descs); + + for (j = 0; j < period->event_stream_count; j++) + { + if (period->event_streams[j].events) + free(period->event_streams[j].events); + } + if (period->event_streams) + free(period->event_streams); + + for (j = 0; j < period->adaptation_set_count; j++) + { + dash_adaptation_set_free(&period->adaptation_sets[j]); + } + if (period->adaptation_sets) + free(period->adaptation_sets); + + for (j = 0; j < period->empty_adaptation_set_count; j++) + { + dash_adaptation_set_free(&period->empty_adaptation_sets[j]); + } + if (period->empty_adaptation_sets) + free(period->empty_adaptation_sets); + + if (period->subsets) + free(period->subsets); + if (period->supplementals.descs) + free(period->supplementals.descs); + if (period->group_labels.labels) + free(period->group_labels.labels); + + for (j = 0; j < period->preselection_count; j++) + { + dash_representation_base_free(&period->preselections[j].base); + if (period->preselections[j].accessibilities.descs) + free(period->preselections[j].accessibilities.descs); + if (period->preselections[j].roles.descs) + free(period->preselections[j].roles.descs); + if (period->preselections[j].ratings.descs) + free(period->preselections[j].ratings.descs); + if (period->preselections[j].viewpoints.descs) + free(period->preselections[j].viewpoints.descs); + } + if (period->preselections) + free(period->preselections); +} + +int dash_mpd_free(struct dash_mpd_t** mpd) +{ + size_t i; + struct dash_mpd_t* p; + + if (mpd && *mpd) + { + p = *mpd; + if (p->infos) + { + assert(p->info_count > 0); + free(p->infos); + } + if (p->urls.urls) + { + assert(p->urls.count > 0); + free(p->urls.urls); + } + if (p->locations) + { + assert(p->location_count > 0); + free(p->locations); + } + + for(i = 0; i < p->period_count; i++) + dash_period_free(&p->periods[i]); + if(p->periods) + free(p->periods); + + for (i = 0; i < p->metric_count; i++) + { + if (p->metrics[i].ranges) + free(p->metrics[i].ranges); + } + if(p->metrics) + free(p->metrics); + + if (p->essentials.descs) + free(p->essentials.descs); + if (p->supplementals.descs) + free(p->supplementals.descs); + if (p->timings.descs) + free(p->timings.descs); + + free(p); + *mpd = NULL; + return 0; + } + return -1; +} + +#if defined(_DEBUG) || defined(DEBUG) +void dash_parser_test(const char* xml) +{ + static char data[2 * 1024 * 1024]; + FILE* fp = fopen(xml, "rb"); + int n = (int)fread(data, 1, sizeof(data), fp); + fclose(fp); + + struct dash_mpd_t* mpd; + assert(0 == dash_mpd_parse(&mpd, data, n)); + dash_mpd_free(&mpd); +} +#endif diff --git a/src/3rdpart/media-server/libdash/src/dash-period.c b/src/3rdpart/media-server/libdash/src/dash-period.c new file mode 100644 index 0000000..cafde88 --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/dash-period.c @@ -0,0 +1,182 @@ +#include "dash-parser.h" +#include "hls-string.h" +#include +#include +#include + +int64_t dash_get_duration(const struct dash_mpd_t* mpd) +{ + size_t i; + int64_t t, start; + const struct dash_period_t* period; + + if (DASH_DYNAMIC == mpd->type) + return -1; + + if (mpd->media_presentation_duration > 0) + return mpd->media_presentation_duration; + + t = 0; + for (i = 0; i < mpd->period_count; i++) + { + period = &mpd->periods[i]; + if (period->start > 0) + { + // a regular Period or an early terminated Period + start = period->start; + } + else if (i > 0 && mpd->periods[i - 1].duration > 0) + { + start = t; + } + else if (0 == i && DASH_STATIC == mpd->type) + { + start = 0; + } + else if ((0 == i || mpd->periods[i - 1].duration == 0) && DASH_DYNAMIC == mpd->type) + { + continue; // Early Available Period + } + else + { + start = t; + } + + if (period->duration > 0) + { + t = start + period->duration; + } + else if (i + 1 < mpd->period_count) + { + assert(0 != mpd->periods[i + 1].start); + t = mpd->periods[i + 1].start; + } + else if (DASH_DYNAMIC == mpd->type) + { + // MPD@mediaPresentationDuration shall be present when neither + // the attribute MPD@minimumUpdatePeriod nor the Period@duration + // of the last Period are present. + assert(mpd->media_presentation_duration > 0 || mpd->minimum_update_period > 0); + t = mpd->media_presentation_duration > 0 ? mpd->media_presentation_duration : (start + mpd->minimum_update_period); + } + else + { + t = start; // ??? + } + } + + return t; +} + +int dash_period_find(const struct dash_mpd_t* mpd, int64_t time) +{ + size_t i; + int64_t t, start; + const struct dash_period_t* period; + + if (1 == mpd->period_count) + return 0; // only one period + + t = 0; + for (i = 0; i < mpd->period_count; i++) + { + // 5.3.2.1 Overview (p27) + // 1. If the attribute @start is present in the Period, then the Period + // is a regular Period or an early terminated Period and the PeriodStart + // is equal to the value of this attribute. + // 2. If the @start attribute is absent, but the previous Period element + // contains a @duration attributethen this new Period is also a regular + // Period or an early terminated Period. The start time of the new Period + // PeriodStart is the sum of the start time of the previous Period PeriodStart + // and the value of the attribute @duration of the previous Period. + // 3. If (i) @start attribute is absent, and (ii) the Period element is the + // first in the MPD, and (iii) the MPD@type is 'static', then the PeriodStart + // time shall be set to zero + // 4. If (i) @start attribute is absent, and (ii) the previous Period element does + // not contain a @durationattribute or the Period element is the first in the + // MPD, and (iii) the MPD@type is 'dynamic', then thisPeriod is an Early Available + // Period (see below for details) + // 5. If (i) @duration attribute is present, and (ii) the next Period element contains + // a @start attribute orthe @minimumUpdatePeriod is present, then this Period + // is an Early Terminated Period (see below for details) + + period = &mpd->periods[i]; + if (period->start > 0) + { + // a regular Period or an early terminated Period + start = period->start; + } + else if (i > 0 && mpd->periods[i - 1].duration > 0) + { + start = t; + } + else if (0 == i && DASH_STATIC == mpd->type) + { + start = 0; + } + else if( (0 == i || mpd->periods[i - 1].duration == 0) && DASH_DYNAMIC == mpd->type) + { + continue; // Early Available Period + } + else + { + start = t; + } + + if (period->duration > 0) + { + t = start + period->duration; + } + else if (i + 1 < mpd->period_count) + { + assert(0 != mpd->periods[i + 1].start); + t = mpd->periods[i + 1].start; + } + else if(DASH_DYNAMIC == mpd->type) + { + // MPD@mediaPresentationDuration shall be present when neither + // the attribute MPD@minimumUpdatePeriod nor the Period@duration + // of the last Period are present. + assert(mpd->media_presentation_duration > 0 || mpd->minimum_update_period > 0); + t = mpd->media_presentation_duration > 0 ? mpd->media_presentation_duration : (start + mpd->minimum_update_period); + } + else + { + t = start; // ??? + } + + if (time < t) + return (int)i; + } + + return -1; // not found +} + +const struct dash_adaptation_set_t* dash_period_select(const struct dash_period_t* period, int media, unsigned int id, unsigned int group, const char* lang, const char* codecs) +{ + size_t i; + const struct dash_adaptation_set_t* set; + + for (i = 0; i < period->adaptation_set_count; i++) + { + set = &period->adaptation_sets[i]; + if (DASH_MEDIA_UNKNOWN != media && media != dash_adaptation_set_media_type(set)) + continue; + + if (0 != id && id != set->id) + continue; + + if (0 != group && group != set->group) + continue; + + if (lang && *lang && set->lang && 0 != strcasecmp(set->lang, lang)) + continue; + + if (codecs && *codecs && set->base.codecs && 0 != strncasecmp(set->base.codecs, codecs, strlen(codecs))) + continue; + + return set; + } + + return NULL; +} diff --git a/src/3rdpart/media-server/libdash/src/dash-representation.c b/src/3rdpart/media-server/libdash/src/dash-representation.c new file mode 100644 index 0000000..89ed99a --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/dash-representation.c @@ -0,0 +1,189 @@ +#include "dash-parser.h" +#include +#include +#include +#include +#include +#include + +const char* dash_segment_initialization(const struct dash_segment_t* segment); +int dash_segment_count(const struct dash_segment_t* segment); +int dash_segment_find(const struct dash_segment_t* segment, int64_t time); +int dash_segment_information(const struct dash_segment_t* segment, int index, int64_t* number, int64_t* start, int64_t* duration, const char** url, const char** range); + +const struct dash_url_t* dash_representation_get_base_url(const struct dash_representation_t* representation) +{ + const struct dash_mpd_t* mpd; + const struct dash_period_t* period; + const struct dash_adaptation_set_t* set; + + if (representation->base_urls.count > 0) + return &representation->base_urls; + + set = representation->parent; + if (set->base_urls.count > 0) + return &set->base_urls; + + period = set->parent; + if (period->base_urls.count > 0) + return &period->base_urls; + + mpd = period->parent; + return &mpd->urls; +} + +const struct dash_segment_t* dash_representation_get_segment(const struct dash_representation_t* representation) +{ + const struct dash_period_t* period; + const struct dash_adaptation_set_t* set; + + if (DASH_SEGMENT_NONE != representation->segment.type) + return &representation->segment; + + set = representation->parent; + if (DASH_SEGMENT_NONE != set->segment.type) + return &set->segment; + + period = set->parent; + if (DASH_SEGMENT_NONE != period->segment.type) + return &period->segment; + + return &representation->segment; +} + +/// @return >=0-ok with length, <0-error +static int dash_representation_template_replace(const struct dash_representation_t* representation, const char* url, int64_t number, int64_t start, char* ptr, size_t len) +{ + // Each identifier may be suffixed, within the enclosing '$' characters, + // with an additional format tag aligned with the printf format tag: + // %0[width]d + //const char* patterns[] = { "RepresentationID", "Number", "Bandwidth", "Time", "SubNumber" }; + size_t i, j; + int width, off; + char format[16]; + + for (j = i = 0; i < strlen(url) && j < len; i++) + { + if ('$' == url[i]) + { + off = 0; + width = 1; + + // Identifier matching is case-sensitive. + if ('$' == url[i + 1]) + { + // skip + ptr[j++] = url[i++]; + continue; + } + else if (0 == strncmp("$RepresentationID$", url + i, 18)) + { + j += snprintf(ptr + j, len - j, "%s", representation->id ? representation->id : ""); + i += 17; + } + else if (0 == strncmp("$Number", url + i, 7) && ('$' == url[i + 7] || ('%' == url[i + 7] && 1 == sscanf(url + i + 7 + 1, "%dd$%n", &width, &off) && '$' == url[i + 7 + off]))) + { + snprintf(format, sizeof(format), "%%0%d" PRId64, width); + j += snprintf(ptr + j, len - j, format, number); + i += 7 + off; + } + else if (0 == strncmp("$Bandwidth", url + i, 10) && ('$' == url[i + 10] || ('%' == url[i + 10] && 1 == sscanf(url + i + 10 + 1, "%dd$%n", &width, &off) && '$' == url[i + 10 + off]))) + { + snprintf(format, sizeof(format), "%%0%du", width); + j += snprintf(ptr + j, len - j, format, representation->bandwidth); + i += 10 + off; + } + else if (0 == strncmp("$Time", url + i, 5) && ('$' == url[i + 5] || ('%' == url[i + 5] && 1 == sscanf(url + i + 5 + 1, "%dd$%n", &width, &off) && '$' == url[i + 5 + off]))) + { + snprintf(format, sizeof(format), "%%0%d" PRId64, width); + j += snprintf(ptr + j, len - j, format, start); + i += 5 + off; + } + else if (0 == strncmp("$SubNumber", url + i, 10) && ('$' == url[i + 10] || '%' == url[i + 10])) + { + // TODO: + assert(0); + } + else + { + assert(0); // ignore + ptr[j++] = url[i]; + } + } + else + { + ptr[j++] = url[i]; + } + } + + if (j < len) + ptr[j] = '\0'; + return j < len ? (int)j : -1; +} + +int dash_representation_get_initialization(const struct dash_representation_t* representation, char* url, size_t size) +{ + const char* ptr; + const struct dash_period_t* period; + const struct dash_adaptation_set_t* set; + + ptr = dash_segment_initialization(&representation->segment); + if (!ptr && representation->parent) + { + set = representation->parent; + ptr = dash_segment_initialization(&set->segment); + if (!ptr && set->parent) + { + period = set->parent; + ptr = dash_segment_initialization(&period->segment); + if (!ptr) + return 0; + } + } + + return dash_representation_template_replace(representation, ptr, 0, 0, url, size); +} + +int dash_representation_segment_url(const struct dash_representation_t* representation, int index, int64_t* number, int64_t* start, int64_t* duration, const char** range, char* url, size_t size) +{ + int r; + const struct dash_segment_t* segment; + const char* url0; + + segment = dash_representation_get_segment(representation); + assert(index >= 0 && index < dash_segment_count(segment)); + r = dash_segment_information(segment, index, number, start, duration, &url0, range); + if (r < 0) + return r; + + return dash_representation_template_replace(representation, url0, *number, *start, url, size); +} + +int dash_representation_find_segment(const struct dash_representation_t* representation, int64_t time) +{ + const struct dash_segment_t* segment; + segment = dash_representation_get_segment(representation); + return dash_segment_find(segment, time); +} + +int dash_representation_segment_count(const struct dash_representation_t* representation) +{ + const struct dash_segment_t* segment; + segment = dash_representation_get_segment(representation); + return dash_segment_count(segment); +} + +#if defined(_DEBUG) || defined(DEBUG) +void dash_representation_test(void) +{ + char ptr[128]; + struct dash_representation_t r; + memset(&r, 0, sizeof(r)); + r.id = "0"; + r.bandwidth = 19200; + assert(23 == dash_representation_template_replace(&r, "dash-$$$$$RepresentationID$$Bandwidth$$Number$$Time$-", 1, 19700101, ptr, sizeof(ptr))); + assert(0 == strcmp(ptr, "dash-$$019200119700101-")); + assert(22 == dash_representation_template_replace(&r, "dash-$Bandwidth%03d$$Number%03d$$Time%03d$-", 1, 19700101, ptr, sizeof(ptr))); + assert(0 == strcmp(ptr, "dash-1920000119700101-")); +} +#endif diff --git a/src/3rdpart/media-server/libdash/src/dash-segment.c b/src/3rdpart/media-server/libdash/src/dash-segment.c new file mode 100644 index 0000000..6ed606d --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/dash-segment.c @@ -0,0 +1,210 @@ +#include "dash-parser.h" +#include +#include +#include +#include + +int dash_segment_count(const struct dash_segment_t* segment) +{ + int n; + size_t i; + + switch (segment->type) + { + case DASH_SEGMENT_BASE: + return 1; + + case DASH_SEGMENT_LIST: + return (int)segment->segment_url_count; + + case DASH_SEGMENT_TEMPLATE: + if (segment->segment_timeline.count < 1) + return INT_MAX; // dynamic + infinite + + // static + timeline + for (i = n = 0; i < segment->segment_timeline.count; i++) + n += 1 + segment->segment_timeline.S[i].r; + return n; + + default: + return 0; // none + } +} + +const char* dash_segment_initialization(const struct dash_segment_t* segment) +{ + if (segment->initialization.source_url && *segment->initialization.source_url) + return segment->initialization.source_url; + + if (DASH_SEGMENT_TEMPLATE == segment->type && segment->initialization_url && *segment->initialization_url) + return segment->initialization_url; + + return NULL; +} + +/// @return 0-ok, <0-error +static int dash_segment_timeline(const struct dash_segment_timeline_t* timeline, size_t index, int64_t* number, int64_t* start, int64_t* duration) +{ + int64_t t; + size_t i, j, step; + + t = 0; + for (j = i = 0; i < timeline->count; i++) + { + // 5.3.9.6 Segment timeline + // The @r attribute has a default value of zero when not present. + // The value of the @r attribute of the S element may be set to a + // negative value indicating that the duration indicated in @d is + // promised to repeat until the S@t of the next S element or if it + // is the last S element in the SegmentTimeline element until the + // end of the Period or the next update of the MPD + + assert(timeline->S[i].d > 0); + if (timeline->S[i].r >= 0) + { + step = timeline->S[i].r + 1; + } + else if (i + 1 == timeline->count) + { + // last + step = 0; + } + else + { + assert(timeline->S[i].t > 0); + step = (size_t)((timeline->S[i + 1].t - t) / (timeline->S[i].d > 0 ? timeline->S[i].d : 1)); + } + + if (0 == step || index < j + step) + { + *number = timeline->S[i].n + (index - j); + *start = (timeline->S[i].t ? timeline->S[i].t : t) + (index - j) * timeline->S[i].d; + *duration = timeline->S[i].d; + return 0; + } + + j += step; + t = (timeline->S[i].t ? timeline->S[i].t : t) + step * timeline->S[i].d; + } + + return -1; +} + +/// @param[out] number start number of representation +/// @param[out] start start time of representation(MUST add period.start) +/// @param[out] duration segment duration, 0 if unknown +/// @param[out] url segment url(MUST resolve with representation base url) +/// @param[out] range url range, NULL if don't have range +/// @return 0-ok, <0-error, >0-undefined +int dash_segment_information(const struct dash_segment_t* segment, int index, int64_t* number, int64_t* start, int64_t* duration, const char** url, const char** range) +{ + int r; + int64_t timescale; + static const char* sc_empty = ""; + + // 5.3.9.2 Segment base information + // 1. If the Representation contains more than one Media Segment, then either + // the attribute @duration or the element SegmentTimeline shall be present. + // 2. The attribute @duration and the element SegmentTimeline shall not be present at the same time. + + // 5.3.9.5.3 Media Segment information + // 1. a valid Media Segment URL and possibly a byte range, + // 2. the number and position of the Media Segment in the Representation, + // 3. the MPD start time of the Media Segment in the Representation providing an approximate presentation start time of the Segment in the Period, + // 4. the MPD duration of the Media Segment providing an approximate presentation duration of the Segment. + // + // SegmentTemplate + // 1. If the Representation contains or inherits a SegmentTemplate element with $Number$ + // then the URL of the Media Segment at position k in the Representation is determined + // by replacing the $Number$ identifier by (k-1) + (k.start-1) with kstart the value of + // the @startNumber attribute, if present, or 1 otherwise. + // 2. If the Representation contains or inherits a SegmentTemplate element with $Time$ then + // the URL of the media segment at position k is determined by replacing the $Time$ + // identifier by MPD start time of this segment, as described below. + // + // SegmentList + // The number of the first Segment in the list within this Period is determined by the value + // of the SegmentList@startNumber attribute, if present, or it is 1 in case this attribute + // is not present.The sequence of multiple SegmentList elements within a Representation shall + // result in Media Segment List with consecutive numbers. + + r = 0; + + if (DASH_SEGMENT_BASE == segment->type) + { + if (0 != index) + return -1; // segment base only have one segment + + *number = 0; + *start = 0 - segment->presentation_time_offset; + *duration = segment->presentation_duration; + *url = sc_empty; + } + else + { + if (index < 0 || (DASH_SEGMENT_LIST == segment->type && index >= (int)segment->segment_url_count)) + return -1; + + if (segment->segment_timeline.count > 0) + { + r = dash_segment_timeline(&segment->segment_timeline, index, number, start, duration); + if (0 != r) + return -1; + } + else + { + *number = (segment->start_number > 0 ? (segment->start_number - 1) : 0) + index; + *start = (int64_t)index * segment->duration - segment->presentation_time_offset; + *duration = segment->duration; + } + + if (DASH_SEGMENT_LIST == segment->type) + { + *url = segment->segment_urls[index].media; + *range = segment->segment_urls[index].media_range; + } + else if (DASH_SEGMENT_TEMPLATE == segment->type) + { + *url = segment->media; + *range = NULL; + } + else + { + assert(0); + return -1; + } + } + + timescale = segment->timescale > 0 ? segment->timescale : 1; + *start = *start / timescale; + *duration = *duration / timescale; + return r; +} + +int dash_segment_find(const struct dash_segment_t* segment, int64_t time) +{ + int r, n, i, mid; + int64_t number, t, d; + const char* url, * range; + + n = dash_segment_count(segment); + i = 0; + mid = -1; + + while (i < n) + { + mid = (i + n) / 2; + r = dash_segment_information(segment, mid, &number, &t, &d, &url, &range); + if (0 != r) + return r; + + if (time < t) + n = mid; + else if (time > t + d) + i = mid + 1; + else + break; + } + + return mid; +} diff --git a/src/3rdpart/media-server/libdash/src/list.h b/src/3rdpart/media-server/libdash/src/list.h new file mode 100644 index 0000000..af2bba9 --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/list.h @@ -0,0 +1,61 @@ +#ifndef _list_h_ +#define _list_h_ + +struct list_head +{ + struct list_head *next, *prev; +}; + +static inline void list_insert_after(struct list_head *item, struct list_head *head) +{ + struct list_head *prev, *next; + prev = head; + next = head->next; + + item->prev = prev; + item->next = next; + next->prev = item; + prev->next = item; +} + +static inline void list_insert_before(struct list_head *item, struct list_head *head) +{ + struct list_head *prev, *next; + prev = head->prev; + next = head; + + item->prev = prev; + item->next = next; + next->prev = item; + prev->next = item; +} + +static inline void list_remove(struct list_head *item) +{ + struct list_head *prev, *next; + prev = item->prev; + next = item->next; + + prev->next = next; + next->prev = prev; + + item->prev = item->next = 0; +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +#define LIST_INIT_HEAD(list) do { (list)->next = (list)->prev = (list); } while (0) + +#define list_entry(ptr, type, member) \ + ((type*)((char*)ptr-(ptrdiff_t)(&((type*)0)->member))) + +#define list_for_each(pos, head) \ + for(pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_safe(pos, n, head) \ + for(pos = (head)->next, n = pos->next; pos != (head); pos = n, n=pos->next) + +#endif /* !_list_h_ */ diff --git a/src/3rdpart/media-server/libdash/src/xs-duration.c b/src/3rdpart/media-server/libdash/src/xs-duration.c new file mode 100644 index 0000000..f38258e --- /dev/null +++ b/src/3rdpart/media-server/libdash/src/xs-duration.c @@ -0,0 +1,160 @@ +#include "xs-datatype.h" +#include +#include +#include + +// https://en.wikipedia.org/wiki/ISO_8601 +// Durations: +// 1. P[n]Y[n]M[n]DT[n]H[n]M[n]S +// 2. P[n]W +// 3. PT