Level2 28181
This commit is contained in:
parent
c3c3aae6ce
commit
ab84aad151
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ release/
|
||||
*.data
|
||||
|
||||
*.len
|
||||
|
||||
*.pcap
|
||||
|
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
@ -22,7 +22,29 @@
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "C/C++: g++ 生成活动文件",
|
||||
"miDebuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
{
|
||||
"name": "pcapsender",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/release/out/PcapSender",
|
||||
"args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapSender/hw28181.pcap",
|
||||
"127.0.0.1","30001","udp src port 20180"],
|
||||
// "args": ["/media/alan/Data/Alan/Documents/WorkSpace/SecMedia/PcapSender/signhw28181.pcap",
|
||||
// "127.0.0.1","30001","udp src port 55763"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "为 gdb 启用整齐打印",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"miDebuggerPath": "/usr/bin/gdb"
|
||||
}
|
||||
]
|
||||
|
@ -6,7 +6,6 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Release")
|
||||
message(STATUS "Release版本")
|
||||
set(BuildType "Release")
|
||||
add_definitions(-DNDEBUG)
|
||||
add_definitions(-DDEBUG_LOG)
|
||||
elseif(${CMAKE_BUILD_TYPE} MATCHES "MinSizeRel")
|
||||
message(STATUS "MinSizeRel版本")
|
||||
set(BuildType "MinSizeRel")
|
||||
@ -18,23 +17,38 @@ else()
|
||||
add_definitions(-DDUMP_FILE)
|
||||
endif()
|
||||
#加载自定义模块
|
||||
add_definitions(-DSIGN_ENABLE)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/release/lib)
|
||||
SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/release/lib/${BuildType})
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/release/out)
|
||||
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_C_FLAGS$ "${CMAKE_C_FLAGS} -fvisibility = hidden")
|
||||
set(CMAKE_CXX_FLAGS$ "${CMAKE_CXX_FLAGS} -fvisibility = hidden")
|
||||
# set(CMAKE_C_FLAGS$ "${CMAKE_C_FLAGS} -fvisibility = hidden")
|
||||
# set(CMAKE_CXX_FLAGS$ "${CMAKE_CXX_FLAGS} -fvisibility = hidden")
|
||||
set(SecMedia_Root ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
add_compile_options(-fPIC)
|
||||
add_compile_options(-fvisibility=hidden)
|
||||
add_compile_options(-std=c++11)
|
||||
# add_compile_options(-fvisibility=hidden)
|
||||
# add_compile_options(-std=c++11)
|
||||
#add_compile_options(-mcpu=cortex-a7 -mfloat-abi=softfp -mfpu=neon-vfpv4 -mno-unaligned-access -fno-aggressive-loop-optimizations -Wcast-align)
|
||||
include(GNUInstallDirs)
|
||||
include(GenerateExportHeader)
|
||||
|
||||
#media-server ps dec enc
|
||||
set(MediaServer_Root ${SecMedia_Root}/3rdpart/media-server)
|
||||
include_directories(${MediaServer_Root}/librtp/include)
|
||||
include_directories(${MediaServer_Root}/libmpeg/include)
|
||||
include_directories(${MediaServer_Root}/libmpeg/source)
|
||||
|
||||
aux_source_directory(${MediaServer_Root}/libmpeg/include src_rtp)
|
||||
aux_source_directory(${MediaServer_Root}/libmpeg/source src_rtp)
|
||||
aux_source_directory(${MediaServer_Root}/librtp/include src_rtp)
|
||||
aux_source_directory(${MediaServer_Root}/librtp/source src_rtp)
|
||||
aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp)
|
||||
add_library(rtp STATIC ${src_rtp})
|
||||
|
||||
|
||||
|
||||
include_directories(${SecMedia_Root}/SVAC/src/svac_src)
|
||||
#添加svac解密
|
||||
@ -68,7 +82,7 @@ file(GLOB SecMedia_api_list ${CMAKE_CURRENT_SOURCE_DIR}/include/common.h )
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED ${SecMedia_src_list})
|
||||
# add_library(${PROJECT_NAME} STATIC ${SecMedia_src_list})
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIB_SVAC_LIST})
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIB_SVAC_LIST} rtp)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${SecMedia_Root}/.)
|
||||
|
||||
# set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
@ -108,3 +122,4 @@ endif()
|
||||
|
||||
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(PcapSender)
|
16
PcapSender/CMakeLists.txt
Normal file
16
PcapSender/CMakeLists.txt
Normal 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++)
|
||||
|
234
PcapSender/main.cpp
Normal file
234
PcapSender/main.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
#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 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, ¶m);
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}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;
|
||||
|
||||
}
|
@ -10,10 +10,19 @@
|
||||
API_EXPORT int SDF_Device_open();
|
||||
API_EXPORT int SDF_Device_close();
|
||||
|
||||
struct sec_set_info{
|
||||
uint8_t camera_id[20];
|
||||
uint8_t vkek_version[32]; // 秘钥的加密秘钥的版本号
|
||||
uint8_t prikey_size;
|
||||
uint8_t pubkey_size;
|
||||
uint8_t prikey[64];
|
||||
uint8_t pubkey[128];
|
||||
};
|
||||
|
||||
API_EXPORT void * HWSign_init();
|
||||
|
||||
API_EXPORT void * HWSign_init(struct sec_set_info * sign_info);
|
||||
API_EXPORT void HWSign_release(void* Handle);
|
||||
API_EXPORT int HWSign_rtp_input(void* Handle, const char * buf, const uint32_t len,int tcp, void * param);
|
||||
API_EXPORT int HWSign_rtp_input(void* Handle, const char * buf, const uint32_t len, void * param);
|
||||
API_EXPORT int HWSign_rtp_out(void* Handle, char * buf, uint32_t * len, void ** param);
|
||||
|
||||
API_EXPORT void * HWSign_tcp_init();
|
||||
|
BIN
src/3rdpart/media-server/.DS_Store
vendored
Normal file
BIN
src/3rdpart/media-server/.DS_Store
vendored
Normal file
Binary file not shown.
28
src/3rdpart/media-server/.travis.yml
Normal file
28
src/3rdpart/media-server/.travis.yml
Normal 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
|
72
src/3rdpart/media-server/Android.mk
Normal file
72
src/3rdpart/media-server/Android.mk
Normal 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)
|
4
src/3rdpart/media-server/Application.mk
Normal file
4
src/3rdpart/media-server/Application.mk
Normal 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
|
21
src/3rdpart/media-server/LICENSE
Normal file
21
src/3rdpart/media-server/LICENSE
Normal 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.
|
41
src/3rdpart/media-server/Makefile
Normal file
41
src/3rdpart/media-server/Makefile
Normal 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
|
64
src/3rdpart/media-server/README.md
Normal file
64
src/3rdpart/media-server/README.md
Normal 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)
|
96
src/3rdpart/media-server/gcc.mk
Normal file
96
src/3rdpart/media-server/gcc.mk
Normal 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)
|
16
src/3rdpart/media-server/libdash/Android.mk
Normal file
16
src/3rdpart/media-server/libdash/Android.mk
Normal 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)
|
45
src/3rdpart/media-server/libdash/Makefile
Normal file
45
src/3rdpart/media-server/libdash/Makefile
Normal 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
|
31
src/3rdpart/media-server/libdash/include/dash-mpd.h
Normal file
31
src/3rdpart/media-server/libdash/include/dash-mpd.h
Normal 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_ */
|
483
src/3rdpart/media-server/libdash/include/dash-parser.h
Normal file
483
src/3rdpart/media-server/libdash/include/dash-parser.h
Normal 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_ */
|
10
src/3rdpart/media-server/libdash/include/dash-proto.h
Normal file
10
src/3rdpart/media-server/libdash/include/dash-proto.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _dash_proto_h_
|
||||
#define _dash_proto_h_
|
||||
|
||||
enum
|
||||
{
|
||||
DASH_STATIC = 0,
|
||||
DASH_DYNAMIC,
|
||||
};
|
||||
|
||||
#endif /* !_dash_proto_h_ */
|
19
src/3rdpart/media-server/libdash/include/xs-datatype.h
Normal file
19
src/3rdpart/media-server/libdash/include/xs-datatype.h
Normal 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_ */
|
151
src/3rdpart/media-server/libdash/libdash.vcxproj
Normal file
151
src/3rdpart/media-server/libdash/libdash.vcxproj
Normal 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>
|
54
src/3rdpart/media-server/libdash/libdash.vcxproj.filters
Normal file
54
src/3rdpart/media-server/libdash/libdash.vcxproj.filters
Normal 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>
|
@ -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 */;
|
||||
}
|
77
src/3rdpart/media-server/libdash/src/dash-adaptation.c
Normal file
77
src/3rdpart/media-server/libdash/src/dash-adaptation.c
Normal 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;
|
||||
}
|
520
src/3rdpart/media-server/libdash/src/dash-mpd.c
Normal file
520
src/3rdpart/media-server/libdash/src/dash-mpd.c
Normal 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;
|
||||
}
|
1537
src/3rdpart/media-server/libdash/src/dash-parser.c
Normal file
1537
src/3rdpart/media-server/libdash/src/dash-parser.c
Normal file
File diff suppressed because it is too large
Load Diff
182
src/3rdpart/media-server/libdash/src/dash-period.c
Normal file
182
src/3rdpart/media-server/libdash/src/dash-period.c
Normal 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;
|
||||
}
|
189
src/3rdpart/media-server/libdash/src/dash-representation.c
Normal file
189
src/3rdpart/media-server/libdash/src/dash-representation.c
Normal 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
|
210
src/3rdpart/media-server/libdash/src/dash-segment.c
Normal file
210
src/3rdpart/media-server/libdash/src/dash-segment.c
Normal 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;
|
||||
}
|
61
src/3rdpart/media-server/libdash/src/list.h
Normal file
61
src/3rdpart/media-server/libdash/src/list.h
Normal 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_ */
|
160
src/3rdpart/media-server/libdash/src/xs-duration.c
Normal file
160
src/3rdpart/media-server/libdash/src/xs-duration.c
Normal 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
|
237
src/3rdpart/media-server/libdash/test/dash-dynamic-test.cpp
Normal file
237
src/3rdpart/media-server/libdash/test/dash-dynamic-test.cpp
Normal 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, ×tamp, &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);
|
||||
}
|
85
src/3rdpart/media-server/libdash/test/dash-static-test.cpp
Normal file
85
src/3rdpart/media-server/libdash/test/dash-static-test.cpp
Normal 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);
|
||||
}
|
14
src/3rdpart/media-server/libflv/Android.mk
Normal file
14
src/3rdpart/media-server/libflv/Android.mk
Normal 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)
|
44
src/3rdpart/media-server/libflv/Makefile
Normal file
44
src/3rdpart/media-server/libflv/Makefile
Normal 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
|
69
src/3rdpart/media-server/libflv/include/amf0.h
Normal file
69
src/3rdpart/media-server/libflv/include/amf0.h
Normal 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_ */
|
56
src/3rdpart/media-server/libflv/include/amf3.h
Normal file
56
src/3rdpart/media-server/libflv/include/amf3.h
Normal 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_ */
|
41
src/3rdpart/media-server/libflv/include/aom-av1.h
Normal file
41
src/3rdpart/media-server/libflv/include/aom-av1.h
Normal 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_ */
|
38
src/3rdpart/media-server/libflv/include/flv-demuxer.h
Normal file
38
src/3rdpart/media-server/libflv/include/flv-demuxer.h
Normal 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_ */
|
126
src/3rdpart/media-server/libflv/include/flv-header.h
Normal file
126
src/3rdpart/media-server/libflv/include/flv-header.h
Normal 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_ */
|
61
src/3rdpart/media-server/libflv/include/flv-muxer.h
Normal file
61
src/3rdpart/media-server/libflv/include/flv-muxer.h
Normal 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_ */
|
33
src/3rdpart/media-server/libflv/include/flv-parser.h
Normal file
33
src/3rdpart/media-server/libflv/include/flv-parser.h
Normal 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_ */
|
30
src/3rdpart/media-server/libflv/include/flv-proto.h
Normal file
30
src/3rdpart/media-server/libflv/include/flv-proto.h
Normal 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_ */
|
26
src/3rdpart/media-server/libflv/include/flv-reader.h
Normal file
26
src/3rdpart/media-server/libflv/include/flv-reader.h
Normal 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_ */
|
26
src/3rdpart/media-server/libflv/include/flv-writer.h
Normal file
26
src/3rdpart/media-server/libflv/include/flv-writer.h
Normal 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_ */
|
109
src/3rdpart/media-server/libflv/include/mp3-header.h
Normal file
109
src/3rdpart/media-server/libflv/include/mp3-header.h
Normal 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_ */
|
148
src/3rdpart/media-server/libflv/include/mpeg4-aac.h
Normal file
148
src/3rdpart/media-server/libflv/include/mpeg4-aac.h
Normal 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_ */
|
68
src/3rdpart/media-server/libflv/include/mpeg4-avc.h
Normal file
68
src/3rdpart/media-server/libflv/include/mpeg4-avc.h
Normal 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_ */
|
169
src/3rdpart/media-server/libflv/include/mpeg4-bits.h
Normal file
169
src/3rdpart/media-server/libflv/include/mpeg4-bits.h
Normal 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_ */
|
64
src/3rdpart/media-server/libflv/include/mpeg4-hevc.h
Normal file
64
src/3rdpart/media-server/libflv/include/mpeg4-hevc.h
Normal 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_ */
|
37
src/3rdpart/media-server/libflv/include/opus-head.h
Normal file
37
src/3rdpart/media-server/libflv/include/opus-head.h
Normal 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_ */
|
32
src/3rdpart/media-server/libflv/include/webm-vpx.h
Normal file
32
src/3rdpart/media-server/libflv/include/webm-vpx.h
Normal 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_ */
|
177
src/3rdpart/media-server/libflv/libflv.vcxproj
Normal file
177
src/3rdpart/media-server/libflv/libflv.vcxproj
Normal 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>
|
132
src/3rdpart/media-server/libflv/libflv.vcxproj.filters
Normal file
132
src/3rdpart/media-server/libflv/libflv.vcxproj.filters
Normal 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>
|
378
src/3rdpart/media-server/libflv/libflv.xcodeproj/project.pbxproj
Normal file
378
src/3rdpart/media-server/libflv/libflv.xcodeproj/project.pbxproj
Normal 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 */;
|
||||
}
|
589
src/3rdpart/media-server/libflv/source/amf0.c
Normal file
589
src/3rdpart/media-server/libflv/source/amf0.c
Normal 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
|
95
src/3rdpart/media-server/libflv/source/amf3.c
Normal file
95
src/3rdpart/media-server/libflv/source/amf3.c
Normal 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;
|
||||
}
|
||||
}
|
130
src/3rdpart/media-server/libflv/source/aom-av1.c
Normal file
130
src/3rdpart/media-server/libflv/source/aom-av1.c
Normal 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
|
81
src/3rdpart/media-server/libflv/source/flv-demuxer-script.c
Normal file
81
src/3rdpart/media-server/libflv/source/flv-demuxer-script.c
Normal 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;
|
||||
}
|
269
src/3rdpart/media-server/libflv/source/flv-demuxer.c
Normal file
269
src/3rdpart/media-server/libflv/source/flv-demuxer.c
Normal 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;
|
||||
}
|
||||
}
|
236
src/3rdpart/media-server/libflv/source/flv-header.c
Normal file
236
src/3rdpart/media-server/libflv/source/flv-header.c
Normal 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;
|
||||
}
|
370
src/3rdpart/media-server/libflv/source/flv-muxer.c
Normal file
370
src/3rdpart/media-server/libflv/source/flv-muxer.c
Normal 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);
|
||||
}
|
89
src/3rdpart/media-server/libflv/source/flv-parser.c
Normal file
89
src/3rdpart/media-server/libflv/source/flv-parser.c
Normal 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;
|
||||
}
|
||||
}
|
135
src/3rdpart/media-server/libflv/source/flv-reader.c
Normal file
135
src/3rdpart/media-server/libflv/source/flv-reader.c
Normal 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;
|
||||
}
|
117
src/3rdpart/media-server/libflv/source/flv-writer.c
Normal file
117
src/3rdpart/media-server/libflv/source/flv-writer.c
Normal 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;
|
||||
}
|
466
src/3rdpart/media-server/libflv/source/hevc-annexbtomp4.c
Normal file
466
src/3rdpart/media-server/libflv/source/hevc-annexbtomp4.c
Normal 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
|
59
src/3rdpart/media-server/libflv/source/hevc-mp4toannexb.c
Normal file
59
src/3rdpart/media-server/libflv/source/hevc-mp4toannexb.c
Normal 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);
|
||||
}
|
233
src/3rdpart/media-server/libflv/source/mp3-header.c
Normal file
233
src/3rdpart/media-server/libflv/source/mp3-header.c
Normal 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
|
453
src/3rdpart/media-server/libflv/source/mpeg4-aac-asc.c
Normal file
453
src/3rdpart/media-server/libflv/source/mpeg4-aac-asc.c
Normal file
@ -0,0 +1,453 @@
|
||||
#include "mpeg4-aac.h"
|
||||
#include "mpeg4-bits.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Table 4.85 - Syntactic elements (p533)
|
||||
enum {
|
||||
ID_SCE = 0x0, // single channel element()
|
||||
ID_CPE = 0x1, // channel_pair_element()
|
||||
ID_CCE = 0x2, // coupling_channel_element()
|
||||
ID_LFE = 0x3, // lfe_channel_element()
|
||||
ID_DSE = 0x4, // data_stream_element()
|
||||
ID_PCE = 0x5, // program_config_element()
|
||||
ID_FIL = 0x6, // fill_element()
|
||||
ID_END = 0x7,
|
||||
};
|
||||
|
||||
// ISO-14496-3 4.4.1.1 Program config element (p488)
|
||||
// There may be up to 16 such elements per raw data block, each one must have a unique element_instance_tag
|
||||
// PCEs must come before all other syntactic elements in a raw_data_block().
|
||||
/*
|
||||
program_config_element()
|
||||
{
|
||||
element_instance_tag; 4 uimsbf
|
||||
object_type; 2 uimsbf
|
||||
sampling_frequency_index; 4 uimsbf
|
||||
num_front_channel_elements; 4 uimsbf
|
||||
num_side_channel_elements; 4 uimsbf
|
||||
num_back_channel_elements; 4 uimsbf
|
||||
num_lfe_channel_elements; 2 uimsbf
|
||||
num_assoc_data_elements; 3 uimsbf
|
||||
num_valid_cc_elements; 4 uimsbf
|
||||
mono_mixdown_present; 1 uimsbf
|
||||
if (mono_mixdown_present == 1 )
|
||||
mono_mixdown_element_number; 4 uimsbf
|
||||
stereo_mixdown_present; 1 uimsbf
|
||||
if (stereo_mixdown_present == 1 )
|
||||
stereo_mixdown_element_number; 4 uimsbf
|
||||
matrix_mixdown_idx_present; 1 uimsbf
|
||||
if (matrix_mixdown_idx_present == 1 ) {
|
||||
matrix_mixdown_idx ; 2 uimsbf
|
||||
pseudo_surround_enable; 1 uimsbf
|
||||
}
|
||||
|
||||
for (i = 0; i < num_front_channel_elements; i++) {
|
||||
front_element_is_cpe[i]; 1 bslbf
|
||||
front_element_tag_select[i]; 4 uimsbf
|
||||
}
|
||||
for (i = 0; i < num_side_channel_elements; i++) {
|
||||
side_element_is_cpe[i]; 1 bslbf
|
||||
side_element_tag_select[i]; 4 uimsbf
|
||||
}
|
||||
for (i = 0; i < num_back_channel_elements; i++) {
|
||||
back_element_is_cpe[i]; 1 bslbf
|
||||
back_element_tag_select[i]; 4 uimsbf
|
||||
}
|
||||
for (i = 0; i < num_lfe_channel_elements; i++)
|
||||
lfe_element_tag_select[i]; 4 uimsbf
|
||||
for ( i = 0; i < num_assoc_data_elements; i++)
|
||||
assoc_data_element_tag_select[i]; 4 uimsbf
|
||||
for (i = 0; i < num_valid_cc_elements; i++) {
|
||||
cc_element_is_ind_sw[i]; 1 uimsbf
|
||||
valid_cc_element_tag_select[i]; 4 uimsbf
|
||||
}
|
||||
byte_alignment(); Note 1
|
||||
comment_field_bytes; 8 uimsbf
|
||||
for (i = 0; i < comment_field_bytes; i++)
|
||||
comment_field_data[i]; 8 uimsbf
|
||||
}
|
||||
*/
|
||||
static inline uint64_t mpeg4_bits_copy(struct mpeg4_bits_t* dst, struct mpeg4_bits_t* src, int n)
|
||||
{
|
||||
uint64_t v;
|
||||
v = mpeg4_bits_read_n(src, n);
|
||||
mpeg4_bits_write_n(dst, v, n);
|
||||
return v;
|
||||
}
|
||||
|
||||
static int mpeg4_aac_pce_load(struct mpeg4_bits_t* bits, struct mpeg4_aac_t* aac, struct mpeg4_bits_t* pce)
|
||||
{
|
||||
uint64_t i, cpe, tag;
|
||||
uint64_t element_instance_tag;
|
||||
uint64_t object_type;
|
||||
uint64_t sampling_frequency_index;
|
||||
uint64_t num_front_channel_elements;
|
||||
uint64_t num_side_channel_elements;
|
||||
uint64_t num_back_channel_elements;;
|
||||
uint64_t num_lfe_channel_elements;
|
||||
uint64_t num_assoc_data_elements;
|
||||
uint64_t num_valid_cc_elements;
|
||||
uint64_t comment_field_bytes;
|
||||
|
||||
aac->channels = 0;
|
||||
element_instance_tag = mpeg4_bits_copy(pce, bits, 4);
|
||||
object_type = mpeg4_bits_copy(pce, bits, 2);
|
||||
sampling_frequency_index = mpeg4_bits_copy(pce, bits, 4);
|
||||
num_front_channel_elements = mpeg4_bits_copy(pce, bits, 4);
|
||||
num_side_channel_elements = mpeg4_bits_copy(pce, bits, 4);
|
||||
num_back_channel_elements = mpeg4_bits_copy(pce, bits, 4);
|
||||
num_lfe_channel_elements = mpeg4_bits_copy(pce, bits, 2);
|
||||
num_assoc_data_elements = mpeg4_bits_copy(pce, bits, 3);
|
||||
num_valid_cc_elements = mpeg4_bits_copy(pce, bits, 4);
|
||||
|
||||
if (mpeg4_bits_copy(pce, bits, 1))
|
||||
mpeg4_bits_copy(pce, bits, 4); // MONO
|
||||
if (mpeg4_bits_copy(pce, bits, 1))
|
||||
mpeg4_bits_copy(pce, bits, 4); // STEREO
|
||||
if (mpeg4_bits_copy(pce, bits, 1))
|
||||
mpeg4_bits_copy(pce, bits, 3); // Matrix, Pseudo surround
|
||||
|
||||
for (i = 0; i < num_front_channel_elements; i++)
|
||||
{
|
||||
cpe = mpeg4_bits_copy(pce, bits, 1); // front_element_is_cpe
|
||||
tag = mpeg4_bits_copy(pce, bits, 4); // front_element_tag_select
|
||||
aac->channels += (cpe || aac->ps) ? 2 : 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_side_channel_elements; i++)
|
||||
{
|
||||
cpe = mpeg4_bits_copy(pce, bits, 1); // side_element_is_cpe
|
||||
tag = mpeg4_bits_copy(pce, bits, 4); // side_element_tag_select
|
||||
aac->channels += (cpe || aac->ps) ? 2 : 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_back_channel_elements; i++)
|
||||
{
|
||||
cpe = mpeg4_bits_copy(pce, bits, 1); // back_element_is_cpe
|
||||
tag = mpeg4_bits_copy(pce, bits, 4); // back_element_tag_select
|
||||
aac->channels += (cpe || aac->ps) ? 2 : 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_lfe_channel_elements; i++)
|
||||
{
|
||||
tag = mpeg4_bits_copy(pce, bits, 4); // lfe_element_tag_select
|
||||
aac->channels += 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_assoc_data_elements; i++)
|
||||
{
|
||||
tag = mpeg4_bits_copy(pce, bits, 4); // assoc_data_element_tag_select
|
||||
}
|
||||
|
||||
for (i = 0; i < num_valid_cc_elements; i++)
|
||||
{
|
||||
cpe = mpeg4_bits_copy(pce, bits, 1); // cc_element_is_ind_sw
|
||||
tag = mpeg4_bits_copy(pce, bits, 4); // valid_cc_element_tag_select
|
||||
}
|
||||
|
||||
mpeg4_bits_aligment(bits, 8); // byte_alignment();
|
||||
mpeg4_bits_aligment(pce, 8);
|
||||
|
||||
comment_field_bytes = mpeg4_bits_copy(pce, bits, 8);
|
||||
for (i = 0; i < comment_field_bytes; i++)
|
||||
mpeg4_bits_copy(pce, bits, 8); // comment_field_data
|
||||
|
||||
assert(aac->sampling_frequency_index == sampling_frequency_index);
|
||||
assert(aac->profile == object_type + 1);
|
||||
return (int)((pce->bits + 7) / 8);
|
||||
}
|
||||
|
||||
// 4.4.1 Decoder configuration (GASpecificConfig) (p487)
|
||||
/*
|
||||
GASpecificConfig (samplingFrequencyIndex, channelConfiguration, audioObjectType)
|
||||
{
|
||||
frameLengthFlag; 1 bslbf
|
||||
dependsOnCoreCoder; 1 bslbf
|
||||
if (dependsOnCoreCoder) {
|
||||
coreCoderDelay; 14 uimsbf
|
||||
}
|
||||
extensionFlag; 1 bslbf
|
||||
if (! channelConfiguration) {
|
||||
program_config_element ();
|
||||
}
|
||||
if ((audioObjectType == 6) || (audioObjectType == 20)) {
|
||||
layerNr; 3 uimsbf
|
||||
}
|
||||
if (extensionFlag) {
|
||||
if (audioObjectType == 22) {
|
||||
numOfSubFrame; 5 bslbf
|
||||
layer_length; 11 bslbf
|
||||
}
|
||||
if (audioObjectType == 17 || audioObjectType == 19 || audioObjectType == 20 || audioObjectType == 23) {
|
||||
aacSectionDataResilienceFlag; 1 bslbf
|
||||
aacScalefactorDataResilienceFlag; 1 bslbf
|
||||
aacSpectralDataResilienceFlag; 1 bslbf
|
||||
}
|
||||
extensionFlag3; 1 bslbf
|
||||
if (extensionFlag3) {
|
||||
// tbd in version 3
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
static int mpeg4_aac_ga_specific_config_load(struct mpeg4_bits_t* bits, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
int extensionFlag;
|
||||
struct mpeg4_bits_t pce;
|
||||
|
||||
mpeg4_bits_read(bits); // frameLengthFlag
|
||||
if (mpeg4_bits_read(bits)) // dependsOnCoreCoder
|
||||
mpeg4_bits_read_uint16(bits, 14); // coreCoderDelay
|
||||
extensionFlag = mpeg4_bits_read(bits); // extensionFlag
|
||||
|
||||
if (0 == aac->channel_configuration)
|
||||
{
|
||||
mpeg4_bits_init(&pce, aac->pce, sizeof(aac->pce));
|
||||
aac->npce = mpeg4_aac_pce_load(bits, aac, &pce); // update channel count
|
||||
}
|
||||
|
||||
if (6 == aac->profile || 20 == aac->profile)
|
||||
mpeg4_bits_read_uint8(bits, 3); // layerNr
|
||||
|
||||
if (extensionFlag)
|
||||
{
|
||||
if (22 == aac->profile)
|
||||
{
|
||||
mpeg4_bits_read_uint8(bits, 5); // numOfSubFrame
|
||||
mpeg4_bits_read_uint16(bits, 11); // layer_length
|
||||
}
|
||||
|
||||
if (17 == aac->profile || 19 == aac->profile || 20 == aac->profile || 23 == aac->profile)
|
||||
{
|
||||
mpeg4_bits_read(bits); // aacSectionDataResilienceFlag
|
||||
mpeg4_bits_read(bits); // aacScalefactorDataResilienceFlag
|
||||
mpeg4_bits_read(bits); // aacSpectralDataResilienceFlag
|
||||
}
|
||||
|
||||
if (mpeg4_bits_read(bits)) // extensionFlag3
|
||||
{
|
||||
// tbd in version 3
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
return mpeg4_bits_error(bits);
|
||||
}
|
||||
|
||||
static int mpeg4_aac_celp_specific_config_load(struct mpeg4_bits_t* bits, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
int ExcitationMode;
|
||||
if (mpeg4_bits_read(bits)) // isBaseLayer
|
||||
{
|
||||
// CelpHeader
|
||||
|
||||
ExcitationMode = mpeg4_bits_read(bits);
|
||||
mpeg4_bits_read(bits); // SampleRateMode
|
||||
mpeg4_bits_read(bits); // FineRateControl
|
||||
|
||||
// Table 3.50 - Description of ExcitationMode
|
||||
if (ExcitationMode == 1 /*RPE*/)
|
||||
{
|
||||
mpeg4_bits_read_n(bits, 3); // RPE_Configuration
|
||||
}
|
||||
if (ExcitationMode == 0 /*MPE*/)
|
||||
{
|
||||
mpeg4_bits_read_n(bits, 5); // MPE_Configuration
|
||||
mpeg4_bits_read_n(bits, 2); // NumEnhLayers
|
||||
mpeg4_bits_read(bits); // BandwidthScalabilityMode
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mpeg4_bits_read(bits)) // isBWSLayer
|
||||
mpeg4_bits_read_n(bits, 2); // BWS_configuration
|
||||
else
|
||||
mpeg4_bits_read_n(bits, 2); // CELP-BRS-id
|
||||
}
|
||||
|
||||
return mpeg4_bits_error(bits);
|
||||
}
|
||||
|
||||
static inline uint8_t mpeg4_aac_get_audio_object_type(struct mpeg4_bits_t* bits)
|
||||
{
|
||||
uint8_t audioObjectType;
|
||||
audioObjectType = mpeg4_bits_read_uint8(bits, 5);
|
||||
if (31 == audioObjectType)
|
||||
audioObjectType = 32 + mpeg4_bits_read_uint8(bits, 6);
|
||||
return audioObjectType;
|
||||
}
|
||||
|
||||
static inline uint8_t mpeg4_aac_get_sampling_frequency(struct mpeg4_bits_t* bits)
|
||||
{
|
||||
uint8_t samplingFrequencyIndex;
|
||||
uint32_t samplingFrequency;
|
||||
samplingFrequencyIndex = mpeg4_bits_read_uint8(bits, 4);
|
||||
if (0x0F == samplingFrequencyIndex)
|
||||
samplingFrequency = mpeg4_bits_read_uint32(bits, 24);
|
||||
return samplingFrequencyIndex;
|
||||
}
|
||||
|
||||
int mpeg4_aac_audio_specific_config_load2(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
uint16_t syncExtensionType;
|
||||
// uint8_t audioObjectType;
|
||||
uint8_t extensionAudioObjectType = 0;
|
||||
// uint8_t samplingFrequencyIndex = 0;
|
||||
uint8_t extensionSamplingFrequencyIndex = 0;
|
||||
// uint8_t channelConfiguration = 0;
|
||||
uint8_t extensionChannelConfiguration = 0;
|
||||
uint8_t epConfig;
|
||||
struct mpeg4_bits_t bits;
|
||||
mpeg4_bits_init(&bits, (void*)data, bytes);
|
||||
|
||||
aac->profile = mpeg4_aac_get_audio_object_type(&bits);
|
||||
aac->sampling_frequency_index = mpeg4_aac_get_sampling_frequency(&bits);
|
||||
aac->channel_configuration = mpeg4_bits_read_uint8(&bits, 4);
|
||||
|
||||
if (5 == aac->profile || 29 == aac->profile)
|
||||
{
|
||||
extensionAudioObjectType = 5;
|
||||
aac->sbr = 1;
|
||||
if (29 == aac->profile)
|
||||
aac->ps = 1;
|
||||
extensionSamplingFrequencyIndex = mpeg4_aac_get_sampling_frequency(&bits);
|
||||
aac->profile = mpeg4_aac_get_audio_object_type(&bits);
|
||||
if (22 == aac->profile)
|
||||
extensionChannelConfiguration = mpeg4_bits_read_uint8(&bits, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
extensionAudioObjectType = 0;
|
||||
}
|
||||
|
||||
switch (aac->profile)
|
||||
{
|
||||
case 1: case 2: case 3: case 4: case 6: case 7:
|
||||
case 17: case 19: case 20: case 21: case 22: case 23:
|
||||
mpeg4_aac_ga_specific_config_load(&bits, aac);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
mpeg4_aac_celp_specific_config_load(&bits, aac);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
switch (aac->profile)
|
||||
{
|
||||
case 17: case 19: case 20: case 21: case 22:
|
||||
case 23: case 24: case 25: case 26: case 27: case 39:
|
||||
epConfig = mpeg4_bits_read_uint8(&bits, 2);
|
||||
if (2 == epConfig || 3 == epConfig)
|
||||
{
|
||||
// 1.8.2.1 Error protection specific configuration (p96)
|
||||
// TODO: ErrorProtectionSpecificConfig();
|
||||
assert(0);
|
||||
}
|
||||
if (3 == epConfig)
|
||||
{
|
||||
if (mpeg4_bits_read(&bits)) // directMapping
|
||||
{
|
||||
// tbd
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: break; // do nothing;
|
||||
}
|
||||
|
||||
if (5 != extensionAudioObjectType && mpeg4_bits_remain(&bits) >= 16)
|
||||
{
|
||||
syncExtensionType = mpeg4_bits_read_uint16(&bits, 11);
|
||||
if (0x2b7 == syncExtensionType)
|
||||
{
|
||||
extensionAudioObjectType = mpeg4_aac_get_audio_object_type(&bits);
|
||||
if (5 == extensionAudioObjectType)
|
||||
{
|
||||
aac->sbr = mpeg4_bits_read(&bits);
|
||||
if (aac->sbr)
|
||||
{
|
||||
extensionSamplingFrequencyIndex = mpeg4_aac_get_sampling_frequency(&bits);
|
||||
if (mpeg4_bits_remain(&bits) >= 12)
|
||||
{
|
||||
syncExtensionType = mpeg4_bits_read_uint16(&bits, 11);
|
||||
if (0x548 == syncExtensionType)
|
||||
aac->ps = mpeg4_bits_read(&bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (22 == extensionAudioObjectType)
|
||||
{
|
||||
aac->sbr = mpeg4_bits_read(&bits);
|
||||
if (aac->sbr)
|
||||
extensionSamplingFrequencyIndex = mpeg4_aac_get_sampling_frequency(&bits);
|
||||
extensionChannelConfiguration = mpeg4_bits_read_uint8(&bits, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mpeg4_bits_aligment(&bits, 8);
|
||||
return mpeg4_bits_error(&bits) ? -1 : (int)(bits.bits / 8);
|
||||
}
|
||||
|
||||
int mpeg4_aac_audio_specific_config_save2(const struct mpeg4_aac_t* aac, uint8_t* data, size_t bytes)
|
||||
{
|
||||
if (bytes < 2 + (size_t)aac->npce)
|
||||
return -1;
|
||||
|
||||
memcpy(data + 2, aac->pce, aac->npce);
|
||||
return 2 + aac->npce;
|
||||
//data[2 + aac->npce] = 0x56;
|
||||
//data[2 + aac->npce + 1] = 0xe5;
|
||||
//data[2 + aac->npce + 2] = 0x00;
|
||||
//return 2 + aac->npce + 3;
|
||||
}
|
||||
|
||||
int mpeg4_aac_adts_pce_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
uint8_t i;
|
||||
size_t offset = 7;
|
||||
struct mpeg4_bits_t bits, pce;
|
||||
|
||||
if (0 == (data[1] & 0x01)) // protection_absent
|
||||
{
|
||||
// number_of_raw_data_blocks_in_frame
|
||||
for (i = 1; i <= (data[6] & 0x03); i++)
|
||||
offset += 2; // raw_data_block_position 16-bits
|
||||
offset += 2; // crc_check 16-bits
|
||||
}
|
||||
|
||||
if (bytes <= offset)
|
||||
return (int)offset;
|
||||
|
||||
mpeg4_bits_init(&bits, (uint8_t*)data + offset, bytes - offset);
|
||||
if (ID_PCE == mpeg4_bits_read_uint8(&bits, 3))
|
||||
{
|
||||
mpeg4_bits_init(&pce, aac->pce, sizeof(aac->pce));
|
||||
aac->npce = mpeg4_aac_pce_load(&bits, aac, &pce);
|
||||
return mpeg4_bits_error(&bits) ? -1 : (int)(7 + (pce.bits + 7) / 8);
|
||||
}
|
||||
return 7;
|
||||
}
|
||||
|
||||
int mpeg4_aac_adts_pce_save(uint8_t* data, size_t bytes, const struct mpeg4_aac_t* aac)
|
||||
{
|
||||
struct mpeg4_aac_t src;
|
||||
struct mpeg4_bits_t pce, adts;
|
||||
if ((size_t)aac->npce + 7 > bytes)
|
||||
return 0;
|
||||
memcpy(&src, aac, sizeof(src));
|
||||
// assert(data[1] & 0x01); // disable protection_absent
|
||||
mpeg4_bits_init(&pce, (uint8_t*)aac->pce, aac->npce);
|
||||
mpeg4_bits_init(&adts, (uint8_t*)data + 7, bytes - 7);
|
||||
mpeg4_bits_write_uint8(&adts, ID_PCE, 3);
|
||||
mpeg4_aac_pce_load(&pce, &src, &adts);
|
||||
assert(src.channels == aac->channels);
|
||||
return mpeg4_bits_error(&pce) ? 0 : (int)((7 + (adts.bits+7) / 8));
|
||||
}
|
310
src/3rdpart/media-server/libflv/source/mpeg4-aac.c
Normal file
310
src/3rdpart/media-server/libflv/source/mpeg4-aac.c
Normal file
@ -0,0 +1,310 @@
|
||||
#include "mpeg4-aac.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int mpeg4_aac_adts_pce_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac);
|
||||
int mpeg4_aac_adts_pce_save(uint8_t* data, size_t bytes, const struct mpeg4_aac_t* aac);
|
||||
int mpeg4_aac_audio_specific_config_load2(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac);
|
||||
int mpeg4_aac_audio_specific_config_save2(const struct mpeg4_aac_t* aac, uint8_t* data, size_t bytes);
|
||||
|
||||
/*
|
||||
// ISO-14496-3 adts_frame (p122)
|
||||
|
||||
adts_fixed_header()
|
||||
{
|
||||
syncword; 12 bslbf
|
||||
ID; 1 bslbf
|
||||
layer; 2 uimsbf
|
||||
protection_absent; 1 bslbf
|
||||
profile_ObjectType; 2 uimsbf
|
||||
sampling_frequency_index; 4 uimsbf
|
||||
private_bit; 1 bslbf
|
||||
channel_configuration; 3 uimsbf
|
||||
original_copy; 1 bslbf
|
||||
home; 1 bslbf
|
||||
}
|
||||
|
||||
adts_variable_header()
|
||||
{
|
||||
copyright_identification_bit; 1 bslbf
|
||||
copyright_identification_start; 1 bslbf
|
||||
aac_frame_length; 13 bslbf
|
||||
adts_buffer_fullness; 11 bslbf
|
||||
number_of_raw_data_blocks_in_frame; 2 uimsbf
|
||||
}
|
||||
*/
|
||||
/// @return >=0-adts header length, <0-error
|
||||
int mpeg4_aac_adts_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
if (bytes < 7) return -1;
|
||||
|
||||
memset(aac, 0, sizeof(struct mpeg4_aac_t));
|
||||
assert(0xFF == data[0] && 0xF0 == (data[1] & 0xF0)); /* syncword */
|
||||
aac->profile = ((data[2] >> 6) & 0x03) + 1; // 2 bits: the MPEG-2 Audio Object Type add 1
|
||||
aac->sampling_frequency_index = (data[2] >> 2) & 0x0F; // 4 bits: MPEG-4 Sampling Frequency Index (15 is forbidden)
|
||||
aac->channel_configuration = ((data[2] & 0x01) << 2) | ((data[3] >> 6) & 0x03); // 3 bits: MPEG-4 Channel Configuration
|
||||
assert(aac->profile > 0 && aac->profile < 31);
|
||||
assert(aac->channel_configuration >= 0 && aac->channel_configuration <= 7);
|
||||
assert(aac->sampling_frequency_index >= 0 && aac->sampling_frequency_index <= 0xc);
|
||||
aac->channels = aac->channel_configuration;
|
||||
aac->sampling_frequency = mpeg4_aac_audio_frequency_to(aac->sampling_frequency_index);
|
||||
|
||||
if (0 == aac->channel_configuration)
|
||||
return mpeg4_aac_adts_pce_load(data, bytes, aac);
|
||||
return 7;
|
||||
}
|
||||
|
||||
/// @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)
|
||||
{
|
||||
const uint8_t ID = 0; // 0-MPEG4/1-MPEG2
|
||||
size_t len = payload + 7;
|
||||
if (bytes < 7 || len >= (1 << 12)) return -1;
|
||||
|
||||
if (0 == aac->channel_configuration && aac->npce > 0)
|
||||
len += mpeg4_aac_adts_pce_save(data, bytes, aac);
|
||||
|
||||
assert(aac->profile > 0 && aac->profile < 31);
|
||||
assert(aac->channel_configuration >= 0 && aac->channel_configuration <= 7);
|
||||
assert(aac->sampling_frequency_index >= 0 && aac->sampling_frequency_index <= 0xc);
|
||||
data[0] = 0xFF; /* 12-syncword */
|
||||
data[1] = 0xF0 /* 12-syncword */ | (ID << 3)/*1-ID*/ | (0x00 << 2) /*2-layer*/ | 0x01 /*1-protection_absent*/;
|
||||
data[2] = ((aac->profile - 1) << 6) | ((aac->sampling_frequency_index & 0x0F) << 2) | ((aac->channel_configuration >> 2) & 0x01);
|
||||
data[3] = ((aac->channel_configuration & 0x03) << 6) | ((len >> 11) & 0x03); /*0-original_copy*/ /*0-home*/ /*0-copyright_identification_bit*/ /*0-copyright_identification_start*/
|
||||
data[4] = (uint8_t)(len >> 3);
|
||||
data[5] = ((len & 0x07) << 5) | 0x1F;
|
||||
data[6] = 0xFC /*| ((len / (1024 * aac->channels)) & 0x03)*/;
|
||||
return (int)(len - payload);
|
||||
}
|
||||
|
||||
int mpeg4_aac_adts_frame_length(const uint8_t* data, size_t bytes)
|
||||
{
|
||||
uint16_t len;
|
||||
if (bytes < 7) return -1;
|
||||
assert(0xFF == data[0] && 0xF0 == (data[1] & 0xF0)); /* syncword */
|
||||
len = ((uint16_t)(data[3] & 0x03) << 11) | ((uint16_t)data[4] << 3) | ((uint16_t)(data[5] >> 5) & 0x07);
|
||||
return len;
|
||||
}
|
||||
|
||||
// ISO-14496-3 AudioSpecificConfig (p52)
|
||||
/*
|
||||
audioObjectType; 5 uimsbf
|
||||
if (audioObjectType == 31) {
|
||||
audioObjectType = 32 + audioObjectTypeExt; 6 uimsbf
|
||||
}
|
||||
samplingFrequencyIndex; 4 bslbf
|
||||
if ( samplingFrequencyIndex == 0xf ) {
|
||||
samplingFrequency; 24 uimsbf
|
||||
}
|
||||
channelConfiguration; 4 bslbf
|
||||
*/
|
||||
/// @return >=0-adts header length, <0-error
|
||||
int mpeg4_aac_audio_specific_config_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
if (bytes < 2) return -1;
|
||||
|
||||
memset(aac, 0, sizeof(struct mpeg4_aac_t));
|
||||
aac->profile = (data[0] >> 3) & 0x1F;
|
||||
aac->sampling_frequency_index = ((data[0] & 0x7) << 1) | ((data[1] >> 7) & 0x01);
|
||||
aac->channel_configuration = (data[1] >> 3) & 0x0F;
|
||||
assert(aac->profile > 0 && aac->profile < 31);
|
||||
assert(aac->channel_configuration >= 0 && aac->channel_configuration <= 7);
|
||||
assert(aac->sampling_frequency_index >= 0 && aac->sampling_frequency_index <= 0xc);
|
||||
aac->channels = aac->channel_configuration;
|
||||
aac->sampling_frequency = mpeg4_aac_audio_frequency_to(aac->sampling_frequency_index);
|
||||
|
||||
if (bytes > 2)
|
||||
return mpeg4_aac_audio_specific_config_load2(data, bytes, aac);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// ISO-14496-3 AudioSpecificConfig
|
||||
int mpeg4_aac_audio_specific_config_save(const struct mpeg4_aac_t* aac, uint8_t* data, size_t bytes)
|
||||
{
|
||||
uint8_t channel_configuration;
|
||||
if (bytes < 2+ (size_t)aac->npce) return -1;
|
||||
|
||||
channel_configuration = aac->npce > 0 ? 0 : aac->channel_configuration;
|
||||
assert(aac->profile > 0 && aac->profile < 31);
|
||||
assert(aac->channel_configuration >= 0 && aac->channel_configuration <= 7);
|
||||
assert(aac->sampling_frequency_index >= 0 && aac->sampling_frequency_index <= 0xc);
|
||||
data[0] = (aac->profile << 3) | ((aac->sampling_frequency_index >> 1) & 0x07);
|
||||
data[1] = ((aac->sampling_frequency_index & 0x01) << 7) | ((channel_configuration & 0xF) << 3) | (0 << 2) /* frame length-1024 samples*/ | (0 << 1) /* don't depend on core */ | 0 /* not extension */;
|
||||
|
||||
if (0 == aac->channel_configuration && aac->npce > 0)
|
||||
return mpeg4_aac_audio_specific_config_save2(aac, data, bytes);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// ISO/IEC 14496-3:2009(E) Table 1.42 - Syntax of StreamMuxConfig() (p83)
|
||||
int mpeg4_aac_stream_mux_config_load(const uint8_t* data, size_t bytes, struct mpeg4_aac_t* aac)
|
||||
{
|
||||
if (bytes < 6) return -1;
|
||||
|
||||
memset(aac, 0, sizeof(*aac));
|
||||
assert(0 == (0x80 & data[0])); // audioMuxVersion: 0
|
||||
// [0] 0-audioMuxVersion(1), 1-allStreamsSameTimeFraming(1), 0-numSubFrames(6)
|
||||
aac->profile = ((data[1] & 0x01) << 4) | (data[2] >> 4); // 0-numProgram(4), 0-numLayer(3), 1-ASC(1)
|
||||
aac->sampling_frequency_index = data[2] & 0x0F;
|
||||
aac->channel_configuration = data[3] >> 4;
|
||||
assert(aac->profile > 0 && aac->profile < 31);
|
||||
assert(aac->channel_configuration >= 0 && aac->channel_configuration <= 7);
|
||||
assert(aac->sampling_frequency_index >= 0 && aac->sampling_frequency_index <= 0xc);
|
||||
aac->channels = aac->channel_configuration;
|
||||
aac->sampling_frequency = mpeg4_aac_audio_frequency_to(aac->sampling_frequency_index);
|
||||
return 6;
|
||||
}
|
||||
|
||||
// ISO/IEC 14496-3:2009(E) Table 1.42 - Syntax of StreamMuxConfig() (p83)
|
||||
int mpeg4_aac_stream_mux_config_save(const struct mpeg4_aac_t* aac, uint8_t* data, size_t bytes)
|
||||
{
|
||||
if (bytes < 6) return -1;
|
||||
|
||||
assert(aac->profile > 0 && aac->profile < 31);
|
||||
assert(aac->channel_configuration >= 0 && aac->channel_configuration <= 7);
|
||||
assert(aac->sampling_frequency_index >= 0 && aac->sampling_frequency_index <= 0xc);
|
||||
data[0] = 0x40; // 0-audioMuxVersion(1), 1-allStreamsSameTimeFraming(1), 0-numSubFrames(6)
|
||||
//data[1] = 0x00 | ((aac->profile >> 4) & 0x01); // 0-numProgram(4), 0-numLayer(3)
|
||||
//data[2] = ((aac->profile & 0x0F) << 4) | (aac->sampling_frequency_index & 0x0F);
|
||||
data[1] = 0x00;
|
||||
data[2] = 0x20 | (aac->sampling_frequency_index & 0x0F); // AAC_LC profile
|
||||
data[3] = ((aac->channel_configuration & 0x0F) << 4) | 0; // 0-GASpecificConfig(3), 0-frameLengthType(1)
|
||||
data[4] = 0x3F; // 0-frameLengthType(2), 111111-latmBufferFullness(6)
|
||||
data[5] = 0xC0; // 11-latmBufferFullness(2), 0-otherDataPresent, 0-crcCheckPresent
|
||||
return 6;
|
||||
}
|
||||
|
||||
// Table 1.6 ¨C Levels for the High Quality Audio Profile
|
||||
static int mpeg4_aac_high_quality_level(const struct mpeg4_aac_t* aac)
|
||||
{
|
||||
if (aac->sampling_frequency <= 22050)
|
||||
{
|
||||
if (aac->channel_configuration <= 2)
|
||||
return 1; // Level 1/5
|
||||
}
|
||||
else if (aac->sampling_frequency <= 48000)
|
||||
{
|
||||
if (aac->channel_configuration <= 2)
|
||||
return 2; // Level 2/6
|
||||
else if (aac->channel_configuration <= 5)
|
||||
return 3; // Level 3/4/7/8
|
||||
}
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
// Table 1.10 ¨C Levels for the AAC Profile
|
||||
static int mpeg4_aac_level(const struct mpeg4_aac_t* aac)
|
||||
{
|
||||
if (aac->sampling_frequency <= 24000)
|
||||
{
|
||||
if (aac->channel_configuration <= 2)
|
||||
return 1; // AAC Profile, Level 1
|
||||
}
|
||||
else if (aac->sampling_frequency <= 48000)
|
||||
{
|
||||
if (aac->channel_configuration <= 2)
|
||||
return 2; // Level 2
|
||||
else if (aac->channel_configuration <= 5)
|
||||
return 4; // Level 4
|
||||
}
|
||||
else if (aac->sampling_frequency <= 96000)
|
||||
{
|
||||
if (aac->channel_configuration <= 5)
|
||||
return 5; // Level 5
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
static int mpeg4_aac_he_level(const struct mpeg4_aac_t* aac)
|
||||
{
|
||||
if (aac->sampling_frequency <= 48000)
|
||||
{
|
||||
if (aac->channel_configuration <= 2)
|
||||
return aac->sbr ? 3 : 2; // Level 2/3
|
||||
else if (aac->channel_configuration <= 5)
|
||||
return 4; // Level 4
|
||||
}
|
||||
else if (aac->sampling_frequency <= 96000)
|
||||
{
|
||||
if (aac->channel_configuration <= 5)
|
||||
return 5; // Level 5
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
// ISO/IEC 14496-3:2009(E) Table 1.14 - audioProfileLevelIndication values (p51)
|
||||
int mpeg4_aac_profile_level(const struct mpeg4_aac_t* aac)
|
||||
{
|
||||
// Table 1.10 - Levels for the AAC Profile (p49)
|
||||
// Table 1.14 - audioProfileLevelIndication values (p51)
|
||||
switch (aac->profile)
|
||||
{
|
||||
case MPEG4_AAC_LC:
|
||||
return mpeg4_aac_level(aac) - 1 + 0x28; // AAC Profile
|
||||
case MPEG4_AAC_SBR:
|
||||
return mpeg4_aac_he_level(aac) - 2 + 0x2C; // High Efficiency AAC Profile
|
||||
case MPEG4_AAC_PS:
|
||||
return mpeg4_aac_he_level(aac) - 2 + 0x30; // High Efficiency AAC v2 Profile
|
||||
case MPEG4_AAC_CELP:
|
||||
return mpeg4_aac_high_quality_level(aac) - 1 + 0x0E; // High Quality Audio Profile
|
||||
default:
|
||||
return 1; // Main Audio Profile, Level 1
|
||||
}
|
||||
}
|
||||
|
||||
#define ARRAYOF(arr) sizeof(arr)/sizeof(arr[0])
|
||||
|
||||
static const int s_frequency[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 };
|
||||
|
||||
int mpeg4_aac_audio_frequency_to(enum mpeg4_aac_frequency index)
|
||||
{
|
||||
if (index < 0 || index >= ARRAYOF(s_frequency))
|
||||
return 0;
|
||||
return s_frequency[index];
|
||||
}
|
||||
|
||||
int mpeg4_aac_audio_frequency_from(int frequence)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < ARRAYOF(s_frequency) && s_frequency[i] != frequence) i++;
|
||||
return i >= ARRAYOF(s_frequency) ? -1 : i;
|
||||
}
|
||||
|
||||
#undef ARRAYOF
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
void mpeg4_aac_test(void)
|
||||
{
|
||||
struct mpeg4_aac_t aac, aac2;
|
||||
const unsigned char asc[] = { 0x13, 0x88 };
|
||||
const unsigned char adts[] = { 0xFF, 0xF1, 0x5C, 0x40, 0x01, 0x1F, 0xFC };
|
||||
// const unsigned char ascsbr[] = { 0x13, 0x10, 0x56, 0xe5, 0x9d, 0x48, 0x00 };
|
||||
const unsigned char ascsbr[] = { 0x2b, 0x92, 0x08, 0x00 };
|
||||
|
||||
unsigned char data[8];
|
||||
|
||||
assert(sizeof(ascsbr) == mpeg4_aac_audio_specific_config_load(ascsbr, sizeof(ascsbr), &aac));
|
||||
assert(2 == aac.profile && 7 == aac.sampling_frequency_index && 2 == aac.channel_configuration);
|
||||
//assert(sizeof(ascsbr) == mpeg4_aac_audio_specific_config_save(&aac, data, sizeof(data)));
|
||||
//assert(0 == memcmp(ascsbr, data, sizeof(ascsbr)));
|
||||
|
||||
assert(sizeof(asc) == mpeg4_aac_audio_specific_config_load(asc, sizeof(asc), &aac));
|
||||
assert(2 == aac.profile && 7 == aac.sampling_frequency_index && 1 == aac.channel_configuration);
|
||||
assert(sizeof(asc) == mpeg4_aac_audio_specific_config_save(&aac, data, sizeof(data)));
|
||||
assert(0 == memcmp(asc, data, sizeof(asc)));
|
||||
|
||||
assert(sizeof(adts) == mpeg4_aac_adts_save(&aac, 1, data, sizeof(data)));
|
||||
assert(0 == memcmp(adts, data, sizeof(adts)));
|
||||
assert(7 == mpeg4_aac_adts_load(data, sizeof(adts), &aac2));
|
||||
assert(0 == memcmp(&aac, &aac2, sizeof(aac)));
|
||||
|
||||
assert(22050 == mpeg4_aac_audio_frequency_to(aac.sampling_frequency_index));
|
||||
assert(aac.sampling_frequency_index == mpeg4_aac_audio_frequency_from(22050));
|
||||
|
||||
//assert(sizeof(ascsbr) == mpeg4_aac_audio_specific_config_load(ascsbr, sizeof(ascsbr), &aac));
|
||||
//assert(2 == aac.profile && 6 == aac.sampling_frequency_index && 1 == aac.channel_configuration);
|
||||
}
|
||||
#endif
|
401
src/3rdpart/media-server/libflv/source/mpeg4-annexbtomp4.c
Normal file
401
src/3rdpart/media-server/libflv/source/mpeg4-annexbtomp4.c
Normal file
@ -0,0 +1,401 @@
|
||||
// ISO/IEC 14496-1:2010(E)
|
||||
// Annex I: Usage of ITU-T Recommendation H.264 | ISO/IEC 14496-10 AVC (p150)
|
||||
//
|
||||
// 1. Start Codes shall not be present in the stream. The field indicating the size of each following NAL unit
|
||||
// shall be added before NAL unit.The size of this field is defined in DecoderSpecificInfo.
|
||||
// 2. It is recommended encapsulating one NAL unit in one SL packet when it is delivered over lossy environment.
|
||||
|
||||
#include "mpeg4-avc.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define H264_NAL_IDR 5 // Coded slice of an IDR picture
|
||||
#define H264_NAL_SPS 7 // Sequence parameter set
|
||||
#define H264_NAL_PPS 8 // Picture parameter set
|
||||
#define H264_NAL_AUD 9 // Access unit delimiter
|
||||
|
||||
struct h264_annexbtomp4_handle_t
|
||||
{
|
||||
struct mpeg4_avc_t* avc;
|
||||
int errcode;
|
||||
int* update; // avc sps/pps update flags
|
||||
int* vcl;
|
||||
|
||||
uint8_t* out;
|
||||
int bytes;
|
||||
int capacity;
|
||||
};
|
||||
|
||||
static const uint8_t* h264_startcode(const uint8_t *data, int bytes)
|
||||
{
|
||||
int i;
|
||||
for (i = 2; i + 1 < bytes; i++)
|
||||
{
|
||||
if (0x01 == data[i] && 0x00 == data[i - 1] && 0x00 == data[i - 2])
|
||||
return data + i + 1;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///@param[in] h264 H.264 byte stream format data(A set of NAL units)
|
||||
void mpeg4_h264_annexb_nalu(const void* h264, int bytes, void (*handler)(void* param, const uint8_t* nalu, int bytes), void* param)
|
||||
{
|
||||
ptrdiff_t n;
|
||||
const uint8_t* p, *next, *end;
|
||||
|
||||
end = (const uint8_t*)h264 + bytes;
|
||||
p = h264_startcode((const uint8_t*)h264, bytes);
|
||||
|
||||
while (p)
|
||||
{
|
||||
next = h264_startcode(p, (int)(end - p));
|
||||
if (next)
|
||||
{
|
||||
n = next - p - 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = end - p;
|
||||
}
|
||||
|
||||
while (n > 0 && 0 == p[n - 1]) n--; // filter tailing zero
|
||||
|
||||
assert(n > 0);
|
||||
if (n > 0)
|
||||
{
|
||||
handler(param, p, (int)n);
|
||||
}
|
||||
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t mpeg4_h264_read_ue(const uint8_t* data, int bytes, int* offset)
|
||||
{
|
||||
int bit, i;
|
||||
int leadingZeroBits = -1;
|
||||
|
||||
for (bit = 0; !bit && *offset / 8 < bytes; ++leadingZeroBits)
|
||||
{
|
||||
bit = (data[*offset / 8] >> (7 - (*offset % 8))) & 0x01;
|
||||
++*offset;
|
||||
}
|
||||
|
||||
bit = 0;
|
||||
assert(leadingZeroBits < 32);
|
||||
for (i = 0; i < leadingZeroBits && *offset / 8 < bytes; i++)
|
||||
{
|
||||
bit = (bit << 1) | ((data[*offset / 8] >> (7 - (*offset % 8))) & 0x01);
|
||||
++*offset;
|
||||
}
|
||||
|
||||
return (uint8_t)((1 << leadingZeroBits) - 1 + bit);
|
||||
}
|
||||
|
||||
static void mpeg4_avc_remove(struct mpeg4_avc_t* avc, uint8_t* ptr, int bytes, const uint8_t* end)
|
||||
{
|
||||
uint8_t i;
|
||||
assert(ptr >= avc->data && ptr + bytes <= end && end <= avc->data + sizeof(avc->data));
|
||||
memmove(ptr, ptr + bytes, end - ptr - bytes);
|
||||
|
||||
for (i = 0; i < avc->nb_sps; i++)
|
||||
{
|
||||
if (avc->sps[i].data > ptr)
|
||||
avc->sps[i].data -= bytes;
|
||||
}
|
||||
|
||||
for (i = 0; i < avc->nb_pps; i++)
|
||||
{
|
||||
if (avc->pps[i].data > ptr)
|
||||
avc->pps[i].data -= bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static int h264_sps_copy(struct mpeg4_avc_t* avc, const uint8_t* nalu, int bytes)
|
||||
{
|
||||
int i;
|
||||
int offset;
|
||||
uint8_t spsid;
|
||||
|
||||
if (bytes < 4 + 1)
|
||||
{
|
||||
assert(0);
|
||||
return -1; // invalid length
|
||||
}
|
||||
|
||||
offset = 4 * 8; // 1-NALU + 3-profile+flags+level
|
||||
spsid = mpeg4_h264_read_ue(nalu, bytes, &offset);
|
||||
|
||||
for (i = 0; i < avc->nb_sps; i++)
|
||||
{
|
||||
offset = 4 * 8; // reset offset
|
||||
if (spsid == mpeg4_h264_read_ue(avc->sps[i].data, avc->sps[i].bytes, &offset))
|
||||
{
|
||||
if (bytes == avc->sps[i].bytes && 0 == memcmp(nalu, avc->sps[i].data, bytes))
|
||||
return 0; // do nothing
|
||||
|
||||
if (bytes > avc->sps[i].bytes && avc->off + (bytes - avc->sps[i].bytes) > sizeof(avc->data))
|
||||
{
|
||||
assert(0);
|
||||
return -1; // too big
|
||||
}
|
||||
|
||||
mpeg4_avc_remove(avc, avc->sps[i].data, avc->sps[i].bytes, avc->data + avc->off);
|
||||
avc->off -= avc->sps[i].bytes;
|
||||
|
||||
avc->sps[i].data = avc->data + avc->off;
|
||||
avc->sps[i].bytes = (uint16_t)bytes;
|
||||
memcpy(avc->sps[i].data, nalu, bytes);
|
||||
avc->off += bytes;
|
||||
return 1; // set update flag
|
||||
}
|
||||
}
|
||||
|
||||
// copy new
|
||||
assert(avc->nb_sps < sizeof(avc->sps) / sizeof(avc->sps[0]));
|
||||
if (avc->nb_sps >= sizeof(avc->sps) / sizeof(avc->sps[0])
|
||||
|| avc->off + bytes > sizeof(avc->data))
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
avc->sps[avc->nb_sps].data = avc->data + avc->off;
|
||||
avc->sps[avc->nb_sps].bytes = (uint16_t)bytes;
|
||||
memcpy(avc->sps[avc->nb_sps].data, nalu, bytes);
|
||||
avc->off += bytes;
|
||||
++avc->nb_sps;
|
||||
return 1; // set update flag
|
||||
}
|
||||
|
||||
static int h264_pps_copy(struct mpeg4_avc_t* avc, const uint8_t* nalu, int bytes)
|
||||
{
|
||||
int i;
|
||||
int offset;
|
||||
uint8_t spsid;
|
||||
uint8_t ppsid;
|
||||
|
||||
if (bytes < 1 + 1)
|
||||
{
|
||||
assert(0);
|
||||
return -1; // invalid length
|
||||
}
|
||||
|
||||
offset = 1 * 8; // 1-NALU
|
||||
spsid = mpeg4_h264_read_ue(nalu, bytes, &offset);
|
||||
ppsid = mpeg4_h264_read_ue(nalu, bytes, &offset);
|
||||
|
||||
for (i = 0; i < avc->nb_pps; i++)
|
||||
{
|
||||
offset = 1 * 8; // reset offset
|
||||
if (spsid == mpeg4_h264_read_ue(avc->pps[i].data, avc->pps[i].bytes, &offset) && ppsid == mpeg4_h264_read_ue(avc->pps[i].data, avc->pps[i].bytes, &offset))
|
||||
{
|
||||
if (bytes == avc->pps[i].bytes && 0 == memcmp(nalu, avc->pps[i].data, bytes))
|
||||
return 0; // do nothing
|
||||
|
||||
if (bytes > avc->pps[i].bytes && avc->off + (bytes - avc->pps[i].bytes) > sizeof(avc->data))
|
||||
{
|
||||
assert(0);
|
||||
return -1; // too big
|
||||
}
|
||||
|
||||
mpeg4_avc_remove(avc, avc->pps[i].data, avc->pps[i].bytes, avc->data + avc->off);
|
||||
avc->off -= avc->pps[i].bytes;
|
||||
|
||||
avc->pps[i].data = avc->data + avc->off;
|
||||
avc->pps[i].bytes = (uint16_t)bytes;
|
||||
memcpy(avc->pps[i].data, nalu, bytes);
|
||||
avc->off += bytes;
|
||||
return 1; // set update flag
|
||||
}
|
||||
}
|
||||
|
||||
// copy new
|
||||
assert((unsigned int)avc->nb_pps < sizeof(avc->pps) / sizeof(avc->pps[0]));
|
||||
if ((unsigned int)avc->nb_pps >= sizeof(avc->pps) / sizeof(avc->pps[0])
|
||||
|| avc->off + bytes > sizeof(avc->data))
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
avc->pps[avc->nb_pps].data = avc->data + avc->off;
|
||||
avc->pps[avc->nb_pps].bytes = (uint16_t)bytes;
|
||||
memcpy(avc->pps[avc->nb_pps].data, nalu, bytes);
|
||||
avc->off += bytes;
|
||||
++avc->nb_pps;
|
||||
return 1; // set update flag
|
||||
}
|
||||
|
||||
int mpeg4_avc_update(struct mpeg4_avc_t* avc, const uint8_t* nalu, int bytes)
|
||||
{
|
||||
int r;
|
||||
|
||||
switch (nalu[0] & 0x1f)
|
||||
{
|
||||
case H264_NAL_SPS:
|
||||
r = h264_sps_copy(avc, nalu, bytes);
|
||||
if (1 == avc->nb_sps)
|
||||
{
|
||||
// update profile/level once only
|
||||
avc->profile = nalu[1];
|
||||
avc->compatibility = nalu[2];
|
||||
avc->level = nalu[3];
|
||||
}
|
||||
break;
|
||||
|
||||
case H264_NAL_PPS:
|
||||
r = h264_pps_copy(avc, nalu, bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void h264_handler(void* param, const uint8_t* nalu, int bytes)
|
||||
{
|
||||
int r;
|
||||
uint8_t nalutype;
|
||||
struct h264_annexbtomp4_handle_t* mp4;
|
||||
mp4 = (struct h264_annexbtomp4_handle_t*)param;
|
||||
|
||||
if (bytes < 1)
|
||||
{
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
nalutype = (nalu[0]) & 0x1f;
|
||||
#if defined(H2645_FILTER_AUD)
|
||||
if (H264_NAL_AUD == nalutype)
|
||||
return; // ignore AUD
|
||||
#endif
|
||||
|
||||
r = mpeg4_avc_update(mp4->avc, nalu, bytes);
|
||||
if (1 == r && mp4->update)
|
||||
*mp4->update = 1;
|
||||
else if (r < 0)
|
||||
mp4->errcode = r;
|
||||
|
||||
// IDR-1, B/P-2, other-0
|
||||
if (mp4->vcl && 1 <= nalutype && nalutype <= H264_NAL_IDR)
|
||||
*mp4->vcl = nalutype == H264_NAL_IDR ? 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 h264_annexbtomp4(struct mpeg4_avc_t* avc, const void* data, int bytes, void* out, int size, int* vcl, int* update)
|
||||
{
|
||||
struct h264_annexbtomp4_handle_t h;
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.avc = avc;
|
||||
h.vcl = vcl;
|
||||
h.update = update;
|
||||
h.out = (uint8_t*)out;
|
||||
h.capacity = size;
|
||||
if (vcl) *vcl = 0;
|
||||
if (update) *update = 0;
|
||||
|
||||
mpeg4_h264_annexb_nalu(data, bytes, h264_handler, &h);
|
||||
avc->nalu = 4;
|
||||
return 0 == h.errcode ? h.bytes : 0;
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
enum { NAL_NIDR = 1, NAL_PARTITION_A = 2, NAL_IDR = 5, NAL_SEI = 6, NAL_SPS = 7, NAL_PPS = 8, NAL_AUD = 9, };
|
||||
|
||||
uint8_t nal_type;
|
||||
|
||||
if(bytes < 2)
|
||||
return 0;
|
||||
|
||||
nal_type = nalu[0] & 0x1f;
|
||||
|
||||
// 7.4.1.2.3 Order of NAL units and coded pictures and association to access units
|
||||
if(NAL_AUD == nal_type || NAL_SPS == nal_type || NAL_PPS == nal_type || NAL_SEI == nal_type || (14 <= nal_type && nal_type <= 18))
|
||||
return 1;
|
||||
|
||||
// 7.4.1.2.4 Detection of the first VCL NAL unit of a primary coded picture
|
||||
if(NAL_NIDR == nal_type || NAL_PARTITION_A == nal_type || NAL_IDR == nal_type)
|
||||
{
|
||||
// Live555 H264or5VideoStreamParser::parse
|
||||
// The high-order bit of the byte after the "nal_unit_header" tells us whether it's
|
||||
// the start of a new 'access unit' (and thus the current NAL unit ends an 'access unit'):
|
||||
return (nalu[1] & 0x80) ? 1 : 0; // first_mb_in_slice
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
static void mpeg4_annexbtomp4_test2(void)
|
||||
{
|
||||
const uint8_t sps[] = { 0x00,0x00,0x00,0x01,0x67,0x42,0xe0,0x1e,0xab,0xcd, };
|
||||
const uint8_t pps[] = { 0x00,0x00,0x00,0x01,0x28,0xce,0x3c,0x80 };
|
||||
const uint8_t sps1[] = { 0x00,0x00,0x00,0x01,0x67,0x42,0xe0,0x1e,0x4b,0xcd, 0x01 };
|
||||
const uint8_t pps1[] = { 0x00,0x00,0x00,0x01,0x28,0xce,0x3c,0x80, 0x01 };
|
||||
const uint8_t sps2[] = { 0x00,0x00,0x00,0x01,0x67,0x42,0xe0,0x1e,0xab };
|
||||
const uint8_t pps2[] = { 0x00,0x00,0x00,0x01,0x28,0xce,0x3c };
|
||||
|
||||
int vcl, update;
|
||||
uint8_t buffer[128];
|
||||
struct mpeg4_avc_t avc;
|
||||
memset(&avc, 0, sizeof(avc));
|
||||
|
||||
h264_annexbtomp4(&avc, sps, sizeof(sps), buffer, sizeof(buffer), &vcl, &update);
|
||||
assert(0 == vcl && 1 == update);
|
||||
h264_annexbtomp4(&avc, pps, sizeof(pps), buffer, sizeof(buffer), &vcl, &update);
|
||||
assert(0 == vcl && 1 == update && 1 == avc.nb_sps && avc.sps[0].bytes == sizeof(sps)-4 && 0 == memcmp(avc.sps[0].data, sps+4, sizeof(sps) - 4) && 1 == avc.nb_pps && avc.pps[0].bytes == sizeof(pps) - 4 && 0 == memcmp(avc.pps[0].data, pps+4, sizeof(pps) - 4));
|
||||
|
||||
h264_annexbtomp4(&avc, sps1, sizeof(sps1), buffer, sizeof(buffer), &vcl, &update);
|
||||
assert(0 == vcl && 1 == update && 2 == avc.nb_sps && avc.sps[0].bytes == sizeof(sps) - 4 && avc.sps[1].bytes == sizeof(sps1) - 4 && 0 == memcmp(avc.sps[0].data, sps+4, sizeof(sps) - 4) && 0 == memcmp(avc.sps[1].data, sps1 + 4, sizeof(sps1) - 4) && 1 == avc.nb_pps && avc.pps[0].bytes == sizeof(pps) - 4 && 0 == memcmp(avc.pps[0].data, pps + 4, sizeof(pps) - 4));
|
||||
|
||||
h264_annexbtomp4(&avc, pps1, sizeof(pps1), buffer, sizeof(buffer), &vcl, &update);
|
||||
assert(0 == vcl && 1 == update && 2 == avc.nb_sps && avc.sps[0].bytes == sizeof(sps) - 4 && avc.sps[1].bytes == sizeof(sps1) - 4 && 0 == memcmp(avc.sps[0].data, sps + 4, sizeof(sps) - 4) && 0 == memcmp(avc.sps[1].data, sps1 + 4, sizeof(sps1) - 4) && 1 == avc.nb_pps && avc.pps[0].bytes == sizeof(pps1) - 4 && 0 == memcmp(avc.pps[0].data, pps1 + 4, sizeof(pps1) - 4));
|
||||
|
||||
h264_annexbtomp4(&avc, sps2, sizeof(sps2), buffer, sizeof(buffer), &vcl, &update);
|
||||
assert(0 == vcl && 1 == update && 2 == avc.nb_sps && avc.sps[0].bytes == sizeof(sps2) - 4 && avc.sps[1].bytes == sizeof(sps1) - 4 && 0 == memcmp(avc.sps[0].data, sps2 + 4, sizeof(sps2) - 4) && 0 == memcmp(avc.sps[1].data, sps1 + 4, sizeof(sps1) - 4) && 1 == avc.nb_pps && avc.pps[0].bytes == sizeof(pps1) - 4 && 0 == memcmp(avc.pps[0].data, pps1 + 4, sizeof(pps1) - 4));
|
||||
|
||||
h264_annexbtomp4(&avc, pps2, sizeof(pps2), buffer, sizeof(buffer), &vcl, &update);
|
||||
assert(0 == vcl && 1 == update && 2 == avc.nb_sps && avc.sps[0].bytes == sizeof(sps2) - 4 && avc.sps[1].bytes == sizeof(sps1) - 4 && 0 == memcmp(avc.sps[0].data, sps2 + 4, sizeof(sps2) - 4) && 0 == memcmp(avc.sps[1].data, sps1 + 4, sizeof(sps1) - 4) && 1 == avc.nb_pps && avc.pps[0].bytes == sizeof(pps2) - 4 && 0 == memcmp(avc.pps[0].data, pps2 + 4, sizeof(pps2) - 4));
|
||||
}
|
||||
|
||||
void mpeg4_annexbtomp4_test(void)
|
||||
{
|
||||
const uint8_t sps[] = { 0x67,0x42,0xe0,0x1e,0xab };
|
||||
const uint8_t pps[] = { 0x28,0xce,0x3c,0x80 };
|
||||
const uint8_t annexb[] = { 0x00,0x00,0x00,0x01,0x67,0x42,0xe0,0x1e,0xab, 0x00,0x00,0x00,0x01,0x28,0xce,0x3c,0x80,0x00,0x00,0x00,0x01,0x65,0x11 };
|
||||
uint8_t output[256];
|
||||
int vcl, update;
|
||||
|
||||
struct mpeg4_avc_t avc;
|
||||
memset(&avc, 0, sizeof(avc));
|
||||
assert(h264_annexbtomp4(&avc, annexb, sizeof(annexb), output, sizeof(output), &vcl, &update) > 0);
|
||||
assert(1 == avc.nb_sps && avc.sps[0].bytes == sizeof(sps) && 0 == memcmp(avc.sps[0].data, sps, sizeof(sps)));
|
||||
assert(1 == avc.nb_pps && avc.pps[0].bytes == sizeof(pps) && 0 == memcmp(avc.pps[0].data, pps, sizeof(pps)));
|
||||
assert(vcl == 1);
|
||||
|
||||
mpeg4_annexbtomp4_test2();
|
||||
}
|
||||
#endif
|
258
src/3rdpart/media-server/libflv/source/mpeg4-avc.c
Normal file
258
src/3rdpart/media-server/libflv/source/mpeg4-avc.c
Normal file
@ -0,0 +1,258 @@
|
||||
#include "mpeg4-avc.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
ISO/IEC 14496-15:2010(E) 5.2.4.1.1 Syntax (p16)
|
||||
|
||||
aligned(8) class AVCDecoderConfigurationRecord {
|
||||
unsigned int(8) configurationVersion = 1;
|
||||
unsigned int(8) AVCProfileIndication;
|
||||
unsigned int(8) profile_compatibility;
|
||||
unsigned int(8) AVCLevelIndication;
|
||||
bit(6) reserved = '111111'b;
|
||||
unsigned int(2) lengthSizeMinusOne;
|
||||
bit(3) reserved = '111'b;
|
||||
|
||||
unsigned int(5) numOfSequenceParameterSets;
|
||||
for (i=0; i< numOfSequenceParameterSets; i++) {
|
||||
unsigned int(16) sequenceParameterSetLength ;
|
||||
bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
|
||||
}
|
||||
|
||||
unsigned int(8) numOfPictureParameterSets;
|
||||
for (i=0; i< numOfPictureParameterSets; i++) {
|
||||
unsigned int(16) pictureParameterSetLength;
|
||||
bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
|
||||
}
|
||||
|
||||
if( profile_idc == 100 || profile_idc == 110 ||
|
||||
profile_idc == 122 || profile_idc == 144 )
|
||||
{
|
||||
bit(6) reserved = '111111'b;
|
||||
unsigned int(2) chroma_format;
|
||||
bit(5) reserved = '11111'b;
|
||||
unsigned int(3) bit_depth_luma_minus8;
|
||||
bit(5) reserved = '11111'b;
|
||||
unsigned int(3) bit_depth_chroma_minus8;
|
||||
unsigned int(8) numOfSequenceParameterSetExt;
|
||||
for (i=0; i< numOfSequenceParameterSetExt; i++) {
|
||||
unsigned int(16) sequenceParameterSetExtLength;
|
||||
bit(8*sequenceParameterSetExtLength) sequenceParameterSetExtNALUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
int mpeg4_avc_decoder_configuration_record_load(const uint8_t* data, size_t bytes, struct mpeg4_avc_t* avc)
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t j;
|
||||
uint16_t len;
|
||||
uint8_t *p, *end;
|
||||
|
||||
if (bytes < 7) return -1;
|
||||
assert(1 == data[0]);
|
||||
// avc->version = data[0];
|
||||
avc->profile = data[1];
|
||||
avc->compatibility = data[2];
|
||||
avc->level = data[3];
|
||||
avc->nalu = (data[4] & 0x03) + 1;
|
||||
avc->nb_sps = data[5] & 0x1F;
|
||||
if (avc->nb_sps > sizeof(avc->sps) / sizeof(avc->sps[0]))
|
||||
{
|
||||
assert(0);
|
||||
return -1; // sps <= 32
|
||||
}
|
||||
|
||||
j = 6;
|
||||
p = avc->data;
|
||||
end = avc->data + sizeof(avc->data);
|
||||
for (i = 0; i < avc->nb_sps && j + 2 < bytes; ++i)
|
||||
{
|
||||
len = (data[j] << 8) | data[j + 1];
|
||||
if (j + 2 + len >= bytes || p + len > end)
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(p, data + j + 2, len);
|
||||
avc->sps[i].data = p;
|
||||
avc->sps[i].bytes = len;
|
||||
j += len + 2;
|
||||
p += len;
|
||||
}
|
||||
|
||||
if (j >= bytes || (unsigned int)data[j] > sizeof(avc->pps) / sizeof(avc->pps[0]))
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
avc->nb_pps = data[j++];
|
||||
for (i = 0; i < avc->nb_pps && j + 2 < bytes; i++)
|
||||
{
|
||||
len = (data[j] << 8) | data[j + 1];
|
||||
if (j + 2 + len > bytes || p + len > end)
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(p, data + j + 2, len);
|
||||
avc->pps[i].data = p;
|
||||
avc->pps[i].bytes = len;
|
||||
j += len + 2;
|
||||
p += len;
|
||||
}
|
||||
|
||||
avc->off = (int)(p - avc->data);
|
||||
return j;
|
||||
}
|
||||
|
||||
int mpeg4_avc_decoder_configuration_record_save(const struct mpeg4_avc_t* avc, uint8_t* data, size_t bytes)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t *p = data;
|
||||
|
||||
assert(0 < avc->nalu && avc->nalu <= 4);
|
||||
if (bytes < 7 || avc->nb_sps > 32) return -1;
|
||||
bytes -= 7;
|
||||
|
||||
// AVCDecoderConfigurationRecord
|
||||
// ISO/IEC 14496-15:2010
|
||||
// 5.2.4.1.1 Syntax
|
||||
p[0] = 1; // configurationVersion
|
||||
p[1] = avc->profile; // AVCProfileIndication
|
||||
p[2] = avc->compatibility; // profile_compatibility
|
||||
p[3] = avc->level; // AVCLevelIndication
|
||||
p[4] = 0xFC | (avc->nalu - 1); // lengthSizeMinusOne: 3
|
||||
p += 5;
|
||||
|
||||
// sps
|
||||
*p++ = 0xE0 | avc->nb_sps;
|
||||
for (i = 0; i < avc->nb_sps && bytes >= (size_t)avc->sps[i].bytes + 2; i++)
|
||||
{
|
||||
*p++ = (avc->sps[i].bytes >> 8) & 0xFF;
|
||||
*p++ = avc->sps[i].bytes & 0xFF;
|
||||
memcpy(p, avc->sps[i].data, avc->sps[i].bytes);
|
||||
|
||||
p += avc->sps[i].bytes;
|
||||
bytes -= avc->sps[i].bytes + 2;
|
||||
}
|
||||
if (i < avc->nb_sps) return -1; // check length
|
||||
|
||||
// pps
|
||||
*p++ = avc->nb_pps;
|
||||
for (i = 0; i < avc->nb_pps && bytes >= (size_t)avc->pps[i].bytes + 2; i++)
|
||||
{
|
||||
*p++ = (avc->pps[i].bytes >> 8) & 0xFF;
|
||||
*p++ = avc->pps[i].bytes & 0xFF;
|
||||
memcpy(p, avc->pps[i].data, avc->pps[i].bytes);
|
||||
|
||||
p += avc->pps[i].bytes;
|
||||
bytes -= avc->pps[i].bytes + 2;
|
||||
}
|
||||
if (i < avc->nb_pps) return -1; // check length
|
||||
|
||||
if (bytes >= 4)
|
||||
{
|
||||
if (avc->profile == 100 || avc->profile == 110 ||
|
||||
avc->profile == 122 || avc->profile == 244 || avc->profile == 44 ||
|
||||
avc->profile == 83 || avc->profile == 86 || avc->profile == 118 ||
|
||||
avc->profile == 128 || avc->profile == 138 || avc->profile == 139 ||
|
||||
avc->profile == 134)
|
||||
{
|
||||
*p++ = 0xFC | avc->chroma_format_idc;
|
||||
*p++ = 0xF8 | avc->bit_depth_luma_minus8;
|
||||
*p++ = 0xF8 | avc->bit_depth_chroma_minus8;
|
||||
*p++ = 0; // numOfSequenceParameterSetExt
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(p - data);
|
||||
}
|
||||
|
||||
#define H264_STARTCODE(p) (p[0]==0 && p[1]==0 && (p[2]==1 || (p[2]==0 && p[3]==1)))
|
||||
|
||||
int mpeg4_avc_to_nalu(const struct mpeg4_avc_t* avc, uint8_t* data, size_t bytes)
|
||||
{
|
||||
uint8_t i;
|
||||
size_t k = 0;
|
||||
uint8_t* h264 = data;
|
||||
|
||||
// sps
|
||||
for (i = 0; i < avc->nb_sps && bytes >= k + avc->sps[i].bytes + 4; i++)
|
||||
{
|
||||
if (avc->sps[i].bytes < 4 || !H264_STARTCODE(avc->sps[i].data))
|
||||
{
|
||||
h264[k++] = 0;
|
||||
h264[k++] = 0;
|
||||
h264[k++] = 0;
|
||||
h264[k++] = 1;
|
||||
}
|
||||
memcpy(h264 + k, avc->sps[i].data, avc->sps[i].bytes);
|
||||
|
||||
k += avc->sps[i].bytes;
|
||||
}
|
||||
if (i < avc->nb_sps) return -1; // check length
|
||||
|
||||
// pps
|
||||
for (i = 0; i < avc->nb_pps && bytes >= k + avc->pps[i].bytes + 2; i++)
|
||||
{
|
||||
if (avc->pps[i].bytes < 4 || !H264_STARTCODE(avc->pps[i].data))
|
||||
{
|
||||
h264[k++] = 0;
|
||||
h264[k++] = 0;
|
||||
h264[k++] = 0;
|
||||
h264[k++] = 1;
|
||||
}
|
||||
memcpy(h264 + k, avc->pps[i].data, avc->pps[i].bytes);
|
||||
|
||||
k += avc->pps[i].bytes;
|
||||
}
|
||||
if (i < avc->nb_pps) return -1; // check length
|
||||
|
||||
assert(k < 0x7FFF);
|
||||
return (int)k;
|
||||
}
|
||||
|
||||
int mpeg4_avc_codecs(const struct mpeg4_avc_t* avc, char* codecs, size_t bytes)
|
||||
{
|
||||
// https://tools.ietf.org/html/rfc6381#section-3.3
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter
|
||||
return snprintf(codecs, bytes, "avc1.%02x%02x%02x", avc->profile, avc->compatibility, avc->level);
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
void mpeg4_annexbtomp4_test(void);
|
||||
void mpeg4_avc_test(void)
|
||||
{
|
||||
const unsigned char src[] = {
|
||||
0x01,0x42,0xe0,0x1e,0xff,0xe1,0x00,0x21,0x67,0x42,0xe0,0x1e,0xab,0x40,0xf0,0x28,
|
||||
0xd0,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x19,0x70,0x20,0x00,0x78,0x00,0x00,0x0f,
|
||||
0x00,0x16,0xb1,0xb0,0x3c,0x50,0xaa,0x80,0x80,0x01,0x00,0x04,0x28,0xce,0x3c,0x80
|
||||
};
|
||||
const unsigned char nalu[] = {
|
||||
0x00,0x00,0x00,0x01,0x67,0x42,0xe0,0x1e,0xab,0x40,0xf0,0x28,0xd0,0x80,0x00,0x00,
|
||||
0x00,0x80,0x00,0x00,0x19,0x70,0x20,0x00,0x78,0x00,0x00,0x0f,0x00,0x16,0xb1,0xb0,
|
||||
0x3c,0x50,0xaa,0x80,0x80,0x00,0x00,0x00,0x01,0x28,0xce,0x3c,0x80
|
||||
};
|
||||
unsigned char data[sizeof(src)];
|
||||
|
||||
struct mpeg4_avc_t avc;
|
||||
assert(sizeof(src) == mpeg4_avc_decoder_configuration_record_load(src, sizeof(src), &avc));
|
||||
assert(0x42 == avc.profile && 0xe0 == avc.compatibility && 0x1e == avc.level);
|
||||
assert(4 == avc.nalu && 1 == avc.nb_sps && 1 == avc.nb_pps);
|
||||
assert(sizeof(src) == mpeg4_avc_decoder_configuration_record_save(&avc, data, sizeof(data)));
|
||||
assert(0 == memcmp(src, data, sizeof(src)));
|
||||
mpeg4_avc_codecs(&avc, (char*)data, sizeof(data));
|
||||
assert(0 == memcmp("avc1.42e01e", data, 11));
|
||||
|
||||
assert(sizeof(nalu) == mpeg4_avc_to_nalu(&avc, data, sizeof(data)));
|
||||
assert(0 == memcmp(nalu, data, sizeof(nalu)));
|
||||
|
||||
mpeg4_annexbtomp4_test();
|
||||
}
|
||||
#endif
|
311
src/3rdpart/media-server/libflv/source/mpeg4-hevc.c
Normal file
311
src/3rdpart/media-server/libflv/source/mpeg4-hevc.c
Normal file
@ -0,0 +1,311 @@
|
||||
#include "mpeg4-hevc.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define H265_VPS 32
|
||||
#define H265_SPS 33
|
||||
#define H265_PPS 34
|
||||
#define H265_PREFIX_SEI 39
|
||||
#define H265_SUFFIX_SEI 40
|
||||
|
||||
static uint8_t* w32(uint8_t* p, uint32_t v)
|
||||
{
|
||||
*p++ = (uint8_t)(v >> 24);
|
||||
*p++ = (uint8_t)(v >> 16);
|
||||
*p++ = (uint8_t)(v >> 8);
|
||||
*p++ = (uint8_t)v;
|
||||
return p;
|
||||
}
|
||||
|
||||
static uint8_t* w16(uint8_t* p, uint16_t v)
|
||||
{
|
||||
*p++ = (uint8_t)(v >> 8);
|
||||
*p++ = (uint8_t)v;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
ISO/IEC 14496-15:2017(E) 8.3.3.1.2 Syntax (p71)
|
||||
|
||||
aligned(8) class HEVCDecoderConfigurationRecord {
|
||||
unsigned int(8) configurationVersion = 1;
|
||||
unsigned int(2) general_profile_space;
|
||||
unsigned int(1) general_tier_flag;
|
||||
unsigned int(5) general_profile_idc;
|
||||
unsigned int(32) general_profile_compatibility_flags;
|
||||
unsigned int(48) general_constraint_indicator_flags;
|
||||
unsigned int(8) general_level_idc;
|
||||
bit(4) reserved = '1111'b;
|
||||
unsigned int(12) min_spatial_segmentation_idc;
|
||||
bit(6) reserved = '111111'b;
|
||||
unsigned int(2) parallelismType;
|
||||
bit(6) reserved = '111111'b;
|
||||
unsigned int(2) chromaFormat;
|
||||
bit(5) reserved = '11111'b;
|
||||
unsigned int(3) bitDepthLumaMinus8;
|
||||
bit(5) reserved = '11111'b;
|
||||
unsigned int(3) bitDepthChromaMinus8;
|
||||
bit(16) avgFrameRate;
|
||||
bit(2) constantFrameRate;
|
||||
bit(3) numTemporalLayers;
|
||||
bit(1) temporalIdNested;
|
||||
unsigned int(2) lengthSizeMinusOne;
|
||||
unsigned int(8) numOfArrays;
|
||||
for (j=0; j < numOfArrays; j++) {
|
||||
bit(1) array_completeness;
|
||||
unsigned int(1) reserved = 0;
|
||||
unsigned int(6) NAL_unit_type;
|
||||
unsigned int(16) numNalus;
|
||||
for (i=0; i< numNalus; i++) {
|
||||
unsigned int(16) nalUnitLength;
|
||||
bit(8*nalUnitLength) nalUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
int mpeg4_hevc_decoder_configuration_record_load(const uint8_t* data, size_t bytes, struct mpeg4_hevc_t* hevc)
|
||||
{
|
||||
uint8_t nalutype;
|
||||
uint16_t i, j, k, n, numOfArrays;
|
||||
const uint8_t* p;
|
||||
uint8_t* dst;
|
||||
|
||||
if (bytes < 23)
|
||||
return -1;
|
||||
|
||||
hevc->configurationVersion = data[0];
|
||||
if (1 != hevc->configurationVersion)
|
||||
return -1;
|
||||
|
||||
hevc->general_profile_space = (data[1] >> 6) & 0x03;
|
||||
hevc->general_tier_flag = (data[1] >> 5) & 0x01;
|
||||
hevc->general_profile_idc = data[1] & 0x1F;
|
||||
hevc->general_profile_compatibility_flags = (data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5];
|
||||
hevc->general_constraint_indicator_flags = ((uint32_t)data[6] << 24) | ((uint32_t)data[7] << 16) | ((uint32_t)data[8] << 8) | (uint32_t)data[9];
|
||||
hevc->general_constraint_indicator_flags = (hevc->general_constraint_indicator_flags << 16) | (((uint64_t)data[10]) << 8) | data[11];
|
||||
hevc->general_level_idc = data[12];
|
||||
hevc->min_spatial_segmentation_idc = ((data[13] & 0x0F) << 8) | data[14];
|
||||
hevc->parallelismType = data[15] & 0x03;
|
||||
hevc->chromaFormat = data[16] & 0x03;
|
||||
hevc->bitDepthLumaMinus8 = data[17] & 0x07;
|
||||
hevc->bitDepthChromaMinus8 = data[18] & 0x07;
|
||||
hevc->avgFrameRate = (data[19] << 8) | data[20];
|
||||
hevc->constantFrameRate = (data[21] >> 6) & 0x03;
|
||||
hevc->numTemporalLayers = (data[21] >> 3) & 0x07;
|
||||
hevc->temporalIdNested = (data[21] >> 2) & 0x01;
|
||||
hevc->lengthSizeMinusOne = data[21] & 0x03;
|
||||
numOfArrays = data[22];
|
||||
|
||||
p = data + 23;
|
||||
dst = hevc->data;
|
||||
hevc->numOfArrays = 0;
|
||||
for (i = 0; i < numOfArrays; i++)
|
||||
{
|
||||
if (p + 3 > data + bytes)
|
||||
return -1;
|
||||
|
||||
nalutype = p[0];
|
||||
n = (p[1] << 8) | p[2];
|
||||
p += 3;
|
||||
|
||||
for (j = 0; j < n; j++)
|
||||
{
|
||||
if (hevc->numOfArrays >= sizeof(hevc->nalu) / sizeof(hevc->nalu[0]))
|
||||
{
|
||||
assert(0);
|
||||
return -1; // too many nalu(s)
|
||||
}
|
||||
|
||||
if (p + 2 > data + bytes)
|
||||
return -1;
|
||||
|
||||
k = (p[0] << 8) | p[1];
|
||||
if (p + 2 + k > data + bytes || dst + k > hevc->data + sizeof(hevc->data))
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert((nalutype & 0x3F) == ((p[2] >> 1) & 0x3F));
|
||||
hevc->nalu[hevc->numOfArrays].array_completeness = (nalutype >> 7) & 0x01;
|
||||
hevc->nalu[hevc->numOfArrays].type = nalutype & 0x3F;
|
||||
hevc->nalu[hevc->numOfArrays].bytes = k;
|
||||
hevc->nalu[hevc->numOfArrays].data = dst;
|
||||
memcpy(hevc->nalu[hevc->numOfArrays].data, p + 2, k);
|
||||
hevc->numOfArrays++;
|
||||
|
||||
p += 2 + k;
|
||||
dst += k;
|
||||
}
|
||||
}
|
||||
|
||||
hevc->off = (int)(dst - hevc->data);
|
||||
return (int)(p - data);
|
||||
}
|
||||
|
||||
int mpeg4_hevc_decoder_configuration_record_save(const struct mpeg4_hevc_t* hevc, uint8_t* data, size_t bytes)
|
||||
{
|
||||
uint16_t n;
|
||||
uint8_t i, j, k;
|
||||
uint8_t *ptr, *end;
|
||||
uint8_t *p = data;
|
||||
uint8_t array_completeness = 1;
|
||||
const uint8_t nalu[] = {H265_VPS, H265_SPS, H265_PPS, H265_PREFIX_SEI, H265_SUFFIX_SEI};
|
||||
|
||||
assert(hevc->lengthSizeMinusOne <= 3);
|
||||
end = data + bytes;
|
||||
if (bytes < 23)
|
||||
return 0; // don't have enough memory
|
||||
|
||||
// HEVCDecoderConfigurationRecord
|
||||
// ISO/IEC 14496-15:2017
|
||||
// 8.3.3.1.2 Syntax
|
||||
assert(1 == hevc->configurationVersion);
|
||||
data[0] = hevc->configurationVersion;
|
||||
|
||||
// general_profile_space + general_tier_flag + general_profile_idc
|
||||
data[1] = ((hevc->general_profile_space & 0x03) << 6) | ((hevc->general_tier_flag & 0x01) << 5) | (hevc->general_profile_idc & 0x1F);
|
||||
|
||||
// general_profile_compatibility_flags
|
||||
w32(data + 2, hevc->general_profile_compatibility_flags);
|
||||
|
||||
// general_constraint_indicator_flags
|
||||
w32(data + 6, (uint32_t)(hevc->general_constraint_indicator_flags >> 16));
|
||||
w16(data + 10, (uint16_t)hevc->general_constraint_indicator_flags);
|
||||
|
||||
// general_level_idc
|
||||
data[12] = hevc->general_level_idc;
|
||||
|
||||
// min_spatial_segmentation_idc
|
||||
w16(data + 13, 0xF000 | hevc->min_spatial_segmentation_idc);
|
||||
|
||||
data[15] = 0xFC | hevc->parallelismType;
|
||||
data[16] = 0xFC | hevc->chromaFormat;
|
||||
data[17] = 0xF8 | hevc->bitDepthLumaMinus8;
|
||||
data[18] = 0xF8 | hevc->bitDepthChromaMinus8;
|
||||
w16(data + 19, hevc->avgFrameRate);
|
||||
data[21] = (hevc->constantFrameRate << 6) | ((hevc->numTemporalLayers & 0x07) << 3) | ((hevc->temporalIdNested & 0x01) << 2) | (hevc->lengthSizeMinusOne & 0x03);
|
||||
// data[22] = hevc->numOfArrays;
|
||||
|
||||
p = data + 23;
|
||||
for (k = i = 0; i < sizeof(nalu)/sizeof(nalu[0]); i++)
|
||||
{
|
||||
ptr = p + 3;
|
||||
for (n = j = 0; j < hevc->numOfArrays; j++)
|
||||
{
|
||||
assert(hevc->nalu[j].type == ((hevc->nalu[j].data[0] >> 1) & 0x3F));
|
||||
if(nalu[i] != hevc->nalu[j].type)
|
||||
continue;
|
||||
|
||||
if (ptr + 2 + hevc->nalu[j].bytes > end)
|
||||
return 0; // don't have enough memory
|
||||
|
||||
array_completeness = hevc->nalu[j].array_completeness;
|
||||
assert(hevc->nalu[i].data + hevc->nalu[j].bytes <= hevc->data + sizeof(hevc->data));
|
||||
w16(ptr, hevc->nalu[j].bytes);
|
||||
memcpy(ptr + 2, hevc->nalu[j].data, hevc->nalu[j].bytes);
|
||||
ptr += 2 + hevc->nalu[j].bytes;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
// array_completeness + NAL_unit_type
|
||||
p[0] = (array_completeness << 7) | (nalu[i] & 0x3F);
|
||||
w16(p + 1, n);
|
||||
p = ptr;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
data[22] = k;
|
||||
|
||||
return (int)(p - data);
|
||||
}
|
||||
|
||||
int mpeg4_hevc_to_nalu(const struct mpeg4_hevc_t* hevc, uint8_t* data, size_t bytes)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t* p, *end;
|
||||
const uint8_t startcode[] = { 0, 0, 0, 1 };
|
||||
|
||||
p = data;
|
||||
end = p + bytes;
|
||||
|
||||
for (i = 0; i < hevc->numOfArrays; i++)
|
||||
{
|
||||
if (p + hevc->nalu[i].bytes + 4 > end)
|
||||
return -1;
|
||||
|
||||
memcpy(p, startcode, 4);
|
||||
memcpy(p + 4, hevc->nalu[i].data, hevc->nalu[i].bytes);
|
||||
assert(hevc->nalu[i].type == ((hevc->nalu[i].data[0] >> 1) & 0x3F));
|
||||
p += 4 + hevc->nalu[i].bytes;
|
||||
}
|
||||
|
||||
return (int)(p - data);
|
||||
}
|
||||
|
||||
int mpeg4_hevc_codecs(const struct mpeg4_hevc_t* hevc, char* codecs, size_t bytes)
|
||||
{
|
||||
// ISO/IEC 14496-15:2017(E)
|
||||
// Annex E Sub-parameters of the MIME type "codecs" parameter (p154)
|
||||
// 'hev1.' or 'hvc1.' prefix (5 chars)
|
||||
// profile, e.g. '.A12' (max 4 chars)
|
||||
// profile_compatibility reserve bit order, dot + 32-bit hex number (max 9 chars)
|
||||
// tier and level, e.g. '.H120' (max 5 chars)
|
||||
// up to 6 constraint bytes, bytes are dot-separated and hex-encoded.
|
||||
const char* tier = "LH";
|
||||
const char* space[] = { "", "A", "B", "C" };
|
||||
uint32_t x;
|
||||
x = hevc->general_profile_compatibility_flags;
|
||||
x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1);
|
||||
x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2);
|
||||
x = ((x >> 4) & 0x0f0f0f0f) | ((x & 0x0f0f0f0f) << 4);
|
||||
x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8);
|
||||
x = (x >> 16) | (x << 16);
|
||||
return snprintf(codecs, bytes, "hvc1.%s%u.%x.%c%u", space[hevc->general_profile_space%4], (unsigned int)hevc->general_profile_idc, (unsigned int)x, tier[hevc->general_tier_flag%2], (unsigned int)hevc->general_level_idc);
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
void hevc_annexbtomp4_test(void);
|
||||
void mpeg4_hevc_test(void)
|
||||
{
|
||||
const unsigned char src[] = {
|
||||
0x01,0x01,0x60,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0xb4,0xf0,0x00,
|
||||
0xfc,0xfd,0xf8,0xf8,0x00,0x00,0x0f,0x03,0xa0,0x00,0x01,0x00,0x18,0x40,0x01,
|
||||
0x0c,0x01,0xff,0xff,0x01,0x60,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x03,0x00,
|
||||
0x00,0x03,0x00,0xb4,0x9d,0xc0,0x90,0xa1,0x00,0x01,0x00,0x29,0x42,0x01,0x01,
|
||||
0x01,0x60,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0xb4,
|
||||
0xa0,0x01,0xe0,0x20,0x02,0x1c,0x59,0x67,0x79,0x24,0x6d,0xae,0x01,0x00,0x00,
|
||||
0x03,0x03,0xe8,0x00,0x00,0x5d,0xc0,0x08,0xa2,0x00,0x01,0x00,0x06,0x44,0x01,
|
||||
0xc1,0x73,0xd1,0x89
|
||||
};
|
||||
const unsigned char nalu[] = {
|
||||
0x00,0x00,0x00,0x01,0x40,0x01,0x0c,0x01,0xff,0xff,0x01,0x60,0x00,0x00,0x03,
|
||||
0x00,0x80,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0xb4,0x9d,0xc0,0x90,0x00,0x00,
|
||||
0x00,0x01,0x42,0x01,0x01,0x01,0x60,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x03,
|
||||
0x00,0x00,0x03,0x00,0xb4,0xa0,0x01,0xe0,0x20,0x02,0x1c,0x59,0x67,0x79,0x24,
|
||||
0x6d,0xae,0x01,0x00,0x00,0x03,0x03,0xe8,0x00,0x00,0x5d,0xc0,0x08,0x00,0x00,
|
||||
0x00,0x01,0x44,0x01,0xc1,0x73,0xd1,0x89
|
||||
};
|
||||
unsigned char data[sizeof(src)];
|
||||
|
||||
struct mpeg4_hevc_t hevc;
|
||||
assert(sizeof(src) == mpeg4_hevc_decoder_configuration_record_load(src, sizeof(src), &hevc));
|
||||
assert(0 == hevc.general_profile_space && 0 == hevc.general_tier_flag);
|
||||
assert(1 == hevc.general_profile_idc && 0xb4 == hevc.general_level_idc);
|
||||
assert(1 == hevc.numTemporalLayers && 1 == hevc.temporalIdNested);
|
||||
assert(3 == hevc.numOfArrays);
|
||||
assert(sizeof(src) == mpeg4_hevc_decoder_configuration_record_save(&hevc, data, sizeof(data)));
|
||||
assert(0 == memcmp(src, data, sizeof(src)));
|
||||
mpeg4_hevc_codecs(&hevc, (char*)data, sizeof(data));
|
||||
assert(0 == memcmp("hvc1.1.6.L180", data, 13));
|
||||
|
||||
assert(sizeof(nalu) == mpeg4_hevc_to_nalu(&hevc, data, sizeof(data)));
|
||||
assert(0 == memcmp(nalu, data, sizeof(nalu)));
|
||||
|
||||
hevc_annexbtomp4_test();
|
||||
}
|
||||
#endif
|
94
src/3rdpart/media-server/libflv/source/mpeg4-mp4toannexb.c
Normal file
94
src/3rdpart/media-server/libflv/source/mpeg4-mp4toannexb.c
Normal file
@ -0,0 +1,94 @@
|
||||
// ISO/IEC 14496-1:2010(E)
|
||||
// Annex I: Usage of ITU-T Recommendation H.264 | ISO/IEC 14496-10 AVC (p150)
|
||||
|
||||
#include "mpeg4-avc.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define H264_NAL_IDR 5 // Coded slice of an IDR picture
|
||||
#define H264_NAL_SPS 7 // Sequence parameter set
|
||||
#define H264_NAL_PPS 8 // Picture parameter set
|
||||
#define H264_NAL_AUD 9 // Access unit delimiter
|
||||
|
||||
static int h264_sps_pps_size(const struct mpeg4_avc_t* avc)
|
||||
{
|
||||
int i, n = 0;
|
||||
for (i = 0; i < avc->nb_sps; i++)
|
||||
n += avc->sps[i].bytes + 4;
|
||||
for (i = 0; i < avc->nb_pps; i++)
|
||||
n += avc->pps[i].bytes + 4;
|
||||
return n;
|
||||
}
|
||||
|
||||
int h264_mp4toannexb(const struct mpeg4_avc_t* avc, const void* data, int bytes, void* out, int size)
|
||||
{
|
||||
int i, n;
|
||||
uint8_t sps_pps_flag;
|
||||
uint8_t* dst;
|
||||
const uint8_t* src, *end;
|
||||
const uint8_t h264_start_code[] = { 0x00, 0x00, 0x00, 0x01 };
|
||||
|
||||
sps_pps_flag = 0;
|
||||
dst = (uint8_t*)out;
|
||||
end = (const uint8_t*)data + bytes;
|
||||
for (src = (const uint8_t*)data; src + avc->nalu + 1 < end; src += n + avc->nalu)
|
||||
{
|
||||
for (n = 0, i = 0; i < avc->nalu; i++)
|
||||
n = (n << 8) + ((uint8_t*)src)[i];
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
// fix 0x00 00 00 01 => flv nalu size
|
||||
if (1 == n && (3 == avc->nalu || 4 == avc->nalu))
|
||||
n = (int)(end - src) - avc->nalu;
|
||||
#endif
|
||||
|
||||
if (n <= 0 || src + n + avc->nalu > end)
|
||||
{
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// insert SPS/PPS before IDR frame
|
||||
switch (src[avc->nalu] & 0x1f)
|
||||
{
|
||||
case H264_NAL_SPS:
|
||||
case H264_NAL_PPS:
|
||||
//flv->data[k++] = 0; // SPS/PPS add zero_byte(ITU H.264 B.1.2 Byte stream NAL unit semantics)
|
||||
sps_pps_flag = 1;
|
||||
break;
|
||||
|
||||
case H264_NAL_IDR:
|
||||
if (0 == sps_pps_flag)
|
||||
{
|
||||
sps_pps_flag = 1; // don't insert more than one-times
|
||||
if (dst != out)
|
||||
{
|
||||
// write sps/pps at first
|
||||
i = h264_sps_pps_size(avc);
|
||||
memmove((uint8_t*)out + i, out, dst - (uint8_t*)out);
|
||||
}
|
||||
i = mpeg4_avc_to_nalu(avc, out, size);
|
||||
if (i <= 0)
|
||||
return 0;
|
||||
dst += i;
|
||||
}
|
||||
break;
|
||||
#if defined(H2645_FILTER_AUD)
|
||||
case H264_NAL_AUD:
|
||||
continue; // ignore AUD
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dst + n + sizeof(h264_start_code) > (uint8_t*)out + size)
|
||||
return 0;
|
||||
|
||||
memcpy(dst, h264_start_code, sizeof(h264_start_code));
|
||||
memcpy(dst + sizeof(h264_start_code), src + avc->nalu, n);
|
||||
dst += sizeof(h264_start_code) + n;
|
||||
}
|
||||
|
||||
assert(src == end);
|
||||
return (int)(dst - (uint8_t*)out);
|
||||
}
|
119
src/3rdpart/media-server/libflv/source/opus-head.c
Normal file
119
src/3rdpart/media-server/libflv/source/opus-head.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include "opus-head.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
// http://www.opus-codec.org/docs/opus_in_isobmff.html
|
||||
// 4.3.2 Opus Specific Box
|
||||
/*
|
||||
class ChannelMappingTable (unsigned int(8) OutputChannelCount){
|
||||
unsigned int(8) StreamCount;
|
||||
unsigned int(8) CoupledCount;
|
||||
unsigned int(8 * OutputChannelCount) ChannelMapping;
|
||||
}
|
||||
|
||||
aligned(8) class OpusSpecificBox extends Box('dOps'){
|
||||
unsigned int(8) Version;
|
||||
unsigned int(8) OutputChannelCount;
|
||||
unsigned int(16) PreSkip;
|
||||
unsigned int(32) InputSampleRate;
|
||||
signed int(16) OutputGain;
|
||||
unsigned int(8) ChannelMappingFamily;
|
||||
if (ChannelMappingFamily != 0) {
|
||||
ChannelMappingTable(OutputChannelCount);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static const uint8_t opus_coupled_stream_cnt[9] = {
|
||||
1, 0, 1, 1, 2, 2, 2, 3, 3
|
||||
};
|
||||
|
||||
static const uint8_t opus_stream_cnt[9] = {
|
||||
1, 1, 1, 2, 2, 3, 4, 4, 5,
|
||||
};
|
||||
|
||||
static const uint8_t opus_channel_map[8][8] = {
|
||||
{ 0 },
|
||||
{ 0,1 },
|
||||
{ 0,2,1 },
|
||||
{ 0,1,2,3 },
|
||||
{ 0,4,1,2,3 },
|
||||
{ 0,4,1,2,3,5 },
|
||||
{ 0,4,1,2,3,5,6 },
|
||||
{ 0,6,1,2,3,4,5,7 },
|
||||
};
|
||||
|
||||
int opus_head_save(const struct opus_head_t* opus, uint8_t* data, size_t bytes)
|
||||
{
|
||||
if (bytes < 19)
|
||||
return -1;
|
||||
|
||||
memcpy(data, "OpusHead", 8);
|
||||
data[8] = opus->version; // 0 only
|
||||
data[9] = opus->channels;
|
||||
data[11] = (uint8_t)(opus->pre_skip >> 8); // LSB
|
||||
data[10] = (uint8_t)opus->pre_skip;
|
||||
data[15] = (uint8_t)(opus->input_sample_rate >> 24); // LSB
|
||||
data[14] = (uint8_t)(opus->input_sample_rate >> 16);
|
||||
data[13] = (uint8_t)(opus->input_sample_rate >> 8);
|
||||
data[12] = (uint8_t)opus->input_sample_rate;
|
||||
data[17] = (uint8_t)(opus->output_gain >> 8); // LSB
|
||||
data[16] = (uint8_t)opus->output_gain;
|
||||
data[18] = opus->channel_mapping_family;
|
||||
if (0 != opus->channel_mapping_family && bytes >= 29)
|
||||
{
|
||||
data[19] = opus->stream_count;
|
||||
data[20] = opus->coupled_count;
|
||||
memcpy(data+21, opus->channel_mapping, 8);
|
||||
return 29;
|
||||
}
|
||||
|
||||
return 19;
|
||||
}
|
||||
|
||||
int opus_head_load(const uint8_t* data, size_t bytes, struct opus_head_t* opus)
|
||||
{
|
||||
if (bytes < 19 || 0 != memcmp(data, "OpusHead", 8))
|
||||
return -1;
|
||||
|
||||
memset(opus, 0, sizeof(*opus));
|
||||
opus->version = data[8];
|
||||
opus->channels = data[9];
|
||||
opus->pre_skip = ((uint16_t)data[11] << 8) | data[10];
|
||||
opus->input_sample_rate = ((uint32_t)data[15] << 24) | ((uint32_t)data[14] << 16) | ((uint32_t)data[13] << 8) | data[12];
|
||||
opus->output_gain = ((uint16_t)data[17] << 8) | data[16];
|
||||
opus->channel_mapping_family = data[18];
|
||||
|
||||
if (0 != opus->channel_mapping_family && bytes >= 29)
|
||||
{
|
||||
opus->stream_count = data[19];
|
||||
opus->coupled_count = data[20];
|
||||
memcpy(opus->channel_mapping, data+21, 8);
|
||||
return 29;
|
||||
}
|
||||
else
|
||||
{
|
||||
opus->stream_count = opus_stream_cnt[opus->channels];
|
||||
opus->coupled_count = opus_coupled_stream_cnt[opus->channels];
|
||||
memcpy(opus->channel_mapping, opus_channel_map[opus_head_channels(opus)-1], 8);
|
||||
}
|
||||
|
||||
return 19;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
void opus_head_test(void)
|
||||
{
|
||||
uint8_t data[29];
|
||||
const uint8_t src[] = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, 0x78, 0x00, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
struct opus_head_t opus;
|
||||
assert(sizeof(src) == opus_head_load(src, sizeof(src), &opus));
|
||||
assert(1 == opus.version && 2 == opus.channels && 120 == opus.pre_skip && 48000 == opus.input_sample_rate && 0 == opus.output_gain);
|
||||
assert(0 == opus.channel_mapping_family && 1 == opus.stream_count && 1 == opus.coupled_count);
|
||||
assert(0 == memcmp(opus_channel_map[opus.channels-1], opus.channel_mapping, 8));
|
||||
assert(sizeof(src) == opus_head_save(&opus, data, sizeof(data)));
|
||||
assert(0 == memcmp(src, data, sizeof(src)));
|
||||
}
|
||||
#endif
|
89
src/3rdpart/media-server/libflv/source/webm-vpx.c
Normal file
89
src/3rdpart/media-server/libflv/source/webm-vpx.c
Normal file
@ -0,0 +1,89 @@
|
||||
#include "webm-vpx.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
enum {
|
||||
WEBM_VP_LEVEL_1 = 10,
|
||||
WEBM_VP_LEVEL_1_1 = 11,
|
||||
WEBM_VP_LEVEL_2 = 20,
|
||||
WEBM_VP_LEVEL_2_1 = 21,
|
||||
WEBM_VP_LEVEL_3 = 30,
|
||||
WEBM_VP_LEVEL_3_1 = 31,
|
||||
WEBM_VP_LEVEL_4 = 40,
|
||||
WEBM_VP_LEVEL_4_1 = 41,
|
||||
WEBM_VP_LEVEL_5 = 50,
|
||||
WEBM_VP_LEVEL_5_1 = 51,
|
||||
WEBM_VP_LEVEL_5_2 = 52,
|
||||
WEBM_VP_LEVEL_6 = 60,
|
||||
WEBM_VP_LEVEL_6_1 = 61,
|
||||
WEBM_VP_LEVEL_6_2 = 62,
|
||||
};
|
||||
|
||||
/*
|
||||
aligned (8) class VPCodecConfigurationRecord {
|
||||
unsigned int (8) profile;
|
||||
unsigned int (8) level;
|
||||
unsigned int (4) bitDepth;
|
||||
unsigned int (3) chromaSubsampling;
|
||||
unsigned int (1) videoFullRangeFlag;
|
||||
unsigned int (8) colourPrimaries;
|
||||
unsigned int (8) transferCharacteristics;
|
||||
unsigned int (8) matrixCoefficients;
|
||||
unsigned int (16) codecIntializationDataSize;
|
||||
unsigned int (8)[] codecIntializationData;
|
||||
}
|
||||
*/
|
||||
|
||||
int webm_vpx_codec_configuration_record_load(const uint8_t* data, size_t bytes, struct webm_vpx_t* vpx)
|
||||
{
|
||||
if (bytes < 8)
|
||||
return -1;
|
||||
|
||||
vpx->profile = data[0];
|
||||
vpx->level = data[1];
|
||||
vpx->bit_depth = (data[2] >> 4) & 0x0F;
|
||||
vpx->chroma_subsampling = (data[2] >> 1) & 0x07;
|
||||
vpx->video_full_range_flag = data[2] & 0x01;
|
||||
vpx->colour_primaries = data[3];
|
||||
vpx->transfer_characteristics = data[4];
|
||||
vpx->matrix_coefficients = data[5];
|
||||
vpx->codec_intialization_data_size = (((uint16_t)data[6]) << 8) | data[7];
|
||||
assert(0 == vpx->codec_intialization_data_size);
|
||||
return 8;
|
||||
}
|
||||
|
||||
int webm_vpx_codec_configuration_record_save(const struct webm_vpx_t* vpx, uint8_t* data, size_t bytes)
|
||||
{
|
||||
if (bytes < 8 + (size_t)vpx->codec_intialization_data_size)
|
||||
return 0; // don't have enough memory
|
||||
|
||||
data[0] = vpx->profile;
|
||||
data[1] = vpx->level;
|
||||
data[2] = (vpx->bit_depth << 4) | ((vpx->chroma_subsampling & 0x07) << 1) | (vpx->video_full_range_flag & 0x01);
|
||||
data[3] = vpx->colour_primaries;
|
||||
data[4] = vpx->transfer_characteristics;
|
||||
data[5] = vpx->matrix_coefficients;
|
||||
data[6] = (uint8_t)(vpx->codec_intialization_data_size >> 8);
|
||||
data[7] = (uint8_t)vpx->codec_intialization_data_size;
|
||||
|
||||
if(vpx->codec_intialization_data_size > 0)
|
||||
memcpy(data + 8, vpx->codec_intialization_data, vpx->codec_intialization_data_size);
|
||||
return 8 + vpx->codec_intialization_data_size;
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
void webm_vpx_test(void)
|
||||
{
|
||||
const unsigned char src[] = {
|
||||
0x00, 0x1f, 0x80, 0x02, 0x02, 0x02, 0x00, 0x00
|
||||
};
|
||||
unsigned char data[sizeof(src)];
|
||||
|
||||
struct webm_vpx_t vpx;
|
||||
assert(sizeof(src) == webm_vpx_codec_configuration_record_load(src, sizeof(src), &vpx));
|
||||
assert(0 == vpx.profile && 31 == vpx.level && 8 == vpx.bit_depth && 0 == vpx.chroma_subsampling && 0 == vpx.video_full_range_flag);
|
||||
assert(sizeof(src) == webm_vpx_codec_configuration_record_save(&vpx, data, sizeof(data)));
|
||||
assert(0 == memcmp(src, data, sizeof(src)));
|
||||
}
|
||||
#endif
|
51
src/3rdpart/media-server/libflv/test/amf0-test.c
Normal file
51
src/3rdpart/media-server/libflv/test/amf0-test.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "amf0.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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; }
|
||||
|
||||
struct rtmp_result_t
|
||||
{
|
||||
char code[64]; // NetStream.Play.Start
|
||||
char level[8]; // warning/status/error
|
||||
char description[256];
|
||||
};
|
||||
|
||||
static int amf0_get(const char* file, void* amf0, size_t bytes)
|
||||
{
|
||||
int r;
|
||||
FILE* fp;
|
||||
fp = fopen(file, "rb");
|
||||
if (NULL == fp)
|
||||
return 0;
|
||||
|
||||
r = fread(amf0, 1, bytes, fp);
|
||||
fclose(fp);
|
||||
return r;
|
||||
}
|
||||
|
||||
void amf0_test2(void)
|
||||
{
|
||||
int r;
|
||||
uint8_t* end;
|
||||
static uint8_t amf0[2 * 1024];
|
||||
struct rtmp_result_t result;
|
||||
struct amf_object_item_t info[3];
|
||||
struct amf_object_item_t items[2];
|
||||
|
||||
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(items[0], AMF_OBJECT, "command", NULL, 0); // Command object
|
||||
AMF_OBJECT_ITEM_VALUE(items[1], AMF_OBJECT, "information", info, sizeof(info) / sizeof(info[0])); // Information object
|
||||
|
||||
r = amf0_get("../libflv/test/rtmp.onStatus.amf0", amf0, sizeof(amf0));
|
||||
|
||||
end = amf0 + r;
|
||||
assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
|
||||
assert(0 == strcmp(result.code, "NetStream.Play.Reset"));
|
||||
assert(0 == strcmp(result.level, "status"));
|
||||
assert(0 == strcmp(result.description, "Playing and resetting 92f509c10c112171f935?token=3129bc162ee05a1353f7&secret=15b1bca0997ab790656c903493cada3b&ckey=17e23e4fd0bb5b54a2434fd1514343ee"));
|
||||
}
|
78
src/3rdpart/media-server/libflv/test/flv-read-write-test.cpp
Normal file
78
src/3rdpart/media-server/libflv/test/flv-read-write-test.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "flv-reader.h"
|
||||
#include "flv-writer.h"
|
||||
#include "flv-demuxer.h"
|
||||
#include "flv-muxer.h"
|
||||
#include "flv-proto.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int flv_onmuxer(void* flv, int type, const void* data, size_t bytes, uint32_t timestamp)
|
||||
{
|
||||
return flv_writer_input(flv, type, data, bytes, timestamp);
|
||||
}
|
||||
|
||||
static int flv_ondemuxer(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int format)
|
||||
{
|
||||
flv_muxer_t* muxer = (flv_muxer_t*)param;
|
||||
switch (codec)
|
||||
{
|
||||
case FLV_AUDIO_AAC:
|
||||
flv_muxer_aac(muxer, data, bytes, pts, dts);
|
||||
break;
|
||||
|
||||
case FLV_AUDIO_MP3:
|
||||
flv_muxer_mp3(muxer, data, bytes, pts, dts);
|
||||
break;
|
||||
|
||||
case FLV_VIDEO_H264:
|
||||
flv_muxer_avc(muxer, data, bytes, pts, dts);
|
||||
break;
|
||||
|
||||
case FLV_VIDEO_H265:
|
||||
flv_muxer_hevc(muxer, data, bytes, pts, dts);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flv_read_write_test(const char* flv)
|
||||
{
|
||||
static char packet[2 * 1024 * 1024];
|
||||
snprintf(packet, sizeof(packet), "%s.flv", flv);
|
||||
|
||||
void* r = flv_reader_create(flv);
|
||||
void* w = flv_writer_create(packet);
|
||||
flv_muxer_t* e = flv_muxer_create(flv_onmuxer, w);
|
||||
flv_demuxer_t* d = flv_demuxer_create(flv_ondemuxer, e);
|
||||
|
||||
//struct flv_metadata_t metadata;
|
||||
//metadata.audiocodecid = 4;
|
||||
//metadata.audiodatarate = 16.1;
|
||||
//metadata.audiosamplerate = 48000;
|
||||
//metadata.audiosamplesize = 16;
|
||||
//metadata.stereo = true;
|
||||
//metadata.videocodecid = 7;
|
||||
//metadata.videodatarate = 64.0;
|
||||
//metadata.framerate = 25;
|
||||
//metadata.width = 1920;
|
||||
//metadata.height = 1080;
|
||||
//flv_muxer_metadata(e, &metadata);
|
||||
|
||||
int ret, tag;
|
||||
size_t taglen;
|
||||
uint32_t timestamp;
|
||||
while (1 == flv_reader_read(r, &tag, ×tamp, &taglen, packet, sizeof(packet)))
|
||||
{
|
||||
ret = flv_demuxer_input(d, tag, packet, taglen, timestamp);
|
||||
assert(0 == ret);
|
||||
}
|
||||
|
||||
flv_muxer_destroy(e);
|
||||
flv_demuxer_destroy(d);
|
||||
flv_reader_destroy(r);
|
||||
flv_writer_destroy(w);
|
||||
}
|
111
src/3rdpart/media-server/libflv/test/flv-reader-test.cpp
Normal file
111
src/3rdpart/media-server/libflv/test/flv-reader-test.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include "flv-demuxer.h"
|
||||
#include "flv-reader.h"
|
||||
#include "flv-proto.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static unsigned char packet[8 * 1024 * 1024];
|
||||
static FILE* aac;
|
||||
static FILE* h264;
|
||||
|
||||
inline const char* ftimestamp(uint32_t t, char* buf)
|
||||
{
|
||||
sprintf(buf, "%02u:%02u:%02u.%03u", t / 3600000, (t / 60000) % 60, (t / 1000) % 60, t % 1000);
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline size_t get_adts_length(const uint8_t* data, size_t bytes)
|
||||
{
|
||||
assert(bytes >= 6);
|
||||
return ((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] >> 5) & 0x07);
|
||||
}
|
||||
|
||||
inline char flv_type(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case FLV_AUDIO_AAC: return 'A';
|
||||
case FLV_AUDIO_MP3: return 'M';
|
||||
case FLV_AUDIO_ASC: return 'a';
|
||||
case FLV_VIDEO_H264: return 'V';
|
||||
case FLV_VIDEO_AVCC: return 'v';
|
||||
case FLV_VIDEO_H265: return 'H';
|
||||
case FLV_VIDEO_HVCC: return 'h';
|
||||
default: return '*';
|
||||
}
|
||||
}
|
||||
|
||||
static int onFLV(void* /*param*/, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
|
||||
{
|
||||
static char s_pts[64], s_dts[64];
|
||||
static uint32_t v_pts = 0, v_dts = 0;
|
||||
static uint32_t a_pts = 0, a_dts = 0;
|
||||
|
||||
printf("[%c] pts: %s, dts: %s, ", flv_type(codec), ftimestamp(pts, s_pts), ftimestamp(dts, s_dts));
|
||||
|
||||
if (FLV_AUDIO_AAC == codec)
|
||||
{
|
||||
printf("diff: %03d/%03d", (int)(pts - a_pts), (int)(dts - a_dts));
|
||||
a_pts = pts;
|
||||
a_dts = dts;
|
||||
|
||||
assert(bytes == get_adts_length((const uint8_t*)data, bytes));
|
||||
fwrite(data, bytes, 1, aac);
|
||||
}
|
||||
else if (FLV_VIDEO_H264 == codec || FLV_VIDEO_H265 == codec)
|
||||
{
|
||||
printf("diff: %03d/%03d %s", (int)(pts - v_pts), (int)(dts - v_dts), flags ? "[I]" : "");
|
||||
v_pts = pts;
|
||||
v_dts = dts;
|
||||
|
||||
fwrite(data, bytes, 1, h264);
|
||||
}
|
||||
else if (FLV_AUDIO_MP3 == codec)
|
||||
{
|
||||
fwrite(data, bytes, 1, aac);
|
||||
}
|
||||
else if (FLV_AUDIO_ASC == codec || FLV_VIDEO_AVCC == codec || FLV_VIDEO_HVCC == codec)
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
else if ((3 << 4) == codec)
|
||||
{
|
||||
fwrite(data, bytes, 1, aac);
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do
|
||||
assert(0);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// flv_reader_test("53340.flv");
|
||||
void flv_reader_test(const char* file)
|
||||
{
|
||||
aac = fopen("audio.aac", "wb");
|
||||
h264 = fopen("video.h264", "wb");
|
||||
|
||||
void* reader = flv_reader_create(file);
|
||||
flv_demuxer_t* flv = flv_demuxer_create(onFLV, NULL);
|
||||
|
||||
int type, r;
|
||||
size_t taglen;
|
||||
uint32_t timestamp;
|
||||
while (1 == flv_reader_read(reader, &type, ×tamp, &taglen, packet, sizeof(packet)))
|
||||
{
|
||||
r = flv_demuxer_input(flv, type, packet, taglen, timestamp);
|
||||
if (r < 0)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
flv_demuxer_destroy(flv);
|
||||
flv_reader_destroy(reader);
|
||||
|
||||
fclose(aac);
|
||||
fclose(h264);
|
||||
}
|
151
src/3rdpart/media-server/libflv/test/flv2ts-test.cpp
Normal file
151
src/3rdpart/media-server/libflv/test/flv2ts-test.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
#include "flv-demuxer.h"
|
||||
#include "flv-reader.h"
|
||||
#include "flv-proto.h"
|
||||
#include "mpeg-ps.h"
|
||||
#include "mpeg-ts.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
|
||||
static void* ts_alloc(void* /*param*/, size_t bytes)
|
||||
{
|
||||
static char s_buffer[188];
|
||||
assert(bytes <= sizeof(s_buffer));
|
||||
return s_buffer;
|
||||
}
|
||||
|
||||
static void ts_free(void* /*param*/, void* /*packet*/)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void ts_write(void* param, const void* packet, size_t bytes)
|
||||
{
|
||||
fwrite(packet, bytes, 1, (FILE*)param);
|
||||
}
|
||||
|
||||
inline const char* ftimestamp(uint32_t t, char* buf)
|
||||
{
|
||||
sprintf(buf, "%u:%02u:%02u.%03u", t / 3600000, (t / 60000) % 60, (t / 1000) % 60, t % 1000);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline char flv_type(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case FLV_AUDIO_ASC: return 'a';
|
||||
case FLV_AUDIO_AAC: return 'A';
|
||||
case FLV_AUDIO_MP3: return 'M';
|
||||
case FLV_AUDIO_OPUS: return 'O';
|
||||
case FLV_AUDIO_OPUS_HEAD: return 'o';
|
||||
case FLV_VIDEO_H264: return 'V';
|
||||
case FLV_VIDEO_AVCC: return 'v';
|
||||
case FLV_VIDEO_H265: return 'H';
|
||||
case FLV_VIDEO_HVCC: return 'h';
|
||||
default: return '*';
|
||||
}
|
||||
}
|
||||
|
||||
static inline int flv2ts_codec_id(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case FLV_AUDIO_ASC:
|
||||
case FLV_AUDIO_AAC: return STREAM_AUDIO_AAC;
|
||||
case FLV_AUDIO_MP3: return STREAM_AUDIO_MP3;
|
||||
case FLV_AUDIO_OPUS:
|
||||
case FLV_AUDIO_OPUS_HEAD: return STREAM_AUDIO_OPUS;
|
||||
case FLV_VIDEO_H264:
|
||||
case FLV_VIDEO_AVCC: return STREAM_VIDEO_H264;
|
||||
case FLV_VIDEO_H265:
|
||||
case FLV_VIDEO_HVCC: return STREAM_VIDEO_H264;
|
||||
default: return '*';
|
||||
}
|
||||
}
|
||||
|
||||
static int ts_stream(void* ts, int codecid, const void* data, size_t bytes)
|
||||
{
|
||||
static std::map<int, int> streams;
|
||||
std::map<int, int>::const_iterator it = streams.find(codecid);
|
||||
if (streams.end() != it)
|
||||
return it->second;
|
||||
|
||||
int i = mpeg_ts_add_stream(ts, flv2ts_codec_id(codecid), data, bytes);
|
||||
streams[codecid] = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int onFLV(void* ts, int codec, const void* data, size_t bytes, unsigned int pts, unsigned int dts, int flags)
|
||||
{
|
||||
static char s_pts[64], s_dts[64];
|
||||
static uint32_t v_pts = 0, v_dts = 0;
|
||||
static uint32_t a_pts = 0, a_dts = 0;
|
||||
|
||||
printf("[%c] pts: %s, dts: %s, ", flv_type(codec), ftimestamp(pts, s_pts), ftimestamp(dts, s_dts));
|
||||
|
||||
if (FLV_AUDIO_AAC == codec || FLV_AUDIO_MP3 == codec || FLV_AUDIO_OPUS == codec)
|
||||
{
|
||||
// assert(0 == a_dts || dts >= a_dts);
|
||||
pts = (a_pts && pts < a_pts) ? a_pts : pts;
|
||||
dts = (a_dts && dts < a_dts) ? a_dts : dts;
|
||||
mpeg_ts_write(ts, ts_stream(ts, codec, NULL, 0), 0, pts * 90, dts * 90, data, bytes);
|
||||
|
||||
printf("diff: %03d/%03d", (int)(pts - a_pts), (int)(dts - a_dts));
|
||||
a_pts = pts;
|
||||
a_dts = dts;
|
||||
}
|
||||
else if (FLV_VIDEO_H264 == codec || FLV_VIDEO_H265 == codec)
|
||||
{
|
||||
assert(0 == v_dts || dts >= v_dts);
|
||||
dts = (a_dts && dts < v_dts) ? v_dts : dts;
|
||||
mpeg_ts_write(ts, ts_stream(ts, codec, NULL, 0), 0x01 & flags ? 1 : 0, pts * 90, dts * 90, data, bytes);
|
||||
|
||||
printf("diff: %03d/%03d%s", (int)(pts - v_pts), (int)(dts - v_dts), flags ? " [I]" : "");
|
||||
v_pts = pts;
|
||||
v_dts = dts;
|
||||
}
|
||||
else if (FLV_AUDIO_OPUS_HEAD == codec)
|
||||
{
|
||||
ts_stream(ts, FLV_AUDIO_OPUS, data, bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flv2ts_test(const char* inputFLV, const char* outputTS)
|
||||
{
|
||||
struct mpeg_ts_func_t tshandler;
|
||||
tshandler.alloc = ts_alloc;
|
||||
tshandler.write = ts_write;
|
||||
tshandler.free = ts_free;
|
||||
|
||||
FILE* fp = fopen(outputTS, "wb");
|
||||
void* ts = mpeg_ts_create(&tshandler, fp);
|
||||
void* reader = flv_reader_create(inputFLV);
|
||||
flv_demuxer_t* flv = flv_demuxer_create(onFLV, ts);
|
||||
|
||||
int type, r;
|
||||
size_t taglen;
|
||||
uint32_t timestamp;
|
||||
static unsigned char s_packet[8 * 1024 * 1024];
|
||||
while (1 == flv_reader_read(reader, &type, ×tamp, &taglen, s_packet, sizeof(s_packet)))
|
||||
{
|
||||
r = flv_demuxer_input(flv, type, s_packet, taglen, timestamp);
|
||||
if (r < 0)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
flv_demuxer_destroy(flv);
|
||||
flv_reader_destroy(reader);
|
||||
mpeg_ts_destroy(ts);
|
||||
fclose(fp);
|
||||
}
|
60
src/3rdpart/media-server/libflv/test/h264-flv-test.cpp
Normal file
60
src/3rdpart/media-server/libflv/test/h264-flv-test.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "flv-writer.h"
|
||||
#include "flv-muxer.h"
|
||||
#include "mpeg4-avc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct h264_raw_t
|
||||
{
|
||||
flv_muxer_t* flv;
|
||||
uint32_t pts, dts;
|
||||
const uint8_t* ptr;
|
||||
int vcl;
|
||||
};
|
||||
|
||||
static int on_flv_packet(void* flv, int type, const void* data, size_t bytes, uint32_t timestamp)
|
||||
{
|
||||
return flv_writer_input(flv, type, data, bytes, timestamp);
|
||||
}
|
||||
|
||||
static void h264_handler(void* param, const uint8_t* nalu, int bytes)
|
||||
{
|
||||
struct h264_raw_t* ctx = (struct h264_raw_t*)param;
|
||||
assert(ctx->ptr < nalu);
|
||||
|
||||
const uint8_t* ptr = nalu - 3;
|
||||
// const uint8_t* end = (const uint8_t*)nalu + bytes;
|
||||
uint8_t nalutype = nalu[0] & 0x1f;
|
||||
if (ctx->vcl > 0 && h264_is_new_access_unit((const uint8_t*)nalu, bytes))
|
||||
{
|
||||
flv_muxer_avc(ctx->flv, ctx->ptr, ptr - ctx->ptr, ctx->pts, ctx->dts);
|
||||
ctx->pts += 40;
|
||||
ctx->dts += 40;
|
||||
|
||||
ctx->ptr = ptr;
|
||||
ctx->vcl = 0;
|
||||
}
|
||||
|
||||
if (1 <= nalutype && nalutype <= 5)
|
||||
++ctx->vcl;
|
||||
}
|
||||
|
||||
void avc2flv_test(const char* inputH264, const char* outputFLV)
|
||||
{
|
||||
struct h264_raw_t ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
void* f = flv_writer_create(outputFLV);
|
||||
ctx.flv = flv_muxer_create(on_flv_packet, f);
|
||||
FILE* fp = fopen(inputH264, "rb");
|
||||
|
||||
static uint8_t buffer[32 * 1024 * 1024];
|
||||
size_t n = fread(buffer, 1, sizeof(buffer), fp);
|
||||
ctx.ptr = buffer;
|
||||
mpeg4_h264_annexb_nalu(buffer, n, h264_handler, &ctx);
|
||||
fclose(fp);
|
||||
|
||||
flv_muxer_destroy(ctx.flv);
|
||||
flv_writer_destroy(f);
|
||||
}
|
54
src/3rdpart/media-server/libflv/test/h265-flv-test.cpp
Normal file
54
src/3rdpart/media-server/libflv/test/h265-flv-test.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "flv-writer.h"
|
||||
#include "flv-muxer.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct h264_raw_t
|
||||
{
|
||||
flv_muxer_t* flv;
|
||||
uint32_t pts, dts;
|
||||
const uint8_t* ptr;
|
||||
};
|
||||
|
||||
extern "C" void mpeg4_h264_annexb_nalu(const void* h264, size_t bytes, void(*handler)(void* param, const void* nalu, size_t bytes), void* param);
|
||||
|
||||
static int on_flv_packet(void* flv, int type, const void* data, size_t bytes, uint32_t timestamp)
|
||||
{
|
||||
return flv_writer_input(flv, type, data, bytes, timestamp);
|
||||
}
|
||||
|
||||
static void h265_handler(void* param, const void* nalu, size_t bytes)
|
||||
{
|
||||
struct h264_raw_t* ctx = (struct h264_raw_t*)param;
|
||||
assert(ctx->ptr < nalu);
|
||||
|
||||
const uint8_t* end = (const uint8_t*)nalu + bytes;
|
||||
uint8_t nalutype = (*(uint8_t*)nalu) & 0x1f;
|
||||
if (1 <= nalutype && nalutype <= 5)
|
||||
{
|
||||
flv_muxer_hevc(ctx->flv, ctx->ptr, end - ctx->ptr, ctx->pts, ctx->dts);
|
||||
ctx->ptr = end;
|
||||
ctx->pts += 40;
|
||||
ctx->dts += 40;
|
||||
}
|
||||
}
|
||||
|
||||
void hevc2flv_test(const char* inputH265, const char* outputFLV)
|
||||
{
|
||||
struct h264_raw_t ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
void* f = flv_writer_create(outputFLV);
|
||||
ctx.flv = flv_muxer_create(on_flv_packet, f);
|
||||
FILE* fp = fopen(inputH265, "rb");
|
||||
|
||||
static uint8_t buffer[4 * 1024 * 1024];
|
||||
size_t n = fread(buffer, 1, sizeof(buffer), fp);
|
||||
ctx.ptr = buffer;
|
||||
mpeg4_h264_annexb_nalu(buffer, n, h265_handler, &ctx);
|
||||
fclose(fp);
|
||||
|
||||
flv_muxer_destroy(ctx.flv);
|
||||
flv_writer_destroy(f);
|
||||
}
|
BIN
src/3rdpart/media-server/libflv/test/rtmp.onStatus.amf0
Normal file
BIN
src/3rdpart/media-server/libflv/test/rtmp.onStatus.amf0
Normal file
Binary file not shown.
69
src/3rdpart/media-server/libflv/test/ts2flv-test.cpp
Normal file
69
src/3rdpart/media-server/libflv/test/ts2flv-test.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "flv-writer.h"
|
||||
#include "flv-muxer.h"
|
||||
#include "mpeg4-aac.h"
|
||||
#include "mpeg-ts.h"
|
||||
#include "mpeg-ts-proto.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int on_flv_packet(void* flv, int type, const void* data, size_t bytes, uint32_t timestamp)
|
||||
{
|
||||
return flv_writer_input(flv, type, data, bytes, timestamp);
|
||||
}
|
||||
|
||||
static int on_ts_packet(void* param, int program, int /*stream*/, int avtype, int flags, int64_t pts, int64_t dts, const void* data, size_t bytes)
|
||||
{
|
||||
static int64_t s_pts = 0;
|
||||
if (0 == s_pts)
|
||||
s_pts = pts;
|
||||
pts -= s_pts;
|
||||
dts -= s_pts;
|
||||
|
||||
flv_muxer_t* muxer = (flv_muxer_t*)param;
|
||||
if (PSI_STREAM_AAC == avtype)
|
||||
{
|
||||
int len = mpeg4_aac_adts_frame_length((const uint8_t*)data, bytes);
|
||||
while (len > 0 && len <= bytes)
|
||||
{
|
||||
flv_muxer_aac(muxer, data, len, (uint32_t)(pts / 90), (uint32_t)(pts / 90));
|
||||
data = (const uint8_t*)data + len;
|
||||
bytes -= len;
|
||||
len = mpeg4_aac_adts_frame_length((const uint8_t*)data, bytes);
|
||||
}
|
||||
assert(0 == bytes);
|
||||
}
|
||||
else if (PSI_STREAM_MP3 == avtype)
|
||||
{
|
||||
flv_muxer_mp3(muxer, data, bytes, (uint32_t)(pts / 90), (uint32_t)(pts / 90));
|
||||
}
|
||||
else if (PSI_STREAM_H264 == avtype)
|
||||
{
|
||||
flv_muxer_avc(muxer, data, bytes, (uint32_t)(pts / 90), (uint32_t)(pts / 90));
|
||||
}
|
||||
else if (PSI_STREAM_H265 == avtype)
|
||||
{
|
||||
flv_muxer_hevc(muxer, data, bytes, (uint32_t)(pts / 90), (uint32_t)(pts / 90));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ts2flv_test(const char* inputTS, const char* outputFLV)
|
||||
{
|
||||
void* f = flv_writer_create(outputFLV);
|
||||
flv_muxer_t* m = flv_muxer_create(on_flv_packet, f);
|
||||
|
||||
unsigned char ptr[188];
|
||||
FILE* fp = fopen(inputTS, "rb");
|
||||
ts_demuxer_t *ts = ts_demuxer_create(on_ts_packet, m);
|
||||
while (1 == fread(ptr, sizeof(ptr), 1, fp))
|
||||
{
|
||||
ts_demuxer_input(ts, ptr, sizeof(ptr));
|
||||
}
|
||||
ts_demuxer_flush(ts);
|
||||
ts_demuxer_destroy(ts);
|
||||
fclose(fp);
|
||||
|
||||
flv_muxer_destroy(m);
|
||||
flv_writer_destroy(f);
|
||||
}
|
2
src/3rdpart/media-server/libflv/version.ver
Normal file
2
src/3rdpart/media-server/libflv/version.ver
Normal file
@ -0,0 +1,2 @@
|
||||
const char* const version_version="0.6.0.$WCREV$";
|
||||
const char* const version_build="$WCNOW$";
|
16
src/3rdpart/media-server/libhls/Android.mk
Normal file
16
src/3rdpart/media-server/libhls/Android.mk
Normal 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)/../libmpeg/include
|
||||
|
||||
LOCAL_SRC_FILES := $(wildcard source/*.c)
|
||||
LOCAL_SRC_FILES += $(wildcard source/*.cpp)
|
||||
|
||||
LOCAL_MODULE := hls
|
||||
include $(BUILD_STATIC_LIBRARY)
|
45
src/3rdpart/media-server/libhls/Makefile
Normal file
45
src/3rdpart/media-server/libhls/Makefile
Normal file
@ -0,0 +1,45 @@
|
||||
#--------------------------------Output------------------------------
|
||||
# OUTTYPE: 0-exe, 1-dll, 2-static
|
||||
#--------------------------------------------------------------------
|
||||
OUTTYPE = 2
|
||||
OUTFILE = libhls.a
|
||||
|
||||
#-------------------------------Include------------------------------
|
||||
#
|
||||
# INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix
|
||||
#--------------------------------------------------------------------
|
||||
INCLUDES = . \
|
||||
./include \
|
||||
../libmov/include \
|
||||
../libmpeg/include
|
||||
|
||||
#-------------------------------Source-------------------------------
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
SOURCE_PATHS = source
|
||||
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
|
62
src/3rdpart/media-server/libhls/demo/Makefile
Normal file
62
src/3rdpart/media-server/libhls/demo/Makefile
Normal file
@ -0,0 +1,62 @@
|
||||
ROOT:=../../../sdk
|
||||
|
||||
#--------------------------------Output------------------------------
|
||||
# OUTTYPE: 0-exe, 1-dll, 2-static
|
||||
#--------------------------------------------------------------------
|
||||
OUTTYPE = 0
|
||||
OUTFILE = hls-server
|
||||
|
||||
#-------------------------------Include------------------------------
|
||||
#
|
||||
# INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix
|
||||
#--------------------------------------------------------------------
|
||||
INCLUDES = . \
|
||||
../include \
|
||||
../../libmpeg/include \
|
||||
../../libflv/include \
|
||||
$(ROOT)/include \
|
||||
$(ROOT)/libhttp/include \
|
||||
$(ROOT)/libaio/include
|
||||
|
||||
#-------------------------------Source-------------------------------
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
#SOURCE_PATHS = source
|
||||
SOURCE_FILES = hls-server.cpp
|
||||
SOURCE_FILES += $(ROOT)/source/unicode.c
|
||||
SOURCE_FILES += $(ROOT)/source/urlcodec.c
|
||||
SOURCE_FILES += $(ROOT)/source/uri-parse.c
|
||||
SOURCE_FILES += $(ROOT)/source/digest/crc32.c
|
||||
SOURCE_FILES += $(ROOT)/libhttp/test/http-list-dir.cpp
|
||||
|
||||
#-----------------------------Library--------------------------------
|
||||
#
|
||||
# LIBPATHS = $(addprefix -L,$(LIBPATHS)) # add -L prefix
|
||||
#--------------------------------------------------------------------
|
||||
LIBPATHS = $(ROOT)/libaio/$(BUILD).$(PLATFORM)
|
||||
ifdef RELEASE
|
||||
# relase library path
|
||||
LIBPATHS +=
|
||||
else
|
||||
LIBPATHS +=
|
||||
endif
|
||||
|
||||
LIBS = rt dl pthread aio
|
||||
|
||||
STATIC_LIBS = ../../libhls/$(BUILD).$(PLATFORM)/libhls.a \
|
||||
../../libmpeg/$(BUILD).$(PLATFORM)/libmpeg.a \
|
||||
../../libflv/$(BUILD).$(PLATFORM)/libflv.a \
|
||||
$(ROOT)/libhttp/$(BUILD).$(PLATFORM)/libhttp.a
|
||||
|
||||
#-----------------------------DEFINES--------------------------------
|
||||
#
|
||||
# DEFINES := $(addprefix -D,$(DEFINES)) # add -L prefix
|
||||
#--------------------------------------------------------------------
|
||||
DEFINES = _HLS_SERVER_TEST_
|
||||
|
||||
include ../../gcc.mk
|
||||
|
||||
GCC_VER_GTE44 := $(shell echo `gcc -dumpversion | cut -f1-2 -d.` \< 4.4 | bc )
|
||||
ifeq ($(GCC_VER_GTE44),1)
|
||||
CFLAGS += -march=i586
|
||||
endif
|
17
src/3rdpart/media-server/libhls/demo/alternate.m3u8
Normal file
17
src/3rdpart/media-server/libhls/demo/alternate.m3u8
Normal file
@ -0,0 +1,17 @@
|
||||
#EXTM3U
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="eng",NAME="English",AUTOSELECT=YES, DEFAULT=YES,URI="englo/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="fre",NAME="Français",AUTOSELECT=YES, DEFAULT=NO,URI="frelo/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-lo",LANGUAGE="es",NAME="Espanol",AUTOSELECT=YES, DEFAULT=NO,URI="splo/prog_index.m3u8"
|
||||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-hi",LANGUAGE="eng",NAME="English",AUTOSELECT=YES, DEFAULT=YES,URI="eng/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-hi",LANGUAGE="fre",NAME="Français",AUTOSELECT=YES, DEFAULT=NO,URI="fre/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-hi",LANGUAGE="es",NAME="Espanol",AUTOSELECT=YES, DEFAULT=NO,URI="sp/prog_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=195023,CODECS="mp4a.40.5", AUDIO="audio-lo"
|
||||
lo/prog_index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=260000,CODECS="avc1.42e01e,mp4a.40.2", AUDIO="audio-lo"
|
||||
hi/prog_index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=591680,CODECS="mp4a.40.2, avc1.64001e", AUDIO="audio-hi"
|
||||
lo/prog_index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=650000,CODECS="avc1.42e01e,mp4a.40.2", AUDIO="audio-hi"
|
||||
hi/prog_index.m3u8
|
16
src/3rdpart/media-server/libhls/demo/alternate1.m3u8
Normal file
16
src/3rdpart/media-server/libhls/demo/alternate1.m3u8
Normal file
@ -0,0 +1,16 @@
|
||||
#EXTM3U
|
||||
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="200kbs",NAME="Angle1",AUTOSELECT=YES,DEFAULT=YES
|
||||
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="200kbs",NAME="Angle2",AUTOSELECT=YES,DEFAULT=NO, URI="Angle2/200kbs/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="200kbs",NAME="Angle3",AUTOSELECT=YES,DEFAULT=NO, URI="Angle3/200kbs/prog_index.m3u8"
|
||||
|
||||
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="500kbs",NAME="Angle1",AUTOSELECT=YES,DEFAULT=YES
|
||||
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="500kbs",NAME="Angle2",AUTOSELECT=YES,DEFAULT=NO, URI="Angle2/500kbs/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="500kbs",NAME="Angle3",AUTOSELECT=YES,DEFAULT=NO, URI="Angle3/500kbs/prog_index.m3u8"
|
||||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",LANGUAGE="en",NAME="English",AUTOSELECT=YES, DEFAULT=YES,URI="eng/prog_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000,CODECS="mp4a.40.2,avc1.4d401e", VIDEO="200kbs",AUDIO="aac"
|
||||
Angle1/200kbs/prog_index.m3u
|
||||
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=754857,CODECS="mp4a.40.2,avc1.4d401e", VIDEO="500kbs",AUDIO="aac"
|
||||
Angle1/500kbs/prog_index.m3u8
|
89
src/3rdpart/media-server/libhls/demo/hls-segmenter-flv.cpp
Normal file
89
src/3rdpart/media-server/libhls/demo/hls-segmenter-flv.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "mpeg-ps.h"
|
||||
#include "hls-m3u8.h"
|
||||
#include "hls-media.h"
|
||||
#include "hls-param.h"
|
||||
#include "flv-proto.h"
|
||||
#include "flv-reader.h"
|
||||
#include "flv-demuxer.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int hls_handler(void* m3u8, const void* data, size_t bytes, int64_t pts, int64_t dts, int64_t duration)
|
||||
{
|
||||
static int64_t s_dts = -1;
|
||||
int discontinue = -1 != s_dts ? 0 : (dts > s_dts + HLS_DURATION / 2 ? 1 : 0);
|
||||
s_dts = dts;
|
||||
|
||||
static int i = 0;
|
||||
char name[128] = {0};
|
||||
snprintf(name, sizeof(name), "%d.ts", i++);
|
||||
hls_m3u8_add((hls_m3u8_t*)m3u8, name, pts, duration, discontinue);
|
||||
|
||||
FILE* fp = fopen(name, "wb");
|
||||
if(fp)
|
||||
{
|
||||
fwrite(data, 1, bytes, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flv_handler(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
|
||||
{
|
||||
hls_media_t* hls = (hls_media_t*)param;
|
||||
|
||||
switch (codec)
|
||||
{
|
||||
case FLV_AUDIO_AAC:
|
||||
return hls_media_input(hls, STREAM_AUDIO_AAC, data, bytes, pts, dts, 0);
|
||||
|
||||
case FLV_AUDIO_MP3:
|
||||
return hls_media_input(hls, STREAM_AUDIO_MP3, data, bytes, pts, dts, 0);
|
||||
|
||||
case FLV_VIDEO_H264:
|
||||
return hls_media_input(hls, STREAM_VIDEO_H264, data, bytes, pts, dts, flags ? HLS_FLAGS_KEYFRAME : 0);
|
||||
|
||||
case FLV_VIDEO_H265:
|
||||
return hls_media_input(hls, STREAM_VIDEO_H265, data, bytes, pts, dts, flags ? HLS_FLAGS_KEYFRAME : 0);
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void hls_segmenter_flv(const char* file)
|
||||
{
|
||||
hls_m3u8_t* m3u = hls_m3u8_create(0, 3);
|
||||
hls_media_t* hls = hls_media_create(HLS_DURATION * 1000, hls_handler, m3u);
|
||||
void* flv = flv_reader_create(file);
|
||||
flv_demuxer_t* demuxer = flv_demuxer_create(flv_handler, hls);
|
||||
|
||||
int r, type;
|
||||
size_t taglen;
|
||||
uint32_t timestamp;
|
||||
static char data[2 * 1024 * 1024];
|
||||
while (1 == flv_reader_read(flv, &type, ×tamp, &taglen, data, sizeof(data)))
|
||||
{
|
||||
r = flv_demuxer_input(demuxer, type, data, taglen, timestamp);
|
||||
assert(0 == r);
|
||||
}
|
||||
|
||||
// write m3u8 file
|
||||
hls_media_input(hls, STREAM_VIDEO_H264, NULL, 0, 0, 0, 0);
|
||||
hls_m3u8_playlist(m3u, 1, data, sizeof(data));
|
||||
FILE* fp = fopen("playlist.m3u8", "wb");
|
||||
if(fp)
|
||||
{
|
||||
fwrite(data, 1, strlen(data), fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
flv_demuxer_destroy(demuxer);
|
||||
flv_reader_destroy(flv);
|
||||
hls_media_destroy(hls);
|
||||
hls_m3u8_destroy(m3u);
|
||||
}
|
159
src/3rdpart/media-server/libhls/demo/hls-segmenter-mp4.cpp
Normal file
159
src/3rdpart/media-server/libhls/demo/hls-segmenter-mp4.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
#if defined(_HAVE_FFMPEG_)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "libavformat/avformat.h"
|
||||
}
|
||||
#include "hls-fmp4.h"
|
||||
#include "hls-m3u8.h"
|
||||
#include "hls-param.h"
|
||||
#include "mov-format.h"
|
||||
#include "mpeg-ps.h"
|
||||
#include <assert.h>
|
||||
|
||||
static char s_packet[2 * 1024 * 1024];
|
||||
|
||||
static void ffmpeg_init()
|
||||
{
|
||||
avcodec_register_all();
|
||||
av_register_all();
|
||||
avformat_network_init();
|
||||
}
|
||||
|
||||
static AVFormatContext* ffmpeg_open(const char* url)
|
||||
{
|
||||
int r;
|
||||
AVFormatContext* ic;
|
||||
AVDictionary* opt = NULL;
|
||||
ic = avformat_alloc_context();
|
||||
if (NULL == ic)
|
||||
{
|
||||
printf("%s(%s): avformat_alloc_context failed.\n", __FUNCTION__, url);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//if (!av_dict_get(ff->opt, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
|
||||
// av_dict_set(&ff->opt, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
|
||||
// scan_all_pmts_set = 1;
|
||||
//}
|
||||
|
||||
r = avformat_open_input(&ic, url, NULL, &opt);
|
||||
if (0 != r)
|
||||
{
|
||||
printf("%s: avformat_open_input(%s) => %d\n", __FUNCTION__, url, r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//if (scan_all_pmts_set)
|
||||
// av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
|
||||
|
||||
//ff->ic->probesize = 100 * 1024;
|
||||
//ff->ic->max_analyze_duration = 5 * AV_TIME_BASE;
|
||||
|
||||
/* If not enough info to get the stream parameters, we decode the
|
||||
first frames to get it. (used in mpeg case for example) */
|
||||
r = avformat_find_stream_info(ic, NULL/*&opt*/);
|
||||
if (r < 0) {
|
||||
printf("%s(%s): could not find codec parameters\n", __FUNCTION__, url);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
av_dict_free(&opt);
|
||||
return ic;
|
||||
}
|
||||
|
||||
static int hls_init_segment(hls_fmp4_t* hls, hls_m3u8_t* m3u)
|
||||
{
|
||||
int bytes = hls_fmp4_init_segment(hls, s_packet, sizeof(s_packet));
|
||||
|
||||
FILE* fp = fopen("hls/0.mp4", "wb");
|
||||
fwrite(s_packet, 1, bytes, fp);
|
||||
fclose(fp);
|
||||
|
||||
return hls_m3u8_set_x_map(m3u, "hls/0.mp4");
|
||||
}
|
||||
|
||||
static int hls_segment(void* m3u8, const void* data, size_t bytes, int64_t /*pts*/, int64_t dts, int64_t duration)
|
||||
{
|
||||
static int i = 0;
|
||||
static char name[128] = { 0 };
|
||||
snprintf(name, sizeof(name), "hls/%d.mp4", ++i);
|
||||
FILE* fp = fopen(name, "wb");
|
||||
fwrite(data, 1, bytes, fp);
|
||||
fclose(fp);
|
||||
|
||||
return hls_m3u8_add((hls_m3u8_t*)m3u8, name, dts, duration, 0);
|
||||
}
|
||||
|
||||
void hls_segmenter_fmp4_test(const char* file)
|
||||
{
|
||||
ffmpeg_init();
|
||||
|
||||
AVPacket pkt;
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
//av_init_packet(&pkt);
|
||||
|
||||
AVFormatContext* ic = ffmpeg_open(file);
|
||||
hls_m3u8_t* m3u = hls_m3u8_create(0, 7);
|
||||
hls_fmp4_t* hls = hls_fmp4_create(HLS_DURATION * 1000, hls_segment, m3u);
|
||||
|
||||
int track_aac = -1;
|
||||
int track_264 = -1;
|
||||
int track_265 = -1;
|
||||
for (unsigned int i = 0; i < ic->nb_streams; i++)
|
||||
{
|
||||
AVStream* st = ic->streams[i];
|
||||
if (AV_CODEC_ID_AAC == st->codecpar->codec_id)
|
||||
track_aac = hls_fmp4_add_audio(hls, MOV_OBJECT_AAC, st->codecpar->channels, st->codecpar->bits_per_coded_sample, st->codecpar->sample_rate, st->codecpar->extradata, st->codecpar->extradata_size);
|
||||
else if (AV_CODEC_ID_H264 == st->codecpar->codec_id)
|
||||
track_264 = hls_fmp4_add_video(hls, MOV_OBJECT_H264, st->codecpar->width, st->codecpar->height, st->codecpar->extradata, st->codecpar->extradata_size);
|
||||
else if(AV_CODEC_ID_H265 == st->codecpar->codec_id)
|
||||
track_265 = hls_fmp4_add_video(hls, MOV_OBJECT_HEVC, st->codecpar->width, st->codecpar->height, st->codecpar->extradata, st->codecpar->extradata_size);
|
||||
}
|
||||
|
||||
// write init segment
|
||||
hls_init_segment(hls, m3u);
|
||||
|
||||
int r = av_read_frame(ic, &pkt);
|
||||
while (0 == r)
|
||||
{
|
||||
AVStream* st = ic->streams[pkt.stream_index];
|
||||
int64_t pts = (int64_t)(pkt.pts * av_q2d(st->time_base) * 1000);
|
||||
int64_t dts = (int64_t)(pkt.dts * av_q2d(st->time_base) * 1000);
|
||||
if (AV_CODEC_ID_AAC == st->codecpar->codec_id)
|
||||
{
|
||||
//printf("[A] pts: %08lld, dts: %08lld\n", pts, dts);
|
||||
hls_fmp4_input(hls, track_aac, pkt.data, pkt.size, pts, dts, 0);
|
||||
}
|
||||
else if (AV_CODEC_ID_H264 == st->codecpar->codec_id)
|
||||
{
|
||||
//printf("[V] pts: %08lld, dts: %08lld\n", pts, dts);
|
||||
hls_fmp4_input(hls, track_264, pkt.data, pkt.size, pts, dts, (pkt.flags & AV_PKT_FLAG_KEY) ? MOV_AV_FLAG_KEYFREAME : 0);
|
||||
}
|
||||
else if (AV_CODEC_ID_H265 == st->codecpar->codec_id)
|
||||
{
|
||||
//printf("[V] pts: %08lld, dts: %08lld\n", pts, dts);
|
||||
hls_fmp4_input(hls, track_265, pkt.data, pkt.size, pts, dts, (pkt.flags & AV_PKT_FLAG_KEY) ? MOV_AV_FLAG_KEYFREAME : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
//av_packet_unref(&pkt);
|
||||
r = av_read_frame(ic, &pkt);
|
||||
}
|
||||
|
||||
avformat_close_input(&ic);
|
||||
avformat_free_context(ic);
|
||||
hls_fmp4_destroy(hls);
|
||||
|
||||
// write m3u8 file
|
||||
hls_m3u8_playlist(m3u, 1, s_packet, sizeof(s_packet));
|
||||
hls_m3u8_destroy(m3u);
|
||||
|
||||
FILE* fp = fopen("playlist.m3u8", "wb");
|
||||
fwrite(s_packet, 1, strlen(s_packet), fp);
|
||||
fclose(fp);
|
||||
}
|
||||
#endif
|
304
src/3rdpart/media-server/libhls/demo/hls-server.cpp
Normal file
304
src/3rdpart/media-server/libhls/demo/hls-server.cpp
Normal file
@ -0,0 +1,304 @@
|
||||
#include "aio-worker.h"
|
||||
#include "aio-socket.h"
|
||||
#include "mpeg-ps.h"
|
||||
#include "mpeg-ts.h"
|
||||
#include "hls-m3u8.h"
|
||||
#include "hls-media.h"
|
||||
#include "hls-param.h"
|
||||
#include "flv-proto.h"
|
||||
#include "flv-reader.h"
|
||||
#include "flv-demuxer.h"
|
||||
#include "http-server.h"
|
||||
#include "http-route.h"
|
||||
#include "sys/thread.h"
|
||||
#include "sys/system.h"
|
||||
#include "sys/path.h"
|
||||
#include "cstringext.h"
|
||||
#include "utf8codec.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#define CWD "d:\\video\\"
|
||||
|
||||
extern "C" int http_list_dir(http_session_t* session, const char* path);
|
||||
|
||||
struct hls_ts_t
|
||||
{
|
||||
std::atomic<int> ref;
|
||||
void* data;
|
||||
size_t size;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct hls_playlist_t
|
||||
{
|
||||
pthread_t t;
|
||||
std::string file;
|
||||
|
||||
hls_media_t* hls;
|
||||
hls_m3u8_t* m3u8;
|
||||
int64_t pts;
|
||||
int64_t last_pts;
|
||||
uint8_t packet[2 * 1024 * 1024];
|
||||
|
||||
int i;
|
||||
std::list<hls_ts_t*> files;
|
||||
};
|
||||
|
||||
static std::map<std::string, hls_playlist_t*> s_playlists;
|
||||
|
||||
static int hls_handler(void* param, const void* data, size_t bytes, int64_t pts, int64_t /*dts*/, int64_t duration)
|
||||
{
|
||||
hls_playlist_t* playlist = (hls_playlist_t*)param;
|
||||
|
||||
int discontinue = 0;
|
||||
if (playlist->i > 0)
|
||||
{
|
||||
discontinue = (playlist->last_pts + HLS_DURATION * 1000 < pts/*discontinue*/ || pts + duration + HLS_DURATION * 1000 < playlist->pts/*rewind*/) ? 1 : 0;
|
||||
}
|
||||
playlist->pts = pts;
|
||||
playlist->last_pts = pts + duration;
|
||||
|
||||
char name[128] = { 0 };
|
||||
snprintf(name, sizeof(name), "%s/%d.ts", playlist->file.c_str(), playlist->i++);
|
||||
hls_m3u8_add(playlist->m3u8, name, pts, duration, discontinue);
|
||||
|
||||
// add new segment
|
||||
hls_ts_t* ts = new hls_ts_t;
|
||||
ts->ref = 1;
|
||||
ts->name = name;
|
||||
ts->size = bytes;
|
||||
ts->data = malloc(bytes);
|
||||
memcpy(ts->data, data, bytes);
|
||||
playlist->files.push_back(ts);
|
||||
|
||||
// remove oldest segment
|
||||
while(playlist->files.size() > HLS_LIVE_NUM + 1)
|
||||
{
|
||||
ts = playlist->files.front();
|
||||
playlist->files.pop_front();
|
||||
if (0 == std::atomic_fetch_sub(&ts->ref, 1) - 1)
|
||||
{
|
||||
free(ts->data);
|
||||
delete ts;
|
||||
}
|
||||
}
|
||||
|
||||
printf("new segment: %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flv_handler(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
|
||||
{
|
||||
hls_media_t* hls = (hls_media_t*)param;
|
||||
|
||||
switch (codec)
|
||||
{
|
||||
case FLV_AUDIO_AAC:
|
||||
return hls_media_input(hls, STREAM_AUDIO_AAC, data, bytes, pts, dts, 0);
|
||||
|
||||
case FLV_AUDIO_MP3:
|
||||
return hls_media_input(hls, STREAM_AUDIO_MP3, data, bytes, pts, dts, 0);
|
||||
|
||||
case FLV_VIDEO_H264:
|
||||
return hls_media_input(hls, STREAM_VIDEO_H264, data, bytes, pts, dts, flags ? HLS_FLAGS_KEYFRAME : 0);
|
||||
|
||||
case FLV_VIDEO_H265:
|
||||
return hls_media_input(hls, STREAM_VIDEO_H265, data, bytes, pts, dts, flags ? HLS_FLAGS_KEYFRAME : 0);
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int STDCALL hls_server_worker(void* param)
|
||||
{
|
||||
int r, type;
|
||||
size_t taglen;
|
||||
uint64_t clock;
|
||||
uint32_t timestamp;
|
||||
hls_playlist_t* playlist = (hls_playlist_t*)param;
|
||||
|
||||
std::string file = playlist->file + ".flv";
|
||||
UTF8Decode utf8(file.c_str());
|
||||
std::string fullpath = CWD;
|
||||
fullpath += utf8;
|
||||
|
||||
while (1)
|
||||
{
|
||||
void* flv = flv_reader_create(fullpath.c_str());
|
||||
flv_demuxer_t* demuxer = flv_demuxer_create(flv_handler, playlist->hls);
|
||||
|
||||
clock = 0;
|
||||
while (1 == flv_reader_read(flv, &type, ×tamp, &taglen, playlist->packet, sizeof(playlist->packet)))
|
||||
{
|
||||
uint64_t now = system_clock();
|
||||
if (0 == clock)
|
||||
{
|
||||
clock = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timestamp > now - clock)
|
||||
system_sleep(timestamp - (now - clock));
|
||||
}
|
||||
|
||||
r = flv_demuxer_input(demuxer, type, playlist->packet, taglen, timestamp);
|
||||
assert(0 == r);
|
||||
}
|
||||
|
||||
flv_demuxer_destroy(demuxer);
|
||||
flv_reader_destroy(flv);
|
||||
}
|
||||
hls_media_destroy(playlist->hls);
|
||||
//hls_m3u8_destroy(playlist->m3u8);
|
||||
//s_playlists.erase();
|
||||
//delete playlist;
|
||||
return thread_destroy(playlist->t);
|
||||
}
|
||||
|
||||
static int hls_server_m3u8(http_session_t* session, const std::string& path)
|
||||
{
|
||||
char playlist[8 * 1024];
|
||||
hls_m3u8_t* m3u8 = s_playlists.find(path)->second->m3u8;
|
||||
assert(m3u8);
|
||||
assert(0 == hls_m3u8_playlist(m3u8, 0, playlist, sizeof(playlist)));
|
||||
|
||||
http_server_set_header(session, "content-type", HLS_M3U8_TYPE);
|
||||
http_server_set_header(session, "Access-Control-Allow-Origin", "*");
|
||||
http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
http_server_reply(session, 200, playlist, strlen(playlist));
|
||||
|
||||
printf("load %s.m3u8 file\n", path.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hls_server_ts_onsend(void* param, int code, size_t bytes)
|
||||
{
|
||||
hls_ts_t* ts = (hls_ts_t*)param;
|
||||
if (0 == std::atomic_fetch_sub(&ts->ref, 1) - 1)
|
||||
{
|
||||
free(ts->data);
|
||||
delete ts;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hls_server_ts(http_session_t* session, const std::string& path, const std::string& ts)
|
||||
{
|
||||
hls_playlist_t* playlist = s_playlists.find(path)->second;
|
||||
assert(playlist);
|
||||
|
||||
std::list<hls_ts_t*>::iterator i;
|
||||
std::string file = path + '/' + ts;
|
||||
for(i = playlist->files.begin(); i != playlist->files.end(); ++i)
|
||||
{
|
||||
hls_ts_t* ts = *i;
|
||||
if(ts->name == file)
|
||||
{
|
||||
std::atomic_fetch_add(&ts->ref, 1);
|
||||
http_server_set_header(session, "Access-Control-Allow-Origin", "*");
|
||||
http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
http_server_send(session, 200, ts->data, ts->size, hls_server_ts_onsend, ts);
|
||||
printf("load file %s\n", file.c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("load ts file(%s) failed\n", file.c_str());
|
||||
return http_server_send(session, 404, "", 0, NULL, NULL);
|
||||
}
|
||||
|
||||
static int hls_server_onlive(void* /*http*/, http_session_t* session, const char* /*method*/, const char* path)
|
||||
{
|
||||
path = path + 6;
|
||||
if (strendswith(path, ".m3u8"))
|
||||
{
|
||||
std::string app(path, strlen(path) - 5);
|
||||
if (s_playlists.find(app) == s_playlists.end())
|
||||
{
|
||||
hls_playlist_t* playlist = new hls_playlist_t();
|
||||
playlist->file = app;
|
||||
playlist->m3u8 = hls_m3u8_create(HLS_LIVE_NUM, 3);
|
||||
playlist->hls = hls_media_create(HLS_DURATION * 1000, hls_handler, playlist);
|
||||
playlist->i = 0;
|
||||
s_playlists[app] = playlist;
|
||||
|
||||
thread_create(&playlist->t, hls_server_worker, playlist);
|
||||
}
|
||||
|
||||
return hls_server_m3u8(session, app);
|
||||
}
|
||||
else if (strendswith(path, ".ts"))
|
||||
{
|
||||
const char* ts = strchr(path, '/');
|
||||
std::string app(path, ts ? ts - path : strlen(path));
|
||||
if (ts && s_playlists.find(app) != s_playlists.end())
|
||||
{
|
||||
return hls_server_ts(session, app, ts + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return http_server_send(session, 404, "", 0, NULL, NULL);
|
||||
}
|
||||
|
||||
static int hls_server_onvod(void* /*http*/, http_session_t* session, const char* /*method*/, const char* path)
|
||||
{
|
||||
UTF8Decode utf8(path + 5 /* /vod/ */);
|
||||
std::string fullpath = CWD;
|
||||
fullpath += utf8;
|
||||
printf("hls_server_onvod: %s\n", fullpath.c_str());
|
||||
|
||||
if (path_testdir(fullpath.c_str()))
|
||||
{
|
||||
return http_list_dir(session, fullpath.c_str());
|
||||
}
|
||||
else if (path_testfile(fullpath.c_str()))
|
||||
{
|
||||
http_server_set_header(session, "Access-Control-Allow-Origin", "*");
|
||||
http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||
//http_server_set_header(session, "Transfer-Encoding", "chunked");
|
||||
if (std::string::npos != fullpath.find(".m3u8"))
|
||||
http_server_set_header(session, "content-type", HLS_M3U8_TYPE);
|
||||
else if (std::string::npos != fullpath.find(".mpd"))
|
||||
http_server_set_header(session, "content-type", "application/dash+xml");
|
||||
else if (std::string::npos != fullpath.find(".mp4") || std::string::npos != fullpath.find(".m4v"))
|
||||
http_server_set_header(session, "content-type", "video/mp4");
|
||||
else if (std::string::npos != fullpath.find(".m4a"))
|
||||
http_server_set_header(session, "content-type", "audio/mp4");
|
||||
return http_server_sendfile(session, fullpath.c_str(), NULL, NULL);
|
||||
}
|
||||
|
||||
return http_server_send(session, 404, "", 0, NULL, NULL);
|
||||
}
|
||||
|
||||
void hls_server_test(const char* ip, int port)
|
||||
{
|
||||
aio_worker_init(4);
|
||||
http_server_t* http = http_server_create(ip, port);
|
||||
http_server_set_handler(http, http_server_route, http);
|
||||
http_server_addroute("/live/", hls_server_onlive);
|
||||
http_server_addroute("/vod/", hls_server_onvod);
|
||||
|
||||
// http process
|
||||
while('q' != getchar())
|
||||
{
|
||||
}
|
||||
|
||||
http_server_destroy(http);
|
||||
aio_worker_clean(4);
|
||||
}
|
||||
|
||||
#if defined(_HLS_SERVER_TEST_)
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
hls_server_test(NULL, 80);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
16
src/3rdpart/media-server/libhls/demo/master.m3u8
Normal file
16
src/3rdpart/media-server/libhls/demo/master.m3u8
Normal file
@ -0,0 +1,16 @@
|
||||
#EXTM3U
|
||||
#EXT-X-SESSION-DATA:DATA-ID="com.example.lyrics",URI="lyrics.json"
|
||||
#EXT-X-SESSION-DATA:DATA-ID="com.example.title",LANGUAGE="en", VALUE="This is an example"
|
||||
#EXT-X-SESSION-DATA:DATA-ID="com.example.title",LANGUAGE="es", VALUE="Este es un ejemplo"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1280000
|
||||
low/audio-video.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI="low/iframe.m3u8"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2560000
|
||||
mid/audio-video.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=150000,URI="mid/iframe.m3u8"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=7680000
|
||||
hi/audio-video.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=550000,URI="hi/iframe.m3u8"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"
|
||||
audio-only.m3u8
|
20
src/3rdpart/media-server/libhls/demo/playlist.m3u8
Normal file
20
src/3rdpart/media-server/libhls/demo/playlist.m3u8
Normal file
@ -0,0 +1,20 @@
|
||||
#EXTM3U
|
||||
#EXT-X-VERSION:3
|
||||
#EXT-X-MEDIA-SEQUENCE:7794
|
||||
#EXT-X-TARGETDURATION:15
|
||||
|
||||
#EXT-X-DATERANGE:ID="splice-6FFFFFF0",START-DATE="2014-03-05T11:15:00Z",PLANNED-DURATION=59.993,SCTE35-OUT=0xFC002F0000000000FF000014056FFFFFF000E011622DCAFF000052636200000000000A0008029896F50000008700000000
|
||||
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"
|
||||
|
||||
#EXTINF:2.833,
|
||||
http://media.example.com/fileSequence52-A.ts
|
||||
#EXTINF:15.0,
|
||||
http://media.example.com/fileSequence52-B.ts
|
||||
#EXTINF:13.333,
|
||||
http://media.example.com/fileSequence52-C.ts
|
||||
|
||||
#EXT-X-DATERANGE:ID="splice-6FFFFFF0",DURATION=59.993,SCTE35-IN=0xFC002A0000000000FF00000F056FFFFFF000401162802E6100000000000A0008029896F50000008700000000
|
||||
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53",IV=0xaa3dcf6a7acb92ff4fb08d9b3b3d6f51
|
||||
|
||||
#EXTINF:15.0,
|
||||
http://media.example.com/fileSequence53-A.ts
|
9
src/3rdpart/media-server/libhls/demo/说明.txt
Normal file
9
src/3rdpart/media-server/libhls/demo/说明.txt
Normal file
@ -0,0 +1,9 @@
|
||||
1. 从github下载依赖的sdk目录,与media-server平级
|
||||
git clone https://github.com/ireader/sdk sdk
|
||||
|
||||
2. 分别编译libaio/libhttp/libmpeg/libflv/libhls
|
||||
make -C sdk/libaio && make -C sdk/libhttp && make -C media-server/libmpeg && make -C media-server/libflv && make -C media-server/libhls
|
||||
|
||||
3. cd media-server/libhls/test目录下执行make
|
||||
|
||||
4. ./debug.linux/hls-server
|
51
src/3rdpart/media-server/libhls/include/hls-fmp4.h
Normal file
51
src/3rdpart/media-server/libhls/include/hls-fmp4.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef _hls_fmp4_h_
|
||||
#define _hls_fmp4_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct hls_fmp4_t hls_fmp4_t;
|
||||
|
||||
/// @param[in] param user-defined parameter(hls_media_create)
|
||||
/// @param[in] data ts file content
|
||||
/// @param[in] bytes ts file length in byte
|
||||
/// @param[in] pts ts file first pts/dts(ms)
|
||||
/// @param[in] dts ts file first pts/dts(ms)
|
||||
/// @param[in] duration file duration(ms)
|
||||
/// @return 0-ok, other-error
|
||||
typedef int (*hls_fmp4_handler)(void* param, const void* data, size_t bytes, int64_t pts, int64_t dts, int64_t duration);
|
||||
|
||||
/// @param[in] duration ts segment duration(millisecond), 0-create segment per video key frame
|
||||
hls_fmp4_t* hls_fmp4_create(int64_t duration, hls_fmp4_handler handler, void* param);
|
||||
|
||||
void hls_fmp4_destroy(hls_fmp4_t* hls);
|
||||
|
||||
/// @param[in] object MPEG-4 systems ObjectTypeIndication such as: MOV_OBJECT_H264, see more @mov-format.h
|
||||
/// @param[in] extra_data AudioSpecificConfig/AVCDecoderConfigurationRecord/HEVCDecoderConfigurationRecord
|
||||
/// @return >=0-track, <0-error
|
||||
int hls_fmp4_add_audio(hls_fmp4_t* hls, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size);
|
||||
int hls_fmp4_add_video(hls_fmp4_t* hls, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size);
|
||||
|
||||
/// @param[in] track hls_fmp4_add_audio/hls_fmp4_add_video return value
|
||||
/// @param[in] data h264/h265 mp4 format stream
|
||||
/// @param[in] pts present timestamp in millisecond
|
||||
/// @param[in] dts decode timestamp in millisecond
|
||||
/// @param[in] flags MOV_AV_FLAG_XXX, such as: MOV_AV_FLAG_KEYFREAME, see more @mov-format.h
|
||||
/// @return 0-ok, other-error
|
||||
int hls_fmp4_input(hls_fmp4_t* hls, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags);
|
||||
|
||||
/// WARNING: hls_fmp4_input/hls_fmp4_init_segment not thread-safe
|
||||
/// @param[in] data init segment buffer
|
||||
/// @param[in] bytes data buffer size
|
||||
/// @return >0-bytes, <0-error
|
||||
int hls_fmp4_init_segment(hls_fmp4_t* hls, void* data, size_t bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_hls_fmp4_h_ */
|
38
src/3rdpart/media-server/libhls/include/hls-m3u8.h
Normal file
38
src/3rdpart/media-server/libhls/include/hls-m3u8.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef _hls_m3u8_h_
|
||||
#define _hls_m3u8_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct hls_m3u8_t hls_m3u8_t;
|
||||
|
||||
///@param[in] live 1-live streaming, 0-vod
|
||||
///@param[in] version m3u8 version 3-TS, 7-MP4
|
||||
hls_m3u8_t* hls_m3u8_create(int live, int version);
|
||||
void hls_m3u8_destroy(hls_m3u8_t* m3u8);
|
||||
|
||||
/// EXT-X-MAP
|
||||
int hls_m3u8_set_x_map(hls_m3u8_t* m3u8, const char* name);
|
||||
|
||||
///@param[in] pts present timestamp (millisecond)
|
||||
///@param[in] duration segment duration (millisecond)
|
||||
///@param[in] discontinuity 1-EXT-X-DISCONTINUITY flag, 0-ignore
|
||||
///@return 0-ok, other-error
|
||||
int hls_m3u8_add(hls_m3u8_t* m3u8, const char* name, int64_t pts, int64_t duration, int discontinuity);
|
||||
|
||||
///@return media segment count
|
||||
size_t hls_m3u8_count(hls_m3u8_t* m3u8);
|
||||
|
||||
///Get m3u8 playlist file
|
||||
///@param[in] eof 1-EXT-X-ENDLIST, 0-ignore
|
||||
///@return 0-ok, other-error
|
||||
int hls_m3u8_playlist(hls_m3u8_t* m3u8, int eof, char* playlist, size_t bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !_hls_m3u8_h_ */
|
42
src/3rdpart/media-server/libhls/include/hls-media.h
Normal file
42
src/3rdpart/media-server/libhls/include/hls-media.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef _hls_media_h_
|
||||
#define _hls_media_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define HLS_FLAGS_KEYFRAME 0x8000
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct hls_media_t hls_media_t;
|
||||
|
||||
/// @param[in] param user-defined parameter(hls_media_create)
|
||||
/// @param[in] data ts file content
|
||||
/// @param[in] bytes ts file length in byte
|
||||
/// @param[in] pts ts file first pts/dts(ms)
|
||||
/// @param[in] dts ts file first pts/dts(ms)
|
||||
/// @param[in] duration file duration(ms)
|
||||
/// @return 0-ok, other-error
|
||||
typedef int (*hls_media_handler)(void* param, const void* data, size_t bytes, int64_t pts, int64_t dts, int64_t duration);
|
||||
|
||||
/// param[in] duration ts segment duration(millisecond), 0-create segment per video key frame
|
||||
hls_media_t* hls_media_create(int64_t duration, hls_media_handler handler, void* param);
|
||||
|
||||
void hls_media_destroy(hls_media_t* hls);
|
||||
|
||||
/// @param[in] avtype audio/video type (mpeg-ps.h STREAM_VIDEO_XXX/STREAM_AUDIO_XXX)
|
||||
/// @param[in] data h264/h265 nalu with startcode(0x00000001), aac with adts
|
||||
/// @param[in] bytes data length in byte, NULL-force new segment
|
||||
/// @param[in] pts present timestamp in millisecond
|
||||
/// @param[in] dts decode timestamp in millisecond
|
||||
/// @param[in] flags HLS_FLAGS_XXX, such as HLS_FLAGS_KEYFRAME
|
||||
/// @return 0-ok, other-error
|
||||
int hls_media_input(hls_media_t* hls, int avtype, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_hls_media_h_*/
|
80
src/3rdpart/media-server/libhls/include/hls-param.h
Normal file
80
src/3rdpart/media-server/libhls/include/hls-param.h
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef _hls_param_h_
|
||||
#define _hls_param_h_
|
||||
|
||||
// https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
|
||||
// https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
|
||||
// 1. What kinds of encoders are supported?
|
||||
// H.264 video and AAC audio (HE-AAC or AAC-LC).
|
||||
// 2. What are the specifics of the video and audio formats supported?
|
||||
// Video: H.264 Baseline Level 3.0, Baseline Level 3.1, Main Level 3.1, and High Profile Level 4.1.
|
||||
// Audio: HE-AAC or AAC-LC up to 48 kHz, stereo audio
|
||||
// MP3 (MPEG-1 Audio Layer 3) 8 kHz to 48 kHz, stereo audio
|
||||
// AC-3 (for Apple TV, in pass-through mode only)
|
||||
|
||||
// 3. What duration should media files be?
|
||||
// A duration of 10 seconds of media per file seems to strike a reasonable balance for most broadcast content.
|
||||
// http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8
|
||||
#define HLS_DURATION 10 // 10s, from Apple recommendation
|
||||
|
||||
// 4. How many files should be listed in the index file during a continuous, ongoing session?
|
||||
// The normal recommendation is 3, but the optimum number may be larger.
|
||||
#define HLS_LIVE_NUM 3
|
||||
|
||||
// HTTP Content-Type
|
||||
// Playlist files whose names end in .m3u8 and/or have the HTTP Content-
|
||||
// Type "application/vnd.apple.mpegurl" are encoded in UTF-8[RFC3629].
|
||||
// Files whose names end with.m3u and/or have the HTTP Content-Type
|
||||
// [RFC2616] "audio/mpegurl" are encoded in US-ASCII[US_ASCII].
|
||||
#define HLS_M3U8_TYPE "application/vnd.apple.mpegURL"
|
||||
#define HLS_M3U_TYPE "audio/mpegurl"
|
||||
#define HLS_TS_TYPE "video/MP2T"
|
||||
|
||||
#define PTS_NO_VALUE INT64_MIN //(int64_t)0x8000000000000000L
|
||||
|
||||
|
||||
// version: 1 (default)
|
||||
//
|
||||
// version: 2
|
||||
// The IV attribute of the EXT-X-KEY tag.
|
||||
// version: 3
|
||||
// Floating-point EXTINF duration values.
|
||||
// version: 4
|
||||
// The EXT-X-BYTERANGE tag.
|
||||
// The EXT-X-I-FRAMES-ONLY tag.
|
||||
// version: 5
|
||||
// The KEYFORMAT and KEYFORMATVERSIONS attributes of the EXT-X-KEY tag.
|
||||
// The EXT-X-MAP tag.
|
||||
// version: 6
|
||||
// The EXT-X-MAP tag in a Media playlist that does not contain EXT-X-I-FRAMES-ONLY.
|
||||
|
||||
|
||||
// 6.2.2. Live Playlists
|
||||
// 1. The server MUST NOT remove a media segment from the Playlist file if
|
||||
// the duration of the Playlist file minus the duration of the segment
|
||||
// is less than three times the target duration
|
||||
// 2. When the server removes a media segment from the Playlist, the
|
||||
// corresponding media URI SHOULD remain available to clients for a
|
||||
// period of time equal to the duration of the segment plus the duration
|
||||
// of the longest Playlist file distributed by the server containing
|
||||
// that segment.
|
||||
|
||||
// 6.3.3. Playing the Playlist file
|
||||
// 1. If the EXT-X-ENDLIST tag is not present, the client SHOULD NOT
|
||||
// choose a segment which starts less than three target durations from
|
||||
// the end of the Playlist file.
|
||||
// 2. The client SHOULD attempt to load media segments in advance of when
|
||||
// they will be required for uninterrupted playback to compensate for
|
||||
// temporary variations in latency and throughput.
|
||||
// 3. The client MUST be prepared to reset its parser(s) and decoder(s)
|
||||
// before playing a media segment that has an EXT-X-DISCONTINUITY tag
|
||||
// applied to it.
|
||||
|
||||
// 6.3.4. Reloading the Playlist file
|
||||
// 1. The client MUST periodically reload the Media Playlist file unless it
|
||||
// contains the EXT-X-ENDLIST tag.
|
||||
// 2. if Playlist file changed since the last time it was loaded, the client MUST wait for
|
||||
// at least the target duration before attempting to reload the Playlist file again
|
||||
// 3. if Playlist file not changed then it MUST wait for a period of one-half the target
|
||||
// duration before retrying
|
||||
|
||||
#endif /* !_hls_param_h_ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user