Level2 28181

This commit is contained in:
AlanRen 2023-10-26 21:54:29 -06:00
parent c3c3aae6ce
commit ab84aad151
535 changed files with 74182 additions and 253 deletions

2
.gitignore vendored
View File

@ -8,3 +8,5 @@ release/
*.data
*.len
*.pcap

24
.vscode/launch.json vendored
View File

@ -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"
}
]

View File

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

@ -0,0 +1,16 @@
include_directories(../include)
file(GLOB sender_src_list ./*.cpp ./*.h ../include/*.h)
MESSAGE(STATUS ${sender_src_list})
find_package(PcapPlusPlus CONFIG REQUIRED)
find_package(DPDK REQUIRED)
message(STATUS "Using Pcap++ ${PcapPlusPlus_VERSION}")
message(STATUS "Include dir: ${PcapPlusPlus_INCLUDE_DIR}")
add_executable(PcapSender ${sender_src_list})
target_link_libraries(PcapSender PUBLIC ${LINK_LIB_LIST} PUBLIC PcapPlusPlus::Pcap++ PUBLIC DPDK::DPDK)
# target_link_libraries(PcapSender PUBLIC PcapPlusPlus::Pcap++)

234
PcapSender/main.cpp Normal file
View 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, &param);
if (append_len==0)
{
sendto(socket,sign_out_buf,sign_out_len,0,(const sockaddr*)&addr,sizeof(addr));
}else
{
sendto(socket,sign_out_buf,offset_len,0,(const sockaddr*)&addr,sizeof(addr));
sendto(socket,sign_out_buf+offset_len,sign_out_len-offset_len,0,(const sockaddr*)&addr,sizeof(addr));
}
}else{
if(sendto(socket,payload,payload_len,0,(const sockaddr*)&addr,sizeof(addr))==-1){
printf("send failed : %s\n", strerror(errno));
}
}
}
// cout<<" stamp:" << rawPacket.getPacketTimeStamp().tv_sec<<" len:"<< rawPacket.getRawDataLen()<<endl;
}
// reader->getNextPacket(rawPacket);
// for (pcpp::Layer* curLayer = parsedPacket.getFirstLayer(); curLayer != NULL; curLayer = curLayer->getNextLayer())
// {
// std::cout
// << "Layer type: " <<getProtocolTypeAsString(curLayer->getProtocol()) << "; " // get layer type
// << "Total data: " << curLayer->getDataLen() << " [bytes]; " // get total length of the layer
// << "Layer data: " << curLayer->getHeaderLen() << " [bytes]; " // get the header length of the layer
// << "Layer payload: " << curLayer->getLayerPayloadSize() << " [bytes]" // get the payload length of the layer (equals total length minus header length)
// << std::endl;
// }
// create the stats object
pcpp::IPcapDevice::PcapStats stats;
reader->getStatistics(stats);
std::cout << "Read " << stats.packetsRecv << " packets successfully and " << stats.packetsDrop << " packets could not be read" << std::flush;
return 0;
}
int main(int argc, char *argv[]){
char ip[16] = {0};
int sockfd, port;
if (argc>4)
{
string filename(argv[1]);
strcpy(ip, argv[2]);
port = atoi(argv[3]);
string filter(argv[4]);
printf("filename:%s\nip:%s\nport:%d\nfilter:%s\n", filename.data(), ip,port,filter.data());
sockfd = socket(AF_INET, SOCK_DGRAM , IPPROTO_UDP);//SOCK_STREAM
if (sockfd == -1) {
printf("create socket failed : %s\n", strerror(errno));
return -1;
}
int rcvBufSize = 2097152;
int rcvlen = sizeof(rcvBufSize);
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&rcvBufSize, sizeof(rcvBufSize));
if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&rcvBufSize, (socklen_t *)&rcvlen) >= 0)
{
printf("set udp socket send buff size to : %d\n", rcvBufSize);
if (rcvBufSize < 4194304)
{
printf("socket send buff too small, please set up to 2097152(2M)");
}
}else {
printf("socket failed : %s\n", strerror(errno));
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr =inet_addr(ip);
// if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr))==-1){
// printf("connection error : %s\n", strerror(errno));
// return -1;
// }
// bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
// while (1)
// {
cout.flush();
auto sign_h=EncrypInit();
ReadPcapAndSend(sockfd,addr,filename,filter,sign_h);
// }
}
else
{
printf("CMD as: filename.pcap ip port\n");
system("pause");
return 0;
}
return 0;
}

View File

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

Binary file not shown.

View File

@ -0,0 +1,28 @@
language: c
install: true
before_script:
- wget https://github.com/ireader/sdk/archive/master.zip -O /tmp/sdk.zip
- unzip /tmp/sdk.zip
- echo $PWD
- mv sdk-master ../sdk
env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "I/9S/L5PVzty9kcCcWKPnF3rlZ1FqVIBMCqZkjf2zx6Pl+FHQoAe/9KSdRFtKfzM+GxRu1ttxvmNignghgO60QGS7tvU79N/u/5o4xZ1UdCotGmFmwytEZ+szuTrH2PuSlA3p1opcvuWsrcIpUFXBU/qp9VYZl8v8L6qpir9vtAeMGjMGSJO4pC5ePnv4oxtrLLY1u+uPVMdqMmK2d1sHOYZy166hn89IgM1hU7kqyYAeyhU6bXEWHKtEEApIwvWSciSFFl0J+EiWDynjnPtIWaNXrIG5Wf1uJ9gQYU/u76NQhxbBouSrGyps4hrmuF2gmGAhPGIIlQlzU/DDnLqRT2fKgy4yhfMylTy+zXMMi0jBDyFGjPYjnDYjWEUNkG79AUJ6t/fe8QPUc5w88p5Hbq3ljom+yflshPshlVdWyqCxBB5aw/oOnslgnEVNkNBEivXcpHhELbYu79NwmrkRSUaBzb77nuue5MsfMSkFISiMvHRPF9FzOO+2NnycGQrLpj14bNIJoNbwtQO40BoeSVYPjBlKNf62vhM2Xf2pm7SpGuezH2IvS0ulLB4Di4ap5vL+MIJer6mQ8xsRT7CQO7r1d1dFp2yqB3dOEERqywd5xl+tfgNy76TwXyRM4u5RbOMJSgwLJE59rHg3RQ8ApEB3P1Bvot838P7Pp+vv5I="
before_install:
- echo -n | openssl s_client -connect https://scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
addons:
coverity_scan:
project:
name: "ireader/media-server"
description: "media-server"
notification_email: tao3@outlook.com
build_command_prepend: "make clean"
build_command: "make"
branch_pattern: coverity_scan
script: make

View File

@ -0,0 +1,72 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := dash
LOCAL_SRC_FILES := libdash/obj/local/$(TARGET_ARCH_ABI)/libdash.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := flv
LOCAL_SRC_FILES := libflv/obj/local/$(TARGET_ARCH_ABI)/libflv.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hls
LOCAL_SRC_FILES := libhls/obj/local/$(TARGET_ARCH_ABI)/libhls.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mov
LOCAL_SRC_FILES := libmov/obj/local/$(TARGET_ARCH_ABI)/libmov.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := mpeg
LOCAL_SRC_FILES := libmpeg/obj/local/$(TARGET_ARCH_ABI)/libmpeg.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := rtmp
LOCAL_SRC_FILES := librtmp/obj/local/$(TARGET_ARCH_ABI)/librtmp.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := rtp
LOCAL_SRC_FILES := librtp/obj/local/$(TARGET_ARCH_ABI)/librtp.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := rtsp
LOCAL_SRC_FILES := librtsp/obj/local/$(TARGET_ARCH_ABI)/librtsp.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := sip
LOCAL_SRC_FILES := libsip/obj/local/$(TARGET_ARCH_ABI)/libsip.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DOS_ANDROID -DOS_LINUX
LOCAL_LDLIBS += -llog -landroid
LOCAL_C_INCLUDES := .
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../sdk/include
LOCAL_SRC_FILES := test/test.cpp
LOCAL_SHARED_LIBRARIES :=
LOCAL_STATIC_LIBRARIES :=
LOCAL_MODULE := test
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,4 @@
APP_BUILD_SCRIPT := ./Android.mk
APP_STL := gnustl_static
APP_ABI := armeabi-v7a #arm64-v8a
#APP_PLATFORM := android-21 #SLES SLAndroidDataFormat_PCM_EX

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 chen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,41 @@
ifdef PLATFORM
CROSS:=$(PLATFORM)-
else
CROSS:=
PLATFORM:=linux
endif
ifeq ($(RELEASE),1)
BUILD:=release
else
BUILD:=debug
endif
all:
$(MAKE) -C libflv
$(MAKE) -C librtmp
$(MAKE) -C libmpeg
$(MAKE) -C libhls
$(MAKE) -C librtp
$(MAKE) -C librtsp
$(MAKE) -C libmov
$(MAKE) -C libdash
$(MAKE) -C libsip
clean:
$(MAKE) -C libflv clean
$(MAKE) -C librtmp clean
$(MAKE) -C libmpeg clean
$(MAKE) -C libhls clean
$(MAKE) -C librtp clean
$(MAKE) -C librtsp clean
$(MAKE) -C libmov clean
$(MAKE) -C libdash clean
$(MAKE) -C libsip clean
$(MAKE) -C test clean
.PHONY : test
test:
$(MAKE) -C ../sdk
$(MAKE) -C test
ln -sf ../sdk/libaio/$(BUILD).$(PLATFORM)/libaio.so . && ./test/$(BUILD).$(PLATFORM)/test

View File

@ -0,0 +1,64 @@
* Build status: [![Build Status](https://travis-ci.org/ireader/media-server.svg?branch=master)](https://travis-ci.org/ireader/media-server) <a href="https://scan.coverity.com/projects/ireader-media-server"> <img alt="Coverity Scan Build Status" src="https://scan.coverity.com/projects/14645/badge.svg"/> </a>
* Build Dependence: https://github.com/ireader/sdk
# libflv
1. Adobe FLV muxer/demuxer
2. MPEG-4 AVCDecoderConfigurationRecord/HEVCDecoderConfigurationRecord/AV1CodecConfigurationRecord/VPCodecConfigurationRecord/AudioSpecificConfig
3. H.264/H.265 AnnexB to/from MP4 stream
4. AAC ADTS to/from ASC/MUX
5. FLV with H.264/H.264/AV1/VPX(vp8/vp9/vp10)
6. FLV with AAC/mp3/G.711/Opus
# librtmp
1. rtmp-client: RTMP publish/play
2. rtmp-server: RTMP Server live/vod streaming
# libmpeg
1. MPEG-2 PS packer/unpacker
2. MPEG-2 TS packer/unpacker
3. ps/ts with H.264/H.265/AAC/MP3/G.711/Opus
# librtp
1. RFC3550 RTP/RTCP
2. RTP with H.264/H.265/MPEG-2/MPEG-4/VP8/VP9/AV1
2. RTP with G.711/G.726/G.729/MP3/AAC/Opus
3. RTP with MPEG-2 PS/TS
# librtsp
1. RFC 2326 RTSP client
2. RFC 2326 RTSP Server
3. RTSP parser
4. RFC 4566 SDP parser
5. SDP with H.264/H.265/AAC/Opus/G.711 fmtp
# libhls
1. HLS Media: TS segmenter
2. HLS M3U8: generate m3u8 file
3. HLS fmp4 segmenter
4. HLS Master/Playlist m3u8 parser
# libdash
1. ISO/IEC 23009-1 MPEG-DASH static(vod)
2. ISO/IEC 23009-1 MPEG-DASH dynamic(live)
3. DASH MPD v3/v4 parser
# libmov
1. MP4 File reader/writer
2. MP4 faststart(moov box before mdat)
3. Fragment MP4 writer
4. MP4 with H.264/H.265/AV1/VP9
5. MP4 with AAC/Opus/MP3/G.711
# libsip
1. sip user-agent (UAC/UAS)
2. sip with ICE
# libhttp(https://github.com/ireader/sdk)
1. HTTP Server(base AIO)
2. HTTP Client
3. HTTP Cookie
### Make
1. make clean && make
2. make RELEASE=1 (make release library, default debug)
3. make PLATFORM=arm-hisiv100nptl-linux (cross compile)

View File

@ -0,0 +1,96 @@
RELEASE ?= 0 # default debug
UNICODE ?= 0 # default ansi
ifdef PLATFORM
CROSS:=$(PLATFORM)-
else
CROSS:=
PLATFORM:=linux
endif
ifeq ($(RELEASE),1)
BUILD:=release
else
BUILD:=debug
endif
KERNEL := $(shell uname -s)
ifeq ($(KERNEL),Linux)
DEFINES += OS_LINUX
endif
ifeq ($(KERNEL),Darwin)
DEFINES += OS_MAC
endif
#--------------------------------Compile-----------------------------
#
#--------------------------------------------------------------------
AR := $(CROSS)ar
CC := $(CROSS)gcc
CXX := $(CROSS)g++
CFLAGS += -Wall -fPIC
CXXFLAGS += -Wall
DEPFLAGS = -MMD -MP -MF $(OUTPATH)/$(*F).d
ifeq ($(RELEASE),1)
CFLAGS += -Wall -O2
CXXFLAGS += $(CFLAGS)
DEFINES += NDEBUG
else
CFLAGS += -g -Wall
# CFLAGS += -fsanitize=address
CXXFLAGS += $(CFLAGS)
DEFINES += DEBUG _DEBUG
endif
# default don't export anything
CFLAGS += -fvisibility=hidden
COMPILE.CC = $(CC) $(addprefix -I,$(INCLUDES)) $(addprefix -D,$(DEFINES)) $(CFLAGS)
COMPILE.CXX = $(CXX) $(addprefix -I,$(INCLUDES)) $(addprefix -D,$(DEFINES)) $(CXXFLAGS)
#-------------------------Compile Output---------------------------
#
#--------------------------------------------------------------------
ifeq ($(UNICODE),1)
OUTPATH += unicode.$(BUILD).$(PLATFORM)
else
OUTPATH += $(BUILD).$(PLATFORM)
endif
# make output dir
$(shell mkdir -p $(OUTPATH) > /dev/null)
OBJECT_FILES := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE_FILES)))
DEPENDENCE_FILES := $(OBJECT_FILES:%.o=%.d)
DEPENDENCE_FILES := $(foreach file,$(DEPENDENCE_FILES),$(OUTPATH)/$(notdir $(file)))
#--------------------------Makefile Rules----------------------------
#
#--------------------------------------------------------------------
$(OUTPATH)/$(OUTFILE): $(OBJECT_FILES) $(STATIC_LIBS)
ifeq ($(OUTTYPE),0)
$(CXX) -o $@ -Wl,-rpath . $(LDFLAGS) $^ $(addprefix -L,$(LIBPATHS)) $(addprefix -l,$(LIBS))
else
ifeq ($(OUTTYPE),1)
$(CXX) -o $@ -shared -fpic -rdynamic -Wl,-rpath . $(LDFLAGS) $^ $(addprefix -L,$(LIBPATHS)) $(addprefix -l,$(LIBS))
else
$(AR) -rc $@ $^
endif
endif
@echo make ok, output: $(OUTPATH)/$(OUTFILE)
%.o : %.c
$(COMPILE.CC) -c $(DEPFLAGS) -o $@ $<
%.o : %.cpp
$(COMPILE.CXX) -c $(DEPFLAGS) -o $@ $<
-include $(DEPENDENCE_FILES)
version.h : version.ver
$(ROOT)/svnver.sh version.ver version.h
.PHONY: clean
clean:
rm -f $(OBJECT_FILES) $(OUTPATH)/$(OUTFILE) $(DEPENDENCE_FILES)

View File

@ -0,0 +1,16 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DOS_LINUX -DOS_ANDROID
LOCAL_LDLIBS +=
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libmov/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libhls/include
LOCAL_SRC_FILES := $(wildcard src/*.c)
LOCAL_SRC_FILES += $(wildcard src/*.cpp)
LOCAL_MODULE := dash
include $(BUILD_STATIC_LIBRARY)

View File

@ -0,0 +1,45 @@
#--------------------------------Output------------------------------
# OUTTYPE: 0-exe, 1-dll, 2-static
#--------------------------------------------------------------------
OUTTYPE = 2
OUTFILE = libdash.a
#-------------------------------Include------------------------------
#
# INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix
#--------------------------------------------------------------------
INCLUDES = . \
./include \
../libmov/include \
../libhls/include
#-------------------------------Source-------------------------------
#
#--------------------------------------------------------------------
SOURCE_PATHS = src
SOURCE_FILES = $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.cpp))
SOURCE_FILES += $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.c))
#-----------------------------Library--------------------------------
#
# LIBPATHS = $(addprefix -L,$(LIBPATHS)) # add -L prefix
#--------------------------------------------------------------------
LIBPATHS =
ifdef RELEASE
# relase library path
LIBPATHS +=
else
LIBPATHS +=
endif
LIBS =
STATIC_LIBS =
#-----------------------------DEFINES--------------------------------
#
# DEFINES := $(addprefix -D,$(DEFINES)) # add -L prefix
#--------------------------------------------------------------------
DEFINES =
include ../gcc.mk

View File

@ -0,0 +1,31 @@
#ifndef _dash_mpd_h_
#define _dash_mpd_h_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct dash_mpd_t dash_mpd_t;
typedef int (*dash_mpd_segment)(void* param, int adapation, const void* data, size_t bytes, int64_t pts, int64_t dts, int64_t duration, const char* name);
dash_mpd_t* dash_mpd_create(int flags, dash_mpd_segment handler, void* param);
void dash_mpd_destroy(dash_mpd_t* mpd);
/// @param[in] prefix dash adapation set name prefix
/// @return >=0-adapation id, <0-error
int dash_mpd_add_video_adaptation_set(dash_mpd_t* mpd, const char* prefix, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size);
int dash_mpd_add_audio_adaptation_set(dash_mpd_t* mpd, const char* prefix, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size);
/// @param[in] adapation create by dash_mpd_add_video_adapation_set/dash_mpd_add_audio_adapation_set
int dash_mpd_input(dash_mpd_t* mpd, int adapation, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags);
size_t dash_mpd_playlist(dash_mpd_t* mpd, char* playlist, size_t bytes);
#ifdef __cplusplus
}
#endif
#endif /* !_dash_mpd_h_ */

View File

@ -0,0 +1,483 @@
#ifndef _dash_parser_h_
#define _dash_parser_h_
#include "dash-proto.h"
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
enum
{
DASH_SEGMENT_NONE = 0,
DASH_SEGMENT_BASE,
DASH_SEGMENT_LIST,
DASH_SEGMENT_TEMPLATE,
};
// https://tools.ietf.org/html/rfc6838
enum
{
DASH_MEDIA_UNKNOWN,
DASH_MEDIA_FONT, // font
DASH_MEDIA_TEXT, // text
DASH_MEDIA_IMAGE, // image
DASH_MEDIA_AUDIO, // audio
DASH_MEDIA_VIDEO, // video
DASH_MEDIA_APPLICATION, // application
};
// 5.3.9.4.4 Template-based Segment URL construction
// @media, @index, @initialization, @bitstreamSwitching
// 1. Either $Number$ or $Time$ may be used but not both at the same time.
// 2. $SubNumber$ shall only be present if either $Number$ or $Time$ are present as well
enum
{
DASH_TEMPLATE_UNKNOWN,
DASH_TEMPLATE_REPRESENTATIONID, // Representation@id
DASH_TEMPLATE_NUMBER,
DASH_TEMPLATE_BANDWIDTH, // Representation@bandwidth
DASH_TEMPLATE_TIME, // SegmentTimeline@t
DASH_TEMPLATE_SUBNUMBER,
};
enum { DASH_SCAN_UNKNOWN = 0, DASH_SCAN_PROGRESSIVE, DASH_SCAN_INTERLACED, };
struct dash_urltype_t
{
char* source_url;
char* range;
};
// BaseURL
struct dash_url_t
{
size_t count;
struct
{
char* uri;
char* service_location;
char* byte_range;
double availability_time_offset;
int availability_time_complete; // boolean
} *urls;
};
struct dash_event_t
{
uint64_t presentation_time;
uint64_t duration;
unsigned int id;
char* message_data;
};
struct dash_event_stream_t
{
char* href;
char* actuate; // default: onRequest
char* scheme_id_uri; // SchemeIdUri
char* value;
unsigned int timescale;
size_t event_count;
struct dash_event_t* events;
};
struct dash_label_t
{
size_t count;
struct
{
char* label;
unsigned int id;
char* lang;
}*labels;
};
struct dash_program_information_t
{
char* lang;
char* more_information;
char* title;
char* source;
char* copyright;
};
struct dash_descriptor_t
{
size_t count;
struct
{
char* scheme_uri;
char* value;
char* id;
}* descs;
};
struct dash_metric_t
{
char* metrics;
size_t range_count;
struct
{
int64_t time;
int64_t duration;
}* ranges;
struct dash_descriptor_t reportings;
};
struct dash_segment_url_t
{
char* media;
char* media_range;
char* index;
char* index_range;
};
struct dash_segment_timeline_t
{
size_t count;
struct
{
uint64_t t;
uint64_t n; // specifies the Segment number of the first Segment in the series.
uint64_t d; // Any @d value shall not exceed the value of MPD@maxSegmentDuration
uint64_t k; // default 1
int r; // default 0
}* S;
};
struct dash_segment_t
{
int type; // DASH_SEGMENT_BASE/DASH_SEGMENT_LIST/DASH_SEGMENT_TEMPLATE
// segment base
unsigned int timescale;
uint64_t presentation_time_offset;
uint64_t presentation_duration;
int64_t time_shift_buffer_depth;
char* index_range;
int index_range_exact; // 0-false, 1-true
double availability_time_offset;
int availability_time_complete;
struct dash_urltype_t initialization; // Initialization
struct dash_urltype_t representation_index; // RepresentationIndex
// Multiple segment base
uint64_t start_number;
// All Segments within this Representation element have the same duration
// unless it is the last Segment within the Period, which can be significantly shorter.
unsigned int duration; // specifies the constant approximate Segment duration.
struct dash_segment_timeline_t segment_timeline; // SegmentTimeline
struct dash_urltype_t bitstream_switching; // BitstreamSwitching
// segment list
char* href;
char* actuate; // default: onRequest
size_t segment_url_count;
struct dash_segment_url_t* segment_urls; // SegmentURL
// segment template
char* media;
char* index;
char* initialization_url; // initialization
char* bitstream_switching_url;
};
struct dash_content_component_t
{
unsigned int id;
char* lang;
char* content_type;
char* par;
char* tag;
struct dash_descriptor_t accessibilities; // Accessibility
struct dash_descriptor_t roles; // Role
struct dash_descriptor_t ratings; // Rating
struct dash_descriptor_t viewpoints; // Viewpoint
};
struct dash_representation_base_t
{
char* profiles;
unsigned int width;
unsigned int height;
char* sar;
char* frame_rate;
char* audio_sampling_rate;
char* mime_type;
char* segment_profiles;
char* codecs;
double maxmum_sap_period;
char* start_with_sap;
double max_playout_rate;
int coding_dependency;
int scan_type; // progressive, interlaced
unsigned int selection_priority; // default 1
int tag;
struct dash_descriptor_t frame_packings; // FramePacking
struct dash_descriptor_t audio_channel_configurations; // AudioChannelConfiguration
struct dash_descriptor_t content_protections; // ContentProtection
struct dash_descriptor_t essentials; // EssentialProperty
struct dash_descriptor_t supplementals; // SupplementalProperty
size_t inband_event_stream_count;
struct dash_event_stream_t* inband_event_streams; // InbandEventStream
size_t switching_count;
struct
{
unsigned int interval;
int type; // media, bitstream
} *switchings; // Switching
size_t random_access_count;
struct
{
unsigned int interval;
int type; // closed, open, gradual
int64_t min_buffer_time;
unsigned int bandwidth;
} *random_accesses; // RandomAccess
struct dash_label_t group_labels; // GroupLabel
struct dash_label_t labels; // Label
};
struct dash_preselection_t
{
char* id;
char* preselection_compoents;
char* lang;
struct dash_representation_base_t base;
struct dash_descriptor_t accessibilities; // Accessibility
struct dash_descriptor_t roles; // Role
struct dash_descriptor_t ratings; // Rating
struct dash_descriptor_t viewpoints; // Viewpoint
};
struct dash_subrepresentation_t
{
unsigned int level;
unsigned int dependency_level;
unsigned int bandwidth;
char* content_component;
struct dash_representation_t* parent;
struct dash_representation_base_t base;
};
struct dash_representation_t
{
char* id;
unsigned int bandwidth;
unsigned int quality_ranking;
char* dependncy_id;
char* association_id;
char* association_type;
char* media_stream_structure_id;
struct dash_adaptation_set_t* parent;
struct dash_representation_base_t base;
struct dash_url_t base_urls; // BaseURL
size_t subrepresentation_count;
struct dash_subrepresentation_t* subrepresentations; // SubRepresentation
struct dash_segment_t segment;
};
struct dash_adaptation_set_t
{
char* href;
char* actuate; // default: onRequest
unsigned int id;
unsigned int group;
char* lang; // und
char* content_type; // text/image/audio/video/application/font
char* par; // n:m 16:9
unsigned int min_bandwidth;
unsigned int max_bandwidth;
unsigned int min_width;
unsigned int max_width;
unsigned int min_height;
unsigned int max_height;
char* min_framerate; // n/m
char* max_framerate;
int segment_alignment; // 0-false, 1-true
int subsegment_aligment; // 0-false, 1-true
int subsegment_start_with_sap; // default 0
int bitstream_switching; // 0-false, 1-true
struct dash_period_t* parent;
struct dash_representation_base_t base;
struct dash_descriptor_t accessibilities; // Accessibility
struct dash_descriptor_t roles; // Role
struct dash_descriptor_t ratings; // Rating
struct dash_descriptor_t viewpoints; // Viewpoint
size_t content_component_count;
struct dash_content_component_t* content_components; // ContentComponent
struct dash_url_t base_urls; // BaseURL
struct dash_segment_t segment;
size_t representation_count;
struct dash_representation_t* representations; // Representation
};
struct dash_period_t
{
char* href;
char* actuate; // default: onRequest
char* id;
int64_t start; // start
int64_t duration; // duration
int bitstream_switching; // 0-false
struct dash_mpd_t* parent;
struct dash_url_t base_urls; // BaseURL
struct dash_segment_t segment; // SegmentBase/SegmentList/SegmentTemplate
struct dash_descriptor_t asset_identifier; // AssetIdentifier
size_t event_stream_count;
size_t event_stream_capacity;
struct dash_event_stream_t* event_streams; // EventStream
size_t adaptation_set_count;
struct dash_adaptation_set_t* adaptation_sets; // AdaptationSet
size_t subset_count;
struct
{
char* contains;
char* id;
} *subsets; // SubSet
struct dash_descriptor_t supplementals; // SupplementalProperty
size_t empty_adaptation_set_count;
struct dash_adaptation_set_t* empty_adaptation_sets; // EmptyAdaptationSet
struct dash_label_t group_labels; // GroupLabel
size_t preselection_count;
struct dash_preselection_t* preselections; // Preselection
};
struct dash_mpd_t
{
int type; // presentation type: 0-static, 1-dynamic
char* id;
char* profiles; // profiles
char* availability_start_time;
char* availability_end_time;
char* publish_time;
int64_t media_presentation_duration; // mediaPresentationDuration, MPD total duration
int64_t minimum_update_period; // minimumUpdatePeriod
int64_t min_buffer_time; // minBufferTime
int64_t time_shift_buffer_depth; // timeShiftBufferDepth
int64_t suggested_presentation_delay; // suggestedPresentationDelay
int64_t max_segment_duration; // maxSegmentDuration
int64_t max_subsegment_duration; // maxSubsegmentDuration
char* xsi; // xmlns:xsi
char* xmlns; // xmlns
char* schema_location; // xsi:schemaLocation
size_t info_count;
struct dash_program_information_t* infos; // ProgramInfomation
struct dash_url_t urls; // BaseURL
size_t location_count;
char** locations; // Location
size_t period_count;
struct dash_period_t* periods; // Period
size_t metric_count;
struct dash_metric_t* metrics; // Metrics
struct dash_descriptor_t essentials; // EssentialProperty
struct dash_descriptor_t supplementals; // SupplementalProperty
struct dash_descriptor_t timings; // UTCTming
};
/// Parse MPD(Media Presentation Description for MPEG-DASH ) manifest file
/// @param[out] mpd mpd object(free by dash_mpd_free)
/// @return 0-ok, other-error
int dash_mpd_parse(struct dash_mpd_t** mpd, const char* data, size_t bytes);
int dash_mpd_free(struct dash_mpd_t** mpd);
/// @return -1-live, >=0-duration(ms), other-error
int64_t dash_get_duration(const struct dash_mpd_t* mpd);
/// Period
/// location period by time
/// @param[in] time from previous period end to current perid end, range: (period-m-end, period-n-end]
/// @return >=0-period index, -1-if don't find, other-undefined
int dash_period_find(const struct dash_mpd_t* mpd, int64_t time);
/// @param[in] media DASH_MEDIA_AUDIO/DASH_MEDIA_VIDEO/xxx
/// @param[in] id adaptation set id, 0 if unknown
/// @param[in] group adaptation set group id, 0 if unknown
/// @param[in] lang adaptation set lang, such as 'en'/'fr', NULL if don't care
/// @param[in] codecs adaptation set codec type, such as 'avc1', NULL if don't care
/// @return NULL if don't find any adaptation set
const struct dash_adaptation_set_t* dash_period_select(const struct dash_period_t* period, int media, unsigned int id, unsigned int group, const char* lang, const char* codecs);
/// Adaptation Set
/// @return adaptation set media type DASH_MEDIA_AUDIO/DASH_MEDIA_VIDEO, DASH_MEDIA_UNKNOWN if unknown
int dash_adaptation_set_media_type(const struct dash_adaptation_set_t* set);
/// Priority: 1. selectionPriority; 2. qualityRanking; 3. bandwidth
/// @return >=0-representation index, -1-error
int dash_adaptation_set_best_representation(const struct dash_adaptation_set_t* set);
/// Representation
const struct dash_url_t* dash_representation_get_base_url(const struct dash_representation_t* representation);
/// Get initialization segment url(MUST use with dash_representation_get_base_url)
/// @return >0-ok with url length, =0-don't have initialization segment, <0-error
int dash_representation_get_initialization(const struct dash_representation_t* representation, char* url, size_t size);
/// Find segment by start time
/// @param[in] time segment time, range: (previous segment end, segment start + duration)
/// @return -1-not found, >=0-segment item index(index is not the startnumber)
int dash_representation_find_segment(const struct dash_representation_t* representation, int64_t time);
/// @return <0-error, >=0-segment count(INT_MAX for segment template)
int dash_representation_segment_count(const struct dash_representation_t* representation);
/// Get segment url(MUST use with dash_representation_get_base_url)
/// @param[in] index segment index(!= start number)
/// @param[out] number start number of representation
/// @param[out] start start time of representation(MUST add period.start)
/// @param[out] duration segment duration, 0 if unknown
/// @param[out] range url range, NULL if don't have range
/// @return >=0-ok with url length, <0-error
int dash_representation_segment_url(const struct dash_representation_t* representation, int index, int64_t* number, int64_t* start, int64_t* duration, const char** range, char* url, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* !_dash_parser_h_ */

View File

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

View File

@ -0,0 +1,19 @@
#ifndef _xs_datatype_h_
#define _xs_datatype_h_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/// @param[in] duration millisecond duration
/// @param[out] data ISO8601 duration: P[n]Y[n]M[n]DT[n]H[n]M[n]S
int xs_duration_write(int64_t duration, char* data, int size);
int xs_duration_read(int64_t* duration, const char* data, int size);
#ifdef __cplusplus
}
#endif
#endif /* !_xs_datatype_h_ */

View File

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

View File

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

View File

@ -0,0 +1,328 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
46C5B2AF2183ED6100419E57 /* list.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C5B2AC2183ED6100419E57 /* list.h */; };
46C5B2B02183ED6100419E57 /* dash-mpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2AD2183ED6100419E57 /* dash-mpd.c */; };
46E55E6824A7395F00D8BDBA /* xs-duration.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6224A7395F00D8BDBA /* xs-duration.c */; };
46E55E6924A7395F00D8BDBA /* dash-period.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6324A7395F00D8BDBA /* dash-period.c */; };
46E55E6A24A7395F00D8BDBA /* dash-adaptation.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6424A7395F00D8BDBA /* dash-adaptation.c */; };
46E55E6B24A7395F00D8BDBA /* dash-representation.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6524A7395F00D8BDBA /* dash-representation.c */; };
46E55E6C24A7395F00D8BDBA /* dash-parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6624A7395F00D8BDBA /* dash-parser.c */; };
46E55E6D24A7395F00D8BDBA /* dash-segment.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E6724A7395F00D8BDBA /* dash-segment.c */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
46C5B22C2183EAAD00419E57 /* libdash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdash.a; sourceTree = BUILT_PRODUCTS_DIR; };
46C5B2A92183ED5400419E57 /* include */ = {isa = PBXFileReference; lastKnownFileType = folder; path = include; sourceTree = "<group>"; };
46C5B2AC2183ED6100419E57 /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = "<group>"; };
46C5B2AD2183ED6100419E57 /* dash-mpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-mpd.c"; sourceTree = "<group>"; };
46E55E6224A7395F00D8BDBA /* xs-duration.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "xs-duration.c"; sourceTree = "<group>"; };
46E55E6324A7395F00D8BDBA /* dash-period.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-period.c"; sourceTree = "<group>"; };
46E55E6424A7395F00D8BDBA /* dash-adaptation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-adaptation.c"; sourceTree = "<group>"; };
46E55E6524A7395F00D8BDBA /* dash-representation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-representation.c"; sourceTree = "<group>"; };
46E55E6624A7395F00D8BDBA /* dash-parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-parser.c"; sourceTree = "<group>"; };
46E55E6724A7395F00D8BDBA /* dash-segment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "dash-segment.c"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
46C5B22A2183EAAD00419E57 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
46C5B2232183EAAD00419E57 = {
isa = PBXGroup;
children = (
46C5B2AA2183ED6100419E57 /* src */,
46C5B2A92183ED5400419E57 /* include */,
46C5B22D2183EAAD00419E57 /* Products */,
);
sourceTree = "<group>";
};
46C5B22D2183EAAD00419E57 /* Products */ = {
isa = PBXGroup;
children = (
46C5B22C2183EAAD00419E57 /* libdash.a */,
);
name = Products;
sourceTree = "<group>";
};
46C5B2AA2183ED6100419E57 /* src */ = {
isa = PBXGroup;
children = (
46C5B2AC2183ED6100419E57 /* list.h */,
46E55E6424A7395F00D8BDBA /* dash-adaptation.c */,
46E55E6624A7395F00D8BDBA /* dash-parser.c */,
46E55E6324A7395F00D8BDBA /* dash-period.c */,
46E55E6524A7395F00D8BDBA /* dash-representation.c */,
46E55E6724A7395F00D8BDBA /* dash-segment.c */,
46E55E6224A7395F00D8BDBA /* xs-duration.c */,
46C5B2AD2183ED6100419E57 /* dash-mpd.c */,
);
path = src;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
46C5B2282183EAAD00419E57 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
46C5B2AF2183ED6100419E57 /* list.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
46C5B22B2183EAAD00419E57 /* libdash */ = {
isa = PBXNativeTarget;
buildConfigurationList = 46C5B2302183EAAD00419E57 /* Build configuration list for PBXNativeTarget "libdash" */;
buildPhases = (
46C5B2282183EAAD00419E57 /* Headers */,
46C5B2292183EAAD00419E57 /* Sources */,
46C5B22A2183EAAD00419E57 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = libdash;
productName = libdash;
productReference = 46C5B22C2183EAAD00419E57 /* libdash.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
46C5B2242183EAAD00419E57 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = ireader;
TargetAttributes = {
46C5B22B2183EAAD00419E57 = {
CreatedOnToolsVersion = 10.0;
};
};
};
buildConfigurationList = 46C5B2272183EAAD00419E57 /* Build configuration list for PBXProject "libdash" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 46C5B2232183EAAD00419E57;
productRefGroup = 46C5B22D2183EAAD00419E57 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
46C5B22B2183EAAD00419E57 /* libdash */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
46C5B2292183EAAD00419E57 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
46E55E6B24A7395F00D8BDBA /* dash-representation.c in Sources */,
46E55E6A24A7395F00D8BDBA /* dash-adaptation.c in Sources */,
46E55E6C24A7395F00D8BDBA /* dash-parser.c in Sources */,
46E55E6824A7395F00D8BDBA /* xs-duration.c in Sources */,
46C5B2B02183ED6100419E57 /* dash-mpd.c in Sources */,
46E55E6924A7395F00D8BDBA /* dash-period.c in Sources */,
46E55E6D24A7395F00D8BDBA /* dash-segment.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
46C5B22E2183EAAD00419E57 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
USER_HEADER_SEARCH_PATHS = (
.,
./include,
../libmov/include,
../libhls/include,
);
};
name = Debug;
};
46C5B22F2183EAAD00419E57 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
USER_HEADER_SEARCH_PATHS = (
.,
./include,
../libmov/include,
../libhls/include,
);
};
name = Release;
};
46C5B2312183EAAD00419E57 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
EXECUTABLE_PREFIX = "";
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
USER_HEADER_SEARCH_PATHS = "$(inherited)";
};
name = Debug;
};
46C5B2322183EAAD00419E57 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
EXECUTABLE_PREFIX = "";
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
USER_HEADER_SEARCH_PATHS = "$(inherited)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
46C5B2272183EAAD00419E57 /* Build configuration list for PBXProject "libdash" */ = {
isa = XCConfigurationList;
buildConfigurations = (
46C5B22E2183EAAD00419E57 /* Debug */,
46C5B22F2183EAAD00419E57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
46C5B2302183EAAD00419E57 /* Build configuration list for PBXNativeTarget "libdash" */ = {
isa = XCConfigurationList;
buildConfigurations = (
46C5B2312183EAAD00419E57 /* Debug */,
46C5B2322183EAAD00419E57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 46C5B2242183EAAD00419E57 /* Project object */;
}

View File

@ -0,0 +1,77 @@
#include "dash-parser.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static int dash_mime_type_to_media_type(const char* mime)
{
switch (mime[0])
{
case 't':
return 0 == strncmp(mime, "text/", 5) ? DASH_MEDIA_TEXT : DASH_MEDIA_UNKNOWN;
case 'i':
return 0 == strncmp(mime, "image/", 6) ? DASH_MEDIA_IMAGE : DASH_MEDIA_UNKNOWN;
case 'a':
if (0 == strncmp(mime, "audio/", 6))
return DASH_MEDIA_AUDIO;
else if (0 == strncmp(mime, "application/", 12))
return DASH_MEDIA_APPLICATION;
else
return DASH_MEDIA_UNKNOWN;
case 'v':
return 0 == strncmp(mime, "video/", 6) ? DASH_MEDIA_VIDEO : DASH_MEDIA_UNKNOWN;
case 'f':
return 0 == strncmp(mime, "font/", 5) ? DASH_MEDIA_FONT : DASH_MEDIA_UNKNOWN;
default:
return DASH_MEDIA_UNKNOWN;
}
}
int dash_adaptation_set_media_type(const struct dash_adaptation_set_t* set)
{
const struct dash_representation_base_t* base;
base = &set->base;
if (base->mime_type && *base->mime_type)
return dash_mime_type_to_media_type(base->mime_type);
else if (set->content_type && *set->content_type)
return dash_mime_type_to_media_type(set->content_type);
if (set->representation_count > 0)
{
base = &set->representations[0].base;
if (base->mime_type && *base->mime_type)
return dash_mime_type_to_media_type(base->mime_type);
if (set->representations[0].subrepresentation_count > 0)
{
base = &set->representations[0].subrepresentations[0].base;
return dash_mime_type_to_media_type(base->mime_type);
}
}
return DASH_MEDIA_UNKNOWN;
}
int dash_adaptation_set_best_representation(const struct dash_adaptation_set_t* set)
{
int best;
size_t i;
const struct dash_representation_t* v;
best = -1;
for (i = 0; i < set->representation_count; i++)
{
v = &set->representations[i];
// qualityRanking: specifies a quality ranking of the Representation
// relative to other Representations in the same Adaptation Set.
// Lower values represent higher quality content.
if (-1 == best
|| v->base.selection_priority > set->representations[best].base.selection_priority
|| v->quality_ranking < set->representations[best].quality_ranking
|| v->bandwidth > set->representations[best].bandwidth)
best = (int)i;
}
return best;
}

View File

@ -0,0 +1,520 @@
#include "dash-mpd.h"
#include "dash-proto.h"
#include "mov-format.h"
#include "fmp4-writer.h"
#include "list.h"
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#define N_TRACK 8
#define N_NAME 128
#define N_COUNT 5
#define N_SEGMENT (1 * 1024 * 1024)
#define N_FILESIZE (100 * 1024 * 1024) // 100M
#define MAX(a, b) ((a) > (b) ? (a) : (b))
struct dash_segment_t
{
struct list_head link;
int64_t timestamp;
int64_t duration;
};
struct dash_adaptation_set_t
{
fmp4_writer_t* fmp4;
char prefix[N_NAME];
uint8_t* ptr;
size_t bytes;
size_t capacity;
size_t offset;
size_t maxsize; // max bytes per mp4 file
int64_t pts;
int64_t dts;
int64_t dts_last;
int64_t raw_bytes;
int bitrate;
int track; // MP4 track id
int setid; // dash adapation set id
int seq;
uint8_t object;
union
{
struct
{
int width;
int height;
int frame_rate;
struct
{
uint8_t profile;
uint8_t compatibility;
uint8_t level;
} avc;
} video;
struct
{
uint8_t profile; // AAC profile
int channel;
int sample_bit;
int sample_rate;
} audio;
} u;
size_t count;
struct list_head root; // segments
};
struct dash_mpd_t
{
int flags;
time_t time;
int64_t duration;
int64_t max_segment_duration;
dash_mpd_segment handler;
void* param;
int count; // adaptation set count
struct dash_adaptation_set_t tracks[N_TRACK];
};
static int mov_buffer_read(void* param, void* data, uint64_t bytes)
{
struct dash_adaptation_set_t* dash;
dash = (struct dash_adaptation_set_t*)param;
if (dash->offset + bytes > dash->bytes)
return E2BIG;
memcpy(data, dash->ptr + dash->offset, (size_t)bytes);
return 0;
}
static int mov_buffer_write(void* param, const void* data, uint64_t bytes)
{
void* ptr;
size_t capacity;
struct dash_adaptation_set_t* dash;
dash = (struct dash_adaptation_set_t*)param;
if (dash->offset + bytes > dash->maxsize)
return E2BIG;
if (dash->offset + (size_t)bytes > dash->capacity)
{
capacity = dash->offset + (size_t)bytes + N_SEGMENT;
capacity = capacity > dash->maxsize ? dash->maxsize : capacity;
ptr = realloc(dash->ptr, capacity);
if (NULL == ptr)
return ENOMEM;
dash->ptr = ptr;
dash->capacity = capacity;
}
memcpy(dash->ptr + dash->offset, data, (size_t)bytes);
dash->offset += (size_t)bytes;
if (dash->offset > dash->bytes)
dash->bytes = dash->offset;
return 0;
}
static int mov_buffer_seek(void* param, uint64_t offset)
{
struct dash_adaptation_set_t* dash;
dash = (struct dash_adaptation_set_t*)param;
if (offset >= dash->maxsize)
return E2BIG;
dash->offset = (size_t)offset;
return 0;
}
static uint64_t mov_buffer_tell(void* param)
{
return ((struct dash_adaptation_set_t*)param)->offset;
}
static struct mov_buffer_t s_io = {
mov_buffer_read,
mov_buffer_write,
mov_buffer_seek,
mov_buffer_tell,
};
static int dash_adaptation_set_segment(struct dash_mpd_t* mpd, struct dash_adaptation_set_t* track)
{
int r;
char name[N_NAME + 32];
struct list_head *link;
struct dash_segment_t* seg;
r = fmp4_writer_save_segment(track->fmp4);
if (0 != r)
return r;
seg = (struct dash_segment_t*)calloc(1, sizeof(*seg));
if(!seg)
return -1; // ENOMEM
seg->timestamp = track->dts;
seg->duration = track->dts_last - track->dts;
if(MOV_OBJECT_AAC == track->object)
snprintf(name, sizeof(name), "%s-%" PRId64 ".m4a", track->prefix, seg->timestamp);
else
snprintf(name, sizeof(name), "%s-%" PRId64 ".m4v", track->prefix, seg->timestamp);
r = mpd->handler(mpd->param, track->setid, track->ptr, track->bytes, track->pts, track->dts, seg->duration, name);
if (0 != r)
{
free(seg);
return r;
}
// link
list_insert_after(&seg->link, track->root.prev);
track->count += 1;
if (DASH_DYNAMIC == mpd->flags && track->count > N_COUNT)
{
link = track->root.next;
list_remove(link);
seg = list_entry(link, struct dash_segment_t, link);
free(seg);
--track->count;
}
return 0;
}
static int dash_mpd_flush(struct dash_mpd_t* mpd)
{
int i, r;
struct dash_adaptation_set_t* track;
for (r = i = 0; i < mpd->count && 0 == r; i++)
{
track = mpd->tracks + i;
if (track->raw_bytes)
{
r = dash_adaptation_set_segment(mpd, track);
// update maximum segment duration
mpd->max_segment_duration = MAX(track->dts_last - track->dts, mpd->max_segment_duration);
if(track->dts_last > track->dts)
track->bitrate = MAX(track->bitrate, (int)(track->raw_bytes * 1000 / (track->dts_last - track->dts) * 8));
}
track->pts = INT64_MIN;
track->dts = INT64_MIN;
track->raw_bytes = 0;
// reset track buffer
track->offset = 0;
track->bytes = 0;
}
return r;
}
struct dash_mpd_t* dash_mpd_create(int flags, dash_mpd_segment segment, void* param)
{
struct dash_mpd_t* mpd;
mpd = (struct dash_mpd_t*)calloc(1, sizeof(*mpd));
if (mpd)
{
mpd->flags = flags;
mpd->handler = segment;
mpd->param = param;
mpd->time = time(NULL);
}
return mpd;
}
void dash_mpd_destroy(struct dash_mpd_t* mpd)
{
int i;
struct list_head *p, *n;
struct dash_segment_t *seg;
struct dash_adaptation_set_t* track;
dash_mpd_flush(mpd);
for (i = 0; i < mpd->count; i++)
{
track = &mpd->tracks[i];
if (track->ptr)
{
free(track->ptr);
track->ptr = NULL;
}
list_for_each_safe(p, n, &track->root)
{
seg = list_entry(p, struct dash_segment_t, link);
free(seg);
}
}
free(mpd);
}
int dash_mpd_add_video_adaptation_set(struct dash_mpd_t* mpd, const char* prefix, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size)
{
int r;
char name[N_NAME + 16];
struct dash_adaptation_set_t* track;
r = (int)strlen(prefix);
if (mpd->count + 1 >= N_TRACK || extra_data_size < 4 || r >= N_NAME)
return -1;
assert(MOV_OBJECT_H264 == object);
track = &mpd->tracks[mpd->count];
memcpy(track->prefix, prefix, r);
LIST_INIT_HEAD(&track->root);
track->setid = mpd->count++;
track->object = object;
track->bitrate = 0;
track->u.video.width = width;
track->u.video.height = height;
track->u.video.frame_rate = 25;
assert(((const uint8_t*)extra_data)[0] == 1); // configurationVersion
if (MOV_OBJECT_H264 == object)
{
track->u.video.avc.profile = ((const uint8_t*)extra_data)[1];
track->u.video.avc.compatibility = ((const uint8_t*)extra_data)[2];
track->u.video.avc.level = ((const uint8_t*)extra_data)[3];
}
track->seq = 1;
track->maxsize = N_FILESIZE;
track->fmp4 = fmp4_writer_create(&s_io, track, MOV_FLAG_SEGMENT);
if (!track->fmp4)
return -1;
track->track = fmp4_writer_add_video(track->fmp4, object, width, height, extra_data, extra_data_size);
// save init segment file
r = fmp4_writer_init_segment(track->fmp4);
if (0 == r)
{
snprintf(name, sizeof(name), "%s-init.m4v", prefix);
r = mpd->handler(mpd->param, mpd->count, track->ptr, track->bytes, 0, 0, 0, name);
}
track->bytes = 0;
track->offset = 0;
return 0 == r ? track->setid : r;
}
int dash_mpd_add_audio_adaptation_set(struct dash_mpd_t* mpd, const char* prefix, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size)
{
int r;
char name[N_NAME + 16];
struct dash_adaptation_set_t* track;
r = (int)strlen(prefix);
if (mpd->count + 1 >= N_TRACK || extra_data_size < 2 || r >= N_NAME)
return -1;
assert(MOV_OBJECT_AAC == object);
track = &mpd->tracks[mpd->count];
memcpy(track->prefix, prefix, r);
LIST_INIT_HEAD(&track->root);
track->setid = mpd->count++;
track->object = object;
track->bitrate = 0;
track->u.audio.channel = channel_count;
track->u.audio.sample_bit = bits_per_sample;
track->u.audio.sample_rate = sample_rate;
track->u.audio.profile = ((const uint8_t*)extra_data)[0] >> 3;
if(MOV_OBJECT_AAC == object && 31 == track->u.audio.profile)
track->u.audio.profile = 32 + (((((const uint8_t*)extra_data)[0] & 0x07) << 3) | ((((const uint8_t*)extra_data)[1] >> 5) & 0x07));
track->seq = 1;
track->maxsize = N_FILESIZE;
track->fmp4 = fmp4_writer_create(&s_io, track, MOV_FLAG_SEGMENT);
if (!track->fmp4)
return -1;
track->track = fmp4_writer_add_audio(track->fmp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size);
r = fmp4_writer_init_segment(track->fmp4);
if (0 == r)
{
snprintf(name, sizeof(name), "%s-init.m4a", prefix);
r = mpd->handler(mpd->param, mpd->count, track->ptr, track->bytes, 0, 0, 0, name);
}
track->bytes = 0;
track->offset = 0;
return 0 == r ? track->setid : r;
}
int dash_mpd_input(struct dash_mpd_t* mpd, int adapation, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags)
{
int r = 0;
struct dash_adaptation_set_t* track;
if (adapation >= mpd->count || adapation < 0)
return -1;
track = &mpd->tracks[adapation];
if (NULL == data || 0 == bytes // flash fragment
|| ((MOV_AV_FLAG_KEYFREAME & flags) && (MOV_OBJECT_H264 == track->object || MOV_OBJECT_HEVC == track->object)))
{
r = dash_mpd_flush(mpd);
// FIXME: live duration
mpd->duration += mpd->max_segment_duration;
}
if (NULL == data || 0 == bytes)
return r;
if (0 == track->raw_bytes)
{
track->pts = pts;
track->dts = dts;
}
track->dts_last = dts;
track->raw_bytes += bytes;
return fmp4_writer_write(track->fmp4, track->track, data, bytes, pts, dts, flags);
}
// ISO/IEC 23009-1:2014(E) 5.4 Media Presentation Description updates (p67)
// 1. the value of MPD@id, if present, shall be the same in the original and the updated MPD;
// 2. the values of any Period@id attributes shall be the same in the original and the updated MPD, unless the containing Period element has been removed;
// 3. the values of any AdaptationSet@id attributes shall be the same in the original and the updated MPD unless the containing Period element has been removed;
size_t dash_mpd_playlist(struct dash_mpd_t* mpd, char* playlist, size_t bytes)
{
// ISO/IEC 23009-1:2014(E)
// G.2 Example for ISO Base media file format Live profile (141)
static const char* s_mpd_dynamic =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<MPD\n"
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
" xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\"\n"
" type=\"dynamic\"\n"
" minimumUpdatePeriod=\"PT%uS\"\n"
" timeShiftBufferDepth=\"PT%uS\"\n"
" availabilityStartTime=\"%s\"\n"
" minBufferTime=\"PT%uS\"\n"
" publishTime=\"%s\"\n"
" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n";
// ISO/IEC 23009-1:2014(E)
// G.1 Example MPD for ISO Base media file format On Demand profile
static const char* s_mpd_static =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<MPD\n"
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
" xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\"\n"
" type=\"static\"\n"
" mediaPresentationDuration=\"PT%uS\"\n"
" minBufferTime=\"PT%uS\"\n"
" profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\">\n";
static const char* s_h264 =
" <AdaptationSet contentType=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n"
" <Representation id=\"H264\" mimeType=\"video/mp4\" codecs=\"avc1.%02x%02x%02x\" width=\"%d\" height=\"%d\" frameRate=\"%d\" startWithSAP=\"1\" bandwidth=\"%d\">\n"
" <SegmentTemplate timescale=\"1000\" media=\"%s-$Time$.m4v\" initialization=\"%s-init.m4v\">\n"
" <SegmentTimeline>\n";
static const char* s_h265 =
" <AdaptationSet contentType=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n"
" <Representation id=\"H265\" mimeType=\"video/mp4\" codecs=\"hvc1.%02x%02x%02x\" width=\"%d\" height=\"%d\" frameRate=\"%d\" startWithSAP=\"1\" bandwidth=\"%d\">\n"
" <SegmentTemplate timescale=\"1000\" media=\"%s-$Time$.m4v\" initialization=\"%s-init.m4v\">\n"
" <SegmentTimeline>\n";
static const char* s_aac =
" <AdaptationSet contentType=\"audio\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n"
" <Representation id=\"AAC\" mimeType=\"audio/mp4\" codecs=\"mp4a.40.%u\" audioSamplingRate=\"%d\" startWithSAP=\"1\" bandwidth=\"%d\">\n"
" <AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\"/>\n"
" <SegmentTemplate timescale=\"1000\" media=\"%s-$Time$.m4a\" initialization=\"%s-init.m4a\">\n"
" <SegmentTimeline>\n";
static const char* s_footer =
" </SegmentTimeline>\n"
" </SegmentTemplate>\n"
" </Representation>\n"
" </AdaptationSet>\n";
int i;
size_t n;
time_t now;
char publishTime[32];
char availabilityStartTime[32];
unsigned int minimumUpdatePeriod;
unsigned int timeShiftBufferDepth;
struct dash_adaptation_set_t* track;
struct dash_segment_t *seg;
struct list_head *link;
now = time(NULL);
strftime(availabilityStartTime, sizeof(availabilityStartTime), "%Y-%m-%dT%H:%M:%SZ", gmtime(&mpd->time));
strftime(publishTime, sizeof(publishTime), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
minimumUpdatePeriod = (unsigned int)MAX(mpd->max_segment_duration / 1000, 1);
if (mpd->flags == DASH_DYNAMIC)
{
timeShiftBufferDepth = minimumUpdatePeriod * N_COUNT + 1;
n = snprintf(playlist, bytes, s_mpd_dynamic, minimumUpdatePeriod, timeShiftBufferDepth, availabilityStartTime, minimumUpdatePeriod, publishTime);
n += snprintf(playlist + n, bytes - n, " <Period start=\"PT0S\" id=\"dash\">\n");
}
else
{
n = snprintf(playlist, bytes, s_mpd_static, (unsigned int)(mpd->duration / 1000), minimumUpdatePeriod);
n += snprintf(playlist + n, bytes - n, " <Period start=\"PT0S\" id=\"dash\">\n");
}
for (i = 0; i < mpd->count; i++)
{
track = &mpd->tracks[i];
if (MOV_OBJECT_H264 == track->object)
{
n += snprintf(playlist + n, bytes - n, s_h264, (unsigned int)track->u.video.avc.profile, (unsigned int)track->u.video.avc.compatibility, (unsigned int)track->u.video.avc.level, track->u.video.width, track->u.video.height, track->u.video.frame_rate, track->bitrate, track->prefix, track->prefix);
list_for_each(link, &track->root)
{
seg = list_entry(link, struct dash_segment_t, link);
n += snprintf(playlist + n, bytes - n, " <S t=\"%" PRId64 "\" d=\"%u\"/>\n", seg->timestamp, (unsigned int)seg->duration);
}
n += snprintf(playlist + n, bytes - n, "%s", s_footer);
}
else if (MOV_OBJECT_HEVC == track->object)
{
n += snprintf(playlist + n, bytes - n, s_h265, (unsigned int)track->u.video.avc.profile, (unsigned int)track->u.video.avc.compatibility, (unsigned int)track->u.video.avc.level, track->u.video.width, track->u.video.height, track->u.video.frame_rate, track->bitrate, track->prefix, track->prefix);
list_for_each(link, &track->root)
{
seg = list_entry(link, struct dash_segment_t, link);
n += snprintf(playlist + n, bytes - n, " <S t=\"%" PRId64 "\" d=\"%u\"/>\n", seg->timestamp, (unsigned int)seg->duration);
}
n += snprintf(playlist + n, bytes - n, "%s", s_footer);
}
else if (MOV_OBJECT_AAC == track->object)
{
n += snprintf(playlist + n, bytes - n, s_aac, (unsigned int)track->u.audio.profile, track->u.audio.sample_rate, track->bitrate, track->u.audio.channel, track->prefix, track->prefix);
list_for_each(link, &track->root)
{
seg = list_entry(link, struct dash_segment_t, link);
n += snprintf(playlist + n, bytes - n, " <S t=\"%" PRId64 "\" d=\"%u\"/>\n", seg->timestamp, (unsigned int)seg->duration);
}
n += snprintf(playlist + n, bytes - n, "%s", s_footer);
}
else
{
assert(0);
}
}
n += snprintf(playlist + n, bytes - n, " </Period>\n</MPD>\n");
return n;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
#include "dash-parser.h"
#include "hls-string.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int64_t dash_get_duration(const struct dash_mpd_t* mpd)
{
size_t i;
int64_t t, start;
const struct dash_period_t* period;
if (DASH_DYNAMIC == mpd->type)
return -1;
if (mpd->media_presentation_duration > 0)
return mpd->media_presentation_duration;
t = 0;
for (i = 0; i < mpd->period_count; i++)
{
period = &mpd->periods[i];
if (period->start > 0)
{
// a regular Period or an early terminated Period
start = period->start;
}
else if (i > 0 && mpd->periods[i - 1].duration > 0)
{
start = t;
}
else if (0 == i && DASH_STATIC == mpd->type)
{
start = 0;
}
else if ((0 == i || mpd->periods[i - 1].duration == 0) && DASH_DYNAMIC == mpd->type)
{
continue; // Early Available Period
}
else
{
start = t;
}
if (period->duration > 0)
{
t = start + period->duration;
}
else if (i + 1 < mpd->period_count)
{
assert(0 != mpd->periods[i + 1].start);
t = mpd->periods[i + 1].start;
}
else if (DASH_DYNAMIC == mpd->type)
{
// MPD@mediaPresentationDuration shall be present when neither
// the attribute MPD@minimumUpdatePeriod nor the Period@duration
// of the last Period are present.
assert(mpd->media_presentation_duration > 0 || mpd->minimum_update_period > 0);
t = mpd->media_presentation_duration > 0 ? mpd->media_presentation_duration : (start + mpd->minimum_update_period);
}
else
{
t = start; // ???
}
}
return t;
}
int dash_period_find(const struct dash_mpd_t* mpd, int64_t time)
{
size_t i;
int64_t t, start;
const struct dash_period_t* period;
if (1 == mpd->period_count)
return 0; // only one period
t = 0;
for (i = 0; i < mpd->period_count; i++)
{
// 5.3.2.1 Overview (p27)
// 1. If the attribute @start is present in the Period, then the Period
// is a regular Period or an early terminated Period and the PeriodStart
// is equal to the value of this attribute.
// 2. If the @start attribute is absent, but the previous Period element
// contains a @duration attributethen this new Period is also a regular
// Period or an early terminated Period. The start time of the new Period
// PeriodStart is the sum of the start time of the previous Period PeriodStart
// and the value of the attribute @duration of the previous Period.
// 3. If (i) @start attribute is absent, and (ii) the Period element is the
// first in the MPD, and (iii) the MPD@type is 'static', then the PeriodStart
// time shall be set to zero
// 4. If (i) @start attribute is absent, and (ii) the previous Period element does
// not contain a @durationattribute or the Period element is the first in the
// MPD, and (iii) the MPD@type is 'dynamic', then thisPeriod is an Early Available
// Period (see below for details)
// 5. If (i) @duration attribute is present, and (ii) the next Period element contains
// a @start attribute orthe @minimumUpdatePeriod is present, then this Period
// is an Early Terminated Period (see below for details)
period = &mpd->periods[i];
if (period->start > 0)
{
// a regular Period or an early terminated Period
start = period->start;
}
else if (i > 0 && mpd->periods[i - 1].duration > 0)
{
start = t;
}
else if (0 == i && DASH_STATIC == mpd->type)
{
start = 0;
}
else if( (0 == i || mpd->periods[i - 1].duration == 0) && DASH_DYNAMIC == mpd->type)
{
continue; // Early Available Period
}
else
{
start = t;
}
if (period->duration > 0)
{
t = start + period->duration;
}
else if (i + 1 < mpd->period_count)
{
assert(0 != mpd->periods[i + 1].start);
t = mpd->periods[i + 1].start;
}
else if(DASH_DYNAMIC == mpd->type)
{
// MPD@mediaPresentationDuration shall be present when neither
// the attribute MPD@minimumUpdatePeriod nor the Period@duration
// of the last Period are present.
assert(mpd->media_presentation_duration > 0 || mpd->minimum_update_period > 0);
t = mpd->media_presentation_duration > 0 ? mpd->media_presentation_duration : (start + mpd->minimum_update_period);
}
else
{
t = start; // ???
}
if (time < t)
return (int)i;
}
return -1; // not found
}
const struct dash_adaptation_set_t* dash_period_select(const struct dash_period_t* period, int media, unsigned int id, unsigned int group, const char* lang, const char* codecs)
{
size_t i;
const struct dash_adaptation_set_t* set;
for (i = 0; i < period->adaptation_set_count; i++)
{
set = &period->adaptation_sets[i];
if (DASH_MEDIA_UNKNOWN != media && media != dash_adaptation_set_media_type(set))
continue;
if (0 != id && id != set->id)
continue;
if (0 != group && group != set->group)
continue;
if (lang && *lang && set->lang && 0 != strcasecmp(set->lang, lang))
continue;
if (codecs && *codecs && set->base.codecs && 0 != strncasecmp(set->base.codecs, codecs, strlen(codecs)))
continue;
return set;
}
return NULL;
}

View File

@ -0,0 +1,189 @@
#include "dash-parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
const char* dash_segment_initialization(const struct dash_segment_t* segment);
int dash_segment_count(const struct dash_segment_t* segment);
int dash_segment_find(const struct dash_segment_t* segment, int64_t time);
int dash_segment_information(const struct dash_segment_t* segment, int index, int64_t* number, int64_t* start, int64_t* duration, const char** url, const char** range);
const struct dash_url_t* dash_representation_get_base_url(const struct dash_representation_t* representation)
{
const struct dash_mpd_t* mpd;
const struct dash_period_t* period;
const struct dash_adaptation_set_t* set;
if (representation->base_urls.count > 0)
return &representation->base_urls;
set = representation->parent;
if (set->base_urls.count > 0)
return &set->base_urls;
period = set->parent;
if (period->base_urls.count > 0)
return &period->base_urls;
mpd = period->parent;
return &mpd->urls;
}
const struct dash_segment_t* dash_representation_get_segment(const struct dash_representation_t* representation)
{
const struct dash_period_t* period;
const struct dash_adaptation_set_t* set;
if (DASH_SEGMENT_NONE != representation->segment.type)
return &representation->segment;
set = representation->parent;
if (DASH_SEGMENT_NONE != set->segment.type)
return &set->segment;
period = set->parent;
if (DASH_SEGMENT_NONE != period->segment.type)
return &period->segment;
return &representation->segment;
}
/// @return >=0-ok with length, <0-error
static int dash_representation_template_replace(const struct dash_representation_t* representation, const char* url, int64_t number, int64_t start, char* ptr, size_t len)
{
// Each identifier may be suffixed, within the enclosing '$' characters,
// with an additional format tag aligned with the printf format tag:
// %0[width]d
//const char* patterns[] = { "RepresentationID", "Number", "Bandwidth", "Time", "SubNumber" };
size_t i, j;
int width, off;
char format[16];
for (j = i = 0; i < strlen(url) && j < len; i++)
{
if ('$' == url[i])
{
off = 0;
width = 1;
// Identifier matching is case-sensitive.
if ('$' == url[i + 1])
{
// skip
ptr[j++] = url[i++];
continue;
}
else if (0 == strncmp("$RepresentationID$", url + i, 18))
{
j += snprintf(ptr + j, len - j, "%s", representation->id ? representation->id : "");
i += 17;
}
else if (0 == strncmp("$Number", url + i, 7) && ('$' == url[i + 7] || ('%' == url[i + 7] && 1 == sscanf(url + i + 7 + 1, "%dd$%n", &width, &off) && '$' == url[i + 7 + off])))
{
snprintf(format, sizeof(format), "%%0%d" PRId64, width);
j += snprintf(ptr + j, len - j, format, number);
i += 7 + off;
}
else if (0 == strncmp("$Bandwidth", url + i, 10) && ('$' == url[i + 10] || ('%' == url[i + 10] && 1 == sscanf(url + i + 10 + 1, "%dd$%n", &width, &off) && '$' == url[i + 10 + off])))
{
snprintf(format, sizeof(format), "%%0%du", width);
j += snprintf(ptr + j, len - j, format, representation->bandwidth);
i += 10 + off;
}
else if (0 == strncmp("$Time", url + i, 5) && ('$' == url[i + 5] || ('%' == url[i + 5] && 1 == sscanf(url + i + 5 + 1, "%dd$%n", &width, &off) && '$' == url[i + 5 + off])))
{
snprintf(format, sizeof(format), "%%0%d" PRId64, width);
j += snprintf(ptr + j, len - j, format, start);
i += 5 + off;
}
else if (0 == strncmp("$SubNumber", url + i, 10) && ('$' == url[i + 10] || '%' == url[i + 10]))
{
// TODO:
assert(0);
}
else
{
assert(0); // ignore
ptr[j++] = url[i];
}
}
else
{
ptr[j++] = url[i];
}
}
if (j < len)
ptr[j] = '\0';
return j < len ? (int)j : -1;
}
int dash_representation_get_initialization(const struct dash_representation_t* representation, char* url, size_t size)
{
const char* ptr;
const struct dash_period_t* period;
const struct dash_adaptation_set_t* set;
ptr = dash_segment_initialization(&representation->segment);
if (!ptr && representation->parent)
{
set = representation->parent;
ptr = dash_segment_initialization(&set->segment);
if (!ptr && set->parent)
{
period = set->parent;
ptr = dash_segment_initialization(&period->segment);
if (!ptr)
return 0;
}
}
return dash_representation_template_replace(representation, ptr, 0, 0, url, size);
}
int dash_representation_segment_url(const struct dash_representation_t* representation, int index, int64_t* number, int64_t* start, int64_t* duration, const char** range, char* url, size_t size)
{
int r;
const struct dash_segment_t* segment;
const char* url0;
segment = dash_representation_get_segment(representation);
assert(index >= 0 && index < dash_segment_count(segment));
r = dash_segment_information(segment, index, number, start, duration, &url0, range);
if (r < 0)
return r;
return dash_representation_template_replace(representation, url0, *number, *start, url, size);
}
int dash_representation_find_segment(const struct dash_representation_t* representation, int64_t time)
{
const struct dash_segment_t* segment;
segment = dash_representation_get_segment(representation);
return dash_segment_find(segment, time);
}
int dash_representation_segment_count(const struct dash_representation_t* representation)
{
const struct dash_segment_t* segment;
segment = dash_representation_get_segment(representation);
return dash_segment_count(segment);
}
#if defined(_DEBUG) || defined(DEBUG)
void dash_representation_test(void)
{
char ptr[128];
struct dash_representation_t r;
memset(&r, 0, sizeof(r));
r.id = "0";
r.bandwidth = 19200;
assert(23 == dash_representation_template_replace(&r, "dash-$$$$$RepresentationID$$Bandwidth$$Number$$Time$-", 1, 19700101, ptr, sizeof(ptr)));
assert(0 == strcmp(ptr, "dash-$$019200119700101-"));
assert(22 == dash_representation_template_replace(&r, "dash-$Bandwidth%03d$$Number%03d$$Time%03d$-", 1, 19700101, ptr, sizeof(ptr)));
assert(0 == strcmp(ptr, "dash-1920000119700101-"));
}
#endif

View File

@ -0,0 +1,210 @@
#include "dash-parser.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
int dash_segment_count(const struct dash_segment_t* segment)
{
int n;
size_t i;
switch (segment->type)
{
case DASH_SEGMENT_BASE:
return 1;
case DASH_SEGMENT_LIST:
return (int)segment->segment_url_count;
case DASH_SEGMENT_TEMPLATE:
if (segment->segment_timeline.count < 1)
return INT_MAX; // dynamic + infinite
// static + timeline
for (i = n = 0; i < segment->segment_timeline.count; i++)
n += 1 + segment->segment_timeline.S[i].r;
return n;
default:
return 0; // none
}
}
const char* dash_segment_initialization(const struct dash_segment_t* segment)
{
if (segment->initialization.source_url && *segment->initialization.source_url)
return segment->initialization.source_url;
if (DASH_SEGMENT_TEMPLATE == segment->type && segment->initialization_url && *segment->initialization_url)
return segment->initialization_url;
return NULL;
}
/// @return 0-ok, <0-error
static int dash_segment_timeline(const struct dash_segment_timeline_t* timeline, size_t index, int64_t* number, int64_t* start, int64_t* duration)
{
int64_t t;
size_t i, j, step;
t = 0;
for (j = i = 0; i < timeline->count; i++)
{
// 5.3.9.6 Segment timeline
// The @r attribute has a default value of zero when not present.
// The value of the @r attribute of the S element may be set to a
// negative value indicating that the duration indicated in @d is
// promised to repeat until the S@t of the next S element or if it
// is the last S element in the SegmentTimeline element until the
// end of the Period or the next update of the MPD
assert(timeline->S[i].d > 0);
if (timeline->S[i].r >= 0)
{
step = timeline->S[i].r + 1;
}
else if (i + 1 == timeline->count)
{
// last
step = 0;
}
else
{
assert(timeline->S[i].t > 0);
step = (size_t)((timeline->S[i + 1].t - t) / (timeline->S[i].d > 0 ? timeline->S[i].d : 1));
}
if (0 == step || index < j + step)
{
*number = timeline->S[i].n + (index - j);
*start = (timeline->S[i].t ? timeline->S[i].t : t) + (index - j) * timeline->S[i].d;
*duration = timeline->S[i].d;
return 0;
}
j += step;
t = (timeline->S[i].t ? timeline->S[i].t : t) + step * timeline->S[i].d;
}
return -1;
}
/// @param[out] number start number of representation
/// @param[out] start start time of representation(MUST add period.start)
/// @param[out] duration segment duration, 0 if unknown
/// @param[out] url segment url(MUST resolve with representation base url)
/// @param[out] range url range, NULL if don't have range
/// @return 0-ok, <0-error, >0-undefined
int dash_segment_information(const struct dash_segment_t* segment, int index, int64_t* number, int64_t* start, int64_t* duration, const char** url, const char** range)
{
int r;
int64_t timescale;
static const char* sc_empty = "";
// 5.3.9.2 Segment base information
// 1. If the Representation contains more than one Media Segment, then either
// the attribute @duration or the element SegmentTimeline shall be present.
// 2. The attribute @duration and the element SegmentTimeline shall not be present at the same time.
// 5.3.9.5.3 Media Segment information
// 1. a valid Media Segment URL and possibly a byte range,
// 2. the number and position of the Media Segment in the Representation,
// 3. the MPD start time of the Media Segment in the Representation providing an approximate presentation start time of the Segment in the Period,
// 4. the MPD duration of the Media Segment providing an approximate presentation duration of the Segment.
//
// SegmentTemplate
// 1. If the Representation contains or inherits a SegmentTemplate element with $Number$
// then the URL of the Media Segment at position k in the Representation is determined
// by replacing the $Number$ identifier by (k-1) + (k.start-1) with kstart the value of
// the @startNumber attribute, if present, or 1 otherwise.
// 2. If the Representation contains or inherits a SegmentTemplate element with $Time$ then
// the URL of the media segment at position k is determined by replacing the $Time$
// identifier by MPD start time of this segment, as described below.
//
// SegmentList
// The number of the first Segment in the list within this Period is determined by the value
// of the SegmentList@startNumber attribute, if present, or it is 1 in case this attribute
// is not present.The sequence of multiple SegmentList elements within a Representation shall
// result in Media Segment List with consecutive numbers.
r = 0;
if (DASH_SEGMENT_BASE == segment->type)
{
if (0 != index)
return -1; // segment base only have one segment
*number = 0;
*start = 0 - segment->presentation_time_offset;
*duration = segment->presentation_duration;
*url = sc_empty;
}
else
{
if (index < 0 || (DASH_SEGMENT_LIST == segment->type && index >= (int)segment->segment_url_count))
return -1;
if (segment->segment_timeline.count > 0)
{
r = dash_segment_timeline(&segment->segment_timeline, index, number, start, duration);
if (0 != r)
return -1;
}
else
{
*number = (segment->start_number > 0 ? (segment->start_number - 1) : 0) + index;
*start = (int64_t)index * segment->duration - segment->presentation_time_offset;
*duration = segment->duration;
}
if (DASH_SEGMENT_LIST == segment->type)
{
*url = segment->segment_urls[index].media;
*range = segment->segment_urls[index].media_range;
}
else if (DASH_SEGMENT_TEMPLATE == segment->type)
{
*url = segment->media;
*range = NULL;
}
else
{
assert(0);
return -1;
}
}
timescale = segment->timescale > 0 ? segment->timescale : 1;
*start = *start / timescale;
*duration = *duration / timescale;
return r;
}
int dash_segment_find(const struct dash_segment_t* segment, int64_t time)
{
int r, n, i, mid;
int64_t number, t, d;
const char* url, * range;
n = dash_segment_count(segment);
i = 0;
mid = -1;
while (i < n)
{
mid = (i + n) / 2;
r = dash_segment_information(segment, mid, &number, &t, &d, &url, &range);
if (0 != r)
return r;
if (time < t)
n = mid;
else if (time > t + d)
i = mid + 1;
else
break;
}
return mid;
}

View File

@ -0,0 +1,61 @@
#ifndef _list_h_
#define _list_h_
struct list_head
{
struct list_head *next, *prev;
};
static inline void list_insert_after(struct list_head *item, struct list_head *head)
{
struct list_head *prev, *next;
prev = head;
next = head->next;
item->prev = prev;
item->next = next;
next->prev = item;
prev->next = item;
}
static inline void list_insert_before(struct list_head *item, struct list_head *head)
{
struct list_head *prev, *next;
prev = head->prev;
next = head;
item->prev = prev;
item->next = next;
next->prev = item;
prev->next = item;
}
static inline void list_remove(struct list_head *item)
{
struct list_head *prev, *next;
prev = item->prev;
next = item->next;
prev->next = next;
next->prev = prev;
item->prev = item->next = 0;
}
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
#define LIST_INIT_HEAD(list) do { (list)->next = (list)->prev = (list); } while (0)
#define list_entry(ptr, type, member) \
((type*)((char*)ptr-(ptrdiff_t)(&((type*)0)->member)))
#define list_for_each(pos, head) \
for(pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, n, head) \
for(pos = (head)->next, n = pos->next; pos != (head); pos = n, n=pos->next)
#endif /* !_list_h_ */

View File

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

View File

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

View File

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

View File

@ -0,0 +1,14 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DOS_LINUX -DOS_ANDROID
LOCAL_LDLIBS +=
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_SRC_FILES := $(wildcard source/*.c)
LOCAL_SRC_FILES += $(wildcard source/*.cpp)
LOCAL_MODULE := flv
include $(BUILD_STATIC_LIBRARY)

View File

@ -0,0 +1,44 @@
#--------------------------------Output------------------------------
# OUTTYPE: 0-exe, 1-dll, 2-static
#--------------------------------------------------------------------
OUTTYPE = 2
OUTFILE = libflv.a
#-------------------------------Include------------------------------
#
# INCLUDES = $(addprefix -I,$(INCLUDES)) # add -I prefix
#--------------------------------------------------------------------
INCLUDES = . ./include
#-------------------------------Source-------------------------------
#
#--------------------------------------------------------------------
SOURCE_PATHS = source
SOURCE_FILES = $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.cpp))
SOURCE_FILES += $(foreach dir,$(SOURCE_PATHS),$(wildcard $(dir)/*.c))
SOURCE_FILES := $(filter-out $(_SOURCE_FILES),$(SOURCE_FILES))
#-----------------------------Library--------------------------------
#
# LIBPATHS = $(addprefix -L,$(LIBPATHS)) # add -L prefix
#--------------------------------------------------------------------
LIBPATHS =
ifdef RELEASE
# relase library path
LIBPATHS +=
else
LIBPATHS +=
endif
LIBS =
STATIC_LIBS =
#-----------------------------DEFINES--------------------------------
#
# DEFINES := $(addprefix -D,$(DEFINES)) # add -L prefix
#--------------------------------------------------------------------
DEFINES =
include ../gcc.mk

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,378 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
4605125A24D6B4D500B04B70 /* opus-head.c in Sources */ = {isa = PBXBuildFile; fileRef = 4605125924D6B4D500B04B70 /* opus-head.c */; };
46733799219B2315009F658F /* mpeg4-aac-asc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46733798219B2315009F658F /* mpeg4-aac-asc.c */; };
468B915C23B8ADAA00EA99A3 /* aom-av1.c in Sources */ = {isa = PBXBuildFile; fileRef = 468B915B23B8ADAA00EA99A3 /* aom-av1.c */; };
468B916023B8AE3100EA99A3 /* flv-header.c in Sources */ = {isa = PBXBuildFile; fileRef = 468B915F23B8AE3100EA99A3 /* flv-header.c */; };
46C5B2DB2183EDA200419E57 /* mpeg4-mp4toannexb.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CB2183EDA200419E57 /* mpeg4-mp4toannexb.c */; };
46C5B2DC2183EDA200419E57 /* mp3-header.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CC2183EDA200419E57 /* mp3-header.c */; };
46C5B2DD2183EDA200419E57 /* flv-writer.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CD2183EDA200419E57 /* flv-writer.c */; };
46C5B2DE2183EDA200419E57 /* amf0.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CE2183EDA200419E57 /* amf0.c */; };
46C5B2DF2183EDA200419E57 /* hevc-annexbtomp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2CF2183EDA200419E57 /* hevc-annexbtomp4.c */; };
46C5B2E02183EDA200419E57 /* mpeg4-hevc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D02183EDA200419E57 /* mpeg4-hevc.c */; };
46C5B2E12183EDA200419E57 /* flv-demuxer.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D12183EDA200419E57 /* flv-demuxer.c */; };
46C5B2E22183EDA200419E57 /* amf3.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D22183EDA200419E57 /* amf3.c */; };
46C5B2E32183EDA200419E57 /* mpeg4-aac.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D32183EDA200419E57 /* mpeg4-aac.c */; };
46C5B2E42183EDA200419E57 /* flv-reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D42183EDA200419E57 /* flv-reader.c */; };
46C5B2E52183EDA200419E57 /* flv-demuxer-script.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D52183EDA200419E57 /* flv-demuxer-script.c */; };
46C5B2E62183EDA200419E57 /* mpeg4-annexbtomp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D62183EDA200419E57 /* mpeg4-annexbtomp4.c */; };
46C5B2E72183EDA200419E57 /* flv-parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D72183EDA200419E57 /* flv-parser.c */; };
46C5B2E82183EDA200419E57 /* flv-muxer.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D82183EDA200419E57 /* flv-muxer.c */; };
46C5B2E92183EDA200419E57 /* mpeg4-avc.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2D92183EDA200419E57 /* mpeg4-avc.c */; };
46C5B2EA2183EDA200419E57 /* hevc-mp4toannexb.c in Sources */ = {isa = PBXBuildFile; fileRef = 46C5B2DA2183EDA200419E57 /* hevc-mp4toannexb.c */; };
46E55E5324694DE000D8BDBA /* webm-vpx.c in Sources */ = {isa = PBXBuildFile; fileRef = 46E55E5224694DE000D8BDBA /* webm-vpx.c */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
4605125924D6B4D500B04B70 /* opus-head.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "opus-head.c"; sourceTree = "<group>"; };
46733798219B2315009F658F /* mpeg4-aac-asc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-aac-asc.c"; sourceTree = "<group>"; };
468B915B23B8ADAA00EA99A3 /* aom-av1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "aom-av1.c"; sourceTree = "<group>"; };
468B915F23B8AE3100EA99A3 /* flv-header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-header.c"; sourceTree = "<group>"; };
46C5B21C2183EA7400419E57 /* libflv.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libflv.a; sourceTree = BUILT_PRODUCTS_DIR; };
46C5B2CB2183EDA200419E57 /* mpeg4-mp4toannexb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-mp4toannexb.c"; sourceTree = "<group>"; };
46C5B2CC2183EDA200419E57 /* mp3-header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mp3-header.c"; sourceTree = "<group>"; };
46C5B2CD2183EDA200419E57 /* flv-writer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-writer.c"; sourceTree = "<group>"; };
46C5B2CE2183EDA200419E57 /* amf0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf0.c; sourceTree = "<group>"; };
46C5B2CF2183EDA200419E57 /* hevc-annexbtomp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "hevc-annexbtomp4.c"; sourceTree = "<group>"; };
46C5B2D02183EDA200419E57 /* mpeg4-hevc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-hevc.c"; sourceTree = "<group>"; };
46C5B2D12183EDA200419E57 /* flv-demuxer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-demuxer.c"; sourceTree = "<group>"; };
46C5B2D22183EDA200419E57 /* amf3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf3.c; sourceTree = "<group>"; };
46C5B2D32183EDA200419E57 /* mpeg4-aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-aac.c"; sourceTree = "<group>"; };
46C5B2D42183EDA200419E57 /* flv-reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-reader.c"; sourceTree = "<group>"; };
46C5B2D52183EDA200419E57 /* flv-demuxer-script.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-demuxer-script.c"; sourceTree = "<group>"; };
46C5B2D62183EDA200419E57 /* mpeg4-annexbtomp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-annexbtomp4.c"; sourceTree = "<group>"; };
46C5B2D72183EDA200419E57 /* flv-parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-parser.c"; sourceTree = "<group>"; };
46C5B2D82183EDA200419E57 /* flv-muxer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "flv-muxer.c"; sourceTree = "<group>"; };
46C5B2D92183EDA200419E57 /* mpeg4-avc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpeg4-avc.c"; sourceTree = "<group>"; };
46C5B2DA2183EDA200419E57 /* hevc-mp4toannexb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "hevc-mp4toannexb.c"; sourceTree = "<group>"; };
46C5B2EB2183EDB400419E57 /* include */ = {isa = PBXFileReference; lastKnownFileType = folder; path = include; sourceTree = "<group>"; };
46E55E5224694DE000D8BDBA /* webm-vpx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "webm-vpx.c"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
46C5B21A2183EA7400419E57 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
46C5B2132183EA7300419E57 = {
isa = PBXGroup;
children = (
46C5B2EB2183EDB400419E57 /* include */,
46C5B2CA2183EDA200419E57 /* source */,
46C5B21D2183EA7400419E57 /* Products */,
);
sourceTree = "<group>";
};
46C5B21D2183EA7400419E57 /* Products */ = {
isa = PBXGroup;
children = (
46C5B21C2183EA7400419E57 /* libflv.a */,
);
name = Products;
sourceTree = "<group>";
};
46C5B2CA2183EDA200419E57 /* source */ = {
isa = PBXGroup;
children = (
46C5B2CE2183EDA200419E57 /* amf0.c */,
46C5B2D22183EDA200419E57 /* amf3.c */,
468B915B23B8ADAA00EA99A3 /* aom-av1.c */,
46C5B2D52183EDA200419E57 /* flv-demuxer-script.c */,
46C5B2D12183EDA200419E57 /* flv-demuxer.c */,
468B915F23B8AE3100EA99A3 /* flv-header.c */,
46C5B2D82183EDA200419E57 /* flv-muxer.c */,
46C5B2D72183EDA200419E57 /* flv-parser.c */,
46C5B2D42183EDA200419E57 /* flv-reader.c */,
46C5B2CD2183EDA200419E57 /* flv-writer.c */,
46C5B2CF2183EDA200419E57 /* hevc-annexbtomp4.c */,
46C5B2DA2183EDA200419E57 /* hevc-mp4toannexb.c */,
46C5B2CC2183EDA200419E57 /* mp3-header.c */,
46733798219B2315009F658F /* mpeg4-aac-asc.c */,
46C5B2D32183EDA200419E57 /* mpeg4-aac.c */,
46C5B2D62183EDA200419E57 /* mpeg4-annexbtomp4.c */,
46C5B2D92183EDA200419E57 /* mpeg4-avc.c */,
46C5B2D02183EDA200419E57 /* mpeg4-hevc.c */,
46C5B2CB2183EDA200419E57 /* mpeg4-mp4toannexb.c */,
4605125924D6B4D500B04B70 /* opus-head.c */,
46E55E5224694DE000D8BDBA /* webm-vpx.c */,
);
path = source;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
46C5B2182183EA7400419E57 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
46C5B21B2183EA7400419E57 /* libflv */ = {
isa = PBXNativeTarget;
buildConfigurationList = 46C5B2202183EA7400419E57 /* Build configuration list for PBXNativeTarget "libflv" */;
buildPhases = (
46C5B2182183EA7400419E57 /* Headers */,
46C5B2192183EA7400419E57 /* Sources */,
46C5B21A2183EA7400419E57 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = libflv;
productName = libflv;
productReference = 46C5B21C2183EA7400419E57 /* libflv.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
46C5B2142183EA7300419E57 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = ireader;
TargetAttributes = {
46C5B21B2183EA7400419E57 = {
CreatedOnToolsVersion = 10.0;
};
};
};
buildConfigurationList = 46C5B2172183EA7300419E57 /* Build configuration list for PBXProject "libflv" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 46C5B2132183EA7300419E57;
productRefGroup = 46C5B21D2183EA7400419E57 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
46C5B21B2183EA7400419E57 /* libflv */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
46C5B2192183EA7400419E57 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
46C5B2E12183EDA200419E57 /* flv-demuxer.c in Sources */,
46C5B2DB2183EDA200419E57 /* mpeg4-mp4toannexb.c in Sources */,
46C5B2E92183EDA200419E57 /* mpeg4-avc.c in Sources */,
468B916023B8AE3100EA99A3 /* flv-header.c in Sources */,
46C5B2E62183EDA200419E57 /* mpeg4-annexbtomp4.c in Sources */,
468B915C23B8ADAA00EA99A3 /* aom-av1.c in Sources */,
46C5B2E02183EDA200419E57 /* mpeg4-hevc.c in Sources */,
46C5B2DC2183EDA200419E57 /* mp3-header.c in Sources */,
46C5B2EA2183EDA200419E57 /* hevc-mp4toannexb.c in Sources */,
46C5B2DD2183EDA200419E57 /* flv-writer.c in Sources */,
46C5B2E82183EDA200419E57 /* flv-muxer.c in Sources */,
46C5B2E42183EDA200419E57 /* flv-reader.c in Sources */,
46C5B2E52183EDA200419E57 /* flv-demuxer-script.c in Sources */,
46C5B2E72183EDA200419E57 /* flv-parser.c in Sources */,
46C5B2DF2183EDA200419E57 /* hevc-annexbtomp4.c in Sources */,
46C5B2E22183EDA200419E57 /* amf3.c in Sources */,
46733799219B2315009F658F /* mpeg4-aac-asc.c in Sources */,
46C5B2DE2183EDA200419E57 /* amf0.c in Sources */,
46E55E5324694DE000D8BDBA /* webm-vpx.c in Sources */,
4605125A24D6B4D500B04B70 /* opus-head.c in Sources */,
46C5B2E32183EDA200419E57 /* mpeg4-aac.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
46C5B21E2183EA7400419E57 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
USER_HEADER_SEARCH_PATHS = (
.,
./include,
../../sdk/include,
);
};
name = Debug;
};
46C5B21F2183EA7400419E57 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
USER_HEADER_SEARCH_PATHS = (
.,
./include,
../../sdk/include,
);
};
name = Release;
};
46C5B2212183EA7400419E57 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
EXECUTABLE_PREFIX = "";
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
USER_HEADER_SEARCH_PATHS = "$(inherited)";
};
name = Debug;
};
46C5B2222183EA7400419E57 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
EXECUTABLE_PREFIX = "";
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
USER_HEADER_SEARCH_PATHS = "$(inherited)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
46C5B2172183EA7300419E57 /* Build configuration list for PBXProject "libflv" */ = {
isa = XCConfigurationList;
buildConfigurations = (
46C5B21E2183EA7400419E57 /* Debug */,
46C5B21F2183EA7400419E57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
46C5B2202183EA7400419E57 /* Build configuration list for PBXNativeTarget "libflv" */ = {
isa = XCConfigurationList;
buildConfigurations = (
46C5B2212183EA7400419E57 /* Debug */,
46C5B2222183EA7400419E57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 46C5B2142183EA7300419E57 /* Project object */;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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));
}

View 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

View 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

View 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

View 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

View 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);
}

View 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

View 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

View 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"));
}

View 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, &timestamp, &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);
}

View 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, &timestamp, &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);
}

View 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, &timestamp, &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);
}

View 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);
}

View 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);
}

View 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);
}

View File

@ -0,0 +1,2 @@
const char* const version_version="0.6.0.$WCREV$";
const char* const version_build="$WCNOW$";

View File

@ -0,0 +1,16 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DOS_LINUX -DOS_ANDROID
LOCAL_LDLIBS +=
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libmov/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libmpeg/include
LOCAL_SRC_FILES := $(wildcard source/*.c)
LOCAL_SRC_FILES += $(wildcard source/*.cpp)
LOCAL_MODULE := hls
include $(BUILD_STATIC_LIBRARY)

View 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

View 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

View 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

View 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

View 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, &timestamp, &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);
}

View 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

View 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, &timestamp, &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

View 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

View 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

View 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

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

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

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

View 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