Compare commits

...

No commits in common. "master" and "tail_sec" have entirely different histories.

620 changed files with 373860 additions and 3 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
.vscode/
build/
release/
*.data
*.len
*.pcap
PcapRawSender/.fuse_hidden000053fa00000002

114
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,114 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "test",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/release/out/tests",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"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/all.pcap",
// "127.0.0.1","40001","udp src port 20002"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapSender/signhw28181.pcap",
// "127.0.0.1","30001","udp src port 55763"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/h265-hikvision-10min.pcap",
// "127.0.0.1","30001","udp src port 15060"], //h265
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapSender/hkh264.pcap",
// "127.0.0.1","60006","udp src port 15060"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "/usr/bin/gdb"
},
{
"name": "pcapRawsender",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/release/out/PcapRawSender",
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/h265-hikvision-10min.pcap",
// "127.0.0.1","30001","udp src port 15060"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/afterSign.pcap",
// "127.0.0.1","30001","udp src port 15060"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/HaiKang_platform.pcap",
// "127.0.0.1","60006","udp src port 22006"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/EncMedia_rtp_encoded.pcapng",
// "127.0.0.1","60006","udp src port 10005"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/EncMedia_rtp.pcapng",
// "127.0.0.1","30001","udp src port 10005"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/rawcamera.pcap",
// "127.0.0.1","30001","udp src port 15060"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/h264.pcap", //
// "127.0.0.1","20001","udp src port 21999"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/123.pcap",
// "127.0.0.1","40001","udp dst port 10001"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/51.pcap",
// "127.0.0.1","40001","udp dst port 30008"], //svac
// // "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/svac_dec.pcap",
// // "127.0.0.1","40001","udp dst port 20001"], //svac
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/1.pcap",
// "127.0.0.1","40001","udp dst port 40001"], //svac
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/hz-pt.pcap",
// "127.0.0.1","40001","udp src port 10000"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/ipc-hz.pcap",
// "127.0.0.1","40001","udp src port 15060"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/videoblur/H265_ps_bug.pcap",
// "127.0.0.1","40001","udp src port 10005"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/videoblur/86h264.pcap",
// "127.0.0.1","30001","udp src port 15060"],
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/cameraAndboxh265.pcap",
// "127.0.0.1","30001","udp src port 15060 && src host 192.168.0.93"],
"args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapRawSender/cameraAndBoxh264.pcap",
"127.0.0.1","40001","udp src port 15060 && src host 192.168.1.95"],
// "args": ["/home/alan/Downloads/br0.pcap",
// "127.0.0.1","60006","udp dst port 10001"]
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "/usr/bin/gdb"
}
]
}

74
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,74 @@
{
"files.associations": {
"*.tcc": "cpp",
"functional": "cpp",
"fstream": "cpp",
"thread": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"array": "cpp",
"atomic": "cpp",
"strstream": "cpp",
"bitset": "cpp",
"chrono": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"cstdint": "cpp",
"deque": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"set": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cfenv": "cpp",
"cinttypes": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"variant": "cpp",
"bit": "cpp",
"core": "cpp",
"geometry": "cpp",
"*.txx": "cpp",
"compare": "cpp",
"concepts": "cpp",
"numbers": "cpp",
"semaphore": "cpp",
"stop_token": "cpp"
}
}

28
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,28 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ 生成活动文件",
"command": "/usr/bin/g++",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "调试器生成的任务。"
}
],
"version": "2.0.0"
}

130
CMakeLists.txt Normal file
View File

@ -0,0 +1,130 @@
cmake_minimum_required(VERSION 3.9)
project(SecMedia VERSION 0.0.1 DESCRIPTION "Security Media Package")
set(CMAKE_CXX_STANDARD 11)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
message(STATUS "Release版本")
set(BuildType "Release")
add_definitions(-DNDEBUG)
elseif(${CMAKE_BUILD_TYPE} MATCHES "MinSizeRel")
message(STATUS "MinSizeRel版本")
set(BuildType "MinSizeRel")
else()
set(BuildType "Debug")
message(STATUS "Debug版本")
add_definitions(-DDEBUG_LOG)
add_definitions(-DNDEBUG)
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/${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} -include ${CMAKE_CURRENT_SOURCE_DIR}/force_link_glibc_2.27.h")
set(CMAKE_CXX_FLAGS$ "${CMAKE_CXX_FLAGS} -include ${CMAKE_CURRENT_SOURCE_DIR}/force_link_glibc_2.27.h")
# 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(-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
aux_source_directory(${SecMedia_Root}/SVAC/src/sm2sm3 src_DEC)
aux_source_directory(${SecMedia_Root}/SVAC/src/svac_src src_DEC)
add_library(SVAC_DEC STATIC ${src_DEC})
#svac
#include_directories(${DEC_ENC_Algorithm}/SVAC/svac_enc)
#file(GLOB src_DEC_ENC ${DEC_ENC_Algorithm}/SVAC/svac_enc/*/*.c ${DEC_ENC_Algorithm}/SVAC/svac_enc/*/*.h)
aux_source_directory(${SecMedia_Root}/SVAC/src/sm2sm3_enc src_ENC)
aux_source_directory(${SecMedia_Root}/SVAC/src/svac_src src_ENC)
add_library(SVAC_ENC STATIC ${src_ENC})
list(APPEND LINK_LIB_SVAC_LIST SVAC_DEC)
list(APPEND LINK_LIB_SVAC_LIST SVAC_ENC)
#
# add_definitions(-DENABLE_HARDWARE_SIGN)
# list(APPEND LINK_LIB_SVAC_LIST sm.so)
include_directories(include src)
# add_definitions(-DGENERATE_EXPORT)
#file(GLOB SecMedia_src_list ${SecMedia_Root}/SVAC/./*.c ${SecMedia_Root}/SVAC/./*.h)
file(GLOB SecMedia_src_list ${SecMedia_Root}/*/*.cpp ${SecMedia_Root}/*/*.h ${SecMedia_Root}/*/*.c ${SecMedia_Root}/*/*/*.cpp ${SecMedia_Root}/*/*/*.h ${SecMedia_Root}/*/*/*.c)
file(GLOB SecMedia_api_list ${CMAKE_CURRENT_SOURCE_DIR}/include/common.h )
# # target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=hidden)
# list(APPEND LINK_LIB_LIST ${LINK_LIB_SVAC_LIST})
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} rtp)
target_include_directories(${PROJECT_NAME} PRIVATE ${SecMedia_Root}/.)
# set_target_properties(${PROJECT_NAME} PROPERTIES
# VERSION ${PROJECT_VERSION}
# SOVERSION 1
# PUBLIC_HEADER ${SecMedia_api_list}
# )
# CXX_VISIBILITY_PRESET hidden
# CMAKE_C_FLAGS hidden)
list(APPEND LINK_LIB_LIST ${PROJECT_NAME})
list(APPEND LINK_LIB_LIST pthread)
#jemalloc
find_package(JEMALLOC QUIET)
if(JEMALLOC_FOUND)
message(STATUS "found library:\"${JEMALLOC_LIBRARIES}\"")
include_directories(${JEMALLOC_INCLUDE_DIR})
list(APPEND LINK_LIB_LIST ${JEMALLOC_LIBRARIES})
else()
message(WARNING "JEMALLOC not found")
endif()
#set(LINK_LIB_LIST )
#MESSAGE(STATUS ${SecMedia_api_list})
#configure_file(SecMedia.pc.in SecMedia.pc @ONLY)
# install(TARGETS ${PROJECT_NAME}
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc
# DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
add_subdirectory(test)
add_subdirectory(PcapSender)
add_subdirectory(PcapRawSender)
add_subdirectory(SecSetGen)

View File

@ -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}")
message(STATUS "lib dir: ${PCAP_LIBRARY} ,${DPDK_LIBRARIES} ")
add_executable(PcapRawSender ${sender_src_list})
target_link_libraries(PcapRawSender PUBLIC ${LINK_LIB_LIST} PUBLIC PcapPlusPlus::Pcap++ PUBLIC DPDK::DPDK)
# target_link_libraries(PcapSender PUBLIC PcapPlusPlus::Pcap++)

Binary file not shown.

Binary file not shown.

205
PcapRawSender/main.cpp Normal file
View File

@ -0,0 +1,205 @@
#include <iostream>
#include <Packet.h>
#include <PcapFileDevice.h>
#include <UdpLayer.h>
#include <string>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/select.h>
#include "HuaWei/HWsign.h"
#include "HuaWei/HWsec.h"
using namespace std;
using namespace pcpp;
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<typename T,typename U>
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){
pcpp::IFileReaderDevice * reader;
reader=pcpp::IFileReaderDevice::getReader(filename);
while (1)
{
// 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<UdpLayer>(false);
if(layer){
payload=layer->getLayerPayload();
payload_len=layer->getLayerPayloadSize();
// cout<<" payload_len:" << payload_len<<endl;
nowtime=rawPacket.getPacketTimeStamp();
clock_gettime(CLOCK_MONOTONIC, &real_now);
if(first_flag){
first_flag=false;
inital_time=TimeDiff(nowtime,real_now);
}
current_time.tv_sec=inital_time.tv_sec+real_now.tv_sec;
current_time.tv_nsec=inital_time.tv_nsec+real_now.tv_nsec;
gaptime=TimeDiff(nowtime,current_time);
if (gaptime.tv_nsec>=0 && gaptime.tv_sec>=0)
{
nanosleep(&gaptime,NULL);
}else{
// cout<<" s:" << gaptime.tv_sec<<" ns:" << gaptime.tv_nsec <<endl;
}
// ///////////////calculate send time//////////////////
if(sendto(socket,payload,payload_len,0,(const sockaddr*)&addr,sizeof(addr))==-1){
printf("send failed : %s\n", strerror(errno));
}
}
// cout<<" stamp:" << rawPacket.getPacketTimeStamp().tv_sec<<" len:"<< rawPacket.getRawDataLen()<<endl;
}
cout<<" resend pcap" <<endl;
reader->close();
// delete reader;
}
// reader->getNextPacket(rawPacket);
// for (pcpp::Layer* curLayer = parsedPacket.getFirstLayer(); curLayer != NULL; curLayer = curLayer->getNextLayer())
// {
// std::cout
// << "Layer type: " <<getProtocolTypeAsString(curLayer->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;
delete reader;
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();
ReadPcapAndSend(sockfd,addr,filename,filter,NULL);
// }
}
else
{
printf("CMD as: filename.pcap ip port\n");
system("pause");
return 0;
}
return 0;
}

16
PcapSender/CMakeLists.txt Normal file
View File

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

238
PcapSender/main.cpp Normal file
View File

@ -0,0 +1,238 @@
#include <iostream>
#include <Packet.h>
#include <PcapFileDevice.h>
#include <UdpLayer.h>
#include <string>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/select.h>
#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<typename T,typename U>
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 sign_h2=EncrypInit();
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<UdpLayer>(false);
if(layer){
payload=layer->getLayerPayload();
payload_len=layer->getLayerPayloadSize();
// cout<<" payload_len:" << payload_len<<endl;
nowtime=rawPacket.getPacketTimeStamp();
clock_gettime(CLOCK_MONOTONIC, &real_now);
if(first_flag){
first_flag=false;
inital_time=TimeDiff(nowtime,real_now);
}
current_time.tv_sec=inital_time.tv_sec+real_now.tv_sec;
current_time.tv_nsec=inital_time.tv_nsec+real_now.tv_nsec;
gaptime=TimeDiff(nowtime,current_time);
if (gaptime.tv_nsec>=0 && gaptime.tv_sec>=0)
{
nanosleep(&gaptime,NULL);
}else{
// cout<<" s:" << gaptime.tv_sec<<" ns:" << gaptime.tv_nsec <<endl;
}
// ///////////////calculate send time//////////////////
if(sign_handle){
GB28181_stream_in(sign_handle,(char*)payload,payload_len,nullptr);
GB28181_stream_out(sign_handle,sign_out_buf,&sign_out_len,&offset_len,&append_len, &param);
if (append_len==0)
{
sendto(socket,sign_out_buf,sign_out_len,0,(const sockaddr*)&addr,sizeof(addr));
}else
{
sendto(socket,sign_out_buf,offset_len,0,(const sockaddr*)&addr,sizeof(addr));
sendto(socket,sign_out_buf+offset_len,sign_out_len-offset_len,0,(const sockaddr*)&addr,sizeof(addr));
}
// GB28181_stream_in(sign_h2,(char*)payload,payload_len,nullptr);
// GB28181_stream_out(sign_h2,sign_out_buf,&sign_out_len,&offset_len,&append_len, &param);
}else{
if(sendto(socket,payload,payload_len,0,(const sockaddr*)&addr,sizeof(addr))==-1){
printf("send failed : %s\n", strerror(errno));
}
}
}
// cout<<" stamp:" << rawPacket.getPacketTimeStamp().tv_sec<<" len:"<< rawPacket.getRawDataLen()<<endl;
}
// reader->getNextPacket(rawPacket);
// for (pcpp::Layer* curLayer = parsedPacket.getFirstLayer(); curLayer != NULL; curLayer = curLayer->getNextLayer())
// {
// std::cout
// << "Layer type: " <<getProtocolTypeAsString(curLayer->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;
}

View File

@ -1,3 +1 @@
# AW-Security-Media-Lib
RTP视频签名库
SecMedia

BIN
SecMedia.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,59 @@
#ifndef _DECRYPT_H
#define _DECRYPT_H
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *dec_media;
typedef struct
{
/// alloc new packet
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] bytes alloc memory size in byte
/// @return memory pointer
void* (API_CALL *alloc)(void* param, size_t bytes);
/// free packet
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
void (API_CALL *free)(void* param, void* packet);
/// callback on PS packet done
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
/// @param[in] bytes packet size
/// @param[in] timestamp packet timestamp
/// @param[in] frametype packet type
void (API_CALL *write)(void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype);
}dec_media_func_t;
/// create a object for decrypt media data
/// @param func call back (dec_media_func_t)
/// @param param user-defined parameter
API_EXPORT dec_media API_CALL dec_media_create(dec_media_func_t * func,void * param) ;
/// input media data(only accept single frame, merged frame(such as sps pps I ) should be feeded in separately)
/// @param frame_type the type of NALU
/// @param prefix the number of prefix bytes, (0x00 0x00 0x00 0x01) => prefix=4
API_EXPORT int API_CALL dec_media_decoded(dec_media ctx,uint8_t* frame,size_t len,uint64_t timestamp, uint8_t frame_type,uint8_t prefix);
/// input media data(only accept single frame, merged frame(such as sps pps I ) should be feeded in separately)
/// @param frame frame data
/// @param prefix the number of prefix bytes, (0x00 0x00 0x00 0x01) => prefix=4
/// @param code 0:h264 1:h265 6:svac
/// @param return <0:error >=0:security level
API_EXPORT int API_CALL dec_security_SEI(uint8_t* frame,size_t len,uint8_t prefix,uint8_t code);
API_EXPORT void API_CALL dec_media_release(dec_media ctx);
#ifdef __cplusplus
}
#endif
#endif /* _DECRYPT_H */

View File

@ -0,0 +1,44 @@
#ifndef _ENCRYPT_H
#define _ENCRYPT_H
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *enc_media;
typedef struct
{
/// alloc new packet
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] bytes alloc memory size in byte
/// @return memory pointer
void* (API_CALL *alloc)(void* param, size_t bytes);
/// free packet
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
void (API_CALL *free)(void* param, void* packet);
/// callback on PS packet done
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
/// @param[in] bytes packet size
/// @param[in] timestamp packet timestamp
/// @param[in] frametype packet type
void (API_CALL *write)(void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype);
}enc_media_func_t;
API_EXPORT enc_media API_CALL enc_media_create(enc_media_func_t * func, void * param) ;
API_EXPORT int API_CALL enc_media_decoded(enc_media ctx,uint8_t* frame,size_t len,uint64_t timestamp, uint8_t frame_type,uint8_t prefix);
API_EXPORT void API_CALL enc_media_release(enc_media ctx);
#ifdef __cplusplus
}
#endif
#endif /* _DECRYPT_H */

View File

@ -0,0 +1,62 @@
#ifndef _COMMON_H
#define _COMMON_H
#if defined(_WIN32) && defined(_MSC_VER)
# define API_CALL __cdecl
# define SecMedia_Api_EXPORTS
#else
# define API_CALL
#endif
#if defined(_WIN32) && defined(_MSC_VER)
# if defined(SecMedia_Api_EXPORTS)
# define API_EXPORT __declspec(dllexport)
# else
# define API_EXPORT __declspec(dllimport)
# endif
#endif
# ifndef API_EXPORT
# define API_EXPORT __attribute__((visibility("default")))
# endif
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CodecInvalid = -1,
CodecH264 = 0,
CodecH265,
CodecAAC,
CodecG711A,
CodecG711U,
CodecOpus,
CodecSVAC,
CodecMax = 0x7FFF
} CodecId;
enum class DecEncType{
NONE=15,
SM1=0,
SM4=1,
RSA=2,
AES=3,
SVACEnc=4,
SVACDec=5,
SM3,
SM2_auth
};
#ifdef __cplusplus
}
#endif
#endif /* MK_COMMON_H */

Binary file not shown.

View File

@ -0,0 +1,10 @@
include_directories(../include)
file(GLOB test_src_list ./*.cpp ./*.h)
MESSAGE(STATUS ${test_src_list})
add_executable(tests ${test_src_list})
target_link_libraries(tests ${LINK_LIB_LIST})

View File

@ -0,0 +1,102 @@
#include<stdlib.h>
#include<stdio.h>
#include "common.h"
#include "Decrypt.h"
#include "Encrypt.h"
#define H264_TYPE(v) ((uint8_t)(v) & 0x1F)
#define FRAME_SIZE 6
#define FRAME_LEN 10
const uint8_t rawframe[FRAME_SIZE][FRAME_LEN]={
{0x00,0x00,0x00,0x01,0x61,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x65,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x67,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x61,0xf1,0xf2,0xf3,0xf4,0xf5},
{0x00,0x00,0x00,0x01,0x61,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x61,0x01,0x02,0x03,0x04,0x05},
};
int main(int argc, char *argv[]){
dec_media dec_media_obj;
dec_media_func_t dec_fun={
//alloc
[](void* param, size_t bytes){
return (void*)malloc(sizeof(uint8_t)*bytes);
},
//free
[](void* param, void* packet){
free(packet);
},
//write
[](void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype){
printf("\n");
printf("type: %d timestamp:%lld lens:%zd\n", frametype, timestamp, bytes);
uint8_t * ptr=(uint8_t*)packet;
for (size_t i = 0; i < bytes; i++)
{
printf("%02X ", *(ptr++));
}
printf("\n");
printf("-----------Decrypted data----------------- ");
}
};
dec_media_obj=dec_media_create(&dec_fun,NULL);
enc_media enc_media_obj;
enc_media_func_t enc_fun;
enc_fun={
//alloc
[](void* param, size_t bytes){
return (void*)malloc(sizeof(uint8_t)*bytes);
},
//free
[](void* param, void* packet){
free(packet);
},
//write
[](void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype){
printf("\n");
printf("type: %d timestamp:%lld lens:%zd\n", frametype, timestamp, bytes);
uint8_t * ptr=(uint8_t*)packet;
for (size_t i = 0; i < bytes; i++)
{
printf("%02X ", *(ptr++));
}
printf("\n");
int ret=dec_security_SEI((uint8_t*)packet,bytes,4,0);
if (ret>0)
{
printf("\nSecurity level: %d\n",ret);
}
printf("############Encrypted data############ ");
dec_media_decoded((dec_media) param,(uint8_t*)packet,bytes,timestamp,frametype,4);
}
};
enc_media_obj=enc_media_create(&enc_fun,(void*)dec_media_obj);
uint32_t timestamp=0;
for (size_t i = 0; i < FRAME_SIZE; i++)
{
uint8_t * ptr=(uint8_t*)rawframe[i];
printf("\n");
printf("type: %d timestamp:%d lens:%d\n", H264_TYPE(*(rawframe[i]+4)), timestamp, FRAME_LEN);
for (size_t i = 0; i < FRAME_LEN; i++)
{
printf("%02X ", *(ptr++));
}
printf("\n");
printf("//////////////////Raw data//////////////////// ");
enc_media_decoded(enc_media_obj,(uint8_t*)rawframe[i],FRAME_LEN,timestamp,H264_TYPE(*(rawframe[i]+4)),4);
timestamp+=100;
}
dec_media_release(dec_media_obj);
enc_media_release(enc_media_obj);
return 0;
}

BIN
SecMedia/Linux/tests Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,59 @@
#ifndef _DECRYPT_H
#define _DECRYPT_H
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *dec_media;
typedef struct
{
/// alloc new packet
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] bytes alloc memory size in byte
/// @return memory pointer
void* (API_CALL *alloc)(void* param, size_t bytes);
/// free packet
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
void (API_CALL *free)(void* param, void* packet);
/// callback on PS packet done
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
/// @param[in] bytes packet size
/// @param[in] timestamp packet timestamp
/// @param[in] frametype packet type
void (API_CALL *write)(void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype);
}dec_media_func_t;
/// create a object for decrypt media data
/// @param func call back (dec_media_func_t)
/// @param param user-defined parameter
API_EXPORT dec_media API_CALL dec_media_create(dec_media_func_t * func,void * param) ;
/// input media data(only accept single frame, merged frame(such as sps pps I ) should be feeded in separately)
/// @param frame_type the type of NALU
/// @param prefix the number of prefix bytes, (0x00 0x00 0x00 0x01) => prefix=4
API_EXPORT int API_CALL dec_media_decoded(dec_media ctx,uint8_t* frame,size_t len,uint64_t timestamp, uint8_t frame_type,uint8_t prefix);
/// input media data(only accept single frame, merged frame(such as sps pps I ) should be feeded in separately)
/// @param frame frame data
/// @param prefix the number of prefix bytes, (0x00 0x00 0x00 0x01) => prefix=4
/// @param code 0:h264 1:h265 6:svac
/// @param return <0:error >=0:security level
API_EXPORT int API_CALL dec_security_SEI(uint8_t* frame,size_t len,uint8_t prefix,uint8_t code);
API_EXPORT void API_CALL dec_media_release(dec_media ctx);
#ifdef __cplusplus
}
#endif
#endif /* _DECRYPT_H */

View File

@ -0,0 +1,44 @@
#ifndef _ENCRYPT_H
#define _ENCRYPT_H
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *enc_media;
typedef struct
{
/// alloc new packet
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] bytes alloc memory size in byte
/// @return memory pointer
void* (API_CALL *alloc)(void* param, size_t bytes);
/// free packet
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
void (API_CALL *free)(void* param, void* packet);
/// callback on PS packet done
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
/// @param[in] bytes packet size
/// @param[in] timestamp packet timestamp
/// @param[in] frametype packet type
void (API_CALL *write)(void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype);
}enc_media_func_t;
API_EXPORT enc_media API_CALL enc_media_create(enc_media_func_t * func, void * param) ;
API_EXPORT int API_CALL enc_media_decoded(enc_media ctx,uint8_t* frame,size_t len,uint64_t timestamp, uint8_t frame_type,uint8_t prefix);
API_EXPORT void API_CALL enc_media_release(enc_media ctx);
#ifdef __cplusplus
}
#endif
#endif /* _DECRYPT_H */

View File

@ -0,0 +1,62 @@
#ifndef _COMMON_H
#define _COMMON_H
#if defined(_WIN32) && defined(_MSC_VER)
# define API_CALL __cdecl
# define SecMedia_Api_EXPORTS
#else
# define API_CALL
#endif
#if defined(_WIN32) && defined(_MSC_VER)
# if defined(SecMedia_Api_EXPORTS)
# define API_EXPORT __declspec(dllexport)
# else
# define API_EXPORT __declspec(dllimport)
# endif
#endif
# ifndef API_EXPORT
# define API_EXPORT __attribute__((visibility("default")))
# endif
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CodecInvalid = -1,
CodecH264 = 0,
CodecH265,
CodecAAC,
CodecG711A,
CodecG711U,
CodecOpus,
CodecSVAC,
CodecMax = 0x7FFF
} CodecId;
enum class DecEncType{
NONE=15,
SM1=0,
SM4=1,
RSA=2,
AES=3,
SVACEnc=4,
SVACDec=5,
SM3,
SM2_auth
};
#ifdef __cplusplus
}
#endif
#endif /* MK_COMMON_H */

View File

@ -0,0 +1,10 @@
include_directories(../include)
file(GLOB test_src_list ./*.cpp ./*.h)
MESSAGE(STATUS ${test_src_list})
add_executable(tests ${test_src_list})
target_link_libraries(tests ${LINK_LIB_LIST})

View File

@ -0,0 +1,102 @@
#include<stdlib.h>
#include<stdio.h>
#include "common.h"
#include "Decrypt.h"
#include "Encrypt.h"
#define H264_TYPE(v) ((uint8_t)(v) & 0x1F)
#define FRAME_SIZE 6
#define FRAME_LEN 10
const uint8_t rawframe[FRAME_SIZE][FRAME_LEN]={
{0x00,0x00,0x00,0x01,0x61,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x65,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x67,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x61,0xf1,0xf2,0xf3,0xf4,0xf5},
{0x00,0x00,0x00,0x01,0x61,0x01,0x02,0x03,0x04,0x05},
{0x00,0x00,0x00,0x01,0x61,0x01,0x02,0x03,0x04,0x05},
};
int main(int argc, char *argv[]){
dec_media dec_media_obj;
dec_media_func_t dec_fun={
//alloc
[](void* param, size_t bytes){
return (void*)malloc(sizeof(uint8_t)*bytes);
},
//free
[](void* param, void* packet){
free(packet);
},
//write
[](void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype){
printf("\n");
printf("type: %d %lld,%zd\n", frametype, timestamp, bytes);
uint8_t * ptr=(uint8_t*)packet;
for (size_t i = 0; i < bytes; i++)
{
printf("%02X ", *(ptr++));
}
printf("\n");
printf("-----------Decrypted data----------------- ");
}
};
dec_media_obj=dec_media_create(&dec_fun,NULL);
enc_media enc_media_obj;
enc_media_func_t enc_fun;
enc_fun={
//alloc
[](void* param, size_t bytes){
return (void*)malloc(sizeof(uint8_t)*bytes);
},
//free
[](void* param, void* packet){
free(packet);
},
//write
[](void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype){
printf("\n");
printf("type: %d %lld,%zd\n", frametype, timestamp, bytes);
uint8_t * ptr=(uint8_t*)packet;
for (size_t i = 0; i < bytes; i++)
{
printf("%02X ", *(ptr++));
}
printf("\n");
int ret=dec_security_SEI((uint8_t*)packet,bytes,4,0);
if (ret>0)
{
printf("\nSecurity level: %d\n",ret);
}
printf("############Encrypted data############ ");
dec_media_decoded((dec_media) param,(uint8_t*)packet,bytes,timestamp,frametype,4);
}
};
enc_media_obj=enc_media_create(&enc_fun,(void*)dec_media_obj);
uint32_t timestamp=0;
for (size_t i = 0; i < FRAME_SIZE; i++)
{
uint8_t * ptr=(uint8_t*)rawframe[i];
printf("\n");
printf("type: %d %d,%d\n", H264_TYPE(*(rawframe[i]+4)), timestamp, FRAME_LEN);
for (size_t i = 0; i < FRAME_LEN; i++)
{
printf("%02X ", *(ptr++));
}
printf("\n");
printf("//////////////////Raw data//////////////////// ");
enc_media_decoded(enc_media_obj,(uint8_t*)rawframe[i],FRAME_LEN,timestamp,H264_TYPE(*(rawframe[i]+4)),4);
timestamp+=100;
}
dec_media_release(dec_media_obj);
enc_media_release(enc_media_obj);
return 0;
}

BIN
SecMedia/Windows/tests.exe Normal file

Binary file not shown.

10
SecSetGen/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
include_directories(../include)
file(GLOB sender_src_list ./*.cpp ./*.h ../include/*.h)
MESSAGE(STATUS ${sender_src_list})
add_executable(SecSetGen ${sender_src_list})
target_link_libraries(SecSetGen PUBLIC ${LINK_LIB_LIST})
# target_link_libraries(PcapSender PUBLIC PcapPlusPlus::Pcap++)

60
SecSetGen/main.cpp Normal file
View File

@ -0,0 +1,60 @@
#include <iostream>
#include <string>
#include <stdio.h>
#include <memory>
#include <string.h>
#include <vector>
using namespace std;
class SecSet
{
public:
typedef shared_ptr<SecSet> Ptr;
private:
vector<char> _buf;
// char * _buf=nullptr;
size_t _len=0;
public:
SecSet(/* args */){};
~SecSet(){
// if(_buf) delete _buf;
_len=0;
}
void setBuf(const char * buf, size_t len){
// _buf.clear();
_buf.assign(buf,buf+len);
// if(!_buf){
// _buf=new char[len] ;_len=len;
// }
// if (len>_len)
// {
// _buf=(char*)realloc(_buf,len) ;_len=len;
// }
// memcpy(_buf,buf,len);
}
void printBuf(){
printf("%s len %lu\n",_buf.data(),_buf.size());
}
Ptr clone(){
return make_shared<remove_reference<decltype(*this)>::type>(*this);
}
};
int main(){
const char hl[]={"Hello World!"};
const char hl2[]={"Hello World_twic!"};
SecSet::Ptr A=make_shared<SecSet>() ;
A->setBuf(hl,sizeof(hl));
A->printBuf();
auto B=A->clone();
A->setBuf(hl2,sizeof(hl2));
A->printBuf();
B->printBuf();
A->setBuf(hl,sizeof(hl));A->printBuf();
B->printBuf();
return 0;
}

3727
force_link_glibc_2.27.h Normal file

File diff suppressed because it is too large Load Diff

59
include/Decrypt.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef _DECRYPT_H
#define _DECRYPT_H
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *dec_media;
typedef struct
{
/// alloc new packet
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] bytes alloc memory size in byte
/// @return memory pointer
void* (API_CALL *alloc)(void* param, size_t bytes);
/// free packet
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
void (API_CALL *free)(void* param, void* packet);
/// callback on PS packet done
/// @param[in] param user-defined parameter(by dec_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
/// @param[in] bytes packet size
/// @param[in] timestamp packet timestamp
/// @param[in] frametype packet type
void (API_CALL *write)(void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype);
}dec_media_func_t;
/// create a object for decrypt media data
/// @param func call back (dec_media_func_t)
/// @param param user-defined parameter
API_EXPORT dec_media API_CALL dec_media_create(dec_media_func_t * func,void * param) ;
/// input media data(only accept single frame, merged frame(such as sps pps I ) should be feeded in separately)
/// @param frame_type the type of NALU
/// @param prefix the number of prefix bytes, (0x00 0x00 0x00 0x01) => prefix=4
API_EXPORT int API_CALL dec_media_decoded(dec_media ctx,uint8_t* frame,size_t len,uint64_t timestamp, uint8_t frame_type,uint8_t prefix);
/// input media data(only accept single frame, merged frame(such as sps pps I ) should be feeded in separately)
/// @param frame frame data
/// @param prefix the number of prefix bytes, (0x00 0x00 0x00 0x01) => prefix=4
/// @param code 0:h264 1:h265 6:svac
/// @param return <0:error >=0:security level
API_EXPORT int API_CALL dec_security_SEI(uint8_t* frame,size_t len,uint8_t prefix,uint8_t code);
API_EXPORT void API_CALL dec_media_release(dec_media ctx);
#ifdef __cplusplus
}
#endif
#endif /* _DECRYPT_H */

44
include/Encrypt.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef _ENCRYPT_H
#define _ENCRYPT_H
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *enc_media;
typedef struct
{
/// alloc new packet
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] bytes alloc memory size in byte
/// @return memory pointer
void* (API_CALL *alloc)(void* param, size_t bytes);
/// free packet
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
void (API_CALL *free)(void* param, void* packet);
/// callback on PS packet done
/// @param[in] param user-defined parameter(by enc_media_create)
/// @param[in] packet frame packet pointer(alloc return pointer)
/// @param[in] bytes packet size
/// @param[in] timestamp packet timestamp
/// @param[in] frametype packet type
void (API_CALL *write)(void* param, void* packet, size_t bytes,uint64_t timestamp,uint8_t frametype);
}enc_media_func_t;
API_EXPORT enc_media API_CALL enc_media_create(enc_media_func_t * func, void * param) ;
API_EXPORT int API_CALL enc_media_decoded(enc_media ctx,uint8_t* frame,size_t len,uint64_t timestamp, uint8_t frame_type,uint8_t prefix);
API_EXPORT void API_CALL enc_media_release(enc_media ctx);
#ifdef __cplusplus
}
#endif
#endif /* _DECRYPT_H */

37
include/HWsign.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef _HWSIGN_H
#define _HWSIGN_H
#include<stdlib.h>
#include<stdio.h>
//#include <functional>
#define API_EXPORT __attribute__((visibility("default")))
// void * HWSign_init(const std::function<void(const char * rtp_ptr, const uint32_t rtp_len)> rtp_callback);
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(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, void * param);
API_EXPORT int HWSign_rtp_out(void* Handle, char * buf, uint32_t * len, void ** param);
API_EXPORT void * HWSign_tcp_init();
API_EXPORT void HWSign_tcp_release(void* Handle);
API_EXPORT int HWSign_tcp_rtp_input(void* Handle, const char * buf, const uint32_t len, void * param);
API_EXPORT int HWSign_tcp_rtp_out(void* Handle, char * buf, uint32_t * len, uint16_t* sei_end_offset,uint16_t* append_length,void ** param);
API_EXPORT void * HWVerify_init();
API_EXPORT void HWVerify_release(void* Handle);
API_EXPORT int HWVerify_rtp_input(void* Handle, const char * buf, const uint32_t len,int tcp, void * param);
#endif /* _DECRYPT_H */

63
include/common.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef _COMMON_H
#define _COMMON_H
#if defined(_WIN32) && defined(_MSC_VER)
# define API_CALL __cdecl
# define SecMedia_Api_EXPORTS
#else
# define API_CALL
#endif
#if defined(_WIN32) && defined(_MSC_VER)
# if defined(SecMedia_Api_EXPORTS)
# define API_EXPORT __declspec(dllexport)
# else
# define API_EXPORT __declspec(dllimport)
# endif
#endif
# ifndef API_EXPORT
# define API_EXPORT __attribute__((visibility("default")))
# endif
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CodecInvalid = -1,
CodecH264 = 0,
CodecH265,
CodecAAC,
CodecG711A,
CodecG711U,
CodecOpus,
CodecSVAC,
CodecMax = 0x7FFF
} CodecId;
enum class DecEncType{
NONE=15,
SM1=0,
SM4=1,
RSA=2,
AES=3,
SVACEnc=4,
SVACDec=5,
SM3,
SM2_auth
};
#ifdef __cplusplus
}
#endif
#endif /* MK_COMMON_H */

BIN
sei.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
sign.pcap Normal file

Binary file not shown.

BIN
sign.pcapng Normal file

Binary file not shown.

BIN
sign_HWsei.pcapng Normal file

Binary file not shown.

BIN
src/3rdpart/media-server/.DS_Store vendored Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) <a href="https://scan.coverity.com/projects/ireader-media-server"> <img alt="Coverity Scan Build Status" src="https://scan.coverity.com/projects/14645/badge.svg"/> </a>
* 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)

View File

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

View File

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

View File

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

View File

@ -0,0 +1,31 @@
#ifndef _dash_mpd_h_
#define _dash_mpd_h_
#include <stddef.h>
#include <stdint.h>
#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_ */

View File

@ -0,0 +1,483 @@
#ifndef _dash_parser_h_
#define _dash_parser_h_
#include "dash-proto.h"
#include <stdint.h>
#include <stddef.h>
#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_ */

View File

@ -0,0 +1,10 @@
#ifndef _dash_proto_h_
#define _dash_proto_h_
enum
{
DASH_STATIC = 0,
DASH_DYNAMIC,
};
#endif /* !_dash_proto_h_ */

View File

@ -0,0 +1,19 @@
#ifndef _xs_datatype_h_
#define _xs_datatype_h_
#include <stdint.h>
#include <stddef.h>
#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_ */

View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\dash-mpd.h" />
<ClInclude Include="include\dash-parser.h" />
<ClInclude Include="include\dash-proto.h" />
<ClInclude Include="include\xs-datatype.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\dash-adaptation.c" />
<ClCompile Include="src\dash-mpd.c" />
<ClCompile Include="src\dash-parser.c" />
<ClCompile Include="src\dash-period.c" />
<ClCompile Include="src\dash-representation.c" />
<ClCompile Include="src\dash-segment.c" />
<ClCompile Include="src\xs-duration.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A5DA231F-6146-43B5-8BBE-891F7E552357}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libdash</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;OS_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;../libmov/include;../libhls/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\dash-mpd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dash-parser.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\xs-duration.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dash-period.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dash-adaptation.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dash-representation.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dash-segment.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\dash-mpd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\dash-proto.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\dash-parser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\xs-datatype.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -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 = "<group>"; };
46C5B2AC2183ED6100419E57 /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = "<group>"; };
46C5B2AD2183ED6100419E57 /* dash-mpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-mpd.c"; sourceTree = "<group>"; };
46E55E6224A7395F00D8BDBA /* xs-duration.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "xs-duration.c"; sourceTree = "<group>"; };
46E55E6324A7395F00D8BDBA /* dash-period.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-period.c"; sourceTree = "<group>"; };
46E55E6424A7395F00D8BDBA /* dash-adaptation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-adaptation.c"; sourceTree = "<group>"; };
46E55E6524A7395F00D8BDBA /* dash-representation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-representation.c"; sourceTree = "<group>"; };
46E55E6624A7395F00D8BDBA /* dash-parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-parser.c"; sourceTree = "<group>"; };
46E55E6724A7395F00D8BDBA /* dash-segment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-segment.c"; sourceTree = "<group>"; };
/* 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 = "<group>";
};
46C5B22D2183EAAD00419E57 /* Products */ = {
isa = PBXGroup;
children = (
46C5B22C2183EAAD00419E57 /* libdash.a */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,77 @@
#include "dash-parser.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
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;
}

View File

@ -0,0 +1,520 @@
#include "dash-mpd.h"
#include "dash-proto.h"
#include "mov-format.h"
#include "fmp4-writer.h"
#include "list.h"
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#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 =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<MPD\n"
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
" xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\"\n"
" type=\"dynamic\"\n"
" minimumUpdatePeriod=\"PT%uS\"\n"
" timeShiftBufferDepth=\"PT%uS\"\n"
" availabilityStartTime=\"%s\"\n"
" minBufferTime=\"PT%uS\"\n"
" publishTime=\"%s\"\n"
" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\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 =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<MPD\n"
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
" xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\"\n"
" type=\"static\"\n"
" mediaPresentationDuration=\"PT%uS\"\n"
" minBufferTime=\"PT%uS\"\n"
" profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\">\n";
static const char* s_h264 =
" <AdaptationSet contentType=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n"
" <Representation id=\"H264\" mimeType=\"video/mp4\" codecs=\"avc1.%02x%02x%02x\" width=\"%d\" height=\"%d\" frameRate=\"%d\" startWithSAP=\"1\" bandwidth=\"%d\">\n"
" <SegmentTemplate timescale=\"1000\" media=\"%s-$Time$.m4v\" initialization=\"%s-init.m4v\">\n"
" <SegmentTimeline>\n";
static const char* s_h265 =
" <AdaptationSet contentType=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n"
" <Representation id=\"H265\" mimeType=\"video/mp4\" codecs=\"hvc1.%02x%02x%02x\" width=\"%d\" height=\"%d\" frameRate=\"%d\" startWithSAP=\"1\" bandwidth=\"%d\">\n"
" <SegmentTemplate timescale=\"1000\" media=\"%s-$Time$.m4v\" initialization=\"%s-init.m4v\">\n"
" <SegmentTimeline>\n";
static const char* s_aac =
" <AdaptationSet contentType=\"audio\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n"
" <Representation id=\"AAC\" mimeType=\"audio/mp4\" codecs=\"mp4a.40.%u\" audioSamplingRate=\"%d\" startWithSAP=\"1\" bandwidth=\"%d\">\n"
" <AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\"/>\n"
" <SegmentTemplate timescale=\"1000\" media=\"%s-$Time$.m4a\" initialization=\"%s-init.m4a\">\n"
" <SegmentTimeline>\n";
static const char* s_footer =
" </SegmentTimeline>\n"
" </SegmentTemplate>\n"
" </Representation>\n"
" </AdaptationSet>\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, " <Period start=\"PT0S\" id=\"dash\">\n");
}
else
{
n = snprintf(playlist, bytes, s_mpd_static, (unsigned int)(mpd->duration / 1000), minimumUpdatePeriod);
n += snprintf(playlist + n, bytes - n, " <Period start=\"PT0S\" id=\"dash\">\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, " <S t=\"%" PRId64 "\" d=\"%u\"/>\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, " <S t=\"%" PRId64 "\" d=\"%u\"/>\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, " <S t=\"%" PRId64 "\" d=\"%u\"/>\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, " </Period>\n</MPD>\n");
return n;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
#include "dash-parser.h"
#include "hls-string.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
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;
}

View File

@ -0,0 +1,189 @@
#include "dash-parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
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

View File

@ -0,0 +1,210 @@
#include "dash-parser.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
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;
}

View File

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

View File

@ -0,0 +1,160 @@
#include "xs-datatype.h"
#include <assert.h>
#include <stdio.h>
#include <time.h>
// 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. P<date>T<time>
// 4. PYYYYMMDDThhmmss
// 5. P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]
// For example, "P3Y6M4DT12H30M5S" represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".
// "P23DT23H" and "P4Y" "P0.5Y" == "P0,5Y"
// "PT0S" or "P0D"
// "P0003-06-04T12:30:05"
/// @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 n = 1;
data[0] = 'P';
if (duration > 24 * 3600 * 1000)
{
n += snprintf(data + n, size - n, "%dD", (int)(duration / (24 * 3600 * 1000)));
duration %= 24 * 3600 * 1000;
}
data[n++] = 'T';
if (duration > 3600 * 1000)
{
n += snprintf(data + n, size - n, "%dH", (int)(duration / (3600 * 1000)));
duration %= 3600 * 1000;
n += snprintf(data + n, size - n, "%dM", (int)(duration / (60 * 1000)));
duration %= 60 * 1000;
}
n += snprintf(data + n, size - n, "%dS", (int)((duration + 999) / 1000));
duration %= 1000;
return n;
}
int xs_duration_read(int64_t* duration, const char* data, int size)
{
int n;
int flags;
unsigned int v1[6];
float v2[6];
const char* ptr;
const char* end;
if (!data || size < 2)
return -1;
ptr = data;
end = data + size;
n = 0;
v1[0] = v1[1] = v1[2] = v1[3] = v1[4] = v1[5] = 0;
v2[0] = v2[1] = v2[2] = v2[3] = v2[4] = v2[5] = 0.0;
flags = 1;
if ('-' == ptr[0])
{
flags = -1;
ptr++;
}
if ('P' != ptr[0])
return -1;
ptr++;
// P20200529T164700
if (6 == sscanf(ptr, "%4u%2u%2uT%2u%2u%2u", &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5]))
{
*duration = v1[0];
*duration = *duration * 12 /* month */ + v1[1];
*duration = *duration * 30 /* day */ + v1[2];
*duration = *duration * 24 /* hour */ + v1[3];
*duration = *duration * 60 /* minute */ + v1[4];
*duration = *duration * 60 /* second */ + v1[5];
*duration = *duration * 1000 /* millisecond */;
}
// P2020-05-29T16:47:00
else if (6 == sscanf(ptr, "%u-%u-%uT%u:%u:%u", &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5]))
{
*duration = v1[0];
*duration = *duration * 12 /* month */ + v1[1];
*duration = *duration * 30 /* day */ + v1[2];
*duration = *duration * 24 /* hour */ + v1[3];
*duration = *duration * 60 /* minute */ + v1[4];
*duration = *duration * 60 /* second */ + v1[5];
*duration = *duration * 1000 /* millisecond */;
}
// P[n]W
else if (1 == sscanf(ptr, "%fW%n", &v2[0], &n) && n > 0)
{
*duration = (int64_t)(v2[0] * 7.0 * 24 * 60 * 60 * 1000);
}
else
{
*duration = 0;
if (1 == sscanf(ptr, "%fY%n", &v2[0], &n) && n > 0)
{
*duration += (int64_t)(v2[0] * 12.0 * 30 * 24 * 60 * 60 * 1000);
ptr += n;
}
if (1 == sscanf(ptr, "%fM%n", &v2[1], &n) && n > 0)
{
*duration += (int64_t)(v2[1] * 30.0 * 24 * 60 * 60 * 1000);
ptr += n;
}
if (1 == sscanf(ptr, "%fD%n", &v2[2], &n) && n > 0)
{
*duration += (int64_t)(v2[2] * 24.0 * 60 * 60 * 1000);
ptr += n;
}
if ('T' == ptr[0])
{
ptr++;
if (1 == sscanf(ptr, "%fH%n", &v2[3], &n) && n > 0)
{
*duration += (int64_t)(v2[3] * 60.0 * 60 * 1000);
ptr += n;
}
if (1 == sscanf(ptr, "%fM%n", &v2[4], &n) && n > 0)
{
*duration += (int64_t)(v2[4] * 60.0 * 1000);
ptr += n;
}
if (1 == sscanf(ptr, "%fS%n", &v2[5], &n) && n > 0)
{
*duration += (int64_t)(v2[5] * 1000.0);
ptr += n;
}
}
}
*duration *= flags;
return 0;
}
#if defined(_DEBUG) || defined(DEBUG)
void xs_datatype_test(void)
{
int64_t duration;
assert(0 == xs_duration_read(&duration, "P3Y6M4DT12H30M5S", 16) && duration == (((((3LL * 12 + 6) * 30 + 4) * 24 + 12) * 60 + 30) * 60 + 5) * 1000);
assert(0 == xs_duration_read(&duration, "PT12H30M5S", 10) && duration == ((12LL *60+30)*60+5)*1000);
assert(0 == xs_duration_read(&duration, "P0.5Y", 5) && duration == (((((0.5 * 12LL + 0) * 30 + 0) * 24 + 0) * 60 + 0) * 60 + 0) * 1000);
assert(0 == xs_duration_read(&duration, "P1W", 3) && duration == ((((1 * 7) * 24 + 0) * 60 + 0) * 60 + 0) * 1000);
assert(0 == xs_duration_read(&duration, "PT1.2S", 6) && duration == 1200);
assert(0 == xs_duration_read(&duration, "P00030604T123005", 16) && duration == (((((3LL * 12 + 6) * 30 + 4) * 24 + 12) * 60 + 30) * 60 + 5) * 1000);
assert(0 == xs_duration_read(&duration, "P0003-06-04T12:30:05", 20) && duration == (((((3LL * 12 + 6) * 30 + 4) * 24 + 12) * 60 + 30) * 60 + 5) * 1000);
assert(0 == xs_duration_read(&duration, "-PT30M5S", 8) && duration == -(30LL * 60 + 5) * 1000);
}
#endif

View File

@ -0,0 +1,237 @@
#include "aio-worker.h"
#include "dash-mpd.h"
#include "dash-proto.h"
#include "flv-proto.h"
#include "flv-reader.h"
#include "flv-parser.h"
#include "mov-format.h"
#include "http-server.h"
#include "http-route.h"
#include "mpeg4-aac.h"
#include "mpeg4-avc.h"
#include "mpeg4-hevc.h"
#include "cstringext.h"
#include "sys/sync.hpp"
#include "sys/thread.h"
#include "sys/system.h"
#include "sys/path.h"
#include "cpm/shared_ptr.h"
#include "app-log.h"
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#define LOCALPATH "./"
struct dash_playlist_t
{
std::string name;
dash_mpd_t* mpd;
int width;
int height;
int64_t timestamp;
int adapation_video;
int adapation_audio;
char playlist[256 * 1024];
uint8_t packet[2 * 1024 * 1024];
std::list<std::string> files;
};
static ThreadLocker s_locker;
extern "C" const struct mov_buffer_t* mov_file_buffer(void);
static int dash_mpd_onsegment(void* param, int /*track*/, const void* data, size_t bytes, int64_t /*pts*/, int64_t /*dts*/, int64_t /*duration*/, const char* name)
{
app_log(LOG_DEBUG, "dash_mpd_onsegment %s\n", name);
FILE* fp = fopen(name, "wb");
if(fp)
{
fwrite(data, 1, bytes, fp);
fclose(fp);
}
dash_playlist_t* dash = (dash_playlist_t*)param;
if(!strendswith(name, "-init.m4v") && !strendswith(name, "-init.m4a"))
dash->files.push_back(name);
while (dash->files.size() > 20)
{
app_log(LOG_DEBUG, "Delete %s\n", dash->files.front().c_str());
path_rmfile(dash->files.front().c_str());
dash->files.pop_front();
}
AutoThreadLocker locker(s_locker);
dash_mpd_playlist(dash->mpd, dash->playlist, sizeof(dash->playlist));
return 0;
}
static int dash_live_onflv(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
{
struct mpeg4_aac_t aac;
struct mpeg4_avc_t avc;
struct mpeg4_hevc_t hevc;
dash_playlist_t* dash = (dash_playlist_t*)param;
switch (codec)
{
case FLV_VIDEO_AVCC:
if (-1 == dash->adapation_video && mpeg4_avc_decoder_configuration_record_load((const uint8_t*)data, bytes, &avc) > 0)
dash->adapation_video = dash_mpd_add_video_adaptation_set(dash->mpd, dash->name.c_str(), MOV_OBJECT_H264, dash->width, dash->height, data, bytes);
break;
case FLV_VIDEO_HVCC:
if (-1 == dash->adapation_video && mpeg4_hevc_decoder_configuration_record_load((const uint8_t*)data, bytes, &hevc) > 0)
dash->adapation_video = dash_mpd_add_video_adaptation_set(dash->mpd, dash->name.c_str(), MOV_OBJECT_HEVC, dash->width, dash->height, data, bytes);
break;
case FLV_AUDIO_ASC:
if (-1 == dash->adapation_audio && mpeg4_aac_audio_specific_config_load((const uint8_t*)data, bytes, &aac) > 0)
{
int rate = mpeg4_aac_audio_frequency_to((enum mpeg4_aac_frequency)aac.sampling_frequency_index);
dash->adapation_audio = dash_mpd_add_audio_adaptation_set(dash->mpd, dash->name.c_str(), MOV_OBJECT_AAC, aac.channel_configuration, 32, rate, data, bytes);
}
break;
case FLV_AUDIO_AAC:
return dash_mpd_input(dash->mpd, dash->adapation_audio, data, bytes, pts, dts, 0);
case FLV_VIDEO_H264:
return dash_mpd_input(dash->mpd, dash->adapation_video, data, bytes, pts, dts, flags ? MOV_AV_FLAG_KEYFREAME : 0);
case FLV_VIDEO_H265:
return dash_mpd_input(dash->mpd, dash->adapation_video, data, bytes, pts, dts, flags ? MOV_AV_FLAG_KEYFREAME : 0);
default:
assert(0);
}
return 0;
}
static int dash_live_worker(const char* file, dash_playlist_t* dash)
{
int r, type;
int avcrecord = 0;
int aacconfig = 0;
size_t taglen;
uint32_t timestamp;
uint32_t s_timestamp = 0;
uint32_t diff = 0;
uint64_t clock;
while (1)
{
void* f = flv_reader_create(file);
clock = system_clock(); // timestamp start from 0
while (1 == flv_reader_read(f, &type, &timestamp, &taglen, dash->packet, sizeof(dash->packet)))
{
uint64_t t = system_clock();
if (clock + timestamp > t && clock + timestamp < t + 3 * 1000)
system_sleep(clock + timestamp - t);
else if (clock + timestamp > t + 3 * 1000)
clock = t - timestamp;
timestamp += diff;
s_timestamp = timestamp > s_timestamp ? timestamp : s_timestamp;
r = flv_parser_input(type, dash->packet, taglen, timestamp, dash_live_onflv, dash);
if (0 != r)
{
assert(0);
break; // TODO: handle send failed
}
}
flv_reader_destroy(f);
diff = s_timestamp + 30;
}
}
static int dash_server_mpd(http_session_t* session, dash_playlist_t* dash)
{
http_server_set_header(session, "Content-Type", "application/xml+dash");
http_server_set_header(session, "Access-Control-Allow-Origin", "*");
http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT");
AutoThreadLocker locker(s_locker);
http_server_reply(session, 200, dash->playlist, strlen(dash->playlist));
return 0;
}
static int dash_server_onlive(void* dash, http_session_t* session, const char* /*method*/, const char* path)
{
char fullpath[1024];
int r = path_concat(path + 6 /* /live/ */, LOCALPATH, fullpath);
printf("live: %s\n", fullpath);
const char* name = path_basename(fullpath);
if (strendswith(name, ".mpd"))
{
return dash_server_mpd(session, (dash_playlist_t*)dash);
}
else if (path_testfile(name))
{
// cross domain
http_server_set_header(session, "Access-Control-Allow-Origin", "*");
http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT");
return http_server_sendfile(session, name, NULL, NULL);
}
return http_server_send(session, 404, "", 0, NULL, NULL);
}
static int dash_server_onvod(void* /*dash*/, http_session_t* session, const char* /*method*/, const char* path)
{
char fullpath[1024];
int r = path_concat(path + 5 /* /vod/ */, LOCALPATH, fullpath);
printf("vod: %s\n", fullpath);
if (0 == r && path_testfile(fullpath))
{
// MIME
if (strendswith(fullpath, ".mpd"))
http_server_set_content_type(session, "application/xml+dash");
else if (strendswith(fullpath, ".mp4") || strendswith(fullpath, ".m4v"))
http_server_set_header(session, "content-type", "video/mp4");
else if (strendswith(fullpath, ".m4a"))
http_server_set_header(session, "content-type", "audio/mp4");
//http_server_set_header(session, "Transfer-Encoding", "chunked");
// cross domain
http_server_set_header(session, "Access-Control-Allow-Origin", "*");
http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT");
return http_server_sendfile(session, fullpath, NULL, NULL);
}
return http_server_send(session, 404, "", 0, NULL, NULL);
}
void dash_dynamic_test(const char* ip, int port, const char* file, int width, int height)
{
std::shared_ptr<dash_playlist_t> live(new dash_playlist_t());
live->mpd = dash_mpd_create(DASH_DYNAMIC, dash_mpd_onsegment, live.get());
live->name = "live";
live->width = width;
live->height = height;
live->adapation_audio = live->adapation_video = -1;
aio_worker_init(4);
http_server_t* http = http_server_create(ip, port);
http_server_set_handler(http, http_server_route, live.get());
http_server_addroute("/live/", dash_server_onlive);
http_server_addroute("/vod/", dash_server_onvod);
// live worker
dash_live_worker(file, live.get());
http_server_destroy(http);
aio_worker_clean(4);
dash_mpd_destroy(live->mpd);
}

View File

@ -0,0 +1,85 @@
#include "dash-mpd.h"
#include "dash-proto.h"
#include "mov-format.h"
#include "mov-reader.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
extern "C" const struct mov_buffer_t* mov_file_buffer(void);
static char s_packet[2 * 1024 * 1024];
static uint32_t s_track_video;
static uint32_t s_track_audio;
static int s_adapation_video;
static int s_adapation_audio;
static void mp4_onvideo(void* mpd, uint32_t track, uint8_t object, int width, int height, const void* extra, size_t bytes)
{
s_track_video = track;
s_adapation_video = dash_mpd_add_video_adaptation_set((dash_mpd_t*)mpd, "dash-static-video", object, width, height, extra, bytes);
}
static void mp4_onaudio(void* mpd, uint32_t track, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes)
{
s_track_audio = track;
s_adapation_audio = dash_mpd_add_audio_adaptation_set((dash_mpd_t*)mpd, "dash-static-audio", object, channel_count, bit_per_sample, sample_rate, extra, bytes);
}
static void mp4_onread(void* mpd, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int flags)
{
if (s_track_video == track)
{
bool keyframe = 5 == (0x1f & ((uint8_t*)buffer)[4]);
dash_mpd_input((dash_mpd_t*)mpd, s_adapation_video, buffer, bytes, pts, dts, keyframe ? MOV_AV_FLAG_KEYFREAME : 0);
}
else if (s_track_audio == track)
{
dash_mpd_input((dash_mpd_t*)mpd, s_adapation_audio, buffer, bytes, pts, dts, 0);
}
else
{
assert(0);
}
}
static int dash_mpd_onsegment(void* /*param*/, int /*track*/, const void* data, size_t bytes, int64_t /*pts*/, int64_t /*dts*/, int64_t /*duration*/, const char* name)
{
FILE* fp = fopen(name, "wb");
fwrite(data, 1, bytes, fp);
fclose(fp);
return 0;
}
static void dash_save_playlist(const char* name, const char* playlist)
{
char filename[256];
snprintf(filename, sizeof(filename), "%s.mpd", name);
FILE* fp = fopen(filename, "wb");
fwrite(playlist, 1, strlen(playlist), fp);
fclose(fp);
}
void dash_static_test(const char* mp4, const char* name)
{
FILE* fp = fopen(mp4, "rb");
mov_reader_t* mov = mov_reader_create(mov_file_buffer(), fp);
dash_mpd_t* mpd = dash_mpd_create(DASH_STATIC, dash_mpd_onsegment, NULL);
struct mov_reader_trackinfo_t info = { mp4_onvideo, mp4_onaudio };
mov_reader_getinfo(mov, &info, mpd);
int r = mov_reader_read(mov, s_packet, sizeof(s_packet), mp4_onread, mpd);
while (1 == r)
{
r = mov_reader_read(mov, s_packet, sizeof(s_packet), mp4_onread, mpd);
}
//flush
dash_mpd_input(mpd, s_adapation_video, NULL, 0, 0, 0, 0);
dash_mpd_playlist(mpd, s_packet, sizeof(s_packet));
dash_save_playlist(name, s_packet);
dash_mpd_destroy(mpd);
mov_reader_destroy(mov);
fclose(fp);
}

View File

@ -0,0 +1,14 @@
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_SRC_FILES := $(wildcard source/*.c)
LOCAL_SRC_FILES += $(wildcard source/*.cpp)
LOCAL_MODULE := flv
include $(BUILD_STATIC_LIBRARY)

View File

@ -0,0 +1,44 @@
#--------------------------------Output------------------------------
# OUTTYPE: 0-exe, 1-dll, 2-static
#--------------------------------------------------------------------
OUTTYPE = 2
OUTFILE = libflv.a
#-------------------------------Include------------------------------
#
# INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix
#--------------------------------------------------------------------
INCLUDES = . ./include
#-------------------------------Source-------------------------------
#
#--------------------------------------------------------------------
SOURCE_PATHS = source
SOURCE_FILES = $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.cpp))
SOURCE_FILES += $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.c))
SOURCE_FILES := $(filter-out $(_SOURCE_FILES),$(SOURCE_FILES))
#-----------------------------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

View File

@ -0,0 +1,69 @@
#ifndef _amf0_h_
#define _amf0_h_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
enum AMFDataType
{
AMF_NUMBER = 0x00,
AMF_BOOLEAN,
AMF_STRING,
AMF_OBJECT,
AMF_MOVIECLIP,
AMF_NULL,
AMF_UNDEFINED,
AMF_REFERENCE,
AMF_ECMA_ARRAY,
AMF_OBJECT_END,
AMF_STRICT_ARRAY,
AMF_DATE,
AMF_LONG_STRING,
AMF_UNSUPPORTED,
AMF_RECORDSET,
AMF_XML_DOCUMENT,
AMF_TYPED_OBJECT,
AMF_AVMPLUS_OBJECT,
};
uint8_t* AMFWriteNull(uint8_t* ptr, const uint8_t* end);
uint8_t* AMFWriteUndefined(uint8_t* ptr, const uint8_t* end);
uint8_t* AMFWriteObject(uint8_t* ptr, const uint8_t* end);
uint8_t* AMFWriteObjectEnd(uint8_t* ptr, const uint8_t* end);
uint8_t* AMFWriteTypedObject(uint8_t* ptr, const uint8_t* end);
uint8_t* AMFWriteECMAArarry(uint8_t* ptr, const uint8_t* end);
uint8_t* AMFWriteBoolean(uint8_t* ptr, const uint8_t* end, uint8_t value);
uint8_t* AMFWriteDouble(uint8_t* ptr, const uint8_t* end, double value);
uint8_t* AMFWriteString(uint8_t* ptr, const uint8_t* end, const char* string, size_t length);
uint8_t* AMFWriteDate(uint8_t* ptr, const uint8_t* end, double milliseconds, int16_t timezone);
uint8_t* AMFWriteNamedString(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, const char* value, size_t length2);
uint8_t* AMFWriteNamedDouble(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, double value);
uint8_t* AMFWriteNamedBoolean(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, uint8_t value);
const uint8_t* AMFReadNull(const uint8_t* ptr, const uint8_t* end);
const uint8_t* AMFReadUndefined(const uint8_t* ptr, const uint8_t* end);
const uint8_t* AMFReadBoolean(const uint8_t* ptr, const uint8_t* end, uint8_t* value);
const uint8_t* AMFReadDouble(const uint8_t* ptr, const uint8_t* end, double* value);
const uint8_t* AMFReadString(const uint8_t* ptr, const uint8_t* end, int isLongString, char* string, size_t length);
const uint8_t* AMFReadDate(const uint8_t* ptr, const uint8_t* end, double *milliseconds, int16_t *timezone);
struct amf_object_item_t
{
enum AMFDataType type;
const char* name;
void* value;
size_t size;
};
const uint8_t* amf_read_items(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t count);
#ifdef __cplusplus
}
#endif
#endif /* !_amf0_h_ */

View File

@ -0,0 +1,56 @@
#ifndef _amf3_h_
#define _amf3_h_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
enum AMF3DataType
{
AMF3_UNDEFINED = 0x00,
AMF3_NULL,
AMF3_FALSE,
AMF3_TRUE,
AMF3_INTEGER,
AMF3_DOUBLE,
AMF3_STRING,
AMF3_XML_DOCUMENT,
AMF3_DATE,
AMF3_ARRAY,
AMF3_OBJECT,
AMF3_XML,
AMF3_BYTE_ARRAY,
AMF3_VECTOR_INT,
AMF3_VECTOR_UINT,
AMF3_VECTOR_DOUBLE,
AMF3_VECTOR_OBJECT,
AMF3_DICTIONARY,
};
//uint8_t* AMF3WriteNull(uint8_t* ptr, const uint8_t* end);
//uint8_t* AMF3WriteObject(uint8_t* ptr, const uint8_t* end);
//uint8_t* AMF3WriteObjectEnd(uint8_t* ptr, const uint8_t* end);
//
//uint8_t* AMF3WriteBoolean(uint8_t* ptr, const uint8_t* end, uint8_t value);
//uint8_t* AMF3WriteInteger(uint8_t* ptr, const uint8_t* end, int32_t value);
//uint8_t* AMF3WriteDouble(uint8_t* ptr, const uint8_t* end, double value);
//uint8_t* AMF3WriteString(uint8_t* ptr, const uint8_t* end, const char* string, size_t length);
//
//uint8_t* AMF3WriteNamedBoolean(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, uint8_t value);
//uint8_t* AMF3WriteNamedInteger(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, int32_t value);
//uint8_t* AMF3WriteNamedString(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, const char* value, size_t length2);
//uint8_t* AM3FWriteNamedDouble(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, double value);
const uint8_t* AMF3ReadNull(const uint8_t* ptr, const uint8_t* end);
const uint8_t* AMF3ReadBoolean(const uint8_t* ptr, const uint8_t* end);
const uint8_t* AMF3ReadInteger(const uint8_t* ptr, const uint8_t* end, int32_t* value);
const uint8_t* AMF3ReadDouble(const uint8_t* ptr, const uint8_t* end, double* value);
const uint8_t* AMF3ReadString(const uint8_t* ptr, const uint8_t* end, char* string, uint32_t* length);
#ifdef __cplusplus
}
#endif
#endif /* !_amf3_h_ */

View File

@ -0,0 +1,41 @@
#ifndef _aom_av1_h_
#define _aom_av1_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
struct aom_av1_t
{
uint32_t marker : 1;
uint32_t version : 7;
uint32_t seq_profile : 3;
uint32_t seq_level_idx_0 : 5;
uint32_t seq_tier_0 : 1;
uint32_t high_bitdepth : 1;
uint32_t twelve_bit : 1;
uint32_t monochrome : 1;
uint32_t chroma_subsampling_x : 1;
uint32_t chroma_subsampling_y : 1;
uint32_t chroma_sample_position : 2;
uint32_t reserved : 3;
uint32_t initial_presentation_delay_present : 1;
uint32_t initial_presentation_delay_minus_one : 4;
uint16_t bytes;
uint8_t data[2 * 1024];
};
int aom_av1_codec_configuration_record_load(const uint8_t* data, size_t bytes, struct aom_av1_t* av1);
int aom_av1_codec_configuration_record_save(const struct aom_av1_t* av1, uint8_t* data, size_t bytes);
int aom_av1_codecs(const struct aom_av1_t* av1, char* codecs, size_t bytes);
#if defined(__cplusplus)
}
#endif
#endif /* !_aom_av1_h_ */

View File

@ -0,0 +1,38 @@
#ifndef _flv_demuxer_h_
#define _flv_demuxer_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct flv_demuxer_t flv_demuxer_t;
/// Audio/Video Elementary Stream
/// @param[in] param user-defined parameter
/// @param[in] codec audio/video format (see more flv-proto.h)
/// @param[in] data audio/video element data, AAC: ADTS + AAC-Frame, H.264: startcode + NALU, MP3-Raw data
/// @param[in] bytes data length in byte
/// @param[in] pts audio/video presentation timestamp
/// @param[in] dts audio/video decoding timestamp
/// @param[in] flags 1-video keyframe, other-undefined
/// @return 0-ok, other-error
typedef int (*flv_demuxer_handler)(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
flv_demuxer_t* flv_demuxer_create(flv_demuxer_handler handler, void* param);
void flv_demuxer_destroy(flv_demuxer_t* demuxer);
/// Input FLV Audio/Video Stream
/// @param[in] type 8-audio, 9-video, 18-script (see more flv-proto.h)
/// @param[in] data flv audio/video Stream, AudioTagHeader/VideoTagHeader + A/V Data
/// @param[in] bytes data length in byte
/// @param[in] timestamp milliseconds relative to the first tag(DTS)
/// @return 0-ok, other-error
int flv_demuxer_input(flv_demuxer_t* demuxer, int type, const void* data, size_t bytes, uint32_t timestamp);
#if defined(__cplusplus)
}
#endif
#endif /* !_flv_demuxer_h_ */

View File

@ -0,0 +1,126 @@
#ifndef _flv_header_h_
#define _flv_header_h_
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
enum
{
FLV_SEQUENCE_HEADER = 0,
FLV_AVPACKET = 1,
FLV_END_OF_SEQUENCE = 2,
};
#define FLV_VIDEO_KEY_FRAME 1
struct flv_header_t
{
uint8_t FLV[3];
uint8_t version;
uint8_t audio;
uint8_t video;
uint32_t offset; // data offset
};
struct flv_tag_header_t
{
uint8_t filter; // 0-No pre-processing required
uint8_t type; // 8-audio, 9-video, 18-script data
uint32_t size; // data size
uint32_t timestamp;
uint32_t streamId;
};
struct flv_audio_tag_header_t
{
uint8_t codecid; /// audio codec id: FLV_AUDIO_AAC
uint8_t rate; /// audio sample frequence: 0-5.5 kHz, 1-11 kHz, 2-22 kHz, 3-44 kHz
uint8_t bits; /// audio sample bits: 0-8 bit samples, 1-16-bit samples
uint8_t channels; /// audio channel count: 0-Mono sound, 1-Stereo sound
uint8_t avpacket; /// AAC only:FLV_SEQUENCE_HEADER/FLV_AVPACKET
};
struct flv_video_tag_header_t
{
uint8_t codecid; /// video codec id: FLV_VIDEO_H264
uint8_t keyframe; /// video frame type: 1-key frame, 2-inter frame
uint8_t avpacket; /// H.264/H.265/AV1 only:FLV_SEQUENCE_HEADER/FLV_AVPACKET/FLV_END_OF_SEQUENCE
int32_t cts; /// video composition time(PTS - DTS), AVC/HEVC/AV1 only
};
/// Read FLV File Header
/// @return >=0-header length in byte, <0-error
int flv_header_read(struct flv_header_t* flv, const uint8_t* buf, int len);
/// Write FLV File Header
/// @param[in] audio 1-has audio, 0-don't have
/// @param[in] video 1-has video, 0-don't have
/// @param[out] buf flv header buffer
/// @param[out] len flv header length
/// @return >=0-header length in byte, <0-error
int flv_header_write(int audio, int video, uint8_t* buf, int len);
/// Read FLV Tag Header
/// @return >=0-header length in byte, <0-error
int flv_tag_header_read(struct flv_tag_header_t* tag, const uint8_t* buf, int len);
/// Write FLV Tag Header
/// @param[out] buf flv tag header buffer
/// @param[out] len flv tag header length
/// @return >=0-header length in byte, <0-error
int flv_tag_header_write(const struct flv_tag_header_t* tag, uint8_t* buf, int len);
/// Read FLV Audio Tag Header
/// @param[out] audio flv audio parameter
/// @param[in] buf flv audio tag header buffer
/// @param[in] len flv audio tag header length
/// @return >=0-header length in byte, <0-error
int flv_audio_tag_header_read(struct flv_audio_tag_header_t* audio, const uint8_t* buf, int len);
/// Write FLV Audio Tag Header
/// @param[in] audio flv audio parameter
/// @param[out] buf flv audio tag header buffer
/// @param[out] len flv audio tag header length
/// @return >=0-header length in byte, <0-error
int flv_audio_tag_header_write(const struct flv_audio_tag_header_t* audio, uint8_t* buf, int len);
/// Read FLV Video Tag Header
/// @param[out] video flv video parameter
/// @param[in] buf flv video tag header buffer
/// @param[in] len flv video tag header length
/// @return >=0-header length in byte, <0-error
int flv_video_tag_header_read(struct flv_video_tag_header_t* video, const uint8_t* buf, int len);
/// Write FLV Video Tag Header
/// @param[in] video flv video parameter
/// @param[out] buf flv video tag header buffer
/// @param[out] len flv video tag header length
/// @return >=0-header length in byte, <0-error
int flv_video_tag_header_write(const struct flv_video_tag_header_t* video, uint8_t* buf, int len);
/// Read FLV Data Tag Header
/// @return >=0-header length in byte, <0-error
int flv_data_tag_header_read(const uint8_t* buf, int len);
/// Write FLV Data Tag Header
/// @param[out] buf flv data tag header buffer
/// @param[out] len flv data tag header length
/// @return >=0-header length in byte, <0-error
int flv_data_tag_header_write(uint8_t* buf, int len);
/// Read/Write FLV previous tag size
int flv_tag_size_read(const uint8_t* buf, int len, uint32_t* size);
int flv_tag_size_write(uint8_t* buf, int len, uint32_t size);
#if defined(__cplusplus)
}
#endif
#endif /* !_flv_header_h_ */

View File

@ -0,0 +1,61 @@
#ifndef _flv_muxer_h_
#define _flv_muxer_h_
#include <stddef.h>
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct flv_muxer_t flv_muxer_t;
///Video: FLV VideoTagHeader + AVCVIDEOPACKET: AVCDecoderConfigurationRecord(ISO 14496-15) / One or more NALUs(four-bytes length + NALU)
///Audio: FLV AudioTagHeader + AACAUDIODATA: AudioSpecificConfig(14496-3) / Raw AAC frame data in UI8
///@param[in] data FLV Audio/Video Data(don't include FLV Tag Header)
///@param[in] type 8-audio, 9-video
///@return 0-ok, other-error
typedef int (*flv_muxer_handler)(void* param, int type, const void* data, size_t bytes, uint32_t timestamp);
flv_muxer_t* flv_muxer_create(flv_muxer_handler handler, void* param);
void flv_muxer_destroy(flv_muxer_t* muxer);
/// re-create AAC/AVC sequence header
int flv_muxer_reset(flv_muxer_t* muxer);
/// @param[in] data AAC ADTS stream, 0xFFF15C40011FFC...
int flv_muxer_aac(flv_muxer_t* muxer, const void* data, size_t bytes, uint32_t pts, uint32_t dts);
/// @param[in] data mp3 stream
int flv_muxer_mp3(flv_muxer_t* muxer, const void* data, size_t bytes, uint32_t pts, uint32_t dts);
/// @param[in] data opus stream, first opus head, then opus samples
int flv_muxer_opus(flv_muxer_t* muxer, const void* data, size_t bytes, uint32_t pts, uint32_t dts);
/// @param[in] data h.264 annexb bitstream: H.264 start code + H.264 NALU, 0x0000000168...
int flv_muxer_avc(flv_muxer_t* muxer, const void* data, size_t bytes, uint32_t pts, uint32_t dts);
/// @param[in] data h.265 annexb bitstream: H.265 start code + H.265 NALU, 0x00000001...
int flv_muxer_hevc(flv_muxer_t* muxer, const void* data, size_t bytes, uint32_t pts, uint32_t dts);
struct flv_metadata_t
{
int audiocodecid;
double audiodatarate; // kbps
int audiosamplerate;
int audiosamplesize;
int stereo;
int videocodecid;
double videodatarate; // kbps
double framerate; // fps
int width;
int height;
};
int flv_muxer_metadata(flv_muxer_t* muxer, const struct flv_metadata_t* metadata);
#if defined(__cplusplus)
}
#endif
#endif /* !_flv_muxer_h_ */

View File

@ -0,0 +1,33 @@
#ifndef _flv_parser_h_
#define _flv_parser_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
/// Audio/Video Elementary Stream
/// @param[in] param user-defined parameter
/// @param[in] codec audio/video format (see more flv-proto.h)
/// @param[in] data audio/video element data, AAC: AAC-Frame, H.264: MP4 Stream, MP3-Raw data
/// @param[in] bytes data length in byte
/// @param[in] pts audio/video presentation timestamp
/// @param[in] dts audio/video decoding timestamp
/// @param[in] flags 1-video keyframe, other-undefined
/// @return 0-ok, other-error
typedef int (*flv_parser_handler)(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags);
/// Input FLV Audio/Video Stream
/// @param[in] type 8-audio, 9-video, 18-script (see more flv-proto.h)
/// @param[in] data flv audio/video Stream, AudioTagHeader/VideoTagHeader + A/V Data
/// @param[in] bytes data length in byte
/// @param[in] timestamp milliseconds relative to the first tag(DTS)
/// @return 0-ok, other-error
int flv_parser_input(int type, const void* data, size_t bytes, uint32_t timestamp, flv_parser_handler handler, void* param);
#if defined(__cplusplus)
}
#endif
#endif /* !_flv_parser_h_ */

View File

@ -0,0 +1,30 @@
#ifndef _flv_proto_h_
#define _flv_proto_h_
// FLV Tag Type
#define FLV_TYPE_AUDIO 8
#define FLV_TYPE_VIDEO 9
#define FLV_TYPE_SCRIPT 18
// FLV Audio Type
#define FLV_AUDIO_ADPCM (1 << 4)
#define FLV_AUDIO_MP3 (2 << 4)
#define FLV_AUDIO_G711A (7 << 4) // G711 A-law
#define FLV_AUDIO_G711U (8 << 4) // G711 mu-law
#define FLV_AUDIO_AAC (10 << 4)
#define FLV_AUDIO_OPUS (13 << 4)
#define FLV_AUDIO_MP3_8K (14 << 4)
#define FLV_AUDIO_ASC 0x100 // AudioSpecificConfig(ISO-14496-3)
#define FLV_AUDIO_OPUS_HEAD 0x101 // opus-codec.org
// FLV Video Type
#define FLV_VIDEO_H263 2
#define FLV_VIDEO_VP6 4
#define FLV_VIDEO_H264 7
#define FLV_VIDEO_H265 12 // https://github.com/CDN-Union/H265
#define FLV_VIDEO_AV1 14 // https://aomediacodec.github.io/av1-isobmff
#define FLV_VIDEO_AVCC 0x200 // AVCDecoderConfigurationRecord(ISO-14496-15)
#define FLV_VIDEO_HVCC 0x201 // HEVCDecoderConfigurationRecord(ISO-14496-15)
#define FLV_VIDEO_AV1C 0x202 // AV1CodecConfigurationRecord(av1-isobmff)
#endif /* !_flv_proto_h_ */

View File

@ -0,0 +1,26 @@
#ifndef _flv_reader_h_
#define _flv_reader_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
void* flv_reader_create(const char* file);
void* flv_reader_create2(int(*read)(void* param, void* buf, int len), void* param);
void flv_reader_destroy(void* flv);
///@param[out] tagtype 8-audio, 9-video, 18-script data
///@param[out] timestamp FLV timestamp
///@param[out] taglen flv tag length(0 is ok but should be silently discard)
///@param[out] buffer FLV stream
///@param[in] bytes buffer size
///@return 1-got a packet, 0-EOF, other-error
int flv_reader_read(void* flv, int* tagtype, uint32_t* timestamp, size_t* taglen, void* buffer, size_t bytes);
#if defined(__cplusplus)
}
#endif
#endif /* !_flv_reader_h_ */

View File

@ -0,0 +1,26 @@
#ifndef _flv_writer_h_
#define _flv_writer_h_
#include <stddef.h>
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
void* flv_writer_create(const char* file);
void* flv_writer_create2(int (*write)(void* param, const void* buf, int len), void* param);
void flv_writer_destroy(void* flv);
/// Video: FLV VideoTagHeader + AVCVIDEOPACKET: AVCDecoderConfigurationRecord(ISO 14496-15) / One or more NALUs(four-bytes length + NALU)
/// Audio: FLV AudioTagHeader + AACAUDIODATA: AudioSpecificConfig(14496-3) / Raw AAC frame data in UI8
/// @param[in] data FLV Audio/Video Data(don't include FLV Tag Header)
/// @param[in] type 8-audio, 9-video
/// @return 0-ok, other-error
int flv_writer_input(void* flv, int type, const void* data, size_t bytes, uint32_t timestamp);
#if defined(__cplusplus)
}
#endif
#endif /* !_flv_writer_h_ */

View File

@ -0,0 +1,109 @@
#ifndef _mp3_header_h_
#define _mp3_header_h_
// https://en.wikipedia.org/wiki/MP3
#if defined(__cplusplus)
extern "C" {
#endif
/*
ISO/IEC 11172-3
2.4.1.3 Header
unsigned int sync: 12
unsigned int version: 1
unsigned int layer: 2
unsigned int error protection: 1
unsigned int bitrate_index: 4
unsigned int sampling_frequency: 2
unsigned int padding: 1
unsigned int private: 1
unsigned int mode: 2
unsigned int mode extension: 2
unsigned int copyright: 1
unsigned int original: 1
unsigned int emphasis: 2
bit_rate_index Layer I Layer II Layer III
'0000' free format free format free format
'0001' 32 kbit/s 32 kbit/s 32 kbit/s
'0010' 64 kbit/s 48 kbit/s 40 kbit/s
'0011' 96 kbit/s 56 kbit/s 48 kbit/s
'0100' 128 kbit/s 64 kbit/s 56 kbit/s
'0101' 160 kbit/s 80 kbit/s 64 kbit/s
'0110' 192 kbit/s 96 kbit/s 80 kbit/s
'0111' 224 kbit/s 112 kbit/s 96 kbit/s
'1000' 256 kbit/s 128 kbit/s 112 kbit/s
'1001' 288 kbit/s 160 kbit/s 128 kbit/s
'1010' 320 kbit/s 192 kbit/s 160 kbit/s
'1011' 352 kbit/s 224 kbit/s 192 kbit/s
'1100' 384 kbit/s 256 kbit/s 224 kbit/s
'1101' 416 kbit/s 320 kbit/s 256 kbit/s
'1110' 448 kbit/s 384 kbit/s 320 kbit/s
sampling_frequency
'00' 44.1 kHz
'01' 48 kHz
'10' 32 kHz
'11' reserved
mode
'00' stereo
'01' joint_stereo (intensity_stereo and/or ms_stereo)
'10' dual_channel
'11' single_channel
mode_extension
'00' subbands 4-31 in intensity_stereo, bound==4
'01' subbands 8-31 in intensity_stereo, bound==8
'10' subbands 12-31 in intensity_stereo, bound==12
'11' subbands 16-31 in intensity_stereo, bound==16
emphasis
'00' no emphasis
'01' 50/15 microsec. emphasis
'10' reserved
'11' CCITT J.17
*/
struct mp3_header_t
{
unsigned int version : 2; // 0-MPEG 2.5, 1-undefined, 2-MPEG-2, 3-MPEG-1
unsigned int layer : 2; // 3-Layer I, 2-Layer II, 1-Layer III, 0-reserved
unsigned int protection : 1;
unsigned int bitrate_index : 4; //0-free,
unsigned int sampling_frequency : 2;
unsigned int priviate : 1;
unsigned int mode : 2;
unsigned int mode_extension : 2;
unsigned int copyright : 1;
unsigned int original : 1;
unsigned int emphasis : 2;
};
// version
#define MP3_MPEG1 3
#define MP3_MPEG2 2
#define MP3_MPEG2_5 0
// layer
#define MP3_LAYER1 3
#define MP3_LAYER2 2
#define MP3_LAYER3 1
#define MP3_BITS_PER_SAMPLE 16
///MP3 Header size: 4
int mp3_header_load(struct mp3_header_t* mp3, const void* data, int bytes);
int mp3_header_save(const struct mp3_header_t* mp3, void* data, int bytes);
int mp3_get_channel(const struct mp3_header_t* mp3);
int mp3_get_bitrate(const struct mp3_header_t* mp3);
int mp3_set_bitrate(struct mp3_header_t* mp3, int bitrate);
int mp3_get_frequency(const struct mp3_header_t* mp3);
int mp3_set_frequency(struct mp3_header_t* mp3, int frequency);
#if defined(__cplusplus)
}
#endif
#endif /* !_mp3_header_h_ */

View File

@ -0,0 +1,148 @@
#ifndef _mpeg4_aac_h_
#define _mpeg4_aac_h_
#include <stddef.h>
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
struct mpeg4_aac_t
{
uint8_t profile; // 0-NULL, 1-AAC Main, 2-AAC LC, 2-AAC SSR, 3-AAC LTP
uint8_t sampling_frequency_index; // 0-96000, 1-88200, 2-64000, 3-48000, 4-44100, 5-32000, 6-24000, 7-22050, 8-16000, 9-12000, 10-11025, 11-8000, 12-7350, 13/14-reserved, 15-frequency is written explictly
uint8_t channel_configuration; // 0-AOT, 1-1channel,front-center, 2-2channels, front-left/right, 3-3channels: front center/left/right, 4-4channels: front-center/left/right, back-center, 5-5channels: front center/left/right, back-left/right, 6-6channels: front center/left/right, back left/right LFE-channel, 7-8channels
// uint32_t frequency; // play frequency
uint32_t sampling_frequency; // valid only in decode
uint8_t channels; // valid only in decode
int sbr; // sbr flag, valid only in decode
int ps; // ps flag, valid only in decode
uint8_t pce[64];
int npce; // pce bytes
};
enum mpeg2_aac_profile
{
MPEG2_AAC_MAIN = 0,
MPEG2_AAC_LC,
MPEG2_AAC_SSR,
};
// ISO/IEC 14496-3:2009(E) Table 1.3 - Audio Profiles definition (p41)
// https://en.wikipedia.org/wiki/MPEG-4_Part_3#Audio_Profiles
enum mpeg4_aac_object_type
{
MPEG4_AAC_MAIN = 1,
MPEG4_AAC_LC,
MPEG4_AAC_SSR,
MPEG4_AAC_LTP,
MPEG4_AAC_SBR, // (used with AAC LC in the "High Efficiency AAC Profile" (HE-AAC v1))
MPEG4_AAC_SCALABLE,
MPEG4_AAC_TWINVQ,
MPEG4_AAC_CELP,
MPEG4_AAC_HVXC,
MPEG4_AAC_TTSI = 12,
MPEG4_AAC_MAIN_SYNTHETIC,
MPEG4_AAC_WAVETABLE_SYNTHETIC,
MPEG4_AAC_GENERAL_MIDI,
MPEG4_AAC_ALGORITHMIC_SYNTHESIS, // Algorithmic Synthesis and Audio FX object type
MPEG4_AAC_ER_LC, // Error Resilient (ER) AAC Low Complexity (LC) object type
MPEG4_AAC_ER_LTP = 19, // Error Resilient (ER) AAC Long Term Predictor (LTP) object type
MPEG4_AAC_ER_SCALABLE, // Error Resilient (ER) AAC scalable object type
MPEG4_AAC_ER_TWINVQ, // Error Resilient (ER) TwinVQ object type
MPEG4_AAC_ER_BSAC, // Error Resilient (ER) BSAC object type
MPEG4_AAC_ER_AAC_LD, // Error Resilient (ER) AAC LD object type(used with CELP, ER CELP, HVXC, ER HVXC and TTSI in the "Low Delay Profile")
MPEG4_AAC_ER_CELP, // Error Resilient (ER) CELP object type
MPEG4_AAC_ER_HVXC, // Error Resilient (ER) HVXC object type
MPEG4_AAC_ER_HILN, // Error Resilient (ER) HILN object type
MPEG4_AAC_ER_PARAMTRIC, // Error Resilient (ER) Parametric object type
MPEG4_AAC_SSC, // SSC Audio object type
MPEG4_AAC_PS, // PS object type(used with AAC LC and SBR in the "HE-AAC v2 Profile")
MPEG4_AAC_MPEG_SURROUND, // MPEG Surround object type
MPEG4_AAC_LAYER_1 = 32, // Layer-1 Audio object type
MPEG4_AAC_LAYER_2, // Layer-2 Audio object type
MPEG4_AAC_LAYER_3, // Layer-3 Audio object type
MPEG4_AAC_DST,
MPEG4_AAC_ALS, // ALS Audio object type
MPEG4_AAC_SLS, // SLS Audio object type
MPEG4_AAC_SLS_NON_CORE, // SLS Non-Core Audio object type
MPEG4_AAC_ER_AAC_ELD, // Error Resilient (ER) AAC ELD object type (uses AAC-LD, AAC-ELD and AAC-ELDv2, "Low Delay AAC v2")
MPEG4_AAC_SMR_SIMPLE, // SMR Simple object type: MPEG-4 Part 23 standard (ISO/IEC 14496-23:2008)
MPEG4_AAC_SMR_MAIN, // SMR Main object type
MPEG4_AAC_USAC_NO_SBR, // Unified Speech and Audio Coding (no SBR)
MPEG4_AAC_SAOC, // Spatial Audio Object Coding: MPEG-D Part 2 standard (ISO/IEC 23003-2:2010)
MPEG4_AAC_LD_MEPG_SURROUND, // MPEG-D Part 2 - ISO/IEC 23003-2
MPEG4_AAC_USAC, // MPEG-D Part 3 - ISO/IEC 23003-3
};
enum mpeg4_audio_profile
{
MPEG4_AAC_PROFILE, // AAC LC
MPEG4_HIGH_EFFICIENCY_AAC_PROFILE, // AAC LC, SBR (<=128 kbps)
MPEG4_HE_AAC_V2_PROFILE, // AAC LC, SBR, PS (approx. 16 - 48 kbit/s)
MPEG4_MAIN_AUDIO_PROFILE, // AAC Main, AAC LC, AAC SSR, AAC LTP, AAC Scalable, TwinVQ, CELP, HVXC, TTSI, Main synthesis
MPEG4_SCALABLE_AUDIO_PROFILE, // AAC LC, AAC LTP, AAC Scalable, TwinVQ, CELP, HVXC, TTSI
MPEG4_SPEECH_AUDIO_PROFILE, // CELP, HVXC, TTSI
MPEG4_SYNTHETIC_AUDIO_PRIFILE, // TTSI, Main synthesis
MPEG4_HIGH_QUALITY_AUDIO_PROFILE, // AAC LC, AAC LTP, AAC Scalable, CELP, ER AAC LC, ER AAC LTP, ER AAC Scalable, ER CELP
MPEG4_LOW_DELAY_AUDIO_PROFILE, // CELP, HVXC, TTSI, ER AAC LD, ER CELP, ER HVXC
MPEG4_NATURAL_AUDIO_PRIFILE, // AAC Main, AAC LC, AAC SSR, AAC LTP, AAC Scalable, TwinVQ, CELP, HVXC, TTSI, ER AAC LC, ER AAC LTP, ER AAC Scalable, ER TwinVQ, ER BSAC, ER AAC LD, ER CELP, ER HVXC, ER HILN, ER Parametric
MPEG4_MOBILE_AUDIO_INTERNETWORKING_PROFILE, // ER AAC LC, ER AAC Scalable, ER TwinVQ, ER BSAC, ER AAC LD
MPEG4_HD_AAC_PROFILE, // AAC LC, SLS
MPEG4_ALS_SIMPLE_PROFILE, // ALS
};
enum mpeg4_aac_frequency
{
MPEG4_AAC_96000 = 0,
MPEG4_AAC_88200, // 0x1
MPEG4_AAC_64000, // 0x2
MPEG4_AAC_48000, // 0x3
MPEG4_AAC_44100, // 0x4
MPEG4_AAC_32000, // 0x5
MPEG4_AAC_24000, // 0x6
MPEG4_AAC_22050, // 0x7
MPEG4_AAC_16000, // 0x8
MPEG4_AAC_12000, // 0x9
MPEG4_AAC_11025, // 0xa
MPEG4_AAC_8000, // 0xb
MPEG4_AAC_7350, // 0xc
// reserved
// reserved
// escape value
};
/// @return >=0-adts header length, <0-error
int mpeg4_aac_adts_save(const struct mpeg4_aac_t* aac, size_t payload, uint8_t* data, size_t bytes);
/// @return >=0-adts header length, <0-error
int mpeg4_aac_adts_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac);
/// @return >=0-audio specific config length, <0-error
int mpeg4_aac_audio_specific_config_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac);
/// @return >=0-audio specific config length, <0-error
int mpeg4_aac_audio_specific_config_save(const struct mpeg4_aac_t* aac, uint8_t* data, size_t bytes);
/// @return >=0-stream mux config length, <0-error
int mpeg4_aac_stream_mux_config_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac);
/// @return >=0-stream mux config length, <0-error
int mpeg4_aac_stream_mux_config_save(const struct mpeg4_aac_t* aac, uint8_t* data, size_t bytes);
/// get AAC profile level indication value
int mpeg4_aac_profile_level(const struct mpeg4_aac_t* aac);
/// MPEG4_AAC_96000 => 96000
/// @return -1-error, other-frequency value
int mpeg4_aac_audio_frequency_to(enum mpeg4_aac_frequency index);
/// 96000 => MPEG4_AAC_96000
/// @return -1-error, other-frequency index
int mpeg4_aac_audio_frequency_from(int frequency);
int mpeg4_aac_adts_frame_length(const uint8_t* data, size_t bytes);
#if defined(__cplusplus)
}
#endif
#endif /* !_mpeg4_aac_h_ */

View File

@ -0,0 +1,68 @@
#ifndef _mpeg4_avc_h_
#define _mpeg4_avc_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
struct mpeg4_avc_t
{
// uint8_t version; // 1-only
uint8_t profile;
uint8_t compatibility; // constraint_set[0-5]_flag
uint8_t level;
uint8_t nalu; // NALUnitLength = (lengthSizeMinusOne + 1), default 4(0x03+1)
uint8_t nb_sps;
uint8_t nb_pps;
struct mpeg4_avc_sps_t
{
uint16_t bytes;
uint8_t* data;
} sps[32]; // [0-31]
struct mpeg4_avc_pps_t
{
uint16_t bytes;
uint8_t* data;
} pps[256];
// extension
uint8_t chroma_format_idc;
uint8_t bit_depth_luma_minus8;
uint8_t bit_depth_chroma_minus8;
uint8_t data[4 * 1024];
int off;
};
int mpeg4_avc_decoder_configuration_record_load(const uint8_t* data, size_t bytes, struct mpeg4_avc_t* avc);
int mpeg4_avc_decoder_configuration_record_save(const struct mpeg4_avc_t* avc, uint8_t* data, size_t bytes);
int mpeg4_avc_to_nalu(const struct mpeg4_avc_t* avc, uint8_t* data, size_t bytes);
int mpeg4_avc_codecs(const struct mpeg4_avc_t* avc, char* codecs, size_t bytes);
/// @param[out] vcl 0-non VCL, 1-IDR, 2-P/B
/// @return <=0-error, >0-output bytes
int h264_annexbtomp4(struct mpeg4_avc_t* avc, const void* data, int bytes, void* out, int size, int* vcl, int* update);
/// @return <=0-error, >0-output bytes
int h264_mp4toannexb(const struct mpeg4_avc_t* avc, const void* data, int bytes, void* out, int size);
/// h264_is_new_access_unit H.264 new access unit(frame)
/// @return 1-new access, 0-not a new access
int h264_is_new_access_unit(const uint8_t* nalu, size_t bytes);
/// H.264 nal unit split
void mpeg4_h264_annexb_nalu(const void* h264, int bytes, void (*handler)(void* param, const uint8_t* nalu, int bytes), void* param);
#if defined(__cplusplus)
}
#endif
#endif /* !_mpeg4_avc_h_ */

View File

@ -0,0 +1,169 @@
#ifndef _mpeg4_bits_h_
#define _mpeg4_bits_h_
#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif
struct mpeg4_bits_t
{
uint8_t* data;
size_t size;
size_t bits; // offset bit
int error;
};
#define mpeg4_bits_read_uint8(bits, n) (uint8_t)mpeg4_bits_read_n(bits, n)
#define mpeg4_bits_read_uint16(bits, n) (uint16_t)mpeg4_bits_read_n(bits, n)
#define mpeg4_bits_read_uint32(bits, n) (uint32_t)mpeg4_bits_read_n(bits, n)
#define mpeg4_bits_read_uint64(bits, n) (uint64_t)mpeg4_bits_read_n(bits, n)
#define mpeg4_bits_write_uint8(bits, v, n) mpeg4_bits_write_n(bits, (uint64_t)v, n)
#define mpeg4_bits_write_uint16(bits, v, n) mpeg4_bits_write_n(bits, (uint64_t)v, n)
#define mpeg4_bits_write_uint32(bits, v, n) mpeg4_bits_write_n(bits, (uint64_t)v, n)
#define mpeg4_bits_write_uint64(bits, v, n) mpeg4_bits_write_n(bits, (uint64_t)v, n)
static inline void mpeg4_bits_init(struct mpeg4_bits_t* bits, void* data, size_t size)
{
bits->data = (uint8_t*)data;
bits->size = size;
bits->bits = 0;
bits->error = 0;
}
/// @return 1-error, 0-no error
static inline int mpeg4_bits_error(struct mpeg4_bits_t* bits)
{
//return bits->bits >= bits->size * 8 ? 1 : 0;
return bits->error;
}
static inline void mpeg4_bits_aligment(struct mpeg4_bits_t* bits, int n)
{
bits->bits = (bits->bits + n - 1) / n * n;
}
static inline size_t mpeg4_bits_remain(struct mpeg4_bits_t* bits)
{
return bits->error ? 0 : (bits->size * 8 - bits->bits);
}
/// read 1-bit from bit stream(offset position)
/// @param[in] bits bit stream
/// @return -1-error, 1-value, 0-value
static inline int mpeg4_bits_read(struct mpeg4_bits_t* bits)
{
uint8_t bit;
assert(bits && bits->data && bits->size > 0);
if (bits->bits >= bits->size * 8)
{
bits->error = -1;
return 0; // throw exception
}
bit = bits->data[bits->bits/8] & (0x80U >> (bits->bits%8));
bits->bits += 1; // update offset
return bit ? 1 : 0;
}
/// read n-bit(n <= 64) from bit stream(offset position)
/// @param[in] bits bit stream
/// @return -1-error, other-value
static inline uint64_t mpeg4_bits_read_n(struct mpeg4_bits_t* bits, int n)
{
int m;
size_t i;
uint64_t v;
assert(n > 0 && n <= 64);
assert(bits && bits->data && bits->size > 0);
if (bits->bits + n > bits->size * 8 || n > 64 || n < 0)
{
bits->error = -1;
return 0; // throw exception
}
m = n;
v = bits->data[bits->bits / 8] & (0xFFU >> (bits->bits%8)); // remain valid value
if (n <= 8 - (int)(bits->bits % 8))
{
v = v >> (8 - (bits->bits % 8) - n); // shift right value
bits->bits += n;
return v;
}
n -= 8 - (int)(bits->bits % 8);
for (i = 1; n >= 8; i++)
{
assert(bits->bits / 8 + i < bits->size);
v <<= 8;
v += bits->data[bits->bits / 8 + i];
n -= 8;
}
if (n > 0)
{
v <<= n;
v += bits->data[bits->bits / 8 + i] >> (8 - n);
}
bits->bits += m;
return v;
}
/// write 1-bit
/// @param[in] v write 0 if v value 0, other, write 1
static inline int mpeg4_bits_write(struct mpeg4_bits_t* bits, int v)
{
assert(bits && bits->data && bits->size > 0);
if (bits->bits >= bits->size * 8)
{
bits->error = -1;
return -1; // throw exception
}
if(v)
bits->data[bits->bits / 8] |= (0x80U >> (bits->bits % 8));
bits->bits += 1; // update offset
return 0;
}
static inline int mpeg4_bits_write_n(struct mpeg4_bits_t* bits, uint64_t v, int n)
{
int m;
size_t i;
assert(n > 0 && n <= 64);
assert(bits && bits->data && bits->size > 0);
if (bits->bits + n > bits->size * 8 || n > 64 || n < 0)
{
bits->error = -1;
return -1; // throw exception
}
m = n;
v = v << (64 - n); // left shift to first bit
bits->data[bits->bits / 8] |= v >> (56 + (bits->bits % 8)); // remain valid value
v <<= 8 - (bits->bits % 8);
n -= 8 - (int)(bits->bits % 8);
for (i = 1; n > 0; i++)
{
assert(bits->bits / 8 + i < bits->size);
bits->data[bits->bits / 8 + i] = (uint8_t)(v >> 56);
v <<= 8;
n -= 8;
}
bits->bits += m;
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* !_mpeg4_bits_h_ */

View File

@ -0,0 +1,64 @@
#ifndef _mpeg4_hevc_h_
#define _mpeg4_hevc_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
struct mpeg4_hevc_t
{
uint8_t configurationVersion; // 1-only
uint8_t general_profile_space; // 2bit,[0,3]
uint8_t general_tier_flag; // 1bit,[0,1]
uint8_t general_profile_idc; // 5bit,[0,31]
uint32_t general_profile_compatibility_flags;
uint64_t general_constraint_indicator_flags;
uint8_t general_level_idc;
uint16_t min_spatial_segmentation_idc;
uint8_t parallelismType; // 2bit,[0,3]
uint8_t chromaFormat; // 2bit,[0,3]
uint8_t bitDepthLumaMinus8; // 3bit,[0,7]
uint8_t bitDepthChromaMinus8; // 3bit,[0,7]
uint16_t avgFrameRate;
uint8_t constantFrameRate; // 2bit,[0,3]
uint8_t numTemporalLayers; // 3bit,[0,7]
uint8_t temporalIdNested; // 1bit,[0,1]
uint8_t lengthSizeMinusOne; // 2bit,[0,3]
uint8_t numOfArrays;
struct
{
uint8_t array_completeness;
uint8_t type; // nalu type
uint16_t bytes;
uint8_t* data;
} nalu[64];
uint8_t array_completeness;
uint8_t data[4 * 1024];
int off;
};
int mpeg4_hevc_decoder_configuration_record_load(const uint8_t* data, size_t bytes, struct mpeg4_hevc_t* hevc);
int mpeg4_hevc_decoder_configuration_record_save(const struct mpeg4_hevc_t* hevc, uint8_t* data, size_t bytes);
int mpeg4_hevc_to_nalu(const struct mpeg4_hevc_t* hevc, uint8_t* data, size_t bytes);
int mpeg4_hevc_codecs(const struct mpeg4_hevc_t* hevc, char* codecs, size_t bytes);
int h265_annexbtomp4(struct mpeg4_hevc_t* hevc, const void* data, int bytes, void* out, int size, int *vcl, int* update);
int h265_mp4toannexb(const struct mpeg4_hevc_t* hevc, const void* data, int bytes, void* out, int size);
/// h265_is_new_access_unit H.265 new access unit(frame)
/// @return 1-new access, 0-not a new access
int h265_is_new_access_unit(const uint8_t* nalu, size_t bytes);
#if defined(__cplusplus)
}
#endif
#endif /* !_mpeg4_hevc_h_ */

View File

@ -0,0 +1,37 @@
#ifndef _opus_head_h_
#define _opus_head_h_
#include <stddef.h>
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
struct opus_head_t
{
uint8_t version;
uint8_t channels;
uint16_t pre_skip;
uint32_t input_sample_rate;
int16_t output_gain;
uint8_t channel_mapping_family;
uint8_t stream_count;
uint8_t coupled_count;
uint8_t channel_mapping[8];
};
/// @return >0-ok, <=0-error
int opus_head_save(const struct opus_head_t* opus, uint8_t* data, size_t bytes);
/// @return >0-ok, <=0-error
int opus_head_load(const uint8_t* data, size_t bytes, struct opus_head_t* opus);
static inline int opus_head_channels(const struct opus_head_t* opus)
{
return 0 == opus->channels ? 2 : opus->channels;
}
#if defined(__cplusplus)
}
#endif
#endif /* !_opus_head_h_ */

View File

@ -0,0 +1,32 @@
#ifndef _webm_vpx_h_
#define _webm_vpx_h_
#include <stdint.h>
#include <stddef.h>
#if defined(__cplusplus)
extern "C" {
#endif
// VP8/VP9/VP10
struct webm_vpx_t
{
uint8_t profile;
uint8_t level;
uint8_t bit_depth;
uint8_t chroma_subsampling; // 0-4:2:0 vertical, 1-4:2:0 colocated with luma (0,0), 2-4:2:2, 3-4:4:4
uint8_t video_full_range_flag; // 0 = legal range (e.g. 16-235 for 8 bit sample depth); 1 = full range (e.g. 0-255 for 8-bit sample depth)
uint8_t colour_primaries; // ISO/IEC 23001-8:2016
uint8_t transfer_characteristics;
uint8_t matrix_coefficients;
uint16_t codec_intialization_data_size; // must be 0
uint8_t codec_intialization_data[1]; // not used for VP8 and VP9
};
int webm_vpx_codec_configuration_record_load(const uint8_t* data, size_t bytes, struct webm_vpx_t* vpx);
int webm_vpx_codec_configuration_record_save(const struct webm_vpx_t* vpx, uint8_t* data, size_t bytes);
#if defined(__cplusplus)
}
#endif
#endif /* !_webm_vpx_h_ */

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\amf0.h" />
<ClInclude Include="include\amf3.h" />
<ClInclude Include="include\aom-av1.h" />
<ClInclude Include="include\flv-demuxer.h" />
<ClInclude Include="include\flv-muxer.h" />
<ClInclude Include="include\flv-parser.h" />
<ClInclude Include="include\flv-proto.h" />
<ClInclude Include="include\flv-reader.h" />
<ClInclude Include="include\flv-writer.h" />
<ClInclude Include="include\mp3-header.h" />
<ClInclude Include="include\mpeg4-aac.h" />
<ClInclude Include="include\mpeg4-avc.h" />
<ClInclude Include="include\mpeg4-bits.h" />
<ClInclude Include="include\mpeg4-hevc.h" />
<ClInclude Include="include\opus-head.h" />
<ClInclude Include="include\webm-vpx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\amf0.c" />
<ClCompile Include="source\amf3.c" />
<ClCompile Include="source\aom-av1.c" />
<ClCompile Include="source\flv-demuxer-script.c" />
<ClCompile Include="source\flv-demuxer.c" />
<ClCompile Include="source\flv-header.c" />
<ClCompile Include="source\flv-muxer.c" />
<ClCompile Include="source\flv-parser.c" />
<ClCompile Include="source\flv-reader.c" />
<ClCompile Include="source\flv-writer.c" />
<ClCompile Include="source\hevc-annexbtomp4.c" />
<ClCompile Include="source\hevc-mp4toannexb.c" />
<ClCompile Include="source\mp3-header.c" />
<ClCompile Include="source\mpeg4-aac-asc.c" />
<ClCompile Include="source\mpeg4-aac.c" />
<ClCompile Include="source\mpeg4-annexbtomp4.c" />
<ClCompile Include="source\mpeg4-avc.c" />
<ClCompile Include="source\mpeg4-hevc.c" />
<ClCompile Include="source\mpeg4-mp4toannexb.c" />
<ClCompile Include="source\opus-head.c" />
<ClCompile Include="source\webm-vpx.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D5BA0BB6-0D84-48CB-8630-F617CB6DE375}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libflv</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\flv-demuxer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\flv-muxer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\flv-reader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\flv-writer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\mpeg4-aac.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\mpeg4-avc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\amf0.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\amf3.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\flv-proto.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\mp3-header.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\flv-parser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\mpeg4-hevc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\mpeg4-bits.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\aom-av1.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\webm-vpx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\opus-head.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\flv-demuxer.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flv-muxer.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flv-reader.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flv-writer.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mpeg4-aac.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mpeg4-avc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\amf0.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\amf3.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mpeg4-mp4toannexb.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mpeg4-annexbtomp4.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mp3-header.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flv-parser.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mpeg4-hevc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\hevc-mp4toannexb.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\hevc-annexbtomp4.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flv-demuxer-script.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\mpeg4-aac-asc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\aom-av1.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flv-header.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\webm-vpx.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\opus-head.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,378 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
4605125A24D6B4D500B04B70 /* opus-head.c in Sources */ = {isa = PBXBuildFile; fileRef = 4605125924D6B4D500B04B70 /* opus-head.c */; };
46733799219B2315009F658F /* mpeg4-aac-asc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46733798219B2315009F658F /* mpeg4-aac-asc.c */; };
468B915C23B8ADAA00EA99A3 /* aom-av1.c in Sources */ = {isa = PBXBuildFile; fileRef = 468B915B23B8ADAA00EA99A3 /* aom-av1.c */; };
468B916023B8AE3100EA99A3 /* flv-header.c in Sources */ = {isa = PBXBuildFile; fileRef = 468B915F23B8AE3100EA99A3 /* flv-header.c */; };
46C5B2DB2183EDA200419E57 /* mpeg4-mp4toannexb.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CB2183EDA200419E57 /* mpeg4-mp4toannexb.c */; };
46C5B2DC2183EDA200419E57 /* mp3-header.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CC2183EDA200419E57 /* mp3-header.c */; };
46C5B2DD2183EDA200419E57 /* flv-writer.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CD2183EDA200419E57 /* flv-writer.c */; };
46C5B2DE2183EDA200419E57 /* amf0.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CE2183EDA200419E57 /* amf0.c */; };
46C5B2DF2183EDA200419E57 /* hevc-annexbtomp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CF2183EDA200419E57 /* hevc-annexbtomp4.c */; };
46C5B2E02183EDA200419E57 /* mpeg4-hevc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D02183EDA200419E57 /* mpeg4-hevc.c */; };
46C5B2E12183EDA200419E57 /* flv-demuxer.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D12183EDA200419E57 /* flv-demuxer.c */; };
46C5B2E22183EDA200419E57 /* amf3.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D22183EDA200419E57 /* amf3.c */; };
46C5B2E32183EDA200419E57 /* mpeg4-aac.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D32183EDA200419E57 /* mpeg4-aac.c */; };
46C5B2E42183EDA200419E57 /* flv-reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D42183EDA200419E57 /* flv-reader.c */; };
46C5B2E52183EDA200419E57 /* flv-demuxer-script.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D52183EDA200419E57 /* flv-demuxer-script.c */; };
46C5B2E62183EDA200419E57 /* mpeg4-annexbtomp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D62183EDA200419E57 /* mpeg4-annexbtomp4.c */; };
46C5B2E72183EDA200419E57 /* flv-parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D72183EDA200419E57 /* flv-parser.c */; };
46C5B2E82183EDA200419E57 /* flv-muxer.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D82183EDA200419E57 /* flv-muxer.c */; };
46C5B2E92183EDA200419E57 /* mpeg4-avc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D92183EDA200419E57 /* mpeg4-avc.c */; };
46C5B2EA2183EDA200419E57 /* hevc-mp4toannexb.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2DA2183EDA200419E57 /* hevc-mp4toannexb.c */; };
46E55E5324694DE000D8BDBA /* webm-vpx.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E5224694DE000D8BDBA /* webm-vpx.c */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
4605125924D6B4D500B04B70 /* opus-head.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "opus-head.c"; sourceTree = "<group>"; };
46733798219B2315009F658F /* mpeg4-aac-asc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-aac-asc.c"; sourceTree = "<group>"; };
468B915B23B8ADAA00EA99A3 /* aom-av1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "aom-av1.c"; sourceTree = "<group>"; };
468B915F23B8AE3100EA99A3 /* flv-header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-header.c"; sourceTree = "<group>"; };
46C5B21C2183EA7400419E57 /* libflv.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libflv.a; sourceTree = BUILT_PRODUCTS_DIR; };
46C5B2CB2183EDA200419E57 /* mpeg4-mp4toannexb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-mp4toannexb.c"; sourceTree = "<group>"; };
46C5B2CC2183EDA200419E57 /* mp3-header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mp3-header.c"; sourceTree = "<group>"; };
46C5B2CD2183EDA200419E57 /* flv-writer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-writer.c"; sourceTree = "<group>"; };
46C5B2CE2183EDA200419E57 /* amf0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf0.c; sourceTree = "<group>"; };
46C5B2CF2183EDA200419E57 /* hevc-annexbtomp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "hevc-annexbtomp4.c"; sourceTree = "<group>"; };
46C5B2D02183EDA200419E57 /* mpeg4-hevc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-hevc.c"; sourceTree = "<group>"; };
46C5B2D12183EDA200419E57 /* flv-demuxer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-demuxer.c"; sourceTree = "<group>"; };
46C5B2D22183EDA200419E57 /* amf3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf3.c; sourceTree = "<group>"; };
46C5B2D32183EDA200419E57 /* mpeg4-aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-aac.c"; sourceTree = "<group>"; };
46C5B2D42183EDA200419E57 /* flv-reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-reader.c"; sourceTree = "<group>"; };
46C5B2D52183EDA200419E57 /* flv-demuxer-script.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-demuxer-script.c"; sourceTree = "<group>"; };
46C5B2D62183EDA200419E57 /* mpeg4-annexbtomp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-annexbtomp4.c"; sourceTree = "<group>"; };
46C5B2D72183EDA200419E57 /* flv-parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-parser.c"; sourceTree = "<group>"; };
46C5B2D82183EDA200419E57 /* flv-muxer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-muxer.c"; sourceTree = "<group>"; };
46C5B2D92183EDA200419E57 /* mpeg4-avc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-avc.c"; sourceTree = "<group>"; };
46C5B2DA2183EDA200419E57 /* hevc-mp4toannexb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "hevc-mp4toannexb.c"; sourceTree = "<group>"; };
46C5B2EB2183EDB400419E57 /* include */ = {isa = PBXFileReference; lastKnownFileType = folder; path = include; sourceTree = "<group>"; };
46E55E5224694DE000D8BDBA /* webm-vpx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "webm-vpx.c"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
46C5B21A2183EA7400419E57 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
46C5B2132183EA7300419E57 = {
isa = PBXGroup;
children = (
46C5B2EB2183EDB400419E57 /* include */,
46C5B2CA2183EDA200419E57 /* source */,
46C5B21D2183EA7400419E57 /* Products */,
);
sourceTree = "<group>";
};
46C5B21D2183EA7400419E57 /* Products */ = {
isa = PBXGroup;
children = (
46C5B21C2183EA7400419E57 /* libflv.a */,
);
name = Products;
sourceTree = "<group>";
};
46C5B2CA2183EDA200419E57 /* source */ = {
isa = PBXGroup;
children = (
46C5B2CE2183EDA200419E57 /* amf0.c */,
46C5B2D22183EDA200419E57 /* amf3.c */,
468B915B23B8ADAA00EA99A3 /* aom-av1.c */,
46C5B2D52183EDA200419E57 /* flv-demuxer-script.c */,
46C5B2D12183EDA200419E57 /* flv-demuxer.c */,
468B915F23B8AE3100EA99A3 /* flv-header.c */,
46C5B2D82183EDA200419E57 /* flv-muxer.c */,
46C5B2D72183EDA200419E57 /* flv-parser.c */,
46C5B2D42183EDA200419E57 /* flv-reader.c */,
46C5B2CD2183EDA200419E57 /* flv-writer.c */,
46C5B2CF2183EDA200419E57 /* hevc-annexbtomp4.c */,
46C5B2DA2183EDA200419E57 /* hevc-mp4toannexb.c */,
46C5B2CC2183EDA200419E57 /* mp3-header.c */,
46733798219B2315009F658F /* mpeg4-aac-asc.c */,
46C5B2D32183EDA200419E57 /* mpeg4-aac.c */,
46C5B2D62183EDA200419E57 /* mpeg4-annexbtomp4.c */,
46C5B2D92183EDA200419E57 /* mpeg4-avc.c */,
46C5B2D02183EDA200419E57 /* mpeg4-hevc.c */,
46C5B2CB2183EDA200419E57 /* mpeg4-mp4toannexb.c */,
4605125924D6B4D500B04B70 /* opus-head.c */,
46E55E5224694DE000D8BDBA /* webm-vpx.c */,
);
path = source;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
46C5B2182183EA7400419E57 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
46C5B21B2183EA7400419E57 /* libflv */ = {
isa = PBXNativeTarget;
buildConfigurationList = 46C5B2202183EA7400419E57 /* Build configuration list for PBXNativeTarget "libflv" */;
buildPhases = (
46C5B2182183EA7400419E57 /* Headers */,
46C5B2192183EA7400419E57 /* Sources */,
46C5B21A2183EA7400419E57 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = libflv;
productName = libflv;
productReference = 46C5B21C2183EA7400419E57 /* libflv.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
46C5B2142183EA7300419E57 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = ireader;
TargetAttributes = {
46C5B21B2183EA7400419E57 = {
CreatedOnToolsVersion = 10.0;
};
};
};
buildConfigurationList = 46C5B2172183EA7300419E57 /* Build configuration list for PBXProject "libflv" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 46C5B2132183EA7300419E57;
productRefGroup = 46C5B21D2183EA7400419E57 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
46C5B21B2183EA7400419E57 /* libflv */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
46C5B2192183EA7400419E57 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
46C5B2E12183EDA200419E57 /* flv-demuxer.c in Sources */,
46C5B2DB2183EDA200419E57 /* mpeg4-mp4toannexb.c in Sources */,
46C5B2E92183EDA200419E57 /* mpeg4-avc.c in Sources */,
468B916023B8AE3100EA99A3 /* flv-header.c in Sources */,
46C5B2E62183EDA200419E57 /* mpeg4-annexbtomp4.c in Sources */,
468B915C23B8ADAA00EA99A3 /* aom-av1.c in Sources */,
46C5B2E02183EDA200419E57 /* mpeg4-hevc.c in Sources */,
46C5B2DC2183EDA200419E57 /* mp3-header.c in Sources */,
46C5B2EA2183EDA200419E57 /* hevc-mp4toannexb.c in Sources */,
46C5B2DD2183EDA200419E57 /* flv-writer.c in Sources */,
46C5B2E82183EDA200419E57 /* flv-muxer.c in Sources */,
46C5B2E42183EDA200419E57 /* flv-reader.c in Sources */,
46C5B2E52183EDA200419E57 /* flv-demuxer-script.c in Sources */,
46C5B2E72183EDA200419E57 /* flv-parser.c in Sources */,
46C5B2DF2183EDA200419E57 /* hevc-annexbtomp4.c in Sources */,
46C5B2E22183EDA200419E57 /* amf3.c in Sources */,
46733799219B2315009F658F /* mpeg4-aac-asc.c in Sources */,
46C5B2DE2183EDA200419E57 /* amf0.c in Sources */,
46E55E5324694DE000D8BDBA /* webm-vpx.c in Sources */,
4605125A24D6B4D500B04B70 /* opus-head.c in Sources */,
46C5B2E32183EDA200419E57 /* mpeg4-aac.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
46C5B21E2183EA7400419E57 /* 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,
../../sdk/include,
);
};
name = Debug;
};
46C5B21F2183EA7400419E57 /* 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,
../../sdk/include,
);
};
name = Release;
};
46C5B2212183EA7400419E57 /* 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;
};
46C5B2222183EA7400419E57 /* 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 */
46C5B2172183EA7300419E57 /* Build configuration list for PBXProject "libflv" */ = {
isa = XCConfigurationList;
buildConfigurations = (
46C5B21E2183EA7400419E57 /* Debug */,
46C5B21F2183EA7400419E57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
46C5B2202183EA7400419E57 /* Build configuration list for PBXNativeTarget "libflv" */ = {
isa = XCConfigurationList;
buildConfigurations = (
46C5B2212183EA7400419E57 /* Debug */,
46C5B2222183EA7400419E57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 46C5B2142183EA7300419E57 /* Project object */;
}

View File

@ -0,0 +1,589 @@
#include "amf0.h"
#include <stddef.h>
#include <string.h>
#include <assert.h>
static double s_double = 1.0; // 3ff0 0000 0000 0000
static uint8_t* AMFWriteInt16(uint8_t* ptr, const uint8_t* end, uint16_t value)
{
if (ptr + 2 > end) return NULL;
ptr[0] = value >> 8;
ptr[1] = value & 0xFF;
return ptr + 2;
}
static uint8_t* AMFWriteInt32(uint8_t* ptr, const uint8_t* end, uint32_t value)
{
if (ptr + 4 > end) return NULL;
ptr[0] = (uint8_t)(value >> 24);
ptr[1] = (uint8_t)(value >> 16);
ptr[2] = (uint8_t)(value >> 8);
ptr[3] = (uint8_t)(value & 0xFF);
return ptr + 4;
}
static uint8_t* AMFWriteString16(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
{
if (ptr + 2 + length > end) return NULL;
ptr = AMFWriteInt16(ptr, end, (uint16_t)length);
memcpy(ptr, string, length);
return ptr + length;
}
static uint8_t* AMFWriteString32(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
{
if (ptr + 4 + length > end) return NULL;
ptr = AMFWriteInt32(ptr, end, (uint32_t)length);
memcpy(ptr, string, length);
return ptr + length;
}
uint8_t* AMFWriteNull(uint8_t* ptr, const uint8_t* end)
{
if (!ptr || ptr + 1 > end) return NULL;
*ptr++ = AMF_NULL;
return ptr;
}
uint8_t* AMFWriteUndefined(uint8_t* ptr, const uint8_t* end)
{
if (!ptr || ptr + 1 > end) return NULL;
*ptr++ = AMF_UNDEFINED;
return ptr;
}
uint8_t* AMFWriteObject(uint8_t* ptr, const uint8_t* end)
{
if (!ptr || ptr + 1 > end) return NULL;
*ptr++ = AMF_OBJECT;
return ptr;
}
uint8_t* AMFWriteObjectEnd(uint8_t* ptr, const uint8_t* end)
{
if (!ptr || ptr + 3 > end) return NULL;
/* end of object - 0x00 0x00 0x09 */
*ptr++ = 0;
*ptr++ = 0;
*ptr++ = AMF_OBJECT_END;
return ptr;
}
uint8_t* AMFWriteTypedObject(uint8_t* ptr, const uint8_t* end)
{
if (!ptr || ptr + 1 > end) return NULL;
*ptr++ = AMF_TYPED_OBJECT;
return ptr;
}
uint8_t* AMFWriteECMAArarry(uint8_t* ptr, const uint8_t* end)
{
if (!ptr || ptr + 1 > end) return NULL;
*ptr++ = AMF_ECMA_ARRAY;
return AMFWriteInt32(ptr, end, 0); // U32 associative-count
}
uint8_t* AMFWriteBoolean(uint8_t* ptr, const uint8_t* end, uint8_t value)
{
if (!ptr || ptr + 2 > end) return NULL;
ptr[0] = AMF_BOOLEAN;
ptr[1] = 0 == value ? 0 : 1;
return ptr + 2;
}
uint8_t* AMFWriteDouble(uint8_t* ptr, const uint8_t* end, double value)
{
if (!ptr || ptr + 9 > end) return NULL;
assert(8 == sizeof(double));
*ptr++ = AMF_NUMBER;
// Little-Endian
if (0x00 == *(char*)&s_double)
{
*ptr++ = ((uint8_t*)&value)[7];
*ptr++ = ((uint8_t*)&value)[6];
*ptr++ = ((uint8_t*)&value)[5];
*ptr++ = ((uint8_t*)&value)[4];
*ptr++ = ((uint8_t*)&value)[3];
*ptr++ = ((uint8_t*)&value)[2];
*ptr++ = ((uint8_t*)&value)[1];
*ptr++ = ((uint8_t*)&value)[0];
}
else
{
memcpy(ptr, &value, 8);
}
return ptr;
}
uint8_t* AMFWriteString(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
{
if (!ptr || ptr + 1 + (length < 65536 ? 2 : 4) + length > end || length > UINT32_MAX)
return NULL;
if (length < 65536)
{
*ptr++ = AMF_STRING;
AMFWriteString16(ptr, end, string, length);
ptr += 2;
}
else
{
*ptr++ = AMF_LONG_STRING;
AMFWriteString32(ptr, end, string, length);
ptr += 4;
}
return ptr + length;
}
uint8_t* AMFWriteDate(uint8_t* ptr, const uint8_t* end, double milliseconds, int16_t timezone)
{
if (!ptr || ptr + 11 > end)
return NULL;
AMFWriteDouble(ptr, end, milliseconds);
*ptr = AMF_DATE; // rewrite to date
return AMFWriteInt16(ptr + 8, end, timezone);
}
uint8_t* AMFWriteNamedBoolean(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, uint8_t value)
{
if (ptr + length + 2 + 2 > end)
return NULL;
ptr = AMFWriteString16(ptr, end, name, length);
return ptr ? AMFWriteBoolean(ptr, end, value) : NULL;
}
uint8_t* AMFWriteNamedDouble(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, double value)
{
if (ptr + length + 2 + 8 + 1 > end)
return NULL;
ptr = AMFWriteString16(ptr, end, name, length);
return ptr ? AMFWriteDouble(ptr, end, value) : NULL;
}
uint8_t* AMFWriteNamedString(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, const char* value, size_t length2)
{
if (ptr + length + 2 + length2 + 3 > end)
return NULL;
ptr = AMFWriteString16(ptr, end, name, length);
return ptr ? AMFWriteString(ptr, end, value, length2) : NULL;
}
static const uint8_t* AMFReadInt16(const uint8_t* ptr, const uint8_t* end, uint32_t* value)
{
if (!ptr || ptr + 2 > end)
return NULL;
if (value)
{
*value = ((uint32_t)ptr[0] << 8) | ptr[1];
}
return ptr + 2;
}
static const uint8_t* AMFReadInt32(const uint8_t* ptr, const uint8_t* end, uint32_t* value)
{
if (!ptr || ptr + 4 > end)
return NULL;
if (value)
{
*value = ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | ((uint32_t)ptr[2] << 8) | ptr[3];
}
return ptr + 4;
}
const uint8_t* AMFReadNull(const uint8_t* ptr, const uint8_t* end)
{
(void)end;
return ptr;
}
const uint8_t* AMFReadUndefined(const uint8_t* ptr, const uint8_t* end)
{
(void)end;
return ptr;
}
const uint8_t* AMFReadBoolean(const uint8_t* ptr, const uint8_t* end, uint8_t* value)
{
if (!ptr || ptr + 1 > end)
return NULL;
if (value)
{
*value = ptr[0];
}
return ptr + 1;
}
const uint8_t* AMFReadDouble(const uint8_t* ptr, const uint8_t* end, double* value)
{
uint8_t* p = (uint8_t*)value;
if (!ptr || ptr + 8 > end)
return NULL;
if (value)
{
if (0x00 == *(char*)&s_double)
{// Little-Endian
*p++ = ptr[7];
*p++ = ptr[6];
*p++ = ptr[5];
*p++ = ptr[4];
*p++ = ptr[3];
*p++ = ptr[2];
*p++ = ptr[1];
*p++ = ptr[0];
}
else
{
memcpy(value, ptr, 8);
}
}
return ptr + 8;
}
const uint8_t* AMFReadString(const uint8_t* ptr, const uint8_t* end, int isLongString, char* string, size_t length)
{
uint32_t len = 0;
if (0 == isLongString)
ptr = AMFReadInt16(ptr, end, &len);
else
ptr = AMFReadInt32(ptr, end, &len);
if (!ptr || ptr + len > end)
return NULL;
if (string && length > len)
{
memcpy(string, ptr, len);
string[len] = 0;
}
return ptr + len;
}
const uint8_t* AMFReadDate(const uint8_t* ptr, const uint8_t* end, double *milliseconds, int16_t *timezone)
{
uint32_t v;
ptr = AMFReadDouble(ptr, end, milliseconds);
if (ptr)
{
ptr = AMFReadInt16(ptr, end, &v);
*timezone = (int16_t)v;
}
return ptr;
}
static const uint8_t* amf_read_object(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n);
static const uint8_t* amf_read_ecma_array(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n);
static const uint8_t* amf_read_strict_array(const uint8_t* ptr, const uint8_t* end, struct amf_object_item_t* items, size_t n);
static const uint8_t* amf_read_item(const uint8_t* data, const uint8_t* end, enum AMFDataType type, struct amf_object_item_t* item)
{
switch (type)
{
case AMF_BOOLEAN:
return AMFReadBoolean(data, end, (uint8_t*)(item ? item->value : NULL));
case AMF_NUMBER:
return AMFReadDouble(data, end, (double*)(item ? item->value : NULL));
case AMF_STRING:
return AMFReadString(data, end, 0, (char*)(item ? item->value : NULL), item ? item->size : 0);
case AMF_LONG_STRING:
return AMFReadString(data, end, 1, (char*)(item ? item->value : NULL), item ? item->size : 0);
case AMF_DATE:
return AMFReadDate(data, end, (double*)(item ? item->value : NULL), (int16_t*)(item ? (char*)item->value + 8 : NULL));
case AMF_OBJECT:
return amf_read_object(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
case AMF_NULL:
return data;
case AMF_UNDEFINED:
return data;
case AMF_ECMA_ARRAY:
return amf_read_ecma_array(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
case AMF_STRICT_ARRAY:
return amf_read_strict_array(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
default:
assert(0);
return NULL;
}
}
static inline int amf_read_item_type_check(uint8_t type0, uint8_t itemtype)
{
// decode AMF_ECMA_ARRAY as AMF_OBJECT
return (type0 == itemtype || (AMF_OBJECT == itemtype && (AMF_ECMA_ARRAY == type0 || AMF_NULL == type0))) ? 1 : 0;
}
static const uint8_t* amf_read_strict_array(const uint8_t* ptr, const uint8_t* end, struct amf_object_item_t* items, size_t n)
{
uint8_t type;
uint32_t i, count;
if (!ptr || ptr + 4 > end)
return NULL;
ptr = AMFReadInt32(ptr, end, &count); // U32 array-count
for (i = 0; i < count && ptr && ptr < end; i++)
{
type = *ptr++;
ptr = amf_read_item(ptr, end, type, (i < n && amf_read_item_type_check(type, items[i].type)) ? &items[i] : NULL);
}
return ptr;
}
static const uint8_t* amf_read_ecma_array(const uint8_t* ptr, const uint8_t* end, struct amf_object_item_t* items, size_t n)
{
if (!ptr || ptr + 4 > end)
return NULL;
ptr += 4; // U32 associative-count
return amf_read_object(ptr, end, items, n);
}
static const uint8_t* amf_read_object(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n)
{
uint8_t type;
uint32_t len;
size_t i;
while (data && data + 2 <= end)
{
len = *data++ << 8;
len |= *data++;
if (0 == len)
break; // last item
if (data + len + 1 > end)
return NULL; // invalid
for (i = 0; i < n; i++)
{
if (strlen(items[i].name) == len && 0 == memcmp(items[i].name, data, len) && amf_read_item_type_check(data[len], items[i].type))
break;
}
data += len; // skip name string
type = *data++; // value type
data = amf_read_item(data, end, type, i < n ? &items[i] : NULL);
}
if (data && data < end && AMF_OBJECT_END == *data)
return data + 1;
return NULL; // invalid object
}
const uint8_t* amf_read_items(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t count)
{
size_t i;
uint8_t type;
for (i = 0; i < count && data && data < end; i++)
{
type = *data++;
if (!amf_read_item_type_check(type, items[i].type))
return NULL;
data = amf_read_item(data, end, type, &items[i]);
}
return data;
}
#if defined(_DEBUG) || defined(DEBUG)
struct rtmp_amf0_command_t
{
char fmsVer[64];
double capabilities;
double mode;
};
struct rtmp_amf0_data_t
{
char version[64];
};
struct rtmp_amf0_information_t
{
char code[64]; // NetStream.Play.Start
char level[8]; // warning/status/error
char description[256];
double clientid;
double objectEncoding;
struct rtmp_amf0_data_t data;
};
static void amf0_test_1(void)
{
const uint8_t amf0[] = {
0x02, 0x00, 0x07, 0x5F, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74,
0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03,
0x00, 0x06, 0x66, 0x6D, 0x73, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0E, 0x46, 0x4D, 0x53, 0x2F, 0x33, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x32, 0x30, 0x30, 0x34,
0x00, 0x0C, 0x63, 0x61, 0x70,0x61, 0x62, 0x69, 0x6C, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x6D, 0x6F, 0x64, 0x65, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x09,
0x03,
0x00, 0x05, 0x6C, 0x65, 0x76, 0x65, 0x6C, 0x02, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x00, 0x04, 0x63, 0x6F, 0x64, 0x65, 0x02, 0x00, 0x1D, 0x4E, 0x65, 0x74, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x2E, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
0x00, 0x0B, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x02, 0x00, 0x15, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x2E,
0x00, 0x04, 0x64, 0x61, 0x74, 0x61,
0x08, 0x00, 0x00, 0x00, 0x01,
0x00, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x02, 0x00, 0x0A, 0x33, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x32, 0x30, 0x30, 0x34,
0x00, 0x00, 0x09,
0x00, 0x08, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x69, 0x64, 0x00, 0x41, 0xD7, 0x9B, 0x78, 0x7C, 0xC0, 0x00, 0x00,
0x00, 0x0E, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x45, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x09,
};
char reply[8];
const uint8_t* end;
double transactionId;
struct rtmp_amf0_command_t fms;
struct rtmp_amf0_information_t result;
struct amf_object_item_t cmd[3];
struct amf_object_item_t data[1];
struct amf_object_item_t info[6];
struct amf_object_item_t items[4];
#define AMF_OBJECT_ITEM_VALUE(v, amf_type, amf_name, amf_value, amf_size) { v.type=amf_type; v.name=amf_name; v.value=amf_value; v.size=amf_size; }
AMF_OBJECT_ITEM_VALUE(cmd[0], AMF_STRING, "fmsVer", fms.fmsVer, sizeof(fms.fmsVer));
AMF_OBJECT_ITEM_VALUE(cmd[1], AMF_NUMBER, "capabilities", &fms.capabilities, sizeof(fms.capabilities));
AMF_OBJECT_ITEM_VALUE(cmd[2], AMF_NUMBER, "mode", &fms.mode, sizeof(fms.mode));
AMF_OBJECT_ITEM_VALUE(data[0], AMF_STRING, "version", result.data.version, sizeof(result.data.version));
AMF_OBJECT_ITEM_VALUE(info[0], AMF_STRING, "code", result.code, sizeof(result.code));
AMF_OBJECT_ITEM_VALUE(info[1], AMF_STRING, "level", result.level, sizeof(result.level));
AMF_OBJECT_ITEM_VALUE(info[2], AMF_STRING, "description", result.description, sizeof(result.description));
AMF_OBJECT_ITEM_VALUE(info[3], AMF_ECMA_ARRAY, "data", data, sizeof(data)/sizeof(data[0]));
AMF_OBJECT_ITEM_VALUE(info[4], AMF_NUMBER, "clientid", &result.clientid, sizeof(result.clientid));
AMF_OBJECT_ITEM_VALUE(info[5], AMF_NUMBER, "objectEncoding", &result.objectEncoding, sizeof(result.objectEncoding));
AMF_OBJECT_ITEM_VALUE(items[0], AMF_STRING, "reply", reply, sizeof(reply)); // Command object
AMF_OBJECT_ITEM_VALUE(items[1], AMF_NUMBER, "transaction", &transactionId, sizeof(transactionId)); // Command object
AMF_OBJECT_ITEM_VALUE(items[2], AMF_OBJECT, "command", cmd, sizeof(cmd)/sizeof(cmd[0])); // Command object
AMF_OBJECT_ITEM_VALUE(items[3], AMF_OBJECT, "information", info, sizeof(info) / sizeof(info[0])); // Information object
end = amf0 + sizeof(amf0);
assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
assert(0 == strcmp(fms.fmsVer, "FMS/3,5,5,2004"));
assert(fms.capabilities == 31.0);
assert(fms.mode == 1.0);
assert(0 == strcmp(result.code, "NetConnection.Connect.Success"));
assert(0 == strcmp(result.level, "status"));
assert(0 == strcmp(result.description, "Connection succeeded."));
assert(0 == strcmp(result.data.version, "3,5,5,2004"));
assert(1584259571.0 == result.clientid);
assert(3.0 == result.objectEncoding);
}
struct rtmp_amf0_connect_t
{
char app[64]; // Server application name, e.g.: testapp
char flashver[32]; // Flash Player version, FMSc/1.0
char swfUrl[256]; // URL of the source SWF file
char tcUrl[256]; // URL of the Server, rtmp://host:1935/testapp/instance1
uint8_t fpad; // boolean: True if proxy is being used.
double capabilities; // double default: 15
double audioCodecs; // double default: 4071
double videoCodecs; // double default: 252
double videoFunction; // double default: 1
double encoding;
char pageUrl[256]; // http://host/sample.html
};
static void amf0_test_2(void)
{
const uint8_t amf0[] = {
0x02, 0x00, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00,
0x03, 0x61, 0x70,
0x70, 0x02, 0x00, 0x06, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2f, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72,
0x6c, 0x02, 0x00, 0x28, 0x72, 0x74, 0x6d, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d,
0x72, 0x74, 0x6d, 0x70, 0x2d, 0x66, 0x35, 0x2d, 0x78, 0x67, 0x2e, 0x69, 0x78, 0x69, 0x67, 0x75,
0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2f, 0x00, 0x04, 0x74, 0x79,
0x70, 0x65, 0x02, 0x00, 0x0a, 0x6e, 0x6f, 0x6e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x00,
0x08, 0x66, 0x6c, 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x1f, 0x46, 0x4d, 0x4c, 0x45,
0x2f, 0x33, 0x2e, 0x30, 0x20, 0x28, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c,
0x65, 0x3b, 0x20, 0x46, 0x4d, 0x53, 0x63, 0x2f, 0x31, 0x2e, 0x30, 0x29, 0x00, 0x06, 0x73, 0x77,
0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x28, 0x72, 0x74, 0x6d, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75,
0x73, 0x68, 0x2d, 0x72, 0x74, 0x6d, 0x70, 0x2d, 0x66, 0x35, 0x2d, 0x78, 0x67, 0x2e, 0x69, 0x78,
0x69, 0x67, 0x75, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2f, 0x00,
0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c,
0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xa8, 0xee, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x76, 0x69, 0x64,
0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x06, 0x00, 0x0e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
};
char reply[8];
const uint8_t* end;
double transactionId;
struct rtmp_amf0_connect_t connect;
struct amf_object_item_t commands[11];
struct amf_object_item_t items[3];
#define AMF_OBJECT_ITEM_VALUE(v, amf_type, amf_name, amf_value, amf_size) { v.type=amf_type; v.name=amf_name; v.value=amf_value; v.size=amf_size; }
AMF_OBJECT_ITEM_VALUE(commands[0], AMF_STRING, "app", connect.app, sizeof(connect.app));
AMF_OBJECT_ITEM_VALUE(commands[1], AMF_STRING, "flashVer", connect.flashver, sizeof(connect.flashver));
AMF_OBJECT_ITEM_VALUE(commands[2], AMF_STRING, "tcUrl", connect.tcUrl, sizeof(connect.tcUrl));
AMF_OBJECT_ITEM_VALUE(commands[3], AMF_BOOLEAN, "fpad", &connect.fpad, 1);
AMF_OBJECT_ITEM_VALUE(commands[4], AMF_NUMBER, "audioCodecs", &connect.audioCodecs, 8);
AMF_OBJECT_ITEM_VALUE(commands[5], AMF_NUMBER, "videoCodecs", &connect.videoCodecs, 8);
AMF_OBJECT_ITEM_VALUE(commands[6], AMF_NUMBER, "videoFunction", &connect.videoFunction, 8);
AMF_OBJECT_ITEM_VALUE(commands[7], AMF_NUMBER, "objectEncoding", &connect.encoding, 8);
AMF_OBJECT_ITEM_VALUE(commands[8], AMF_NUMBER, "capabilities", &connect.capabilities, 8);
AMF_OBJECT_ITEM_VALUE(commands[9], AMF_STRING, "pageUrl", &connect.pageUrl, sizeof(connect.pageUrl));
AMF_OBJECT_ITEM_VALUE(commands[10], AMF_STRING, "swfUrl", &connect.swfUrl, sizeof(connect.swfUrl));
AMF_OBJECT_ITEM_VALUE(items[0], AMF_STRING, "reply", reply, sizeof(reply)); // Command object
AMF_OBJECT_ITEM_VALUE(items[1], AMF_NUMBER, "transaction", &transactionId, sizeof(transactionId)); // Command object
AMF_OBJECT_ITEM_VALUE(items[2], AMF_OBJECT, "command", commands, sizeof(commands) / sizeof(commands[0])); // Command object
end = amf0 + sizeof(amf0);
memset(&connect, 0, sizeof(connect));
assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
assert(0 == strcmp(connect.app, "media/"));
assert(0 == strcmp(connect.tcUrl, "rtmp://push-rtmp-f5-xg.ixigua.com/media/"));
assert(0 == strcmp(connect.flashver, "FMLE/3.0 (compatible; FMSc/1.0)"));
assert(0 == strcmp(connect.swfUrl, "rtmp://push-rtmp-f5-xg.ixigua.com/media/"));
assert(0 == strcmp(connect.pageUrl, "")); // pageUrl undefined
assert(connect.fpad == 0);
assert(connect.capabilities == 15);
assert(connect.audioCodecs == 3191);
assert(connect.videoCodecs == 252);
assert(connect.videoFunction == 1);
assert(connect.encoding == 0);
}
void amf0_test(void)
{
amf0_test_1();
amf0_test_2();
}
#endif

View File

@ -0,0 +1,95 @@
#include "amf3.h"
#include <string.h>
#include <assert.h>
static double s_double = 1.0; // 3ff0 0000 0000 0000
const uint8_t* AMF3ReadNull(const uint8_t* ptr, const uint8_t* end)
{
(void)end;
return ptr;
}
const uint8_t* AMF3ReadBoolean(const uint8_t* ptr, const uint8_t* end)
{
(void)end;
return ptr;
}
const uint8_t* AMF3ReadInteger(const uint8_t* ptr, const uint8_t* end, int32_t* value)
{
int i;
int32_t v = 0;
for (i = 0; i < 3 && ptr + i < end && (0x80 & ptr[i]); i++)
{
v <<= 7;
v |= (ptr[i] & 0x7F);
}
if (ptr + i >= end)
return NULL;
if (3 == i)
{
v <<= 8;
v |= ptr[i];
if (v >= (1 << 28))
v -= (1 << 29);
}
else
{
v <<= 7;
v |= ptr[i];
}
*value = v;
return ptr + i + 1;
}
const uint8_t* AMF3ReadDouble(const uint8_t* ptr, const uint8_t* end, double* value)
{
uint8_t* p = (uint8_t*)value;
if (!ptr || end - ptr < 8)
return NULL;
if (value)
{
if (0x00 == *(char*)&s_double)
{// Little-Endian
*p++ = ptr[7];
*p++ = ptr[6];
*p++ = ptr[5];
*p++ = ptr[4];
*p++ = ptr[3];
*p++ = ptr[2];
*p++ = ptr[1];
*p++ = ptr[0];
}
else
{
memcpy(&value, ptr, 8);
}
}
return ptr + 8;
}
const uint8_t* AMF3ReadString(const uint8_t* ptr, const uint8_t* end, char* string, uint32_t* length)
{
uint32_t v;
ptr = AMF3ReadInteger(ptr, end, (int32_t*)&v);
if (v & 0x01)
{
// reference
return ptr;
}
else
{
*length = v >> 1;
memcpy(string, ptr, *length);
string[*length] = 0;
return ptr + *length;
}
}

View File

@ -0,0 +1,130 @@
#include "aom-av1.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
// https://aomediacodec.github.io/av1-isobmff
// https://aomediacodec.github.io/av1-avif/
/*
aligned (8) class AV1CodecConfigurationRecord {
unsigned int (1) marker = 1;
unsigned int (7) version = 1;
unsigned int (3) seq_profile;
unsigned int (5) seq_level_idx_0;
unsigned int (1) seq_tier_0;
unsigned int (1) high_bitdepth;
unsigned int (1) twelve_bit;
unsigned int (1) monochrome;
unsigned int (1) chroma_subsampling_x;
unsigned int (1) chroma_subsampling_y;
unsigned int (2) chroma_sample_position;
unsigned int (3) reserved = 0;
unsigned int (1) initial_presentation_delay_present;
if (initial_presentation_delay_present) {
unsigned int (4) initial_presentation_delay_minus_one;
} else {
unsigned int (4) reserved = 0;
}
unsigned int (8)[] configOBUs;
}
*/
int aom_av1_codec_configuration_record_load(const uint8_t* data, size_t bytes, struct aom_av1_t* av1)
{
if (bytes < 4)
return -1;
av1->marker = data[0] >> 7;
av1->version = data[0] & 0x7F;
av1->seq_profile = data[1] >> 5;
av1->seq_level_idx_0 = data[1] & 0x1F;
av1->seq_tier_0 = data[2] >> 7;
av1->high_bitdepth = (data[2] >> 6) & 0x01;
av1->twelve_bit = (data[2] >> 5) & 0x01;
av1->monochrome = (data[2] >> 4) & 0x01;
av1->chroma_subsampling_x = (data[2] >> 3) & 0x01;
av1->chroma_subsampling_y = (data[2] >> 2) & 0x01;
av1->chroma_sample_position = data[2] & 0x03;
av1->reserved = data[3] >> 5;
av1->initial_presentation_delay_present = (data[3] >> 4) & 0x01;
av1->initial_presentation_delay_minus_one = data[3] & 0x0F;
if (bytes - 4 > sizeof(av1->data))
return -1;
av1->bytes = (uint16_t)(bytes - 4);
memcpy(av1->data, data + 4, av1->bytes);
return (int)bytes;
}
int aom_av1_codec_configuration_record_save(const struct aom_av1_t* av1, uint8_t* data, size_t bytes)
{
if (bytes < (size_t)av1->bytes + 4)
return 0; // don't have enough memory
data[0] = (uint8_t)((av1->marker << 7) | av1->version);
data[1] = (uint8_t)((av1->seq_profile << 5) | av1->seq_level_idx_0);
data[2] = (uint8_t)((av1->seq_tier_0 << 7) | (av1->high_bitdepth << 6) | (av1->twelve_bit << 5) | (av1->monochrome << 4) | (av1->chroma_subsampling_x << 3) | (av1->chroma_subsampling_y << 2) | av1->chroma_sample_position);
data[3] = (uint8_t)((av1->initial_presentation_delay_present << 4) | av1->initial_presentation_delay_minus_one);
memcpy(data + 4, av1->data, av1->bytes);
return av1->bytes + 4;
}
//static inline const uint8_t* leb128(const uint8_t* data, size_t bytes, int64_t* v)
//{
// size_t i;
// int64_t b;
//
// b = 0x80;
// for (*v = i = 0; i < 8 && i < bytes && 0 != (b & 0x80); i++)
// {
// b = data[i];
// *v |= (b & 0x7F) << (i * 7);
// }
// return data + i;
//}
int aom_av1_codecs(const struct aom_av1_t* av1, char* codecs, size_t bytes)
{
unsigned int bitdepth;
// AV1 5.5.2.Color config syntax
if (2 == av1->seq_profile && av1->high_bitdepth)
bitdepth = av1->twelve_bit ? 12 : 10;
else
bitdepth = av1->high_bitdepth ? 10 : 8;
// https://aomediacodec.github.io/av1-isobmff/#codecsparam
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter
// <sample entry 4CC>.<profile>.<level><tier>.<bitDepth>.<monochrome>.<chromaSubsampling>.<colorPrimaries>.<transferCharacteristics>.<matrixCoefficients>.<videoFullRangeFlag>
return snprintf(codecs, bytes, "av01.%u.%02u%c.%02u", (unsigned int)av1->seq_profile, (unsigned int)av1->seq_level_idx_0, av1->seq_tier_0 ? 'H' : 'M', (unsigned int)bitdepth);
}
#if defined(_DEBUG) || defined(DEBUG)
void aom_av1_test(void)
{
const unsigned char src[] = {
0x81, 0x04, 0x0c, 0x00, 0x0a, 0x0b, 0x00, 0x00, 0x00, 0x24, 0xcf, 0x7f, 0x0d, 0xbf, 0xff, 0x30, 0x08
};
unsigned char data[sizeof(src)];
struct aom_av1_t av1;
assert(sizeof(src) == aom_av1_codec_configuration_record_load(src, sizeof(src), &av1));
assert(1 == av1.version && 0 == av1.seq_profile && 4 == av1.seq_level_idx_0);
assert(0 == av1.seq_tier_0 && 0 == av1.high_bitdepth && 0 == av1.twelve_bit && 0 == av1.monochrome && 1 == av1.chroma_subsampling_x && 1 == av1.chroma_subsampling_y && 0 == av1.chroma_sample_position);
assert(0 == av1.initial_presentation_delay_present && 0 == av1.initial_presentation_delay_minus_one);
assert(13 == av1.bytes);
assert(sizeof(src) == aom_av1_codec_configuration_record_save(&av1, data, sizeof(data)));
assert(0 == memcmp(src, data, sizeof(src)));
aom_av1_codecs(&av1, (char*)data, sizeof(data));
assert(0 == memcmp("av01.0.04M.08", data, 13));
}
#endif

View File

@ -0,0 +1,81 @@
#include "flv-demuxer.h"
#include "amf0.h"
#include <errno.h>
#include <assert.h>
#include <string.h>
// http://www.cnblogs.com/musicfans/archive/2012/11/07/2819291.html
// metadata keyframes/filepositions
int flv_demuxer_script(struct flv_demuxer_t* flv, const uint8_t* data, size_t bytes)
{
const uint8_t* end;
char buffer[64] = { 0 };
double audiocodecid = 0;
double audiodatarate = 0; // bitrate / 1024
double audiodelay = 0;
double audiosamplerate = 0;
double audiosamplesize = 0;
double videocodecid = 0;
double videodatarate = 0; // bitrate / 1024
double framerate = 0;
double height = 0;
double width = 0;
double duration = 0;
double filesize = 0;
int canSeekToEnd = 0;
int stereo = 0;
struct amf_object_item_t keyframes[2];
struct amf_object_item_t prop[16];
struct amf_object_item_t items[1];
#define AMF_OBJECT_ITEM_VALUE(v, amf_type, amf_name, amf_value, amf_size) { v.type=amf_type; v.name=amf_name; v.value=amf_value; v.size=amf_size; }
AMF_OBJECT_ITEM_VALUE(keyframes[0], AMF_STRICT_ARRAY, "filepositions", NULL, 0); // ignore keyframes
AMF_OBJECT_ITEM_VALUE(keyframes[1], AMF_STRICT_ARRAY, "times", NULL, 0);
AMF_OBJECT_ITEM_VALUE(prop[0], AMF_NUMBER, "audiocodecid", &audiocodecid, sizeof(audiocodecid));
AMF_OBJECT_ITEM_VALUE(prop[1], AMF_NUMBER, "audiodatarate", &audiodatarate, sizeof(audiodatarate));
AMF_OBJECT_ITEM_VALUE(prop[2], AMF_NUMBER, "audiodelay", &audiodelay, sizeof(audiodelay));
AMF_OBJECT_ITEM_VALUE(prop[3], AMF_NUMBER, "audiosamplerate", &audiosamplerate, sizeof(audiosamplerate));
AMF_OBJECT_ITEM_VALUE(prop[4], AMF_NUMBER, "audiosamplesize", &audiosamplesize, sizeof(audiosamplesize));
AMF_OBJECT_ITEM_VALUE(prop[5], AMF_BOOLEAN, "stereo", &stereo, sizeof(stereo));
AMF_OBJECT_ITEM_VALUE(prop[6], AMF_BOOLEAN, "canSeekToEnd", &canSeekToEnd, sizeof(canSeekToEnd));
AMF_OBJECT_ITEM_VALUE(prop[7], AMF_STRING, "creationdate", buffer, sizeof(buffer));
AMF_OBJECT_ITEM_VALUE(prop[8], AMF_NUMBER, "duration", &duration, sizeof(duration));
AMF_OBJECT_ITEM_VALUE(prop[9], AMF_NUMBER, "filesize", &filesize, sizeof(filesize));
AMF_OBJECT_ITEM_VALUE(prop[10], AMF_NUMBER, "videocodecid", &videocodecid, sizeof(videocodecid));
AMF_OBJECT_ITEM_VALUE(prop[11], AMF_NUMBER, "videodatarate", &videodatarate, sizeof(videodatarate));
AMF_OBJECT_ITEM_VALUE(prop[12], AMF_NUMBER, "framerate", &framerate, sizeof(framerate));
AMF_OBJECT_ITEM_VALUE(prop[13], AMF_NUMBER, "height", &height, sizeof(height));
AMF_OBJECT_ITEM_VALUE(prop[14], AMF_NUMBER, "width", &width, sizeof(width));
AMF_OBJECT_ITEM_VALUE(prop[15], AMF_OBJECT, "keyframes", keyframes, 2); // FLV I-index
AMF_OBJECT_ITEM_VALUE(items[0], AMF_OBJECT, "onMetaData", prop, sizeof(prop) / sizeof(prop[0]));
#undef AMF_OBJECT_ITEM_VALUE
end = data + bytes;
if (AMF_STRING != data[0] || NULL == (data = AMFReadString(data + 1, end, 0, buffer, sizeof(buffer) - 1)))
{
assert(0);
return -1;
}
// filter @setDataFrame
if (0 == strcmp(buffer, "@setDataFrame"))
{
if (AMF_STRING != data[0] || NULL == (data = AMFReadString(data + 1, end, 0, buffer, sizeof(buffer) - 1)))
{
assert(0);
return -1;
}
}
// onTextData/onCaption/onCaptionInfo/onCuePoint/|RtmpSampleAccess
if (0 != strcmp(buffer, "onMetaData"))
return 0; // skip
(void)flv;
return amf_read_items(data, end, items, sizeof(items) / sizeof(items[0])) ? 0 : EINVAL;
}

View File

@ -0,0 +1,269 @@
#include "flv-demuxer.h"
#include "flv-header.h"
#include "flv-proto.h"
#include "mpeg4-aac.h"
#include "mpeg4-avc.h"
#include "mpeg4-hevc.h"
#include "opus-head.h"
#include "aom-av1.h"
#include "amf0.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
struct flv_demuxer_t
{
union
{
struct mpeg4_aac_t aac;
struct opus_head_t opus;
} a;
union
{
struct aom_av1_t av1;
struct mpeg4_avc_t avc;
struct mpeg4_hevc_t hevc;
} v;
flv_demuxer_handler handler;
void* param;
uint8_t* ptr;
int capacity;
};
struct flv_demuxer_t* flv_demuxer_create(flv_demuxer_handler handler, void* param)
{
struct flv_demuxer_t* flv;
flv = (struct flv_demuxer_t*)malloc(sizeof(struct flv_demuxer_t));
if (NULL == flv)
return NULL;
memset(flv, 0, sizeof(struct flv_demuxer_t));
flv->handler = handler;
flv->param = param;
return flv;
}
void flv_demuxer_destroy(struct flv_demuxer_t* flv)
{
if (flv->ptr)
{
assert(flv->capacity > 0);
free(flv->ptr);
}
free(flv);
}
static int flv_demuxer_check_and_alloc(struct flv_demuxer_t* flv, int bytes)
{
if (bytes > flv->capacity)
{
void* p = realloc(flv->ptr, bytes);
if (NULL == p)
return -1;
flv->ptr = (uint8_t*)p;
flv->capacity = bytes;
}
return 0;
}
static int flv_demuxer_audio(struct flv_demuxer_t* flv, const uint8_t* data, int bytes, uint32_t timestamp)
{
int r, n;
struct flv_audio_tag_header_t audio;
n = flv_audio_tag_header_read(&audio, data, bytes);
if (n < 0)
return n;
if (FLV_AUDIO_AAC == audio.codecid)
{
// Adobe Flash Video File Format Specification Version 10.1 >> E.4.2.1 AUDIODATA (p77)
// If the SoundFormat indicates AAC, the SoundType should be 1 (stereo) and the SoundRate should be 3 (44 kHz).
// However, this does not mean that AAC audio in FLV is always stereo, 44 kHz data.Instead, the Flash Player ignores
// these values and extracts the channel and sample rate data is encoded in the AAC bit stream.
//assert(3 == audio.bitrate && 1 == audio.channel);
if (FLV_SEQUENCE_HEADER == audio.avpacket)
{
mpeg4_aac_audio_specific_config_load(data + n, bytes - n, &flv->a.aac);
return flv->handler(flv->param, FLV_AUDIO_ASC, data + n, bytes - n, timestamp, timestamp, 0);
}
else
{
if (0 != flv_demuxer_check_and_alloc(flv, bytes + 7 + 1 + flv->a.aac.npce))
return -ENOMEM;
// AAC ES stream with ADTS header
assert(bytes <= 0x1FFF);
assert(bytes > 2 && 0xFFF0 != (((data[2] << 8) | data[3]) & 0xFFF0)); // don't have ADTS
r = mpeg4_aac_adts_save(&flv->a.aac, (uint16_t)bytes - n, flv->ptr, 7 + 1 + flv->a.aac.npce); // 13-bits
if (r < 7) return -EINVAL; // invalid pce
flv->a.aac.npce = 0; // pce write only once
memmove(flv->ptr + r, data + n, bytes - n);
return flv->handler(flv->param, FLV_AUDIO_AAC, flv->ptr, bytes - n + r, timestamp, timestamp, 0);
}
}
else if (FLV_AUDIO_OPUS == audio.codecid)
{
if (FLV_SEQUENCE_HEADER == audio.avpacket)
{
opus_head_load(data + n, bytes - n, &flv->a.opus);
return flv->handler(flv->param, FLV_AUDIO_OPUS_HEAD, data + n, bytes - n, timestamp, timestamp, 0);
}
else
{
return flv->handler(flv->param, audio.codecid, data + n, bytes - n, timestamp, timestamp, 0);
}
}
else if (FLV_AUDIO_MP3 == audio.codecid || FLV_AUDIO_MP3_8K == audio.codecid)
{
return flv->handler(flv->param, audio.codecid, data + n, bytes - n, timestamp, timestamp, 0);
}
else
{
// Audio frame data
return flv->handler(flv->param, audio.codecid, data + n, bytes - n, timestamp, timestamp, 0);
}
}
static int flv_demuxer_video(struct flv_demuxer_t* flv, const uint8_t* data, int bytes, uint32_t timestamp)
{
int n;
struct flv_video_tag_header_t video;
n = flv_video_tag_header_read(&video, data, bytes);
if (n < 0)
return n;
if (FLV_VIDEO_H264 == video.codecid)
{
if (FLV_SEQUENCE_HEADER == video.avpacket)
{
// AVCDecoderConfigurationRecord
assert(bytes > n + 7);
mpeg4_avc_decoder_configuration_record_load(data + n, bytes - n, &flv->v.avc);
return flv->handler(flv->param, FLV_VIDEO_AVCC, data + n, bytes - n, timestamp + video.cts, timestamp, 0);
}
else if(FLV_AVPACKET == video.avpacket)
{
assert(flv->v.avc.nalu > 0); // parse AVCDecoderConfigurationRecord failed
if (flv->v.avc.nalu > 0 && bytes > n) // 5 == bytes flv eof
{
// H.264
if (0 != flv_demuxer_check_and_alloc(flv, bytes + 4 * 1024))
return -ENOMEM;
assert(flv->v.avc.nalu <= 4);
n = h264_mp4toannexb(&flv->v.avc, data + n, bytes - n, flv->ptr, flv->capacity);
if (n <= 0 || n > flv->capacity)
{
assert(0);
return -ENOMEM;
}
return flv->handler(flv->param, FLV_VIDEO_H264, flv->ptr, n, timestamp + video.cts, timestamp, (FLV_VIDEO_KEY_FRAME == video.keyframe) ? 1 : 0);
}
return -EINVAL;
}
else if (FLV_END_OF_SEQUENCE == video.avpacket)
{
return 0; // AVC end of sequence (lower level NALU sequence ender is not required or supported)
}
else
{
assert(0);
return -EINVAL;
}
}
else if (FLV_VIDEO_H265 == video.codecid)
{
if (FLV_SEQUENCE_HEADER == video.avpacket)
{
// HEVCDecoderConfigurationRecord
assert(bytes > n + 7);
mpeg4_hevc_decoder_configuration_record_load(data + n, bytes - n, &flv->v.hevc);
return flv->handler(flv->param, FLV_VIDEO_HVCC, data + n, bytes - n, timestamp + video.cts, timestamp, 0);
}
else if (FLV_AVPACKET == video.avpacket)
{
assert(flv->v.hevc.numOfArrays > 0); // parse HEVCDecoderConfigurationRecord failed
if (flv->v.hevc.numOfArrays > 0 && bytes > n) // 5 == bytes flv eof
{
// H.265
if (0 != flv_demuxer_check_and_alloc(flv, bytes + 4 * 1024))
return -ENOMEM;
n = h265_mp4toannexb(&flv->v.hevc, data + n, bytes - n, flv->ptr, flv->capacity);
if (n <= 0 || n > flv->capacity)
{
assert(0);
return -ENOMEM;
}
return flv->handler(flv->param, FLV_VIDEO_H265, flv->ptr, n, timestamp + video.cts, timestamp, (FLV_VIDEO_KEY_FRAME == video.keyframe) ? 1 : 0);
}
return -EINVAL;
}
else if (FLV_END_OF_SEQUENCE == video.avpacket)
{
return 0; // AVC end of sequence (lower level NALU sequence ender is not required or supported)
}
else
{
assert(0);
return -EINVAL;
}
}
else if (FLV_VIDEO_AV1 == video.codecid)
{
if (FLV_SEQUENCE_HEADER == video.avpacket)
{
// HEVCDecoderConfigurationRecord
assert(bytes > n + 5);
aom_av1_codec_configuration_record_load(data + n, bytes - n, &flv->v.av1);
return flv->handler(flv->param, FLV_VIDEO_AV1C, data + n, bytes - n, timestamp + video.cts, timestamp, 0);
}
else if (FLV_AVPACKET == video.avpacket)
{
return flv->handler(flv->param, FLV_VIDEO_AV1, data + n, bytes - n, timestamp + video.cts, timestamp, (FLV_VIDEO_KEY_FRAME == video.keyframe) ? 1 : 0);
}
else if (FLV_END_OF_SEQUENCE == video.avpacket)
{
return 0; // AV1 end of sequence (lower level NALU sequence ender is not required or supported)
}
else
{
assert(0);
return -EINVAL;
}
}
else
{
// Video frame data
return flv->handler(flv->param, video.codecid, data + n, bytes - n, timestamp + video.cts, timestamp, (FLV_VIDEO_KEY_FRAME==video.keyframe) ? 1 : 0);
}
}
int flv_demuxer_script(struct flv_demuxer_t* flv, const uint8_t* data, size_t bytes);
int flv_demuxer_input(struct flv_demuxer_t* flv, int type, const void* data, size_t bytes, uint32_t timestamp)
{
if (bytes < 1)
return 0;
switch (type)
{
case FLV_TYPE_AUDIO:
return flv_demuxer_audio(flv, data, (int)bytes, timestamp);
case FLV_TYPE_VIDEO:
return flv_demuxer_video(flv, data, (int)bytes, timestamp);
case FLV_TYPE_SCRIPT:
//return flv_demuxer_script(flv, data, bytes);
return 0;
default:
assert(0);
return -1;
}
}

View File

@ -0,0 +1,236 @@
#include "flv-header.h"
#include "flv-proto.h"
#include <assert.h>
#include <errno.h>
#define N_TAG_SIZE 4 // previous tag size
#define FLV_HEADER_SIZE 9 // DataOffset included
#define FLV_TAG_HEADER_SIZE 11 // StreamID included
static inline uint32_t be_read_uint32(const uint8_t* ptr)
{
return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}
static inline void be_write_uint32(uint8_t* ptr, uint32_t val)
{
ptr[0] = (uint8_t)((val >> 24) & 0xFF);
ptr[1] = (uint8_t)((val >> 16) & 0xFF);
ptr[2] = (uint8_t)((val >> 8) & 0xFF);
ptr[3] = (uint8_t)(val & 0xFF);
}
int flv_header_read(struct flv_header_t* flv, const uint8_t* buf, int len)
{
if (len < FLV_HEADER_SIZE || 'F' != buf[0] || 'L' != buf[1] || 'V' != buf[2])
{
assert(0);
return -1;
}
flv->FLV[0] = buf[0];
flv->FLV[1] = buf[1];
flv->FLV[2] = buf[2];
flv->version = buf[3];
assert(0x00 == (buf[4] & 0xF8) && 0x00 == (buf[4] & 0x20));
flv->audio = (buf[4] >> 2) & 0x01;
flv->video = buf[4] & 0x01;
flv->offset = be_read_uint32(buf + 5);
return FLV_HEADER_SIZE;
}
int flv_tag_header_read(struct flv_tag_header_t* tag, const uint8_t* buf, int len)
{
if (len < FLV_TAG_HEADER_SIZE)
{
assert(0);
return -1;
}
// TagType
tag->type = buf[0] & 0x1F;
tag->filter = (buf[0] >> 5) & 0x01;
assert(FLV_TYPE_VIDEO == tag->type || FLV_TYPE_AUDIO == tag->type || FLV_TYPE_SCRIPT == tag->type);
// DataSize
tag->size = (buf[1] << 16) | (buf[2] << 8) | buf[3];
// TimestampExtended | Timestamp
tag->timestamp = (buf[4] << 16) | (buf[5] << 8) | buf[6] | (buf[7] << 24);
// StreamID Always 0
tag->streamId = (buf[8] << 16) | (buf[9] << 8) | buf[10];
//assert(0 == tag->streamId);
return FLV_TAG_HEADER_SIZE;
}
int flv_audio_tag_header_read(struct flv_audio_tag_header_t* audio, const uint8_t* buf, int len)
{
assert(len > 0);
audio->codecid = (buf[0] & 0xF0) /*>> 4*/;
audio->rate = (buf[0] & 0x0C) >> 2;
audio->bits = (buf[0] & 0x02) >> 1;
audio->channels = buf[0] & 0x01;
if (FLV_AUDIO_AAC == audio->codecid || FLV_AUDIO_OPUS == audio->codecid)
{
if (len < 2)
{
assert(0);
return -1;
}
audio->avpacket = buf[1];
assert(FLV_SEQUENCE_HEADER == audio->avpacket || FLV_AVPACKET == audio->avpacket);
return 2;
}
else
{
return 1;
}
}
int flv_video_tag_header_read(struct flv_video_tag_header_t* video, const uint8_t* buf, int len)
{
assert(len > 0);
video->keyframe = (buf[0] & 0xF0) >> 4;
video->codecid = (buf[0] & 0x0F);
if (FLV_VIDEO_H264 == video->codecid || FLV_VIDEO_H265 == video->codecid || FLV_VIDEO_AV1 == video->codecid)
{
if (len < 5)
return -1;
video->avpacket = buf[1]; // AVCPacketType
video->cts = (buf[2] << 16) | (buf[3] << 8) | buf[4];
//if (video->cts >= (1 << 23)) video->cts -= (1 << 24);
video->cts = (video->cts + 0xFF800000) ^ 0xFF800000; // signed 24-integer
assert(FLV_SEQUENCE_HEADER == video->avpacket || FLV_AVPACKET == video->avpacket || FLV_END_OF_SEQUENCE == video->avpacket);
return 5;
}
else
{
return 1;
}
}
int flv_data_tag_header_read(const uint8_t* buf, int len)
{
(void)buf;
return len;
}
int flv_header_write(int audio, int video, uint8_t* buf, int len)
{
if (len < FLV_HEADER_SIZE)
{
assert(0);
return -1;
}
buf[0] = 'F'; // FLV signature
buf[1] = 'L';
buf[2] = 'V';
buf[3] = 0x01; // File version
buf[4] = ((audio ? 1 : 0) << 2) | (video ? 1 : 0); // Type flags (audio & video)
be_write_uint32(buf + 5, FLV_HEADER_SIZE); // Data offset
return FLV_HEADER_SIZE;
}
int flv_tag_header_write(const struct flv_tag_header_t* tag, uint8_t* buf, int len)
{
if (len < FLV_TAG_HEADER_SIZE)
{
assert(0);
return -1;
}
// TagType
assert(FLV_TYPE_VIDEO == tag->type || FLV_TYPE_AUDIO == tag->type || FLV_TYPE_SCRIPT == tag->type);
buf[0] = (tag->type & 0x1F) | ((tag->filter & 0x01) << 5);
// DataSize
buf[1] = (tag->size >> 16) & 0xFF;
buf[2] = (tag->size >> 8) & 0xFF;
buf[3] = tag->size & 0xFF;
// Timestamp
buf[4] = (tag->timestamp >> 16) & 0xFF;
buf[5] = (tag->timestamp >> 8) & 0xFF;
buf[6] = (tag->timestamp >> 0) & 0xFF;
buf[7] = (tag->timestamp >> 24) & 0xFF; // Timestamp Extended
// StreamID(Always 0)
buf[8] = (tag->streamId >> 16) & 0xFF;
buf[9] = (tag->streamId >> 8) & 0xFF;
buf[10] = (tag->streamId) & 0xFF;
return FLV_TAG_HEADER_SIZE;
}
int flv_audio_tag_header_write(const struct flv_audio_tag_header_t* audio, uint8_t* buf, int len)
{
if (len < 1 + ((FLV_AUDIO_AAC == audio->codecid || FLV_AUDIO_OPUS == audio->codecid)? 1 : 0))
return -1;
if (FLV_AUDIO_AAC == audio->codecid || FLV_AUDIO_OPUS == audio->codecid)
{
assert(FLV_SEQUENCE_HEADER == audio->avpacket || FLV_AVPACKET == audio->avpacket);
buf[0] = (audio->codecid /* <<4 */) /* SoundFormat */ | (3 << 2) /* 44k-SoundRate */ | (1 << 1) /* 16-bit samples */ | 1 /* Stereo sound */;
buf[1] = audio->avpacket; // AACPacketType
return 2;
}
else
{
buf[0] = (audio->codecid /* <<4 */) | ((audio->rate & 0x03) << 2) | ((audio->bits & 0x01) << 1) | (audio->channels & 0x01);
return 1;
}
}
int flv_video_tag_header_write(const struct flv_video_tag_header_t* video, uint8_t* buf, int len)
{
if (len < 1)
return -1;
buf[0] = (video->keyframe << 4) /*FrameType*/ | (video->codecid & 0x0F) /*CodecID*/;
if (FLV_VIDEO_H264 == video->codecid || FLV_VIDEO_H265 == video->codecid || FLV_VIDEO_AV1 == video->codecid)
{
assert(FLV_SEQUENCE_HEADER == video->avpacket || FLV_AVPACKET == video->avpacket || FLV_END_OF_SEQUENCE == video->avpacket);
if (len < 5)
return -1;
buf[1] = video->avpacket; // AVCPacketType
buf[2] = (video->cts >> 16) & 0xFF;
buf[3] = (video->cts >> 8) & 0xFF;
buf[4] = video->cts & 0xFF;
return 5;
}
return 1;
}
int flv_data_tag_header_write(uint8_t* buf, int len)
{
(void)buf;
(void)len;
return 0;
}
int flv_tag_size_read(const uint8_t* buf, int len, uint32_t* size)
{
if(len < 4)
return -1;
*size = be_read_uint32(buf);
return 4;
}
int flv_tag_size_write(uint8_t* buf, int len, uint32_t size)
{
if(len < 4)
return -1;
be_write_uint32(buf, size);
return 4;
}

View File

@ -0,0 +1,370 @@
#include "flv-muxer.h"
#include "flv-proto.h"
#include "flv-header.h"
#include "amf0.h"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "mpeg4-aac.h"
#include "mpeg4-avc.h"
#include "mpeg4-hevc.h"
#include "mp3-header.h"
#include "opus-head.h"
#define FLV_MUXER "libflv"
struct flv_muxer_t
{
flv_muxer_handler handler;
void* param;
uint8_t audio_sequence_header;
uint8_t video_sequence_header;
union
{
struct mpeg4_aac_t aac;
struct opus_head_t opus;
} a;
union
{
struct mpeg4_avc_t avc;
struct mpeg4_hevc_t hevc;
} v;
int vcl; // 0-non vcl, 1-idr, 2-p/b
int update; // avc/hevc sequence header update
uint8_t* ptr;
int bytes;
int capacity;
};
struct flv_muxer_t* flv_muxer_create(flv_muxer_handler handler, void* param)
{
struct flv_muxer_t* flv;
flv = (struct flv_muxer_t*)calloc(1, sizeof(struct flv_muxer_t));
if (NULL == flv)
return NULL;
flv_muxer_reset(flv);
flv->handler = handler;
flv->param = param;
return flv;
}
void flv_muxer_destroy(struct flv_muxer_t* flv)
{
if (flv->ptr)
{
assert(flv->capacity > 0);
free(flv->ptr);
flv->ptr = NULL;
}
free(flv);
}
int flv_muxer_reset(struct flv_muxer_t* flv)
{
memset(&flv->v, 0, sizeof(flv->v));
flv->audio_sequence_header = 0;
flv->video_sequence_header = 0;
return 0;
}
static int flv_muxer_alloc(struct flv_muxer_t* flv, int bytes)
{
void* p;
p = realloc(flv->ptr, bytes);
if (!p)
return ENOMEM;
flv->ptr = (uint8_t*)p;
flv->capacity = bytes;
return 0;
}
int flv_muxer_mp3(struct flv_muxer_t* flv, const void* data, size_t sz, uint32_t pts, uint32_t dts)
{
int bytes;
struct mp3_header_t mp3;
struct flv_audio_tag_header_t audio;
(void)pts;
bytes = (int)sz;
if (0 == mp3_header_load(&mp3, data, bytes))
{
return EINVAL;
}
else
{
audio.channels = 3 == mp3.mode ? 0 : 1;
switch (mp3_get_frequency(&mp3))
{
case 5500: audio.rate = 0; break;
case 11000: audio.rate = 1; break;
case 22000: audio.rate = 2; break;
case 44100: audio.rate = 3; break;
default: audio.rate = 3;
}
}
if (flv->capacity < bytes + 1)
{
if (0 != flv_muxer_alloc(flv, bytes + 4))
return ENOMEM;
}
audio.bits = 1; // 16-bit samples
audio.codecid = FLV_AUDIO_MP3;
audio.avpacket = FLV_AVPACKET;
flv_audio_tag_header_write(&audio, flv->ptr, 1);
memcpy(flv->ptr + 1, data, bytes); // MP3
return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes + 1, dts);
}
int flv_muxer_aac(struct flv_muxer_t* flv, const void* data, size_t sz, uint32_t pts, uint32_t dts)
{
int r, n, m, bytes;
struct flv_audio_tag_header_t audio;
(void)pts;
bytes = (int)sz;
if (flv->capacity < bytes + 2/*AudioTagHeader*/ + 2/*AudioSpecificConfig*/)
{
if (0 != flv_muxer_alloc(flv, bytes + 4))
return ENOMEM;
}
/* ADTS */
n = mpeg4_aac_adts_load(data, bytes, &flv->a.aac);
if (n <= 0)
return -1; // invalid data
audio.codecid = FLV_AUDIO_AAC;
audio.rate = 3; // 44k-SoundRate
audio.bits = 1; // 16-bit samples
audio.channels = 1; // Stereo sound
if (0 == flv->audio_sequence_header)
{
flv->audio_sequence_header = 1; // once only
audio.avpacket = FLV_SEQUENCE_HEADER;
// AudioSpecificConfig(AAC sequence header)
flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
m = mpeg4_aac_audio_specific_config_save(&flv->a.aac, flv->ptr + 2, flv->capacity - 2);
assert(m + 2 <= (int)flv->capacity);
r = flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, m + 2, dts);
if (0 != r) return r;
}
audio.avpacket = FLV_AVPACKET;
flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
memcpy(flv->ptr + 2, (uint8_t*)data + n, bytes - n); // AAC exclude ADTS
assert(bytes - n + 2 <= (int)flv->capacity);
return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes - n + 2, dts);
}
int flv_muxer_opus(flv_muxer_t* flv, const void* data, size_t sz, uint32_t pts, uint32_t dts)
{
int r, m, bytes;
struct flv_audio_tag_header_t audio;
(void)pts;
bytes = (int)sz;
if (flv->capacity < bytes + 2/*AudioTagHeader*/ + 2/*AudioSpecificConfig*/)
{
if (0 != flv_muxer_alloc(flv, bytes + 4))
return ENOMEM;
}
audio.codecid = FLV_AUDIO_OPUS;
audio.rate = 3; // 44k-SoundRate
audio.bits = 1; // 16-bit samples
audio.channels = 1; // Stereo sound
if (0 == flv->audio_sequence_header)
{
if (opus_head_load(data, bytes, &flv->a.opus) < 0)
return -1;
flv->audio_sequence_header = 1; // once only
audio.avpacket = FLV_SEQUENCE_HEADER;
// Opus Head
m = flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
assert(m + bytes <= (int)flv->capacity);
memcpy(flv->ptr + m, data, bytes);
r = flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, m + bytes, dts);
return r;
}
audio.avpacket = FLV_AVPACKET;
m = flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
memcpy(flv->ptr + m, (uint8_t*)data, bytes);
assert(bytes - m <= (int)flv->capacity);
return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes + m, dts);
}
static int flv_muxer_h264(struct flv_muxer_t* flv, uint32_t pts, uint32_t dts)
{
int r;
int m;
struct flv_video_tag_header_t video;
video.codecid = FLV_VIDEO_H264;
if ( /*0 == flv->video_sequence_header &&*/ flv->update && flv->v.avc.nb_sps > 0 && flv->v.avc.nb_pps > 0)
{
video.cts = 0;
video.keyframe = 1; // keyframe
video.avpacket = FLV_SEQUENCE_HEADER;
flv_video_tag_header_write(&video, flv->ptr + flv->bytes, flv->capacity - flv->bytes);
m = mpeg4_avc_decoder_configuration_record_save(&flv->v.avc, flv->ptr + flv->bytes + 5, flv->capacity - flv->bytes - 5);
if (m <= 0)
return -1; // invalid data
flv->video_sequence_header = 1; // once only
assert(flv->bytes + m + 5 <= (int)flv->capacity);
r = flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr + flv->bytes, m + 5, dts);
if (0 != r) return r;
}
// has video frame
if (flv->vcl && flv->video_sequence_header)
{
video.cts = pts - dts;
video.keyframe = 1 == flv->vcl ? 1 : 2;
video.avpacket = FLV_AVPACKET;
flv_video_tag_header_write(&video, flv->ptr, flv->capacity);
assert(flv->bytes <= (int)flv->capacity);
return flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr, flv->bytes, dts);
}
return 0;
}
int flv_muxer_avc(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
{
if (flv->capacity < (int)bytes + 2048/*AVCDecoderConfigurationRecord*/)
{
if (0 != flv_muxer_alloc(flv, (int)bytes + 2048))
return ENOMEM;
}
flv->bytes = 5;
flv->bytes += h264_annexbtomp4(&flv->v.avc, data, (int)bytes, flv->ptr + flv->bytes, flv->capacity - flv->bytes, &flv->vcl, &flv->update);
if (flv->bytes <= 5)
return ENOMEM;
return flv_muxer_h264(flv, pts, dts);
}
static int flv_muxer_h265(struct flv_muxer_t* flv, uint32_t pts, uint32_t dts)
{
int r;
int m;
struct flv_video_tag_header_t video;
video.codecid = FLV_VIDEO_H265;
if ( /*0 == flv->avc_sequence_header &&*/ flv->update && flv->v.hevc.numOfArrays >= 3) // vps + sps + pps
{
video.cts = 0;
video.keyframe = 1; // keyframe
video.avpacket = FLV_SEQUENCE_HEADER;
flv_video_tag_header_write(&video, flv->ptr + flv->bytes, flv->capacity - flv->bytes);
m = mpeg4_hevc_decoder_configuration_record_save(&flv->v.hevc, flv->ptr + flv->bytes + 5, flv->capacity - flv->bytes - 5);
if (m <= 0)
return -1; // invalid data
flv->video_sequence_header = 1; // once only
assert(flv->bytes + m + 5 <= (int)flv->capacity);
r = flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr + flv->bytes, m + 5, dts);
if (0 != r) return r;
}
// has video frame
if (flv->vcl && flv->video_sequence_header)
{
video.cts = pts - dts;
video.keyframe = 1 == flv->vcl ? 1 : 2;
video.avpacket = FLV_AVPACKET;
flv_video_tag_header_write(&video, flv->ptr, flv->capacity);
assert(flv->bytes <= (int)flv->capacity);
return flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr, flv->bytes, dts);
}
return 0;
}
int flv_muxer_hevc(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
{
if (flv->capacity < (int)bytes + 2048/*HEVCDecoderConfigurationRecord*/)
{
if (0 != flv_muxer_alloc(flv, (int)bytes + 2048))
return ENOMEM;
}
flv->bytes = 5;
flv->bytes += h265_annexbtomp4(&flv->v.hevc, data, (int)bytes, flv->ptr + flv->bytes, flv->capacity - flv->bytes, &flv->vcl, &flv->update);
if (flv->bytes <= 5)
return ENOMEM;
return flv_muxer_h265(flv, pts, dts);
}
int flv_muxer_metadata(flv_muxer_t* flv, const struct flv_metadata_t* metadata)
{
uint8_t* ptr, *end;
uint32_t count;
if (!metadata) return -1;
count = (metadata->audiocodecid ? 5 : 0) + (metadata->videocodecid ? 5 : 0) + 1;
if (flv->capacity < 1024)
{
if (0 != flv_muxer_alloc(flv, 1024))
return ENOMEM;
}
ptr = flv->ptr;
end = flv->ptr + flv->capacity;
count = (metadata->audiocodecid ? 5 : 0) + (metadata->videocodecid ? 5 : 0) + 1;
// ScriptTagBody
// name
ptr = AMFWriteString(ptr, end, "onMetaData", 10);
// value: SCRIPTDATAECMAARRAY
ptr[0] = AMF_ECMA_ARRAY;
ptr[1] = (uint8_t)((count >> 24) & 0xFF);;
ptr[2] = (uint8_t)((count >> 16) & 0xFF);;
ptr[3] = (uint8_t)((count >> 8) & 0xFF);
ptr[4] = (uint8_t)(count & 0xFF);
ptr += 5;
if (metadata->audiocodecid)
{
ptr = AMFWriteNamedDouble(ptr, end, "audiocodecid", 12, metadata->audiocodecid);
ptr = AMFWriteNamedDouble(ptr, end, "audiodatarate", 13, metadata->audiodatarate);
ptr = AMFWriteNamedDouble(ptr, end, "audiosamplerate", 15, metadata->audiosamplerate);
ptr = AMFWriteNamedDouble(ptr, end, "audiosamplesize", 15, metadata->audiosamplesize);
ptr = AMFWriteNamedBoolean(ptr, end, "stereo", 6, (uint8_t)metadata->stereo);
}
if (metadata->videocodecid)
{
ptr = AMFWriteNamedDouble(ptr, end, "videocodecid", 12, metadata->videocodecid);
ptr = AMFWriteNamedDouble(ptr, end, "videodatarate", 13, metadata->videodatarate);
ptr = AMFWriteNamedDouble(ptr, end, "framerate", 9, metadata->framerate);
ptr = AMFWriteNamedDouble(ptr, end, "height", 6, metadata->height);
ptr = AMFWriteNamedDouble(ptr, end, "width", 5, metadata->width);
}
ptr = AMFWriteNamedString(ptr, end, "encoder", 7, FLV_MUXER, strlen(FLV_MUXER));
ptr = AMFWriteObjectEnd(ptr, end);
return flv->handler(flv->param, FLV_TYPE_SCRIPT, flv->ptr, ptr - flv->ptr, 0);
}

View File

@ -0,0 +1,89 @@
#include "flv-parser.h"
#include "flv-header.h"
#include "flv-proto.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#define FLV_VIDEO_CODEC_NAME(codecid) (FLV_VIDEO_H264==(codecid) ? FLV_VIDEO_AVCC : (FLV_VIDEO_H265==(codecid) ? FLV_VIDEO_HVCC : FLV_VIDEO_AV1C))
static int flv_parser_audio(const uint8_t* data, int bytes, uint32_t timestamp, flv_parser_handler handler, void* param)
{
int n;
struct flv_audio_tag_header_t audio;
n = flv_audio_tag_header_read(&audio, data, bytes);
if (n < 0)
return n;
if (FLV_SEQUENCE_HEADER == audio.avpacket)
return handler(param, FLV_AUDIO_AAC == audio.codecid ? FLV_AUDIO_ASC : FLV_AUDIO_OPUS_HEAD, data + n, bytes - n, timestamp, timestamp, 0);
else
return handler(param, audio.codecid, data + n, bytes - n, timestamp, timestamp, 0);
}
static int flv_parser_video(const uint8_t* data, int bytes, uint32_t timestamp, flv_parser_handler handler, void* param)
{
int n;
struct flv_video_tag_header_t video;
n = flv_video_tag_header_read(&video, data, bytes);
if (n < 0)
return n;
if (FLV_VIDEO_H264 == video.codecid || FLV_VIDEO_H265 == video.codecid || FLV_VIDEO_AV1 == video.codecid)
{
if (FLV_SEQUENCE_HEADER == video.avpacket)
{
return handler(param, FLV_VIDEO_CODEC_NAME(video.codecid), data + n, bytes - n, timestamp, timestamp, 0);
}
else if (FLV_AVPACKET == video.avpacket)
{
return handler(param, video.codecid, data + n, bytes - n, timestamp + video.cts, timestamp, (FLV_VIDEO_KEY_FRAME == video.keyframe) ? 1 : 0);
}
else if (FLV_END_OF_SEQUENCE == video.avpacket)
{
return 0; // AVC end of sequence (lower level NALU sequence ender is not required or supported)
}
else
{
assert(0);
return -EINVAL;
}
}
else
{
// Video frame data
return handler(param, video.codecid, data + n, bytes - n, timestamp, timestamp, (FLV_VIDEO_KEY_FRAME == video.keyframe) ? 1 : 0);
}
}
// http://www.cnblogs.com/musicfans/archive/2012/11/07/2819291.html
// metadata keyframes/filepositions
static int flv_parser_script(const uint8_t* data, int bytes, uint32_t timestamp, flv_parser_handler handler, void* param)
{
int n;
n = flv_data_tag_header_read(data, bytes);
if (n < 0)
return n;
return handler(param, 0, data + n, bytes - n, timestamp, timestamp, 0);
}
int flv_parser_input(int type, const void* data, size_t bytes, uint32_t timestamp, flv_parser_handler handler, void* param)
{
if (bytes < 1) return -EINVAL;
switch (type)
{
case FLV_TYPE_AUDIO:
return flv_parser_audio(data, (int)bytes, timestamp, handler, param);
case FLV_TYPE_VIDEO:
return flv_parser_video(data, (int)bytes, timestamp, handler, param);
case FLV_TYPE_SCRIPT:
return flv_parser_script(data, (int)bytes, timestamp, handler, param);
default:
assert(0);
return -1;
}
}

View File

@ -0,0 +1,135 @@
#include "flv-reader.h"
#include "flv-header.h"
#include "flv-proto.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#define FLV_HEADER_SIZE 9 // DataOffset included
#define FLV_TAG_HEADER_SIZE 11 // StreamID included
struct flv_reader_t
{
FILE* fp;
int (*read)(void* param, void* buf, int len);
void* param;
};
static int flv_read_header(struct flv_reader_t* flv)
{
uint32_t sz;
uint8_t data[FLV_HEADER_SIZE];
struct flv_header_t h;
int n;
if (FLV_HEADER_SIZE != flv->read(flv->param, data, FLV_HEADER_SIZE))
return -1;
if(FLV_HEADER_SIZE != flv_header_read(&h, data, FLV_HEADER_SIZE))
return -1;
assert(h.offset >= FLV_HEADER_SIZE && h.offset < FLV_HEADER_SIZE + 4096);
for(n = (int)(h.offset - FLV_HEADER_SIZE); n > 0 && n < 4096; n -= sizeof(data))
flv->read(flv->param, data, n >= sizeof(data) ? sizeof(data) : n); // skip
// PreviousTagSize0
if (4 != flv->read(flv->param, data, 4))
return -1;
flv_tag_size_read(data, 4, &sz);
assert(0 == sz);
return 0 == sz ? 0 : -1;
}
static int file_read(void* param, void* buf, int len)
{
return (int)fread(buf, 1, len, (FILE*)param);
}
void* flv_reader_create(const char* file)
{
FILE* fp;
struct flv_reader_t* flv;
fp = fopen(file, "rb");
if (!fp)
return NULL;
flv = flv_reader_create2(file_read, fp);
if (!flv)
{
fclose(fp);
return NULL;
}
flv->fp = fp;
return flv;
}
void* flv_reader_create2(int (*read)(void* param, void* buf, int len), void* param)
{
struct flv_reader_t* flv;
flv = (struct flv_reader_t*)calloc(1, sizeof(*flv));
if (!flv)
return NULL;
flv->read = read;
flv->param = param;
if (0 != flv_read_header(flv))
{
flv_reader_destroy(flv);
return NULL;
}
return flv;
}
void flv_reader_destroy(void* p)
{
struct flv_reader_t* flv;
flv = (struct flv_reader_t*)p;
if (NULL != flv)
{
if (flv->fp)
fclose(flv->fp);
free(flv);
}
}
int flv_reader_read(void* p, int* tagtype, uint32_t* timestamp, size_t* taglen, void* buffer, size_t bytes)
{
int r;
uint32_t sz;
uint8_t header[FLV_TAG_HEADER_SIZE];
struct flv_tag_header_t tag;
struct flv_reader_t* flv;
flv = (struct flv_reader_t*)p;
r = flv->read(flv->param, &header, FLV_TAG_HEADER_SIZE);
if (r != FLV_TAG_HEADER_SIZE)
return r < 0 ? r : 0; // 0-EOF
if (FLV_TAG_HEADER_SIZE != flv_tag_header_read(&tag, header, FLV_TAG_HEADER_SIZE))
return -1;
if (bytes < tag.size)
return -1;
// FLV stream
r = flv->read(flv->param, buffer, tag.size);
if(tag.size != (uint32_t)r)
return r < 0 ? r : 0; // 0-EOF
// PreviousTagSizeN
r = flv->read(flv->param, header, 4);
if (4 != r)
return r < 0 ? r : 0; // 0-EOF
*taglen = tag.size;
*tagtype = tag.type;
*timestamp = tag.timestamp;
flv_tag_size_read(header, 4, &sz);
assert(0 == tag.streamId); // StreamID Always 0
assert(sz == tag.size + FLV_TAG_HEADER_SIZE);
return (sz == tag.size + FLV_TAG_HEADER_SIZE) ? 1 : -1;
}

View File

@ -0,0 +1,117 @@
#include "flv-writer.h"
#include "flv-header.h"
#include "flv-proto.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#define FLV_HEADER_SIZE 9 // DataOffset included
#define FLV_TAG_HEADER_SIZE 11 // StreamID included
struct flv_writer_t
{
FILE* fp;
int (*write)(void* param, const void* buf, int len);
void* param;
};
static int flv_write_header(struct flv_writer_t* flv)
{
uint8_t header[FLV_HEADER_SIZE + 4];
flv_header_write(1, 1, header, FLV_HEADER_SIZE);
flv_tag_size_write(header + FLV_HEADER_SIZE, 4, 0); // PreviousTagSize0(Always 0)
return sizeof(header) == flv->write(flv->param, header, sizeof(header)) ? 0 : -1;
}
static int flv_write_eos(struct flv_writer_t* flv)
{
int n;
uint8_t header[16];
struct flv_video_tag_header_t video;
memset(&video, 0, sizeof(video));
video.codecid = FLV_VIDEO_H264;
video.keyframe = 1;
video.avpacket = FLV_END_OF_SEQUENCE;
video.cts = 0;
n = flv_video_tag_header_write(&video, header, sizeof(header));
return n > 0 ? flv_writer_input(flv, FLV_TYPE_VIDEO, header, n, 0) : -1;
}
static int file_write(void* param, const void* buf, int len)
{
return (int)fwrite(buf, 1, len, (FILE*)param);
}
void* flv_writer_create(const char* file)
{
FILE* fp;
struct flv_writer_t* flv;
fp = fopen(file, "wb");
if (!fp)
return NULL;
flv = flv_writer_create2(file_write, fp);
if (!flv)
{
fclose(fp);
return NULL;
}
flv->fp = fp;
return flv;
}
void* flv_writer_create2(int (*write)(void* param, const void* buf, int len), void* param)
{
struct flv_writer_t* flv;
flv = (struct flv_writer_t*)calloc(1, sizeof(*flv));
if (!flv)
return NULL;
flv->write = write;
flv->param = param;
if (0 != flv_write_header(flv))
{
flv_writer_destroy(flv);
return NULL;
}
return flv;
}
void flv_writer_destroy(void* p)
{
struct flv_writer_t* flv;
flv = (struct flv_writer_t*)p;
if (NULL != flv)
{
flv_write_eos(flv);
if (flv->fp)
fclose(flv->fp);
free(flv);
}
}
int flv_writer_input(void* p, int type, const void* data, size_t bytes, uint32_t timestamp)
{
uint8_t buf[FLV_TAG_HEADER_SIZE + 4];
struct flv_writer_t* flv;
struct flv_tag_header_t tag;
flv = (struct flv_writer_t*)p;
memset(&tag, 0, sizeof(tag));
tag.size = (int)bytes;
tag.type = (uint8_t)type;
tag.timestamp = timestamp;
flv_tag_header_write(&tag, buf, FLV_TAG_HEADER_SIZE);
flv_tag_size_write(buf + FLV_TAG_HEADER_SIZE, 4, (uint32_t)bytes + FLV_TAG_HEADER_SIZE);
if(FLV_TAG_HEADER_SIZE != flv->write(flv->param, buf, FLV_TAG_HEADER_SIZE) // FLV Tag Header
|| bytes != (size_t)flv->write(flv->param, data, (int)bytes)
|| 4 != flv->write(flv->param, buf + FLV_TAG_HEADER_SIZE, 4)) // TAG size
return -1;
return 0;
}

View File

@ -0,0 +1,466 @@
#include "mpeg4-hevc.h"
#include "mpeg4-avc.h"
#include <string.h>
#include <assert.h>
#define H265_NAL_VPS 32
#define H265_NAL_SPS 33
#define H265_NAL_PPS 34
#define H265_NAL_AUD 35
#define H265_NAL_SEI_PREFIX 39
#define H265_NAL_SEI_SUFFIX 40
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define BIT(ptr, off) (((ptr)[(off) / 8] >> (7 - ((off) % 8))) & 0x01)
struct h265_annexbtomp4_handle_t
{
struct mpeg4_hevc_t* hevc;
int errcode;
int* update; // avc sps/pps update flags
int* vcl;
uint8_t* out;
int bytes;
int capacity;
};
uint8_t mpeg4_h264_read_ue(const uint8_t* data, int bytes, int* offset);
static int hevc_rbsp_decode(const uint8_t* nalu, int bytes, uint8_t* sodb)
{
int i, j;
for (j = i = 0; i < bytes; i++)
{
if (i + 2 < bytes && 0 == nalu[i] && 0 == nalu[i + 1] && 0x03 == nalu[i + 2])
{
sodb[j++] = nalu[i];
sodb[j++] = nalu[i + 1];
i += 2;
}
else
{
sodb[j++] = nalu[i];
}
}
return j;
}
static int hevc_profile_tier_level(const uint8_t* nalu, int bytes, uint8_t maxNumSubLayersMinus1, struct mpeg4_hevc_t* hevc)
{
int n;
uint8_t i;
uint8_t sub_layer_profile_present_flag[8];
uint8_t sub_layer_level_present_flag[8];
if (bytes < 12)
return -1;
hevc->general_profile_space = (nalu[0] >> 6) & 0x03;
hevc->general_tier_flag = (nalu[0] >> 5) & 0x01;
hevc->general_profile_idc = nalu[0] & 0x1f;
hevc->general_profile_compatibility_flags = 0;
hevc->general_profile_compatibility_flags |= nalu[1] << 24;
hevc->general_profile_compatibility_flags |= nalu[2] << 16;
hevc->general_profile_compatibility_flags |= nalu[3] << 8;
hevc->general_profile_compatibility_flags |= nalu[4];
hevc->general_constraint_indicator_flags = 0;
hevc->general_constraint_indicator_flags |= ((uint64_t)nalu[5]) << 40;
hevc->general_constraint_indicator_flags |= ((uint64_t)nalu[6]) << 32;
hevc->general_constraint_indicator_flags |= ((uint64_t)nalu[7]) << 24;
hevc->general_constraint_indicator_flags |= ((uint64_t)nalu[8]) << 16;
hevc->general_constraint_indicator_flags |= ((uint64_t)nalu[9]) << 8;
hevc->general_constraint_indicator_flags |= nalu[10];
hevc->general_level_idc = nalu[11];
if (maxNumSubLayersMinus1 < 1)
return 12;
if (bytes < 14)
return -1; // error
for (i = 0; i < maxNumSubLayersMinus1; i++)
{
sub_layer_profile_present_flag[i] = BIT(nalu, 12 * 8 + i * 2);
sub_layer_level_present_flag[i] = BIT(nalu, 12 * 8 + i * 2 + 1);
}
n = 12 + 2;
for (i = 0; i < maxNumSubLayersMinus1; i++)
{
if(sub_layer_profile_present_flag[i])
n += 11;
if (sub_layer_level_present_flag[i])
n += 1;
}
return bytes < n ? n : -1;
}
static uint8_t hevc_vps_id(const uint8_t* rbsp, int bytes, struct mpeg4_hevc_t* hevc, uint8_t* ptr)
{
int sodb;
uint8_t vps;
uint8_t vps_max_sub_layers_minus1;
uint8_t vps_temporal_id_nesting_flag;
sodb = hevc_rbsp_decode(rbsp, bytes, ptr);
if (sodb < 16 + 2)
return 0xFF;
vps = ptr[2] >> 4; // 2-nalu type
vps_max_sub_layers_minus1 = (ptr[3] >> 1) & 0x07;
vps_temporal_id_nesting_flag = ptr[3] & 0x01;
hevc->numTemporalLayers = MAX(hevc->numTemporalLayers, vps_max_sub_layers_minus1 + 1);
hevc->temporalIdNested = (hevc->temporalIdNested || vps_temporal_id_nesting_flag) ? 1 : 0;
hevc_profile_tier_level(ptr + 6, sodb - 6, vps_max_sub_layers_minus1, hevc);
return vps;
}
static uint8_t hevc_sps_id(const uint8_t* rbsp, int bytes, struct mpeg4_hevc_t* hevc, uint8_t* ptr, uint8_t* vps)
{
int n;
int sodb;
uint8_t sps;
uint8_t sps_max_sub_layers_minus1;
uint8_t sps_temporal_id_nesting_flag;
uint8_t conformance_window_flag;
sodb = hevc_rbsp_decode(rbsp, bytes, ptr);
if (sodb < 12+3)
return 0xFF;
*vps = ptr[2] >> 4; // 2-nalu type
sps_max_sub_layers_minus1 = (ptr[2] >> 1) & 0x07;
sps_temporal_id_nesting_flag = ptr[2] & 0x01;
n = hevc_profile_tier_level(ptr + 3, sodb - 3, sps_max_sub_layers_minus1, hevc);
if (n <= 0)
return 0xFF;
n = (n + 3) * 8;
sps = mpeg4_h264_read_ue(ptr, sodb, &n);
hevc->chromaFormat = mpeg4_h264_read_ue(ptr, sodb, &n);
if (3 == hevc->chromaFormat)
n++;
mpeg4_h264_read_ue(ptr, sodb, &n); // pic_width_in_luma_samples
mpeg4_h264_read_ue(ptr, sodb, &n); // pic_height_in_luma_samples
conformance_window_flag = BIT(ptr, n); n++; // conformance_window_flag
if (conformance_window_flag)
{
mpeg4_h264_read_ue(ptr, sodb, &n); // conf_win_left_offset
mpeg4_h264_read_ue(ptr, sodb, &n); // conf_win_right_offset
mpeg4_h264_read_ue(ptr, sodb, &n); // conf_win_top_offset
mpeg4_h264_read_ue(ptr, sodb, &n); // conf_win_bottom_offset
}
hevc->bitDepthLumaMinus8 = mpeg4_h264_read_ue(ptr, sodb, &n);
hevc->bitDepthChromaMinus8 = mpeg4_h264_read_ue(ptr, sodb, &n);
// TODO: vui_parameters
//mp4->hevc->min_spatial_segmentation_idc; // min_spatial_segmentation_idc
return sps;
}
static uint8_t hevc_pps_id(const uint8_t* rbsp, int bytes, struct mpeg4_hevc_t* hevc, uint8_t* ptr, uint8_t* sps)
{
// TODO:
//hevc->parallelismType; // entropy_coding_sync_enabled_flag
(void)hevc;
int sodb;
int offset = 2 * 8; // 2-nalu type
sodb = hevc_rbsp_decode(rbsp, bytes, ptr);
if (sodb < 3)
return 0xFF;
*sps = mpeg4_h264_read_ue(ptr, sodb, &offset);
return mpeg4_h264_read_ue(ptr, sodb, &offset);
}
static void mpeg4_hevc_remove(struct mpeg4_hevc_t* hevc, uint8_t* ptr, int bytes, const uint8_t* end)
{
uint8_t i;
assert(ptr >= hevc->data && ptr + bytes <= end && end <= hevc->data + sizeof(hevc->data));
memmove(ptr, ptr + bytes, end - ptr - bytes);
for (i = 0; i < hevc->numOfArrays; i++)
{
if (hevc->nalu[i].data > ptr)
hevc->nalu[i].data -= bytes;
}
}
static int mpeg4_hevc_update2(struct mpeg4_hevc_t* hevc, int i, const uint8_t* nalu, int bytes)
{
if (bytes == hevc->nalu[i].bytes && 0 == memcmp(nalu, hevc->nalu[i].data, bytes))
return 0; // do nothing
if (bytes > hevc->nalu[i].bytes && hevc->off + (bytes - hevc->nalu[i].bytes) > sizeof(hevc->data))
{
assert(0);
return -1; // too big
}
mpeg4_hevc_remove(hevc, hevc->nalu[i].data, hevc->nalu[i].bytes, hevc->data + hevc->off);
hevc->off -= hevc->nalu[i].bytes;
hevc->nalu[i].data = hevc->data + hevc->off;
hevc->nalu[i].bytes = (uint16_t)bytes;
memcpy(hevc->nalu[i].data, nalu, bytes);
hevc->off += bytes;
return 1;
}
static int mpeg4_hevc_add(struct mpeg4_hevc_t* hevc, uint8_t type, const uint8_t* nalu, int bytes)
{
// copy new
assert(hevc->numOfArrays < sizeof(hevc->nalu) / sizeof(hevc->nalu[0]));
if (hevc->numOfArrays >= sizeof(hevc->nalu) / sizeof(hevc->nalu[0])
|| hevc->off + bytes > sizeof(hevc->data))
{
assert(0);
return -1;
}
hevc->nalu[hevc->numOfArrays].type = type;
hevc->nalu[hevc->numOfArrays].bytes = (uint16_t)bytes;
hevc->nalu[hevc->numOfArrays].array_completeness = 1;
hevc->nalu[hevc->numOfArrays].data = hevc->data + hevc->off;
memcpy(hevc->nalu[hevc->numOfArrays].data, nalu, bytes);
hevc->off += bytes;
++hevc->numOfArrays;
return 1;
}
static int h265_vps_copy(struct mpeg4_hevc_t* hevc, const uint8_t* nalu, int bytes)
{
int i;
uint8_t vpsid;
if (bytes < 3)
{
assert(0);
return -1; // invalid length
}
vpsid = hevc_vps_id(nalu, bytes, hevc, hevc->data + hevc->off);
for (i = 0; i < hevc->numOfArrays; i++)
{
if (H265_NAL_VPS == hevc->nalu[i].type && vpsid == hevc_vps_id(hevc->nalu[i].data, hevc->nalu[i].bytes, hevc, hevc->data + hevc->off))
return mpeg4_hevc_update2(hevc, i, nalu, bytes);
}
return mpeg4_hevc_add(hevc, H265_NAL_VPS, nalu, bytes);
}
static int h265_sps_copy(struct mpeg4_hevc_t* hevc, const uint8_t* nalu, int bytes)
{
int i;
uint8_t spsid;
uint8_t vpsid, vpsid2;
if (bytes < 13 + 2)
{
assert(0);
return -1; // invalid length
}
spsid = hevc_sps_id(nalu, bytes, hevc, hevc->data + hevc->off, &vpsid);
for (i = 0; i < hevc->numOfArrays; i++)
{
if (H265_NAL_SPS == hevc->nalu[i].type && spsid == hevc_sps_id(hevc->nalu[i].data, hevc->nalu[i].bytes, hevc, hevc->data + hevc->off, &vpsid2) && vpsid == vpsid2)
return mpeg4_hevc_update2(hevc, i, nalu, bytes);
}
return mpeg4_hevc_add(hevc, H265_NAL_SPS, nalu, bytes);
}
static int h265_pps_copy(struct mpeg4_hevc_t* hevc, const uint8_t* nalu, int bytes)
{
int i;
uint8_t ppsid;
uint8_t spsid, spsid2;
if (bytes < 1 + 2)
{
assert(0);
return -1; // invalid length
}
ppsid = hevc_pps_id(nalu, bytes, hevc, hevc->data + hevc->off, &spsid);
for (i = 0; i < hevc->numOfArrays; i++)
{
if (H265_NAL_PPS == hevc->nalu[i].type && ppsid == hevc_pps_id(hevc->nalu[i].data, hevc->nalu[i].bytes, hevc, hevc->data + hevc->off, &spsid2) && spsid == spsid2)
return mpeg4_hevc_update2(hevc, i, nalu, bytes);
}
return mpeg4_hevc_add(hevc, H265_NAL_PPS, nalu, bytes);
}
static int h265_sei_clear(struct mpeg4_hevc_t* hevc)
{
int i;
for (i = 0; i < hevc->numOfArrays; i++)
{
if (H265_NAL_SEI_PREFIX == hevc->nalu[i].type || H265_NAL_SEI_SUFFIX == hevc->nalu[i].type)
{
mpeg4_hevc_remove(hevc, hevc->nalu[i].data, hevc->nalu[i].bytes, hevc->data + hevc->off);
hevc->off -= hevc->nalu[i].bytes;
if(i + 1 < hevc->numOfArrays)
memmove(hevc->nalu + i, hevc->nalu + i + 1, sizeof(hevc->nalu[0]) * (hevc->numOfArrays - i - 1));
--hevc->numOfArrays;
--i;
}
}
return 0;
}
int mpeg4_hevc_update(struct mpeg4_hevc_t* hevc, const uint8_t* nalu, int bytes)
{
int r;
switch ((nalu[0] >> 1) & 0x3f)
{
case H265_NAL_VPS:
h265_sei_clear(hevc); // remove all prefix/suffix sei
r = h265_vps_copy(hevc, nalu, bytes);
break;
case H265_NAL_SPS:
r = h265_sps_copy(hevc, nalu, bytes);
break;
case H265_NAL_PPS:
r = h265_pps_copy(hevc, nalu, bytes);
break;
#if defined(H265_FILTER_SEI)
case H265_NAL_SEI_PREFIX:
r = mpeg4_hevc_add(hevc, H265_NAL_SEI_PREFIX, nalu, bytes);
break;
case H265_NAL_SEI_SUFFIX:
r = mpeg4_hevc_add(hevc, H265_NAL_SEI_SUFFIX, nalu, bytes);
break;
#endif
default:
r = 0;
break;
}
return r;
}
static void hevc_handler(void* param, const uint8_t* nalu, int bytes)
{
int r;
uint8_t nalutype;
struct h265_annexbtomp4_handle_t* mp4;
mp4 = (struct h265_annexbtomp4_handle_t*)param;
nalutype = (nalu[0] >> 1) & 0x3f;
#if defined(H2645_FILTER_AUD)
if(H265_NAL_AUD == nalutype)
return; // ignore AUD
#endif
r = mpeg4_hevc_update(mp4->hevc, nalu, bytes);
if (1 == r && mp4->update)
*mp4->update = 1;
else if (r < 0)
mp4->errcode = r;
// IRAP-1, B/P-2, other-0
if (mp4->vcl && nalutype < H265_NAL_VPS)
*mp4->vcl = 16<=nalutype && nalutype<=23 ? 1 : 2;
if (mp4->capacity >= mp4->bytes + bytes + 4)
{
mp4->out[mp4->bytes + 0] = (uint8_t)((bytes >> 24) & 0xFF);
mp4->out[mp4->bytes + 1] = (uint8_t)((bytes >> 16) & 0xFF);
mp4->out[mp4->bytes + 2] = (uint8_t)((bytes >> 8) & 0xFF);
mp4->out[mp4->bytes + 3] = (uint8_t)((bytes >> 0) & 0xFF);
memcpy(mp4->out + mp4->bytes + 4, nalu, bytes);
mp4->bytes += bytes + 4;
}
else
{
mp4->errcode = -1;
}
}
int h265_annexbtomp4(struct mpeg4_hevc_t* hevc, const void* data, int bytes, void* out, int size, int *vcl, int* update)
{
struct h265_annexbtomp4_handle_t h;
memset(&h, 0, sizeof(h));
h.hevc = hevc;
h.vcl = vcl;
h.update = update;
h.out = (uint8_t*)out;
h.capacity = size;
if (vcl) *vcl = 0;
if (update) *update = 0;
// hevc->numTemporalLayers = 0;
// hevc->temporalIdNested = 0;
// hevc->min_spatial_segmentation_idc = 0;
// hevc->general_profile_compatibility_flags = 0xffffffff;
// hevc->general_constraint_indicator_flags = 0xffffffffffULL;
// hevc->chromaFormat = 1; // 4:2:0
mpeg4_h264_annexb_nalu((const uint8_t*)data, bytes, hevc_handler, &h);
hevc->configurationVersion = 1;
hevc->lengthSizeMinusOne = 3; // 4 bytes
return 0 == h.errcode ? h.bytes : 0;
}
int h265_is_new_access_unit(const uint8_t* nalu, size_t bytes)
{
enum { NAL_VPS = 32, NAL_SPS = 33, NAL_PPS = 34, NAL_AUD = 35, NAL_PREFIX_SEI = 39, };
uint8_t nal_type;
uint8_t nuh_layer_id;
if(bytes < 3)
return 0;
nal_type = (nalu[0] >> 1) & 0x3f;
nuh_layer_id = ((nalu[0] & 0x01) << 5) | ((nalu[1] >> 3) &0x1F);
// 7.4.2.4.4 Order of NAL units and coded pictures and their association to access units
if(NAL_VPS == nal_type || NAL_SPS == nal_type || NAL_PPS == nal_type ||
(nuh_layer_id == 0 && (NAL_AUD == nal_type || NAL_PREFIX_SEI == nal_type || (41 <= nal_type && nal_type <= 44) || (48 <= nal_type && nal_type <= 55))))
return 1;
// 7.4.2.4.5 Order of VCL NAL units and association to coded pictures
if (nal_type <= 31)
{
//first_slice_segment_in_pic_flag 0x80
return (nalu[2] & 0x80) ? 1 : 0;
}
return 0;
}
#if defined(_DEBUG) || defined(DEBUG)
void hevc_annexbtomp4_test(void)
{
const uint8_t vps[] = { 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x9d, 0xc0, 0x90 };
const uint8_t sps[] = { 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x32, 0x16, 0x59, 0xde, 0x49, 0x1b, 0x6b, 0x80, 0x40, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x17, 0x70, 0x02 };
const uint8_t pps[] = { 0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89 };
const uint8_t annexb[] = { 0x00, 0x00, 0x00, 0x01, 0x4e, 0x01, 0x06, 0x01, 0xd0, 0x80, 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x9d, 0xc0, 0x90, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x32, 0x16, 0x59, 0xde, 0x49, 0x1b, 0x6b, 0x80, 0x40, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x17, 0x70, 0x02, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89 };
uint8_t output[512];
int vcl, update;
struct mpeg4_hevc_t hevc;
memset(&hevc, 0, sizeof(hevc));
assert(h265_annexbtomp4(&hevc, annexb, sizeof(annexb), output, sizeof(output), &vcl, &update) > 0);
assert(3 == hevc.numOfArrays && vcl == 0 && update == 1);
assert(hevc.nalu[0].bytes == sizeof(vps) && 0 == memcmp(hevc.nalu[0].data, vps, sizeof(vps)));
assert(hevc.nalu[1].bytes == sizeof(sps) && 0 == memcmp(hevc.nalu[1].data, sps, sizeof(sps)));
assert(hevc.nalu[2].bytes == sizeof(pps) && 0 == memcmp(hevc.nalu[2].data, pps, sizeof(pps)));
}
#endif

View File

@ -0,0 +1,59 @@
#include "mpeg4-hevc.h"
#include <string.h>
#include <assert.h>
#define H265_NAL_AUD 35 // Access unit delimiter
int h265_mp4toannexb(const struct mpeg4_hevc_t* hevc, const void* data, int bytes, void* out, int size)
{
int r;
uint8_t i;
uint8_t* dst, *dstend;
const uint8_t* src, *srcend;
const uint8_t startcode[] = { 0, 0, 0, 1 };
uint8_t irap, nalu_type;
uint32_t nalu_size;
uint8_t vps_sps_pps_flag;
srcend = (uint8_t*)data + bytes;
dst = (uint8_t*)out;
dstend = dst + size;
vps_sps_pps_flag = 0;
for(src = (uint8_t*)data; src + hevc->lengthSizeMinusOne + 1 < srcend; src += nalu_size)
{
nalu_size = 0;
for (i = 0; i < hevc->lengthSizeMinusOne + 1; i++)
nalu_size = (nalu_size << 8) | src[i];
src += hevc->lengthSizeMinusOne + 1;
if (nalu_size < 1 || src + nalu_size > srcend)
{
assert(0);
return 0; // invalid
}
nalu_type = (src[0] >> 1) & 0x3F;
#if defined(H2645_FILTER_AUD)
if (H265_NAL_AUD == nalu_type)
continue; // ignore AUD
#endif
irap = 16 <= nalu_type && nalu_type <= 23;
if (irap && 0 == vps_sps_pps_flag)
{
r = mpeg4_hevc_to_nalu(hevc, dst, dstend - dst);
if (r <= 0)
return 0; // don't have enough memory
dst += r;
vps_sps_pps_flag = 1;
}
if (dst + nalu_size + 4 > dstend)
return 0; // don't have enough memory
memcpy(dst, startcode, 4);
memcpy(dst + 4, src, nalu_size);
dst += 4 + nalu_size;
}
return (int)(dst - (uint8_t*)out);
}

View File

@ -0,0 +1,233 @@
#include "mp3-header.h"
#include <stdint.h>
#include <string.h>
#include <assert.h>
// layer-1, layer-2, layer-3
static int s_bitrate_mpeg1[3][16] = {
{ 0/*free*/, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, -1 },
{ 0/*free*/, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, -1 },
{ 0/*free*/, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, -1 },
};
// layer-1, layer-2, layer-3
static int s_bitrate_mpeg2[3][16] = {
{ 0/*free*/, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, -1 },
{ 0/*free*/, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, -1 },
{ 0/*free*/, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, -1 },
};
// layer-1, layer-2, layer-3
static int s_frequency_mpeg1[4] = { 44100, 48000, 32000, -1 };
static int s_frequency_mpeg2[4] = { 22050, 24000, 16000, -1 };
static int s_frequency_mpeg25[4] = { 11025, 12000, 8000, -1 };
// layer-1, layer-2, layer-3
//static int s_frames_mpeg1[3] = { 384, 1152, 1152 };
//static int s_frames_mpeg2[3] = { 384, 1152, 576 };
//static int s_frames_mpeg25[3] = { 384, 1152, 576 };
// layer-1 bytes = ((frames / 8 * bitrate) / frequency + padding * 4
// layer-2/3 bytes = ((frames / 8 * bitrate) / frequency + padding
int mp3_header_load(struct mp3_header_t* mp3, const void* data, int bytes)
{
const uint8_t* p;
if (bytes < 4)
return 0;
p = data;
if (0 == memcmp("TAG", p, 3))
{
if (bytes < 128/*ID3v1*/ + 4)
return 0;
p += 128;
}
else if (0 == memcmp("ID3", p, 3))
{
uint32_t n;
if (3 != p[3]/*version*/ || bytes < 10)
return 0;
n = (((uint32_t)p[6] & 0x7F) << 21) | (((uint32_t)p[7] & 0x7F) << 14) | (((uint32_t)p[8] & 0x7F) << 7) | (p[9] & 0x7F);
if (bytes < (int)n + 10)
return 0;
p += n + 10;
}
//sync: 1111 1111 111
if (0xFF != p[0] || 0xE0 != (p[1] & 0xE0))
{
assert(0);
return 0;
}
mp3->version = (p[1] >> 3) & 0x03;
mp3->layer = (p[1] >> 1) & 0x03;
mp3->protection = p[1] & 0x01;
mp3->bitrate_index = (p[2] >> 4) & 0x0F;
mp3->sampling_frequency = (p[2] >> 2) & 0x03;
mp3->priviate = p[2] & 0x01;
mp3->mode = (p[3] >> 6) & 0x03;
mp3->mode_extension = (p[3] >> 4) & 0x03;
mp3->copyright = (p[3] >> 3) & 0x01;
mp3->original = (p[3] >> 2) & 0x01;
mp3->emphasis = p[3] & 0x03;
return (int)(p - (uint8_t*)data) + 4;
}
int mp3_header_save(const struct mp3_header_t* mp3, void* data, int bytes)
{
uint8_t* p;
if (bytes < 4)
return 0;
p = data;
p[0] = 0xFF;
p[1] = (uint8_t)(0xE0 | (mp3->version << 3) | (mp3->layer << 1) | mp3->protection);
p[2] = (uint8_t)((mp3->bitrate_index << 4) | (mp3->sampling_frequency << 2) | 0x00 /*padding*/ | mp3->priviate);
p[3] = (uint8_t)((mp3->mode << 6) | (mp3->mode_extension << 4) | (mp3->copyright << 3) | (mp3->original << 2) | mp3->emphasis);
return 4;
}
int mp3_get_channel(const struct mp3_header_t* mp3)
{
return 0x03 == mp3->mode ? 1 : 2;
}
int mp3_get_bitrate(const struct mp3_header_t* mp3)
{
if (mp3->layer < 1 || mp3->layer > 3)
{
assert(0);
return -1;
}
switch (mp3->version)
{
case MP3_MPEG1:
return s_bitrate_mpeg1[3 - mp3->layer][mp3->bitrate_index];
case MP3_MPEG2:
case MP3_MPEG2_5:
return s_bitrate_mpeg2[3 - mp3->layer][mp3->bitrate_index];
default:
assert(0);
return -1;
}
}
static int mp3_find_bitrate(const int* arr, int bitrate)
{
int i;
for (i = 0; i < 16; i++)
{
if (bitrate == arr[i])
return i;
}
return -1;
}
int mp3_set_bitrate(struct mp3_header_t* mp3, int bitrate)
{
int r;
if (mp3->layer < 1 || mp3->layer > 3)
{
assert(0);
return -1;
}
switch (mp3->version)
{
case MP3_MPEG1:
r = mp3_find_bitrate(s_bitrate_mpeg1[3 - mp3->layer], bitrate);
break;
case MP3_MPEG2:
case MP3_MPEG2_5:
r = mp3_find_bitrate(s_bitrate_mpeg2[3 - mp3->layer], bitrate);
break;
default:
assert(0);
r = -1;
}
if (-1 == r)
return -1;
mp3->bitrate_index = (unsigned int)r;
return 0;
}
int mp3_get_frequency(const struct mp3_header_t* mp3)
{
if (mp3->sampling_frequency < 0 || mp3->sampling_frequency > 3)
return -1;
switch (mp3->version)
{
case MP3_MPEG1: return s_frequency_mpeg1[mp3->sampling_frequency];
case MP3_MPEG2: return s_frequency_mpeg2[mp3->sampling_frequency];
case MP3_MPEG2_5: return s_frequency_mpeg25[mp3->sampling_frequency];
default: assert(0); return -1;
}
}
static int mp3_find_frequency(const int* arr, int frequency)
{
int i;
for (i = 0; i < 4; i++)
{
if (frequency == arr[i])
return i;
}
return -1;
}
int mp3_set_frequency(struct mp3_header_t* mp3, int frequency)
{
int r;
switch (mp3->version)
{
case MP3_MPEG1:
r = mp3_find_frequency(s_frequency_mpeg1, frequency);
break;
case MP3_MPEG2:
r = mp3_find_frequency(s_frequency_mpeg2, frequency);
break;
case MP3_MPEG2_5:
r = mp3_find_frequency(s_frequency_mpeg25, frequency);
break;
default:
assert(0);
r = -1;
}
if (-1 == r)
return -1;
mp3->sampling_frequency = (unsigned int)r;
return 0;
}
#if defined(DEBUG) || defined(_DEBUG)
void mp3_header_test(void)
{
uint8_t v[4] = { 0xff, 0xfb, 0xe0, 0x64 };
uint8_t v2[4];
struct mp3_header_t mp3;
assert(4 == mp3_header_load(&mp3, v, 4));
assert(MP3_MPEG1 == mp3.version && MP3_LAYER3 == mp3.layer);
assert(14 == mp3.bitrate_index && 320000 == mp3_get_bitrate(&mp3));
assert(0 == mp3.sampling_frequency && 44100 == mp3_get_frequency(&mp3));
assert(1 == mp3.mode && 1 == mp3.protection);
assert(4 == mp3_header_save(&mp3, v2, 4));
assert(0 == memcmp(v, v2, 4));
}
#endif

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