mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 10:40:05 +08:00
初始提交
This commit is contained in:
parent
aef0ecbcb9
commit
3f73024a9b
279
.cproject
Normal file
279
.cproject
Normal file
@ -0,0 +1,279 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
<cconfiguration id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1947026272">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1947026272" moduleId="org.eclipse.cdt.core.settings" name="ARM">
|
||||
<externalSettings>
|
||||
<externalSetting>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/streamPusher"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/SMServer"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/streamPusher/ARM"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/SMServer/ARM"/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="streamPusher" srcPrefixMapping="" srcRootPath=""/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="SMServer" srcPrefixMapping="" srcRootPath=""/>
|
||||
</externalSetting>
|
||||
</externalSettings>
|
||||
<extensions>
|
||||
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactExtension="a" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.staticLib" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.staticLib,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1947026272" name="ARM" parent="cdt.managedbuild.config.gnu.cross.exe.debug">
|
||||
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1947026272." name="/" resourcePath="">
|
||||
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.830471881" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
|
||||
<option id="cdt.managedbuild.option.gnu.cross.prefix.1087638266" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix" useByScannerDiscovery="false" value="arm-linux-gnueabi-" valueType="string"/>
|
||||
<option id="cdt.managedbuild.option.gnu.cross.path.1835104267" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path" useByScannerDiscovery="false" value="/home/xzl/soft/arm-gcc-6.2/bin" valueType="string"/>
|
||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.657700061" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
|
||||
<builder arguments="-j8" buildPath="${workspace_loc:/streamPusher}/Debug" command="make" id="cdt.managedbuild.builder.gnu.cross.1414815504" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.builder.gnu.cross"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1784351355" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
|
||||
<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.option.optimization.level.221016008" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.c.optimization.level.most" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.debugging.level.646402024" name="Debug Level" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.none" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.dialect.std.1972177397" name="Language standard" superClass="gnu.c.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.c.compiler.dialect.default" valueType="enumerated"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1474182404" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||
</tool>
|
||||
<tool command="g++" id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.886102444" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
|
||||
<option id="gnu.cpp.compiler.option.optimization.level.1960810243" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.debugging.level.809016166" name="Debug Level" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.dialect.std.1534690969" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.cpp.compiler.dialect.c++1y" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.include.paths.269637576" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value="/home/xzl/soft"/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/src}""/>
|
||||
<listOptionValue builtIn="false" value="../../ZLToolKit/src"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.preprocessor.def.275206662" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1840656821" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1688616453" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
|
||||
<tool command="g++ -pthread -static-libstdc++" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.156178488" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
|
||||
<option id="gnu.cpp.link.option.paths.1099158999" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" useByScannerDiscovery="false" valueType="libPaths">
|
||||
<listOptionValue builtIn="false" value="/home/xzl/workspace/ZLToolKit/ARM"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.link.option.libs.1286710800" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false" valueType="libs">
|
||||
<listOptionValue builtIn="false" value="ZLToolKit"/>
|
||||
</option>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.463559224" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||
</inputType>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.237778560" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.987251820" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.321229939" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
<cconfiguration id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1912216574">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1912216574" moduleId="org.eclipse.cdt.core.settings" name="X64">
|
||||
<externalSettings>
|
||||
<externalSetting>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/streamPusher"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/SMServer"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/ZLMeidaKit"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/streamPusher/Debug"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/SMServer/X64"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/ZLMeidaKit/X64"/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="streamPusher" srcPrefixMapping="" srcRootPath=""/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="SMServer" srcPrefixMapping="" srcRootPath=""/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="ZLMeidaKit" srcPrefixMapping="" srcRootPath=""/>
|
||||
</externalSetting>
|
||||
</externalSettings>
|
||||
<extensions>
|
||||
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactExtension="so" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.sharedLib" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.sharedLib,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1912216574" name="X64" parent="cdt.managedbuild.config.gnu.cross.exe.debug">
|
||||
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1912216574." name="/" resourcePath="">
|
||||
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.1731210621" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
|
||||
<option id="cdt.managedbuild.option.gnu.cross.prefix.268604998" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix" useByScannerDiscovery="false" value="" valueType="string"/>
|
||||
<option id="cdt.managedbuild.option.gnu.cross.path.105061222" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path" useByScannerDiscovery="false" value="" valueType="string"/>
|
||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1847785660" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
|
||||
<builder arguments="-j8" buildPath="${workspace_loc:/streamPusher}/Debug" command="make" id="cdt.managedbuild.builder.gnu.cross.1420064773" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.builder.gnu.cross"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.883243436" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
|
||||
<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.option.optimization.level.807081323" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.c.optimization.level.most" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.debugging.level.769882155" name="Debug Level" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.none" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.dialect.std.1386736984" name="Language standard" superClass="gnu.c.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.c.compiler.dialect.default" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.misc.pic.542757068" name="Position Independent Code (-fPIC)" superClass="gnu.c.compiler.option.misc.pic" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1437084291" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||
</tool>
|
||||
<tool command="g++ " id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.148300194" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
|
||||
<option id="gnu.cpp.compiler.option.optimization.level.18009049" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.debugging.level.1462613868" name="Debug Level" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.dialect.std.387448796" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.cpp.compiler.dialect.c++1y" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.include.paths.1977874877" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/src}""/>
|
||||
<listOptionValue builtIn="false" value="../../ZLToolKit/src"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.preprocessor.def.863813077" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
|
||||
<listOptionValue builtIn="false" value="ENABLE_FAAC"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_RTSP2RTMP"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_RTMP2RTSP"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_MEDIAFILE"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_X264"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.other.pic.198751626" name="Position Independent Code (-fPIC)" superClass="gnu.cpp.compiler.option.other.pic" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.776990372" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.885569113" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker">
|
||||
<option defaultValue="true" id="gnu.c.link.option.shared.1562973613" name="Shared (-shared)" superClass="gnu.c.link.option.shared" valueType="boolean"/>
|
||||
</tool>
|
||||
<tool command="g++" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1728730527" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
|
||||
<option id="gnu.cpp.link.option.paths.262581495" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" useByScannerDiscovery="false"/>
|
||||
<option id="gnu.cpp.link.option.libs.1197706329" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false"/>
|
||||
<option defaultValue="true" id="gnu.cpp.link.option.shared.1825641787" name="Shared (-shared)" superClass="gnu.cpp.link.option.shared" useByScannerDiscovery="false" valueType="boolean"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.437103762" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||
</inputType>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1902434321" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.1152237148" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.583426329" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
<cconfiguration id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1508415929">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1508415929" moduleId="org.eclipse.cdt.core.settings" name="WIN32">
|
||||
<externalSettings>
|
||||
<externalSetting>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/streamPusher"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="includePath" name="/SMServer"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/streamPusher/Debug"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH" kind="libraryPath" name="/SMServer/WIN32"/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="streamPusher" srcPrefixMapping="" srcRootPath=""/>
|
||||
<entry flags="RESOLVED" kind="libraryFile" name="SMServer" srcPrefixMapping="" srcRootPath=""/>
|
||||
</externalSetting>
|
||||
</externalSettings>
|
||||
<extensions>
|
||||
<extension id="org.eclipse.cdt.core.Cygwin_PE" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactExtension="a" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.staticLib" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.staticLib,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1508415929" name="WIN32" parent="cdt.managedbuild.config.gnu.cross.exe.debug">
|
||||
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1508415929." name="/" resourcePath="">
|
||||
<toolChain id="cdt.managedbuild.toolchain.gnu.cygwin.base.1626305239" name="Cygwin GCC" superClass="cdt.managedbuild.toolchain.gnu.cygwin.base">
|
||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.Cygwin_PE" id="cdt.managedbuild.target.gnu.platform.cygwin.base.187178930" name="Debug Platform" osList="win32" superClass="cdt.managedbuild.target.gnu.platform.cygwin.base"/>
|
||||
<builder arguments="-j8" buildPath="${workspace_loc:/SMServer}/WIN32" command="make" id="cdt.managedbuild.target.gnu.builder.cygwin.base.629377026" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.cygwin.base"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.assembler.cygwin.base.1636318789" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.cygwin.base">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.2010110108" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.archiver.cygwin.base.1059143675" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.cygwin.base"/>
|
||||
<tool command="g++ -std=c++11" id="cdt.managedbuild.tool.gnu.cpp.compiler.cygwin.base.666287751" name="Cygwin C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.cygwin.base">
|
||||
<option id="gnu.cpp.compiler.option.include.paths.1644470871" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/extra/x264/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/extra/faac-1.28/include}""/>
|
||||
<listOptionValue builtIn="false" value=""E:\ServiceCenter\ZLToolKit\src""/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.preprocessor.def.696656907" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
|
||||
<listOptionValue builtIn="false" value="ENABLE_X264"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_FAAC"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_RTSP2RTMP"/>
|
||||
<listOptionValue builtIn="false" value="ENABLE_RTMP2RTSP"/>
|
||||
<listOptionValue builtIn="false" value="__WIN32__"/>
|
||||
<listOptionValue builtIn="false" value="__x86__"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.optimization.level.930471751" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.debugging.level.654866617" name="Debug Level" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
|
||||
<option id="gnu.cpp.compiler.option.dialect.std.1431836233" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.cpp.compiler.dialect.default" valueType="enumerated"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.cygwin.665214370" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input.cygwin"/>
|
||||
</tool>
|
||||
<tool command="gcc" id="cdt.managedbuild.tool.gnu.c.compiler.cygwin.base.1643775314" name="Cygwin C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.cygwin.base">
|
||||
<option id="gnu.c.compiler.option.preprocessor.def.symbols.681328153" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
|
||||
<listOptionValue builtIn="false" value="__WIN32__"/>
|
||||
<listOptionValue builtIn="false" value="__x86__"/>
|
||||
</option>
|
||||
<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.option.optimization.level.173223743" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.c.optimization.level.most" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.debugging.level.1909420880" name="Debug Level" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.none" valueType="enumerated"/>
|
||||
<option id="gnu.c.compiler.option.dialect.std.1329653493" name="Language standard" superClass="gnu.c.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.c.compiler.dialect.default" valueType="enumerated"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.cygwin.1758181712" superClass="cdt.managedbuild.tool.gnu.c.compiler.input.cygwin"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.c.linker.cygwin.base.1324002540" name="Cygwin C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.cygwin.base">
|
||||
<option defaultValue="true" id="gnu.c.link.option.shared.174924190" name="Shared (-shared)" superClass="gnu.c.link.option.shared" valueType="boolean"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.cygwin.base.290725694" name="Cygwin C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.cygwin.base">
|
||||
<option id="gnu.cpp.link.option.libs.1424883692" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false" valueType="libs">
|
||||
<listOptionValue builtIn="false" value="ZLToolKit"/>
|
||||
<listOptionValue builtIn="false" value="x264-148"/>
|
||||
<listOptionValue builtIn="false" value="faac"/>
|
||||
<listOptionValue builtIn="false" value="ws2_32"/>
|
||||
</option>
|
||||
<option id="gnu.cpp.link.option.paths.675383842" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" useByScannerDiscovery="false" valueType="libPaths">
|
||||
<listOptionValue builtIn="false" value=""E:\ServiceCenter\ZLToolKit\WIN32""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/extra/x264/lib}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/extra/faac-1.28/lib}""/>
|
||||
</option>
|
||||
<option defaultValue="true" id="gnu.cpp.link.option.shared.1889712682" name="Shared (-shared)" superClass="gnu.cpp.link.option.shared" valueType="boolean"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.837966139" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||
</inputType>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<project id="streamPusher.cdt.managedbuild.target.gnu.cross.exe.434173064" name="Executable" projectType="cdt.managedbuild.target.gnu.cross.exe"/>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
<storageModule moduleId="refreshScope" versionNumber="2">
|
||||
<configuration configurationName="X64"/>
|
||||
<configuration configurationName="WIN32">
|
||||
<resource resourceType="PROJECT" workspacePath="/SMServer"/>
|
||||
</configuration>
|
||||
<configuration configurationName="ARM"/>
|
||||
<configuration configurationName="Debug">
|
||||
<resource resourceType="PROJECT" workspacePath="/streamPusher"/>
|
||||
</configuration>
|
||||
<configuration configurationName="MAC"/>
|
||||
<configuration configurationName="Android"/>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
|
||||
<storageModule moduleId="scannerConfiguration">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880;cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.;cdt.managedbuild.tool.gnu.cross.c.compiler.1525620747;cdt.managedbuild.tool.gnu.c.compiler.input.1391645717">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880;cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.;cdt.managedbuild.tool.gnu.c.compiler.base.1084336804;cdt.managedbuild.tool.gnu.c.compiler.input.1990378779">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880;cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1871589793;cdt.managedbuild.tool.gnu.cpp.compiler.input.237645072">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880;cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.;cdt.managedbuild.tool.gnu.cpp.compiler.base.1358852830;cdt.managedbuild.tool.gnu.cpp.compiler.input.280550491">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880;cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.;cdt.managedbuild.tool.gnu.cross.c.compiler.249358015;cdt.managedbuild.tool.gnu.c.compiler.input.886452886">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880;cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1903614826;cdt.managedbuild.tool.gnu.cpp.compiler.input.1655699253">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
|
||||
</cproject>
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,3 +27,4 @@
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
/X64/
|
||||
|
27
.project
Normal file
27
.project
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ZLMediaKit</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
36
.settings/language.settings.xml
Normal file
36
.settings/language.settings.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project>
|
||||
<configuration id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1947026272" name="ARM">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="2101233173096" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
</extension>
|
||||
</configuration>
|
||||
<configuration id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1912216574" name="X64">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-863197511674744034" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
</extension>
|
||||
</configuration>
|
||||
<configuration id="cdt.managedbuild.config.gnu.cross.exe.debug.1307511880.1508415929" name="WIN32">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="org.eclipse.cdt.managedbuilder.internal.language.settings.providers.GCCBuiltinSpecsDetectorCygwin" console="false" env-hash="-1608958325086134348" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorCygwin" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings Cygwin" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
</extension>
|
||||
</configuration>
|
||||
</project>
|
70
ARM/makefile
Normal file
70
ARM/makefile
Normal file
@ -0,0 +1,70 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
-include ../makefile.init
|
||||
|
||||
RM := rm -rf
|
||||
|
||||
# All of the sources participating in the build are defined here
|
||||
-include sources.mk
|
||||
-include src/Shell/subdir.mk
|
||||
-include src/Rtsp/subdir.mk
|
||||
-include src/Rtmp/subdir.mk
|
||||
-include src/RTP/subdir.mk
|
||||
-include src/Player/subdir.mk
|
||||
-include src/MedaiFile/CRC/subdir.mk
|
||||
-include src/MedaiFile/subdir.mk
|
||||
-include src/Http/subdir.mk
|
||||
-include src/H264/subdir.mk
|
||||
-include src/Device/subdir.mk
|
||||
-include src/Codec/subdir.mk
|
||||
-include src/subdir.mk
|
||||
-include subdir.mk
|
||||
-include objects.mk
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(strip $(CC_DEPS)),)
|
||||
-include $(CC_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(C++_DEPS)),)
|
||||
-include $(C++_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(C_UPPER_DEPS)),)
|
||||
-include $(C_UPPER_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(CXX_DEPS)),)
|
||||
-include $(CXX_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(C_DEPS)),)
|
||||
-include $(C_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(CPP_DEPS)),)
|
||||
-include $(CPP_DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
-include ../makefile.defs
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
|
||||
# All Target
|
||||
all: libZLMediaKit.a
|
||||
|
||||
# Tool invocations
|
||||
libZLMediaKit.a: $(OBJS) $(USER_OBJS)
|
||||
@echo 'Building target: $@'
|
||||
@echo 'Invoking: Cross GCC Archiver'
|
||||
arm-linux-gnueabi-ar -r "libZLMediaKit.a" $(OBJS) $(USER_OBJS) $(LIBS)
|
||||
@echo 'Finished building target: $@'
|
||||
@echo ' '
|
||||
|
||||
# Other Targets
|
||||
clean:
|
||||
-$(RM) $(CC_DEPS)$(C++_DEPS)$(OBJS)$(C_UPPER_DEPS)$(CXX_DEPS)$(ARCHIVES)$(C_DEPS)$(CPP_DEPS) libZLMediaKit.a
|
||||
-@echo ' '
|
||||
|
||||
.PHONY: all clean dependents
|
||||
.SECONDARY:
|
||||
|
||||
-include ../makefile.targets
|
8
ARM/objects.mk
Normal file
8
ARM/objects.mk
Normal file
@ -0,0 +1,8 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
USER_OBJS :=
|
||||
|
||||
LIBS :=
|
||||
|
38
ARM/sources.mk
Normal file
38
ARM/sources.mk
Normal file
@ -0,0 +1,38 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
C_UPPER_SRCS :=
|
||||
CXX_SRCS :=
|
||||
C++_SRCS :=
|
||||
OBJ_SRCS :=
|
||||
CC_SRCS :=
|
||||
ASM_SRCS :=
|
||||
C_SRCS :=
|
||||
CPP_SRCS :=
|
||||
O_SRCS :=
|
||||
S_UPPER_SRCS :=
|
||||
CC_DEPS :=
|
||||
C++_DEPS :=
|
||||
OBJS :=
|
||||
C_UPPER_DEPS :=
|
||||
CXX_DEPS :=
|
||||
ARCHIVES :=
|
||||
C_DEPS :=
|
||||
CPP_DEPS :=
|
||||
|
||||
# Every subdirectory with source files must be described here
|
||||
SUBDIRS := \
|
||||
src/Codec \
|
||||
src/Device \
|
||||
src/H264 \
|
||||
src/Http \
|
||||
src/MedaiFile/CRC \
|
||||
src/MedaiFile \
|
||||
src/Player \
|
||||
src/RTP \
|
||||
src/Rtmp \
|
||||
src/Rtsp \
|
||||
src/Shell \
|
||||
src \
|
||||
|
27
ARM/src/Codec/subdir.mk
Normal file
27
ARM/src/Codec/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Codec/AACEncoder.cpp \
|
||||
../src/Codec/H264Encoder.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Codec/AACEncoder.o \
|
||||
./src/Codec/H264Encoder.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Codec/AACEncoder.d \
|
||||
./src/Codec/H264Encoder.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Codec/%.o: ../src/Codec/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
30
ARM/src/Device/subdir.mk
Normal file
30
ARM/src/Device/subdir.mk
Normal file
@ -0,0 +1,30 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Device/Device.cpp \
|
||||
../src/Device/PlayerProxy.cpp \
|
||||
../src/Device/base64.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Device/Device.o \
|
||||
./src/Device/PlayerProxy.o \
|
||||
./src/Device/base64.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Device/Device.d \
|
||||
./src/Device/PlayerProxy.d \
|
||||
./src/Device/base64.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Device/%.o: ../src/Device/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
50
ARM/src/H264/subdir.mk
Normal file
50
ARM/src/H264/subdir.mk
Normal file
@ -0,0 +1,50 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
C_SRCS += \
|
||||
../src/H264/SPSParser.c
|
||||
|
||||
CPP_SRCS += \
|
||||
../src/H264/H264Parser.cpp \
|
||||
../src/H264/h264_bit_reader.cpp \
|
||||
../src/H264/h264_parser.cpp \
|
||||
../src/H264/h264_poc.cpp \
|
||||
../src/H264/ranges.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/H264/H264Parser.o \
|
||||
./src/H264/SPSParser.o \
|
||||
./src/H264/h264_bit_reader.o \
|
||||
./src/H264/h264_parser.o \
|
||||
./src/H264/h264_poc.o \
|
||||
./src/H264/ranges.o
|
||||
|
||||
C_DEPS += \
|
||||
./src/H264/SPSParser.d
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/H264/H264Parser.d \
|
||||
./src/H264/h264_bit_reader.d \
|
||||
./src/H264/h264_parser.d \
|
||||
./src/H264/h264_poc.d \
|
||||
./src/H264/ranges.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/H264/%.o: ../src/H264/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
src/H264/%.o: ../src/H264/%.c
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross GCC Compiler'
|
||||
arm-linux-gnueabi-gcc -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
27
ARM/src/Http/subdir.mk
Normal file
27
ARM/src/Http/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Http/HttpSession.cpp \
|
||||
../src/Http/strCoding.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Http/HttpSession.o \
|
||||
./src/Http/strCoding.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Http/HttpSession.d \
|
||||
./src/Http/strCoding.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Http/%.o: ../src/Http/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
24
ARM/src/MedaiFile/CRC/subdir.mk
Normal file
24
ARM/src/MedaiFile/CRC/subdir.mk
Normal file
@ -0,0 +1,24 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/MedaiFile/CRC/crc32.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/MedaiFile/CRC/crc32.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/MedaiFile/CRC/crc32.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/MedaiFile/CRC/%.o: ../src/MedaiFile/CRC/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
36
ARM/src/MedaiFile/subdir.mk
Normal file
36
ARM/src/MedaiFile/subdir.mk
Normal file
@ -0,0 +1,36 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/MedaiFile/HLSMaker.cpp \
|
||||
../src/MedaiFile/MediaReader.cpp \
|
||||
../src/MedaiFile/MediaRecorder.cpp \
|
||||
../src/MedaiFile/Mp4Maker.cpp \
|
||||
../src/MedaiFile/TSMaker.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/MedaiFile/HLSMaker.o \
|
||||
./src/MedaiFile/MediaReader.o \
|
||||
./src/MedaiFile/MediaRecorder.o \
|
||||
./src/MedaiFile/Mp4Maker.o \
|
||||
./src/MedaiFile/TSMaker.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/MedaiFile/HLSMaker.d \
|
||||
./src/MedaiFile/MediaReader.d \
|
||||
./src/MedaiFile/MediaRecorder.d \
|
||||
./src/MedaiFile/Mp4Maker.d \
|
||||
./src/MedaiFile/TSMaker.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/MedaiFile/%.o: ../src/MedaiFile/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
30
ARM/src/Player/subdir.mk
Normal file
30
ARM/src/Player/subdir.mk
Normal file
@ -0,0 +1,30 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Player/MediaPlayer.cpp \
|
||||
../src/Player/Player.cpp \
|
||||
../src/Player/PlayerBase.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Player/MediaPlayer.o \
|
||||
./src/Player/Player.o \
|
||||
./src/Player/PlayerBase.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Player/MediaPlayer.d \
|
||||
./src/Player/Player.d \
|
||||
./src/Player/PlayerBase.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Player/%.o: ../src/Player/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
27
ARM/src/RTP/subdir.mk
Normal file
27
ARM/src/RTP/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/RTP/RtpMakerAAC.cpp \
|
||||
../src/RTP/RtpMakerH264.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/RTP/RtpMakerAAC.o \
|
||||
./src/RTP/RtpMakerH264.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/RTP/RtpMakerAAC.d \
|
||||
./src/RTP/RtpMakerH264.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/RTP/%.o: ../src/RTP/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
51
ARM/src/Rtmp/subdir.mk
Normal file
51
ARM/src/Rtmp/subdir.mk
Normal file
@ -0,0 +1,51 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Rtmp/RtmpMediaSource.cpp \
|
||||
../src/Rtmp/RtmpParser.cpp \
|
||||
../src/Rtmp/RtmpPlayer.cpp \
|
||||
../src/Rtmp/RtmpPlayerImp.cpp \
|
||||
../src/Rtmp/RtmpProtocol.cpp \
|
||||
../src/Rtmp/RtmpPusher.cpp \
|
||||
../src/Rtmp/RtmpSession.cpp \
|
||||
../src/Rtmp/RtmpToRtspMediaSource.cpp \
|
||||
../src/Rtmp/amf.cpp \
|
||||
../src/Rtmp/utils.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Rtmp/RtmpMediaSource.o \
|
||||
./src/Rtmp/RtmpParser.o \
|
||||
./src/Rtmp/RtmpPlayer.o \
|
||||
./src/Rtmp/RtmpPlayerImp.o \
|
||||
./src/Rtmp/RtmpProtocol.o \
|
||||
./src/Rtmp/RtmpPusher.o \
|
||||
./src/Rtmp/RtmpSession.o \
|
||||
./src/Rtmp/RtmpToRtspMediaSource.o \
|
||||
./src/Rtmp/amf.o \
|
||||
./src/Rtmp/utils.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Rtmp/RtmpMediaSource.d \
|
||||
./src/Rtmp/RtmpParser.d \
|
||||
./src/Rtmp/RtmpPlayer.d \
|
||||
./src/Rtmp/RtmpPlayerImp.d \
|
||||
./src/Rtmp/RtmpProtocol.d \
|
||||
./src/Rtmp/RtmpPusher.d \
|
||||
./src/Rtmp/RtmpSession.d \
|
||||
./src/Rtmp/RtmpToRtspMediaSource.d \
|
||||
./src/Rtmp/amf.d \
|
||||
./src/Rtmp/utils.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Rtmp/%.o: ../src/Rtmp/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
48
ARM/src/Rtsp/subdir.mk
Normal file
48
ARM/src/Rtsp/subdir.mk
Normal file
@ -0,0 +1,48 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Rtsp/RtpBroadCaster.cpp \
|
||||
../src/Rtsp/RtpParser.cpp \
|
||||
../src/Rtsp/Rtsp.cpp \
|
||||
../src/Rtsp/RtspMediaSource.cpp \
|
||||
../src/Rtsp/RtspPlayer.cpp \
|
||||
../src/Rtsp/RtspPlayerImp.cpp \
|
||||
../src/Rtsp/RtspSession.cpp \
|
||||
../src/Rtsp/RtspToRtmpMediaSource.cpp \
|
||||
../src/Rtsp/UDPServer.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Rtsp/RtpBroadCaster.o \
|
||||
./src/Rtsp/RtpParser.o \
|
||||
./src/Rtsp/Rtsp.o \
|
||||
./src/Rtsp/RtspMediaSource.o \
|
||||
./src/Rtsp/RtspPlayer.o \
|
||||
./src/Rtsp/RtspPlayerImp.o \
|
||||
./src/Rtsp/RtspSession.o \
|
||||
./src/Rtsp/RtspToRtmpMediaSource.o \
|
||||
./src/Rtsp/UDPServer.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Rtsp/RtpBroadCaster.d \
|
||||
./src/Rtsp/RtpParser.d \
|
||||
./src/Rtsp/Rtsp.d \
|
||||
./src/Rtsp/RtspMediaSource.d \
|
||||
./src/Rtsp/RtspPlayer.d \
|
||||
./src/Rtsp/RtspPlayerImp.d \
|
||||
./src/Rtsp/RtspSession.d \
|
||||
./src/Rtsp/RtspToRtmpMediaSource.d \
|
||||
./src/Rtsp/UDPServer.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Rtsp/%.o: ../src/Rtsp/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
27
ARM/src/Shell/subdir.mk
Normal file
27
ARM/src/Shell/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Shell/CMD.cpp \
|
||||
../src/Shell/ShellSession.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Shell/CMD.o \
|
||||
./src/Shell/ShellSession.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Shell/CMD.d \
|
||||
./src/Shell/ShellSession.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Shell/%.o: ../src/Shell/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
24
ARM/src/subdir.mk
Normal file
24
ARM/src/subdir.mk
Normal file
@ -0,0 +1,24 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/config.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/config.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/config.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/%.o: ../src/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
arm-linux-gnueabi-g++ -std=c++1y -I/home/xzl/soft -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
70
X64/makefile
Normal file
70
X64/makefile
Normal file
@ -0,0 +1,70 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
-include ../makefile.init
|
||||
|
||||
RM := rm -rf
|
||||
|
||||
# All of the sources participating in the build are defined here
|
||||
-include sources.mk
|
||||
-include src/Shell/subdir.mk
|
||||
-include src/Rtsp/subdir.mk
|
||||
-include src/Rtmp/subdir.mk
|
||||
-include src/RTP/subdir.mk
|
||||
-include src/Player/subdir.mk
|
||||
-include src/MedaiFile/CRC/subdir.mk
|
||||
-include src/MedaiFile/subdir.mk
|
||||
-include src/Http/subdir.mk
|
||||
-include src/H264/subdir.mk
|
||||
-include src/Device/subdir.mk
|
||||
-include src/Codec/subdir.mk
|
||||
-include src/subdir.mk
|
||||
-include subdir.mk
|
||||
-include objects.mk
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(strip $(CC_DEPS)),)
|
||||
-include $(CC_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(C++_DEPS)),)
|
||||
-include $(C++_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(C_UPPER_DEPS)),)
|
||||
-include $(C_UPPER_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(CXX_DEPS)),)
|
||||
-include $(CXX_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(C_DEPS)),)
|
||||
-include $(C_DEPS)
|
||||
endif
|
||||
ifneq ($(strip $(CPP_DEPS)),)
|
||||
-include $(CPP_DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
-include ../makefile.defs
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
|
||||
# All Target
|
||||
all: libZLMediaKit.so
|
||||
|
||||
# Tool invocations
|
||||
libZLMediaKit.so: $(OBJS) $(USER_OBJS)
|
||||
@echo 'Building target: $@'
|
||||
@echo 'Invoking: Cross G++ Linker'
|
||||
g++ -shared -o "libZLMediaKit.so" $(OBJS) $(USER_OBJS) $(LIBS)
|
||||
@echo 'Finished building target: $@'
|
||||
@echo ' '
|
||||
|
||||
# Other Targets
|
||||
clean:
|
||||
-$(RM) $(LIBRARIES)$(CC_DEPS)$(C++_DEPS)$(OBJS)$(C_UPPER_DEPS)$(CXX_DEPS)$(C_DEPS)$(CPP_DEPS) libZLMediaKit.so
|
||||
-@echo ' '
|
||||
|
||||
.PHONY: all clean dependents
|
||||
.SECONDARY:
|
||||
|
||||
-include ../makefile.targets
|
8
X64/objects.mk
Normal file
8
X64/objects.mk
Normal file
@ -0,0 +1,8 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
USER_OBJS :=
|
||||
|
||||
LIBS :=
|
||||
|
38
X64/sources.mk
Normal file
38
X64/sources.mk
Normal file
@ -0,0 +1,38 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
C_UPPER_SRCS :=
|
||||
CXX_SRCS :=
|
||||
C++_SRCS :=
|
||||
OBJ_SRCS :=
|
||||
CC_SRCS :=
|
||||
ASM_SRCS :=
|
||||
C_SRCS :=
|
||||
CPP_SRCS :=
|
||||
O_SRCS :=
|
||||
S_UPPER_SRCS :=
|
||||
LIBRARIES :=
|
||||
CC_DEPS :=
|
||||
C++_DEPS :=
|
||||
OBJS :=
|
||||
C_UPPER_DEPS :=
|
||||
CXX_DEPS :=
|
||||
C_DEPS :=
|
||||
CPP_DEPS :=
|
||||
|
||||
# Every subdirectory with source files must be described here
|
||||
SUBDIRS := \
|
||||
src/Codec \
|
||||
src/Device \
|
||||
src/H264 \
|
||||
src/Http \
|
||||
src/MedaiFile/CRC \
|
||||
src/MedaiFile \
|
||||
src/Player \
|
||||
src/RTP \
|
||||
src/Rtmp \
|
||||
src/Rtsp \
|
||||
src/Shell \
|
||||
src \
|
||||
|
27
X64/src/Codec/subdir.mk
Normal file
27
X64/src/Codec/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Codec/AACEncoder.cpp \
|
||||
../src/Codec/H264Encoder.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Codec/AACEncoder.o \
|
||||
./src/Codec/H264Encoder.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Codec/AACEncoder.d \
|
||||
./src/Codec/H264Encoder.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Codec/%.o: ../src/Codec/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
30
X64/src/Device/subdir.mk
Normal file
30
X64/src/Device/subdir.mk
Normal file
@ -0,0 +1,30 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Device/Device.cpp \
|
||||
../src/Device/PlayerProxy.cpp \
|
||||
../src/Device/base64.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Device/Device.o \
|
||||
./src/Device/PlayerProxy.o \
|
||||
./src/Device/base64.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Device/Device.d \
|
||||
./src/Device/PlayerProxy.d \
|
||||
./src/Device/base64.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Device/%.o: ../src/Device/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
50
X64/src/H264/subdir.mk
Normal file
50
X64/src/H264/subdir.mk
Normal file
@ -0,0 +1,50 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
C_SRCS += \
|
||||
../src/H264/SPSParser.c
|
||||
|
||||
CPP_SRCS += \
|
||||
../src/H264/H264Parser.cpp \
|
||||
../src/H264/h264_bit_reader.cpp \
|
||||
../src/H264/h264_parser.cpp \
|
||||
../src/H264/h264_poc.cpp \
|
||||
../src/H264/ranges.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/H264/H264Parser.o \
|
||||
./src/H264/SPSParser.o \
|
||||
./src/H264/h264_bit_reader.o \
|
||||
./src/H264/h264_parser.o \
|
||||
./src/H264/h264_poc.o \
|
||||
./src/H264/ranges.o
|
||||
|
||||
C_DEPS += \
|
||||
./src/H264/SPSParser.d
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/H264/H264Parser.d \
|
||||
./src/H264/h264_bit_reader.d \
|
||||
./src/H264/h264_parser.d \
|
||||
./src/H264/h264_poc.d \
|
||||
./src/H264/ranges.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/H264/%.o: ../src/H264/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
src/H264/%.o: ../src/H264/%.c
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross GCC Compiler'
|
||||
gcc -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
27
X64/src/Http/subdir.mk
Normal file
27
X64/src/Http/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Http/HttpSession.cpp \
|
||||
../src/Http/strCoding.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Http/HttpSession.o \
|
||||
./src/Http/strCoding.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Http/HttpSession.d \
|
||||
./src/Http/strCoding.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Http/%.o: ../src/Http/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
24
X64/src/MedaiFile/CRC/subdir.mk
Normal file
24
X64/src/MedaiFile/CRC/subdir.mk
Normal file
@ -0,0 +1,24 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/MedaiFile/CRC/crc32.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/MedaiFile/CRC/crc32.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/MedaiFile/CRC/crc32.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/MedaiFile/CRC/%.o: ../src/MedaiFile/CRC/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
36
X64/src/MedaiFile/subdir.mk
Normal file
36
X64/src/MedaiFile/subdir.mk
Normal file
@ -0,0 +1,36 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/MedaiFile/HLSMaker.cpp \
|
||||
../src/MedaiFile/MediaReader.cpp \
|
||||
../src/MedaiFile/MediaRecorder.cpp \
|
||||
../src/MedaiFile/Mp4Maker.cpp \
|
||||
../src/MedaiFile/TSMaker.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/MedaiFile/HLSMaker.o \
|
||||
./src/MedaiFile/MediaReader.o \
|
||||
./src/MedaiFile/MediaRecorder.o \
|
||||
./src/MedaiFile/Mp4Maker.o \
|
||||
./src/MedaiFile/TSMaker.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/MedaiFile/HLSMaker.d \
|
||||
./src/MedaiFile/MediaReader.d \
|
||||
./src/MedaiFile/MediaRecorder.d \
|
||||
./src/MedaiFile/Mp4Maker.d \
|
||||
./src/MedaiFile/TSMaker.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/MedaiFile/%.o: ../src/MedaiFile/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
30
X64/src/Player/subdir.mk
Normal file
30
X64/src/Player/subdir.mk
Normal file
@ -0,0 +1,30 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Player/MediaPlayer.cpp \
|
||||
../src/Player/Player.cpp \
|
||||
../src/Player/PlayerBase.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Player/MediaPlayer.o \
|
||||
./src/Player/Player.o \
|
||||
./src/Player/PlayerBase.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Player/MediaPlayer.d \
|
||||
./src/Player/Player.d \
|
||||
./src/Player/PlayerBase.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Player/%.o: ../src/Player/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
27
X64/src/RTP/subdir.mk
Normal file
27
X64/src/RTP/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/RTP/RtpMakerAAC.cpp \
|
||||
../src/RTP/RtpMakerH264.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/RTP/RtpMakerAAC.o \
|
||||
./src/RTP/RtpMakerH264.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/RTP/RtpMakerAAC.d \
|
||||
./src/RTP/RtpMakerH264.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/RTP/%.o: ../src/RTP/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
51
X64/src/Rtmp/subdir.mk
Normal file
51
X64/src/Rtmp/subdir.mk
Normal file
@ -0,0 +1,51 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Rtmp/RtmpMediaSource.cpp \
|
||||
../src/Rtmp/RtmpParser.cpp \
|
||||
../src/Rtmp/RtmpPlayer.cpp \
|
||||
../src/Rtmp/RtmpPlayerImp.cpp \
|
||||
../src/Rtmp/RtmpProtocol.cpp \
|
||||
../src/Rtmp/RtmpPusher.cpp \
|
||||
../src/Rtmp/RtmpSession.cpp \
|
||||
../src/Rtmp/RtmpToRtspMediaSource.cpp \
|
||||
../src/Rtmp/amf.cpp \
|
||||
../src/Rtmp/utils.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Rtmp/RtmpMediaSource.o \
|
||||
./src/Rtmp/RtmpParser.o \
|
||||
./src/Rtmp/RtmpPlayer.o \
|
||||
./src/Rtmp/RtmpPlayerImp.o \
|
||||
./src/Rtmp/RtmpProtocol.o \
|
||||
./src/Rtmp/RtmpPusher.o \
|
||||
./src/Rtmp/RtmpSession.o \
|
||||
./src/Rtmp/RtmpToRtspMediaSource.o \
|
||||
./src/Rtmp/amf.o \
|
||||
./src/Rtmp/utils.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Rtmp/RtmpMediaSource.d \
|
||||
./src/Rtmp/RtmpParser.d \
|
||||
./src/Rtmp/RtmpPlayer.d \
|
||||
./src/Rtmp/RtmpPlayerImp.d \
|
||||
./src/Rtmp/RtmpProtocol.d \
|
||||
./src/Rtmp/RtmpPusher.d \
|
||||
./src/Rtmp/RtmpSession.d \
|
||||
./src/Rtmp/RtmpToRtspMediaSource.d \
|
||||
./src/Rtmp/amf.d \
|
||||
./src/Rtmp/utils.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Rtmp/%.o: ../src/Rtmp/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
48
X64/src/Rtsp/subdir.mk
Normal file
48
X64/src/Rtsp/subdir.mk
Normal file
@ -0,0 +1,48 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Rtsp/RtpBroadCaster.cpp \
|
||||
../src/Rtsp/RtpParser.cpp \
|
||||
../src/Rtsp/Rtsp.cpp \
|
||||
../src/Rtsp/RtspMediaSource.cpp \
|
||||
../src/Rtsp/RtspPlayer.cpp \
|
||||
../src/Rtsp/RtspPlayerImp.cpp \
|
||||
../src/Rtsp/RtspSession.cpp \
|
||||
../src/Rtsp/RtspToRtmpMediaSource.cpp \
|
||||
../src/Rtsp/UDPServer.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Rtsp/RtpBroadCaster.o \
|
||||
./src/Rtsp/RtpParser.o \
|
||||
./src/Rtsp/Rtsp.o \
|
||||
./src/Rtsp/RtspMediaSource.o \
|
||||
./src/Rtsp/RtspPlayer.o \
|
||||
./src/Rtsp/RtspPlayerImp.o \
|
||||
./src/Rtsp/RtspSession.o \
|
||||
./src/Rtsp/RtspToRtmpMediaSource.o \
|
||||
./src/Rtsp/UDPServer.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Rtsp/RtpBroadCaster.d \
|
||||
./src/Rtsp/RtpParser.d \
|
||||
./src/Rtsp/Rtsp.d \
|
||||
./src/Rtsp/RtspMediaSource.d \
|
||||
./src/Rtsp/RtspPlayer.d \
|
||||
./src/Rtsp/RtspPlayerImp.d \
|
||||
./src/Rtsp/RtspSession.d \
|
||||
./src/Rtsp/RtspToRtmpMediaSource.d \
|
||||
./src/Rtsp/UDPServer.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Rtsp/%.o: ../src/Rtsp/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
27
X64/src/Shell/subdir.mk
Normal file
27
X64/src/Shell/subdir.mk
Normal file
@ -0,0 +1,27 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/Shell/CMD.cpp \
|
||||
../src/Shell/ShellSession.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/Shell/CMD.o \
|
||||
./src/Shell/ShellSession.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/Shell/CMD.d \
|
||||
./src/Shell/ShellSession.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/Shell/%.o: ../src/Shell/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
24
X64/src/subdir.mk
Normal file
24
X64/src/subdir.mk
Normal file
@ -0,0 +1,24 @@
|
||||
################################################################################
|
||||
# Automatically-generated file. Do not edit!
|
||||
################################################################################
|
||||
|
||||
# Add inputs and outputs from these tool invocations to the build variables
|
||||
CPP_SRCS += \
|
||||
../src/config.cpp
|
||||
|
||||
OBJS += \
|
||||
./src/config.o
|
||||
|
||||
CPP_DEPS += \
|
||||
./src/config.d
|
||||
|
||||
|
||||
# Each subdirectory must supply rules for building sources it contributes
|
||||
src/%.o: ../src/%.cpp
|
||||
@echo 'Building file: $<'
|
||||
@echo 'Invoking: Cross G++ Compiler'
|
||||
g++ -std=c++1y -DENABLE_FAAC -DENABLE_RTSP2RTMP -DENABLE_RTMP2RTSP -DENABLE_MEDIAFILE -DENABLE_X264 -I"/Users/xzl/git/ZLMediaKit/src" -I../../ZLToolKit/src -O3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||
@echo 'Finished building: $<'
|
||||
@echo ' '
|
||||
|
||||
|
110
src/Codec/AACEncoder.cpp
Normal file
110
src/Codec/AACEncoder.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* AACEncoder.cpp
|
||||
*
|
||||
* Created on: 2016年8月11日
|
||||
* Author: xzl
|
||||
*/
|
||||
#ifdef ENABLE_FAAC
|
||||
#include "AACEncoder.h"
|
||||
#include "Util/logger.h"
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <faac.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Codec {
|
||||
|
||||
AACEncoder::AACEncoder() {
|
||||
|
||||
}
|
||||
|
||||
AACEncoder::~AACEncoder() {
|
||||
if (m_hEncoder != nullptr) {
|
||||
faacEncClose(m_hEncoder);
|
||||
m_hEncoder = nullptr;
|
||||
}
|
||||
if (m_pucAacBuf != nullptr) {
|
||||
delete[] m_pucAacBuf;
|
||||
m_pucAacBuf = nullptr;
|
||||
}
|
||||
if (m_pucPcmBuf != nullptr) {
|
||||
delete[] m_pucPcmBuf;
|
||||
m_pucPcmBuf = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AACEncoder::init(int iSampleRate, int iChannels, int iSampleBit) {
|
||||
if (iSampleBit != 16) {
|
||||
return false;
|
||||
}
|
||||
// (1) Open FAAC engine
|
||||
m_hEncoder = faacEncOpen(iSampleRate, iChannels, &m_ulInputSamples,
|
||||
&m_ulMaxOutputBytes);
|
||||
if (m_hEncoder == NULL) {
|
||||
return false;
|
||||
}
|
||||
m_pucAacBuf = new unsigned char[m_ulMaxOutputBytes];
|
||||
m_ulMaxInputBytes = m_ulInputSamples * iSampleBit / 8;
|
||||
m_pucPcmBuf = new unsigned char[m_ulMaxInputBytes * 4];
|
||||
|
||||
// (2.1) Get current encoding configuration
|
||||
faacEncConfigurationPtr pConfiguration = faacEncGetCurrentConfiguration(m_hEncoder);
|
||||
if (pConfiguration == NULL) {
|
||||
faacEncClose(m_hEncoder);
|
||||
return false;
|
||||
}
|
||||
pConfiguration->aacObjectType =LOW;
|
||||
pConfiguration->mpegVersion = 4;
|
||||
pConfiguration->useTns = 1;
|
||||
pConfiguration->shortctl = SHORTCTL_NORMAL;
|
||||
pConfiguration->useLfe = 1;
|
||||
pConfiguration->allowMidside = 1;
|
||||
pConfiguration->bitRate = 0;
|
||||
pConfiguration->bandWidth = 0;
|
||||
pConfiguration->quantqual = 50;
|
||||
pConfiguration->outputFormat = 1;
|
||||
pConfiguration->inputFormat = FAAC_INPUT_16BIT;
|
||||
|
||||
// (2.2) Set encoding configuration
|
||||
if(!faacEncSetConfiguration(m_hEncoder, pConfiguration)){
|
||||
ErrorL << "faacEncSetConfiguration failed";
|
||||
faacEncClose(m_hEncoder);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int AACEncoder::inputData(char *pcPcmBufr, int iLen, unsigned char **ppucOutBuffer) {
|
||||
memcpy(m_pucPcmBuf + m_uiPcmLen, pcPcmBufr, iLen);
|
||||
m_uiPcmLen += iLen;
|
||||
if (m_uiPcmLen < m_ulMaxInputBytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nRet = faacEncEncode(m_hEncoder, (int32_t *) (m_pucPcmBuf), m_ulInputSamples, m_pucAacBuf, m_ulMaxOutputBytes);
|
||||
m_uiPcmLen -= m_ulMaxInputBytes;
|
||||
memmove(m_pucPcmBuf, m_pucPcmBuf + m_ulMaxInputBytes, m_uiPcmLen);
|
||||
*ppucOutBuffer = m_pucAacBuf;
|
||||
return nRet;
|
||||
}
|
||||
|
||||
} /* namespace Codec */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif //ENABLE_FAAC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
38
src/Codec/AACEncoder.h
Normal file
38
src/Codec/AACEncoder.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* AACEncoder.h
|
||||
*
|
||||
* Created on: 2016年8月11日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef CODEC_AACENCODER_H_
|
||||
#define CODEC_AACENCODER_H_
|
||||
|
||||
|
||||
namespace ZL {
|
||||
namespace Codec {
|
||||
|
||||
class AACEncoder {
|
||||
public:
|
||||
AACEncoder(void);
|
||||
virtual ~AACEncoder(void);
|
||||
bool init(int iSampleRate, int iAudioChannel, int iAudioSampleBit);
|
||||
int inputData(char *pcData, int iLen, unsigned char **ppucOutBuffer);
|
||||
|
||||
private:
|
||||
unsigned char *m_pucPcmBuf = nullptr;
|
||||
unsigned int m_uiPcmLen = 0;
|
||||
|
||||
unsigned char *m_pucAacBuf = nullptr;
|
||||
void *m_hEncoder = nullptr;
|
||||
|
||||
unsigned long m_ulInputSamples = 0;
|
||||
unsigned long m_ulMaxInputBytes = 0;
|
||||
unsigned long m_ulMaxOutputBytes = 0;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace Codec */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* CODEC_AACENCODER_H_ */
|
349
src/Codec/H264Encoder.cpp
Normal file
349
src/Codec/H264Encoder.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
* H264Encoder.cpp
|
||||
*
|
||||
* Created on: 2016年8月11日
|
||||
* Author: xzl
|
||||
*/
|
||||
#ifdef ENABLE_X264
|
||||
#include "H264Encoder.h"
|
||||
|
||||
#include "Util/TimeTicker.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Codec {
|
||||
|
||||
H264Encoder::H264Encoder() {
|
||||
|
||||
}
|
||||
|
||||
H264Encoder::~H264Encoder() {
|
||||
//* 清除图像区域
|
||||
if (m_pPicIn) {
|
||||
delete m_pPicIn;
|
||||
m_pPicIn = nullptr;
|
||||
}
|
||||
if (m_pPicOut) {
|
||||
delete m_pPicOut;
|
||||
m_pPicOut = nullptr;
|
||||
}
|
||||
|
||||
//* 关闭编码器句柄
|
||||
if (m_pX264Handle) {
|
||||
x264_encoder_close(m_pX264Handle);
|
||||
m_pX264Handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*typedef struct x264_param_t
|
||||
{
|
||||
CPU 标志位
|
||||
unsigned int cpu;
|
||||
int i_threads; 并行编码多帧
|
||||
int b_deterministic; 是否允许非确定性时线程优化
|
||||
int i_sync_lookahead; 线程超前缓冲
|
||||
|
||||
视频属性
|
||||
int i_width; 宽度
|
||||
int i_height; 高度
|
||||
int i_csp; 编码比特流的CSP,仅支持i420,色彩空间设置
|
||||
int i_level_idc; level值的设置
|
||||
int i_frame_total; 编码帧的总数, 默认 0
|
||||
Vui参数集视频可用性信息视频标准化选项
|
||||
struct
|
||||
{
|
||||
they will be reduced to be 0 < x <= 65535 and prime
|
||||
int i_sar_height;
|
||||
int i_sar_width; 设置长宽比
|
||||
|
||||
int i_overscan; 0=undef, 1=no overscan, 2=overscan 过扫描线,默认"undef"(不设置),可选项:show(观看)/crop(去除)
|
||||
|
||||
见以下的值h264附件E
|
||||
Int i_vidformat; 视频格式,默认"undef",component/pal/ntsc/secam/mac/undef
|
||||
int b_fullrange; Specify full range samples setting,默认"off",可选项:off/on
|
||||
int i_colorprim; 原始色度格式,默认"undef",可选项:undef/bt709/bt470m/bt470bg,smpte170m/smpte240m/film
|
||||
int i_transfer; 转换方式,默认"undef",可选项:undef/bt709/bt470m/bt470bg/linear,log100/log316/smpte170m/smpte240m
|
||||
int i_colmatrix; 色度矩阵设置,默认"undef",undef/bt709/fcc/bt470bg,smpte170m/smpte240m/GBR/YCgCo
|
||||
int i_chroma_loc; both top & bottom色度样本指定,范围0~5,默认0
|
||||
} vui;
|
||||
|
||||
int i_fps_num;
|
||||
int i_fps_den;
|
||||
这两个参数是由fps帧率确定的,赋值的过程见下:
|
||||
{ float fps;
|
||||
if( sscanf( value, "%d/%d", &p->i_fps_num, &p->i_fps_den ) == 2 )
|
||||
;
|
||||
else if( sscanf( value, "%f", &fps ) )
|
||||
{
|
||||
p->i_fps_num = (int)(fps * 1000 + .5);
|
||||
p->i_fps_den = 1000;
|
||||
}
|
||||
else
|
||||
b_error = 1;
|
||||
}
|
||||
Value的值就是fps。
|
||||
|
||||
流参数
|
||||
int i_frame_reference; 参考帧最大数目
|
||||
int i_keyint_max; 在此间隔设置IDR关键帧
|
||||
int i_keyint_min; 场景切换少于次值编码位I, 而不是 IDR.
|
||||
int i_scenecut_threshold; 如何积极地插入额外的I帧
|
||||
int i_bframe; 两个相关图像间P帧的数目
|
||||
int i_bframe_adaptive; 自适应B帧判定
|
||||
int i_bframe_bias; 控制插入B帧判定,范围-100~+100,越高越容易插入B帧,默认0
|
||||
int b_bframe_pyramid; 允许部分B为参考帧
|
||||
去块滤波器需要的参数
|
||||
int b_deblocking_filter;
|
||||
int i_deblocking_filter_alphac0; [-6, 6] -6 light filter, 6 strong
|
||||
int i_deblocking_filter_beta; [-6, 6] idem
|
||||
熵编码
|
||||
int b_cabac;
|
||||
int i_cabac_init_idc;
|
||||
|
||||
int b_interlaced; 隔行扫描
|
||||
量化
|
||||
int i_cqm_preset; 自定义量化矩阵(CQM),初始化量化模式为flat
|
||||
char *psz_cqm_file; JM format读取JM格式的外部量化矩阵文件,自动忽略其他—cqm 选项
|
||||
uint8_t cqm_4iy[16]; used only if i_cqm_preset == X264_CQM_CUSTOM
|
||||
uint8_t cqm_4ic[16];
|
||||
uint8_t cqm_4py[16];
|
||||
uint8_t cqm_4pc[16];
|
||||
uint8_t cqm_8iy[64];
|
||||
uint8_t cqm_8py[64];
|
||||
|
||||
日志
|
||||
void (*pf_log)( void *, int i_level, const char *psz, va_list );
|
||||
void *p_log_private;
|
||||
int i_log_level;
|
||||
int b_visualize;
|
||||
char *psz_dump_yuv; 重建帧的名字
|
||||
|
||||
编码分析参数
|
||||
struct
|
||||
{
|
||||
unsigned int intra; 帧间分区
|
||||
unsigned int inter; 帧内分区
|
||||
|
||||
int b_transform_8x8; 帧间分区
|
||||
int b_weighted_bipred; 为b帧隐式加权
|
||||
int i_direct_mv_pred; 时间空间队运动预测
|
||||
int i_chroma_qp_offset; 色度量化步长偏移量
|
||||
|
||||
int i_me_method; 运动估计算法 (X264_ME_*)
|
||||
int i_me_range; 整像素运动估计搜索范围 (from predicted mv)
|
||||
int i_mv_range; 运动矢量最大长度(in pixels). -1 = auto, based on level
|
||||
int i_mv_range_thread; 线程之间的最小空间. -1 = auto, based on number of threads.
|
||||
int i_subpel_refine; 亚像素运动估计质量
|
||||
int b_chroma_me; 亚像素色度运动估计和P帧的模式选择
|
||||
int b_mixed_references; 允许每个宏块的分区在P帧有它自己的参考号
|
||||
int i_trellis; Trellis量化,对每个8x8的块寻找合适的量化值,需要CABAC,默认0 0:关闭1:只在最后编码时使用2:一直使用
|
||||
int b_fast_pskip; 快速P帧跳过检测
|
||||
int b_dct_decimate; 在P-frames转换参数域
|
||||
int i_noise_reduction; 自适应伪盲区
|
||||
float f_psy_rd; Psy RD strength
|
||||
float f_psy_trellis; Psy trellis strength
|
||||
int b_psy; Toggle all psy optimizations
|
||||
|
||||
,亮度量化中使用的无效区大小
|
||||
int i_luma_deadzone[2]; {帧间, 帧内}
|
||||
|
||||
int b_psnr; 计算和打印PSNR信息
|
||||
int b_ssim; 计算和打印SSIM信息
|
||||
} analyse;
|
||||
|
||||
码率控制参数
|
||||
struct
|
||||
{
|
||||
int i_rc_method; X264_RC_*
|
||||
|
||||
int i_qp_constant; 0-51
|
||||
int i_qp_min; 允许的最小量化值
|
||||
int i_qp_max; 允许的最大量化值
|
||||
int i_qp_step; 帧间最大量化步长
|
||||
|
||||
int i_bitrate; 设置平均码率
|
||||
float f_rf_constant; 1pass VBR, nominal QP
|
||||
float f_rate_tolerance;
|
||||
int i_vbv_max_bitrate; 平均码率模式下,最大瞬时码率,默认0(与-B设置相同)
|
||||
int i_vbv_buffer_size; 码率控制缓冲区的大小,单位kbit,默认0
|
||||
float f_vbv_buffer_init; <=1: fraction of buffer_size. >1: kbit码率控制缓冲区数据保留的最大数据量与缓冲区大小之比,范围0~1.0,默认0.9
|
||||
float f_ip_factor;
|
||||
float f_pb_factor;
|
||||
|
||||
int i_aq_mode; psy adaptive QP. (X264_AQ_*)
|
||||
float f_aq_strength;
|
||||
int b_mb_tree; Macroblock-tree ratecontrol.
|
||||
int i_lookahead;
|
||||
|
||||
2pass 多次压缩码率控制
|
||||
int b_stat_write; Enable stat writing in psz_stat_out
|
||||
char *psz_stat_out;
|
||||
int b_stat_read; Read stat from psz_stat_in and use it
|
||||
char *psz_stat_in;
|
||||
|
||||
2pass params (same as ffmpeg ones)
|
||||
float f_qcompress; 0.0 => cbr, 1.0 => constant qp
|
||||
float f_qblur; 时间上模糊量化
|
||||
float f_complexity_blur; 时间上模糊复杂性
|
||||
x264_zone_t *zones; 码率控制覆盖
|
||||
int i_zones; number of zone_t's
|
||||
char *psz_zones; 指定区的另一种方法
|
||||
} rc;
|
||||
|
||||
Muxing parameters
|
||||
int b_aud; 生成访问单元分隔符
|
||||
int b_repeat_headers; 在每个关键帧前放置SPS/PPS
|
||||
int i_sps_id; SPS 和 PPS id 号
|
||||
|
||||
切片(像条)参数
|
||||
int i_slice_max_size; 每片字节的最大数,包括预计的NAL开销.
|
||||
int i_slice_max_mbs; 每片宏块的最大数,重写 i_slice_count
|
||||
int i_slice_count; 每帧的像条数目: 设置矩形像条.
|
||||
|
||||
Optional callback for freeing this x264_param_t when it is done being used.
|
||||
* Only used when the x264_param_t sits in memory for an indefinite period of time,
|
||||
* i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones.
|
||||
* Not used when x264_encoder_reconfig is called directly.
|
||||
void (*param_free)( void* );
|
||||
} x264_param_t;*/
|
||||
|
||||
bool H264Encoder::init(int iWidth, int iHeight, int iFps) {
|
||||
if (m_pX264Handle) {
|
||||
return true;
|
||||
}
|
||||
x264_param_t X264Param, *pX264Param = &X264Param;
|
||||
//* 配置参数
|
||||
//* 使用默认参数
|
||||
x264_param_default_preset(pX264Param, "ultrafast", "zerolatency");
|
||||
|
||||
//* cpuFlags
|
||||
pX264Param->i_threads = X264_SYNC_LOOKAHEAD_AUTO; //* 取空缓冲区继续使用不死锁的保证.
|
||||
//* video Properties
|
||||
pX264Param->i_width = iWidth; //* 宽度.
|
||||
pX264Param->i_height = iHeight; //* 高度
|
||||
pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.
|
||||
pX264Param->i_keyint_max = iFps * 3; //ffmpeg:gop_size 关键帧最大间隔
|
||||
pX264Param->i_keyint_min = iFps * 1; //ffmpeg:keyint_min 关键帧最小间隔
|
||||
//* Rate control Parameters
|
||||
pX264Param->rc.i_bitrate = 5000; //* 码率(比特率,单位Kbps)
|
||||
pX264Param->rc.i_qp_step = 1; //最大的在帧与帧之间进行切变的量化因子的变化量。ffmpeg:max_qdiff
|
||||
pX264Param->rc.i_qp_min = 10; //ffmpeg:qmin;最小的量化因子。取值范围1-51。建议在10-30之间。
|
||||
pX264Param->rc.i_qp_max = 41; //ffmpeg:qmax;最大的量化因子。取值范围1-51。建议在10-30之间。
|
||||
pX264Param->rc.f_qcompress = 0.6;//ffmpeg:qcompress 量化器压缩比率0-1.越小则比特率越区域固定,但是越高越使量化器参数越固定
|
||||
pX264Param->analyse.i_me_range = 16; //ffmpeg:me_range 运动侦测的半径
|
||||
pX264Param->i_frame_reference = 3; //ffmpeg:refsB和P帧向前预测参考的帧数。取值范围1-16。
|
||||
//该值不影响解码的速度,但是越大解码
|
||||
//所需的内存越大。这个值在一般情况下
|
||||
//越大效果越好,但是超过6以后效果就
|
||||
//不明显了。
|
||||
|
||||
pX264Param->analyse.i_trellis = 1; //ffmpeg:trellis
|
||||
//pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO 运动侦测的方式
|
||||
pX264Param->rc.f_qblur = 0.5; //ffmpeg:qblur
|
||||
|
||||
//* bitstream parameters
|
||||
/*open-GOP
|
||||
码流里面包含B帧的时候才会出现open-GOP。
|
||||
一个GOP里面的某一帧在解码时要依赖于前一个GOP的某些帧,
|
||||
这个GOP就称为open-GOP。
|
||||
有些解码器不能完全支持open-GOP码流,
|
||||
例如蓝光解码器,因此在x264里面open-GOP是默认关闭的。
|
||||
对于解码端,接收到的码流如果如下:I0 B0 B1 P0 B2 B3...这就是一个open-GOP码流(I帧后面紧跟B帧)。
|
||||
因此B0 B1的解码需要用到I0前面一个GOP的数据,B0 B1的dts是小于I0的。
|
||||
如果码流如下: I0 P0 B0 B1 P1 B2 B3...这就是一个close-GOP码流,
|
||||
I0后面所有帧的解码不依赖于I0前面的帧,I0后面所有帧的dts都比I0的大。
|
||||
如果码流是IDR0 B0 B1 P0 B2 B3...那个这个GOP是close-GOP,B0,B1虽然dst比IDR0小,
|
||||
但编解码端都刷新了参考缓冲,B0,B1参考不到前向GOP帧。
|
||||
对于编码端,如果编码帧类型决定如下: ...P0 B1 B2 P3 B4 B5 I6这就会输出open-Gop码流 (P0 P3 B1 B2 I6 B4 B5...),
|
||||
B4 B5的解码依赖P3。
|
||||
如果编码帧类型决定如下...P0 B1 B2 P3 B4 P5 I6这样就不会输出open-GOP码流(P0 P3 B1 B2 P5 B4 I6...)。
|
||||
两者区别在于I6前面的第5帧是设置为B帧还是P帧,
|
||||
如果一个GOP的最后一帧(上例中是第5帧)设置为B帧,
|
||||
这个码流就是open-GOP,设置为P帧就是close-GOP。
|
||||
由于B帧压缩性能好于P帧,因此open-GOP在编码性能上稍微优于close-GOP,
|
||||
但为了兼容性和少一些麻烦,还是把opne-GOP关闭的好。*/
|
||||
pX264Param->b_open_gop = 0;
|
||||
pX264Param->i_bframe = 0; //最大B帧数.
|
||||
pX264Param->i_bframe_pyramid = 0;
|
||||
pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
|
||||
//* Log
|
||||
pX264Param->i_log_level = X264_LOG_ERROR;
|
||||
|
||||
//* muxing parameters
|
||||
pX264Param->i_fps_den = 1; //* 帧率分母
|
||||
pX264Param->i_fps_num = iFps; //* 帧率分子
|
||||
pX264Param->i_timebase_den = pX264Param->i_fps_num;
|
||||
pX264Param->i_timebase_num = pX264Param->i_fps_den;
|
||||
|
||||
pX264Param->analyse.i_subpel_refine = 1; //这个参数控制在运动估算过程中质量和速度的权衡。Subq=5可以压缩>10%于subq=1。1-7
|
||||
pX264Param->analyse.b_fast_pskip = 1; //在P帧内执行早期快速跳跃探测。这个经常在没有任何损失的前提下提高了速度。
|
||||
|
||||
pX264Param->b_annexb = 1; //1前面为0x00000001,0为nal长度
|
||||
pX264Param->b_repeat_headers = 1; //关键帧前面是否放sps跟pps帧,0 否 1,放
|
||||
|
||||
//* 设置Profile.使用baseline
|
||||
x264_param_apply_profile(pX264Param, "high");
|
||||
|
||||
//* 打开编码器句柄,通过x264_encoder_parameters得到设置给X264
|
||||
//* 的参数.通过x264_encoder_reconfig更新X264的参数
|
||||
m_pX264Handle = x264_encoder_open(pX264Param);
|
||||
if (!m_pX264Handle) {
|
||||
return false;
|
||||
}
|
||||
m_pPicIn = new x264_picture_t;
|
||||
m_pPicOut = new x264_picture_t;
|
||||
x264_picture_init(m_pPicIn);
|
||||
x264_picture_init(m_pPicOut);
|
||||
m_pPicIn->img.i_csp = X264_CSP_I420;
|
||||
m_pPicIn->img.i_plane = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
int H264Encoder::inputData(char* apcYuv[3], int aiYuvLen[3], int64_t i64Pts, H264Frame** ppFrame) {
|
||||
//TimeTicker1(5);
|
||||
m_pPicIn->img.i_stride[0] = aiYuvLen[0];
|
||||
m_pPicIn->img.i_stride[1] = aiYuvLen[1];
|
||||
m_pPicIn->img.i_stride[2] = aiYuvLen[2];
|
||||
m_pPicIn->img.plane[0] = (uint8_t *) apcYuv[0];
|
||||
m_pPicIn->img.plane[1] = (uint8_t *) apcYuv[1];
|
||||
m_pPicIn->img.plane[2] = (uint8_t *) apcYuv[2];
|
||||
m_pPicIn->i_pts = i64Pts;
|
||||
int iNal;
|
||||
x264_nal_t* pNals;
|
||||
|
||||
int iResult = x264_encoder_encode(m_pX264Handle, &pNals, &iNal, m_pPicIn,
|
||||
m_pPicOut);
|
||||
if (iResult <= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < iNal; i++) {
|
||||
x264_nal_t pNal = pNals[i];
|
||||
m_aFrames[i].iType = pNal.i_type;
|
||||
m_aFrames[i].iLength = pNal.i_payload;
|
||||
m_aFrames[i].pucData = pNal.p_payload;
|
||||
}
|
||||
*ppFrame = m_aFrames;
|
||||
return iNal;
|
||||
}
|
||||
|
||||
} /* namespace Codec */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif //ENABLE_X264
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
48
src/Codec/H264Encoder.h
Normal file
48
src/Codec/H264Encoder.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* H264Encoder.h
|
||||
*
|
||||
* Created on: 2016年8月11日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef CODEC_H264ENCODER_H_
|
||||
#define CODEC_H264ENCODER_H_
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
|
||||
#include <x264.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#endif //__cplusplus
|
||||
|
||||
namespace ZL {
|
||||
namespace Codec {
|
||||
|
||||
class H264Encoder {
|
||||
public:
|
||||
typedef struct {
|
||||
int iType;
|
||||
int iLength;
|
||||
uint8_t *pucData;
|
||||
} H264Frame;
|
||||
|
||||
H264Encoder(void);
|
||||
virtual ~H264Encoder(void);
|
||||
bool init(int iWidth, int iHeight, int iFps);
|
||||
int inputData(char *apcYuv[3], int aiYuvLen[3], int64_t i64Pts, H264Frame **ppFrame);
|
||||
private:
|
||||
x264_t* m_pX264Handle = nullptr;
|
||||
x264_picture_t* m_pPicIn = nullptr;
|
||||
x264_picture_t* m_pPicOut = nullptr;
|
||||
H264Frame m_aFrames[10];
|
||||
};
|
||||
|
||||
} /* namespace Codec */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* CODEC_H264ENCODER_H_ */
|
256
src/Device/Device.cpp
Normal file
256
src/Device/Device.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Device.cpp
|
||||
*
|
||||
* Created on: 2016年8月10日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "Device.h"
|
||||
#include "Util/logger.h"
|
||||
#include <stdio.h>
|
||||
#include "Util/util.h"
|
||||
#include <cstdio>
|
||||
#include "base64.h"
|
||||
|
||||
#include "Util/TimeTicker.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace DEV {
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef ENABLE_RTSP2RTMP
|
||||
DevChannel::DevChannel(const char *strApp, const char *strId,float fDuration,bool bLiveStream ) :
|
||||
m_mediaSrc(new RtspToRtmpMediaSource(strApp,strId , bLiveStream)) {
|
||||
#else
|
||||
DevChannel::DevChannel(const char *strApp, const char *strId,float fDuration,bool bLiveStream ) :
|
||||
m_mediaSrc(new RtspToRtmpMediaSource(strApp,strId )) {
|
||||
#endif //ENABLE_RTSP2RTMP
|
||||
m_strSDP = "v=0\r\n";
|
||||
m_strSDP += "o=- 1383190487994921 1 IN IP4 0.0.0.0\r\n";
|
||||
m_strSDP += "s=RTSP Session, streamed by the ZL\r\n";
|
||||
m_strSDP += "i=ZL Live Stream\r\n";
|
||||
m_strSDP += "c=IN IP4 0.0.0.0\r\n";
|
||||
m_strSDP += "t=0 0\r\n";
|
||||
//直播,时间长度永远
|
||||
if(fDuration <= 0 || bLiveStream){
|
||||
m_strSDP += "a=range:npt=0-\r\n";
|
||||
}else{
|
||||
m_strSDP += StrPrinter <<"a=range:npt=0-" << fDuration << "\r\n" << endl;
|
||||
}
|
||||
m_strSDP += "a=control:*\r\n";
|
||||
}
|
||||
DevChannel::~DevChannel() {
|
||||
}
|
||||
|
||||
void DevChannel::inputYUV(char* apcYuv[3], int aiYuvLen[3], uint32_t uiStamp) {
|
||||
//TimeTicker1(50);
|
||||
#ifdef ENABLE_X264
|
||||
if (!m_pH264Enc) {
|
||||
m_pH264Enc.reset(new H264Encoder());
|
||||
if (!m_pH264Enc->init(m_video->iWidth, m_video->iHeight, m_video->iFrameRate)) {
|
||||
m_pH264Enc.reset();
|
||||
WarnL << "H264Encoder init failed!";
|
||||
}
|
||||
}
|
||||
if (m_pH264Enc) {
|
||||
H264Encoder::H264Frame *pOut;
|
||||
int iFrames = m_pH264Enc->inputData(apcYuv, aiYuvLen, uiStamp, &pOut);
|
||||
for (int i = 0; i < iFrames; i++) {
|
||||
inputH264((char *) pOut[i].pucData, pOut[i].iLength, uiStamp);
|
||||
}
|
||||
}
|
||||
#else
|
||||
ErrorL << "libx264 was not enabled!";
|
||||
#endif //ENABLE_X264
|
||||
}
|
||||
|
||||
void DevChannel::inputPCM(char* pcData, int iDataLen, uint32_t uiStamp) {
|
||||
#ifdef ENABLE_FAAC
|
||||
if (!m_pAacEnc) {
|
||||
m_pAacEnc.reset(new AACEncoder());
|
||||
if (!m_pAacEnc->init(m_audio->iSampleRate, m_audio->iChannel, m_audio->iSampleBit)) {
|
||||
m_pAacEnc.reset();
|
||||
WarnL << "AACEncoder init failed!";
|
||||
}
|
||||
}
|
||||
if (m_pAacEnc) {
|
||||
unsigned char *pucOut;
|
||||
int iRet = m_pAacEnc->inputData(pcData, iDataLen, &pucOut);
|
||||
if (iRet > 0) {
|
||||
inputAAC((char *) pucOut, iRet, uiStamp);
|
||||
}
|
||||
}
|
||||
#else
|
||||
ErrorL << "libfaac was not enabled!";
|
||||
#endif //ENABLE_FAAC
|
||||
}
|
||||
|
||||
void DevChannel::inputH264(char* pcData, int iDataLen, uint32_t uiStamp) {
|
||||
if (!m_pRtpMaker_h264) {
|
||||
uint32_t ui32Ssrc;
|
||||
memcpy(&ui32Ssrc, makeRandStr(4, false).data(), 4);
|
||||
auto lam = [this](const RtpPacket::Ptr &pkt, bool bKeyPos) {
|
||||
m_mediaSrc->onGetRTP(pkt,bKeyPos);
|
||||
};
|
||||
static uint32_t videoMtu = mINI::Instance()[Config::Rtp::kVideoMtuSize].as<uint32_t>();
|
||||
m_pRtpMaker_h264.reset(new RtpMaker_H264(lam, ui32Ssrc,videoMtu));
|
||||
}
|
||||
if (!m_bSdp_gotH264 && m_video) {
|
||||
makeSDP_264((unsigned char*) pcData, iDataLen);
|
||||
}
|
||||
int iOffset = 4;
|
||||
if (memcmp("\x00\x00\x01", pcData, 3) == 0) {
|
||||
iOffset = 3;
|
||||
}
|
||||
m_pRtpMaker_h264->makeRtp(pcData + iOffset, iDataLen - iOffset, uiStamp);
|
||||
}
|
||||
|
||||
void DevChannel::inputAAC(char* pcData, int iDataLen, uint32_t uiStamp) {
|
||||
if (!m_pRtpMaker_aac) {
|
||||
uint32_t ssrc;
|
||||
memcpy(&ssrc, makeRandStr(8, false).data() + 4, 4);
|
||||
auto lam = [this](const RtpPacket::Ptr &pkt, bool keyPos) {
|
||||
m_mediaSrc->onGetRTP(pkt,keyPos);
|
||||
};
|
||||
static uint32_t audioMtu = mINI::Instance()[Config::Rtp::kAudioMtuSize].as<uint32_t>();
|
||||
m_pRtpMaker_aac.reset(new RtpMaker_AAC(lam, ssrc, audioMtu,m_audio->iSampleRate));
|
||||
}
|
||||
if (!m_bSdp_gotAAC && m_audio) {
|
||||
makeSDP_AAC((unsigned char*) pcData, iDataLen);
|
||||
}
|
||||
m_pRtpMaker_aac->makeRtp(pcData + 7, iDataLen - 7, uiStamp);
|
||||
|
||||
}
|
||||
|
||||
inline void DevChannel::makeSDP_264(unsigned char *pcData, int iDataLen) {
|
||||
int offset = 4;
|
||||
if (memcmp("\x00\x00\x01", pcData, 3) == 0) {
|
||||
offset = 3;
|
||||
}
|
||||
switch (pcData[offset] & 0x1F) {
|
||||
case 7:/*SPS frame*/
|
||||
{
|
||||
if (m_uiSPSLen != 0) {
|
||||
break;
|
||||
}
|
||||
memcpy(m_aucSPS, pcData + offset, iDataLen - offset);
|
||||
m_uiSPSLen = iDataLen - offset;
|
||||
}
|
||||
break;
|
||||
case 8:/*PPS frame*/
|
||||
{
|
||||
if (m_uiPPSLen != 0) {
|
||||
break;
|
||||
}
|
||||
memcpy(m_aucPPS, pcData + offset, iDataLen - offset);
|
||||
m_uiPPSLen = iDataLen - offset;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!m_uiSPSLen || !m_uiPPSLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
char acTmp[256];
|
||||
int profile_level_id = 0;
|
||||
if (m_uiSPSLen >= 4) { // sanity check
|
||||
profile_level_id = (m_aucSPS[1] << 16) | (m_aucSPS[2] << 8) | m_aucSPS[3]; // profile_idc|constraint_setN_flag|level_idc
|
||||
}
|
||||
|
||||
//视频通道
|
||||
m_strSDP += StrPrinter << "m=video 0 RTP/AVP "
|
||||
<< m_pRtpMaker_h264->getPlayloadType() << "\r\n" << endl;
|
||||
m_strSDP += "b=AS:5100\r\n";
|
||||
m_strSDP += StrPrinter << "a=rtpmap:" << m_pRtpMaker_h264->getPlayloadType()
|
||||
<< " H264/" << m_pRtpMaker_h264->getSampleRate() << "\r\n" << endl;
|
||||
m_strSDP += StrPrinter << "a=fmtp:" << m_pRtpMaker_h264->getPlayloadType()
|
||||
<< " packetization-mode=1;profile-level-id=" << endl;
|
||||
|
||||
memset(acTmp, 0, sizeof(acTmp));
|
||||
sprintf(acTmp, "%06X", profile_level_id);
|
||||
m_strSDP += acTmp;
|
||||
m_strSDP += ";sprop-parameter-sets=";
|
||||
memset(acTmp, 0, sizeof(acTmp));
|
||||
av_base64_encode(acTmp, sizeof(acTmp), (uint8_t *) m_aucSPS, m_uiSPSLen);
|
||||
//WarnL<<"SPS base64:"<<strTemp;
|
||||
//WarnL<<"SPS hexdump:"<<hexdump(SPS_BUF, SPS_LEN);
|
||||
m_strSDP += acTmp;
|
||||
m_strSDP += ",";
|
||||
memset(acTmp, 0, sizeof(acTmp));
|
||||
av_base64_encode(acTmp, sizeof(acTmp), (uint8_t *) m_aucPPS, m_uiPPSLen);
|
||||
m_strSDP += acTmp;
|
||||
m_strSDP += "\r\n";
|
||||
if (m_video->iFrameRate > 0 && m_video->iHeight > 0 && m_video->iWidth > 0) {
|
||||
m_strSDP += "a=framerate:";
|
||||
m_strSDP += StrPrinter << m_video->iFrameRate << endl;
|
||||
m_strSDP += StrPrinter << "\r\na=framesize:"
|
||||
<< m_pRtpMaker_h264->getPlayloadType() << " " << endl;
|
||||
m_strSDP += StrPrinter << m_video->iWidth << endl;
|
||||
m_strSDP += "-";
|
||||
m_strSDP += StrPrinter << m_video->iHeight << endl;
|
||||
m_strSDP += "\r\n";
|
||||
}
|
||||
m_strSDP += StrPrinter << "a=control:trackID="
|
||||
<< m_pRtpMaker_h264->getInterleaved() / 2 << "\r\n" << endl;
|
||||
m_bSdp_gotH264 = true;
|
||||
if (m_audio) {
|
||||
if (m_bSdp_gotAAC) {
|
||||
makeSDP(m_strSDP);
|
||||
}
|
||||
} else {
|
||||
makeSDP(m_strSDP);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DevChannel::makeSDP_AAC(unsigned char *fixedHeader, int dataLen) {
|
||||
auto audioSpecificConfig = makeAdtsConfig(fixedHeader);
|
||||
if (audioSpecificConfig.size() != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
char fConfigStr[5] = { 0 };
|
||||
sprintf(fConfigStr, "%02X%02x", (uint8_t)audioSpecificConfig[0],(uint8_t)audioSpecificConfig[1]);
|
||||
|
||||
m_strSDP += StrPrinter << "m=audio 0 RTP/AVP "
|
||||
<< m_pRtpMaker_aac->getPlayloadType() << "\r\n" << endl;
|
||||
m_strSDP += "b=AS:96\r\n";
|
||||
m_strSDP += StrPrinter << "a=rtpmap:" << m_pRtpMaker_aac->getPlayloadType()
|
||||
<< " MPEG4-GENERIC/" << m_pRtpMaker_aac->getSampleRate() << "\r\n"
|
||||
<< endl;
|
||||
m_strSDP += StrPrinter << "a=fmtp:" << m_pRtpMaker_aac->getPlayloadType()
|
||||
<< " streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config="
|
||||
<< endl;
|
||||
m_strSDP += fConfigStr;
|
||||
m_strSDP += "\r\n";
|
||||
m_strSDP += StrPrinter << "a=control:trackID="
|
||||
<< m_pRtpMaker_aac->getInterleaved() / 2 << "\r\n" << endl;
|
||||
|
||||
m_bSdp_gotAAC = true;
|
||||
if (m_video) {
|
||||
if (m_bSdp_gotH264) {
|
||||
makeSDP(m_strSDP);
|
||||
}
|
||||
} else {
|
||||
makeSDP(m_strSDP);
|
||||
}
|
||||
}
|
||||
|
||||
void DevChannel::makeSDP(const string& strSdp) {
|
||||
m_mediaSrc->onGetSDP(strSdp);
|
||||
m_mediaSrc->regist();
|
||||
}
|
||||
|
||||
void DevChannel::initVideo(const VideoInfo& info) {
|
||||
m_video.reset(new VideoInfo(info));
|
||||
}
|
||||
|
||||
void DevChannel::initAudio(const AudioInfo& info) {
|
||||
m_audio.reset(new AudioInfo(info));
|
||||
}
|
||||
} /* namespace DEV */
|
||||
} /* namespace ZL */
|
||||
|
108
src/Device/Device.h
Normal file
108
src/Device/Device.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Device.h
|
||||
*
|
||||
* Created on: 2016年8月10日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_DEVICE_H_
|
||||
#define DEVICE_DEVICE_H_
|
||||
#include <sys/time.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "Util/util.h"
|
||||
#include "RTP/RtpMakerAAC.h"
|
||||
#include "RTP/RtpMakerH264.h"
|
||||
#include "Rtsp/RtspToRtmpMediaSource.h"
|
||||
using namespace std;
|
||||
using namespace ZL::Rtsp;
|
||||
using namespace ZL::Util;
|
||||
|
||||
#ifdef ENABLE_FAAC
|
||||
#include "Codec/AACEncoder.h"
|
||||
using namespace ZL::Codec;
|
||||
#endif //ENABLE_FAAC
|
||||
|
||||
#ifdef ENABLE_X264
|
||||
#include "Codec/H264Encoder.h"
|
||||
using namespace ZL::Codec;
|
||||
#endif //ENABLE_X264
|
||||
|
||||
using namespace ZL::Rtsp;
|
||||
namespace ZL {
|
||||
namespace DEV {
|
||||
|
||||
class VideoInfo {
|
||||
public:
|
||||
int iWidth;
|
||||
int iHeight;
|
||||
float iFrameRate;
|
||||
};
|
||||
class AudioInfo {
|
||||
public:
|
||||
int iChannel;
|
||||
int iSampleBit;
|
||||
int iSampleRate;
|
||||
};
|
||||
|
||||
|
||||
class DevChannel {
|
||||
public:
|
||||
typedef std::shared_ptr<DevChannel> Ptr;
|
||||
DevChannel(const char *strApp, const char *strId,float fDuration = 0,bool bLiveStream = true);
|
||||
virtual ~DevChannel();
|
||||
|
||||
void initVideo(const VideoInfo &info);
|
||||
void initAudio(const AudioInfo &info);
|
||||
|
||||
void inputYUV(char *apcYuv[3], int aiYuvLen[3], uint32_t uiStamp);
|
||||
void inputPCM(char *pcData, int iDataLen, uint32_t uiStamp);
|
||||
|
||||
void inputH264(char *pcData, int iDataLen, uint32_t uiStamp);
|
||||
void inputAAC(char *pcData, int iDataLen, uint32_t uiStamp);
|
||||
#ifdef ENABLE_RTSP2RTMP
|
||||
int readerCount() {
|
||||
return m_mediaSrc ? m_mediaSrc->readerCount() : 0;
|
||||
}
|
||||
void updateTimeStamp(uint32_t uiStamp){
|
||||
m_mediaSrc->updateTimeStamp(uiStamp);
|
||||
}
|
||||
#endif //ENABLE_RTSP2RTMP
|
||||
void setOnSeek(const function<bool(uint32_t)> &onSeek){
|
||||
m_mediaSrc->setOnSeek(onSeek);
|
||||
}
|
||||
void setOnStamp(const function<uint32_t()> &cb) {
|
||||
m_mediaSrc->setOnStamp(cb);
|
||||
}
|
||||
private:
|
||||
inline void makeSDP_264(unsigned char *pucData, int iDataLen);
|
||||
inline void makeSDP_AAC(unsigned char *pucData, int iDataLen);
|
||||
inline void makeSDP(const string& strSdp);
|
||||
#ifdef ENABLE_X264
|
||||
std::shared_ptr<H264Encoder> m_pH264Enc;
|
||||
#endif //ENABLE_X264
|
||||
|
||||
#ifdef ENABLE_FAAC
|
||||
std::shared_ptr<AACEncoder> m_pAacEnc;
|
||||
#endif //ENABLE_FAAC
|
||||
RtpMaker_AAC::Ptr m_pRtpMaker_aac;
|
||||
RtpMaker_H264::Ptr m_pRtpMaker_h264;
|
||||
RtspToRtmpMediaSource::Ptr m_mediaSrc;
|
||||
string m_strSDP;
|
||||
bool m_bSdp_gotH264 = false;
|
||||
bool m_bSdp_gotAAC = false;
|
||||
|
||||
unsigned char m_aucSPS[256];
|
||||
unsigned int m_uiSPSLen = 0;
|
||||
unsigned char m_aucPPS[256];
|
||||
unsigned int m_uiPPSLen = 0;
|
||||
std::shared_ptr<VideoInfo> m_video;
|
||||
std::shared_ptr<AudioInfo> m_audio;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace DEV */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* DEVICE_DEVICE_H_ */
|
153
src/Device/PlayerProxy.cpp
Normal file
153
src/Device/PlayerProxy.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* PlyerProxy.cpp
|
||||
*
|
||||
* Created on: 2016年12月6日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "PlayerProxy.h"
|
||||
#include "Thread/AsyncTaskThread.h"
|
||||
#include "Util/MD5.h"
|
||||
#include "Util/logger.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Thread;
|
||||
|
||||
namespace ZL {
|
||||
namespace DEV {
|
||||
|
||||
PlayerProxy::PlayerProxy(const char *strApp,const char *strSrc){
|
||||
m_strApp = strApp;
|
||||
m_strSrc = strSrc;
|
||||
}
|
||||
|
||||
void PlayerProxy::play(const char* strUrl, const char *strUser,
|
||||
const char *strPwd, PlayerBase::eRtpType eType, uint32_t iSecond) {
|
||||
m_aliveSecond = iSecond;
|
||||
string strUrlTmp(strUrl);
|
||||
string strUserTmp(strUser);
|
||||
string strPwdTmp(strPwd);
|
||||
|
||||
m_pPlayer.reset(new MediaPlayer());
|
||||
m_pPlayer->play(strUrl, strUser, strPwd, eType);
|
||||
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
||||
m_pPlayer->setOnVideoCB( [weakSelf,strUrlTmp](const H264Frame &data ) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf){
|
||||
return;
|
||||
}
|
||||
if(strongSelf->m_pChn){
|
||||
strongSelf->m_pChn->inputH264((char *)data.data.data(), data.data.size(), data.timeStamp);
|
||||
}else{
|
||||
strongSelf->initMedia();
|
||||
}
|
||||
strongSelf->checkExpired();
|
||||
});
|
||||
m_pPlayer->setOnAudioCB( [weakSelf,strUrlTmp](const AdtsFrame &data ) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf){
|
||||
return;
|
||||
}
|
||||
if(strongSelf->m_pChn){
|
||||
strongSelf->m_pChn->inputAAC((char *)data.data, data.aac_frame_length, data.timeStamp);
|
||||
}else{
|
||||
strongSelf->initMedia();
|
||||
}
|
||||
strongSelf->checkExpired();
|
||||
});
|
||||
|
||||
std::shared_ptr<uint64_t> piFailedCnt(new uint64_t(0)); //连续播放失败次数
|
||||
m_pPlayer->setOnPlayResult([weakSelf,strUrlTmp,strUserTmp,strPwdTmp,eType,piFailedCnt](const SockException &err) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
static uint64_t replayCnt = mINI::Instance()[Config::Proxy::kReplayCount].as<uint64_t>();
|
||||
if(!err) {
|
||||
// 播放成功
|
||||
*piFailedCnt = 0;//连续播放失败次数清0
|
||||
}else if(*piFailedCnt < replayCnt) {
|
||||
// 播放失败,延时重试播放
|
||||
strongSelf->rePlay(strUrlTmp, strUserTmp, strPwdTmp, eType,(*piFailedCnt)++);
|
||||
}else{
|
||||
strongSelf->expired();
|
||||
}
|
||||
});
|
||||
weak_ptr<MediaPlayer> weakPtr= m_pPlayer;
|
||||
m_pPlayer->setOnShutdown([weakSelf,weakPtr,strUrlTmp,strUserTmp,strPwdTmp,eType,piFailedCnt](const SockException &err) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
if(strongSelf->m_pChn) {
|
||||
strongSelf->m_pChn.reset();
|
||||
}
|
||||
//播放异常中断,延时重试播放
|
||||
static uint64_t replayCnt = mINI::Instance()[Config::Proxy::kReplayCount].as<uint64_t>();
|
||||
if(*piFailedCnt < replayCnt) {
|
||||
strongSelf->rePlay(strUrlTmp, strUserTmp, strPwdTmp, eType,(*piFailedCnt)++);
|
||||
}else{
|
||||
strongSelf->expired();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PlayerProxy::~PlayerProxy() {
|
||||
auto iTaskId = reinterpret_cast<uint64_t>(this);
|
||||
AsyncTaskThread::Instance().CancelTask(iTaskId);
|
||||
}
|
||||
void PlayerProxy::rePlay(const string &strUrl, const string &strUser, const string &strPwd, PlayerBase::eRtpType eType, uint64_t iFailedCnt){
|
||||
checkExpired();
|
||||
auto iTaskId = reinterpret_cast<uint64_t>(this);
|
||||
auto iDelay = MAX((uint64_t)2 * 1000, MIN(iFailedCnt * 3000,(uint64_t)60*1000));
|
||||
weak_ptr<MediaPlayer> weakPtr = m_pPlayer;
|
||||
AsyncTaskThread::Instance().CancelTask(iTaskId);
|
||||
AsyncTaskThread::Instance().DoTaskDelay(iTaskId, iDelay, [weakPtr,strUrl,strUser,strPwd,eType,iFailedCnt]() {
|
||||
//播放失败次数越多,则延时越长
|
||||
auto strongPlayer = weakPtr.lock();
|
||||
if(!strongPlayer) {
|
||||
return false;
|
||||
}
|
||||
WarnL << "重试播放[" << iFailedCnt << "]:" << strUrl;
|
||||
strongPlayer->play(strUrl.data(), strUser.data(), strPwd.data(), eType);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
void PlayerProxy::initMedia() {
|
||||
if (!m_pPlayer->isInited()) {
|
||||
return;
|
||||
}
|
||||
m_pChn.reset(new DevChannel(m_strApp.data(),m_strSrc.data(),m_pPlayer->getDuration()));
|
||||
if (m_pPlayer->containVideo()) {
|
||||
VideoInfo info;
|
||||
info.iFrameRate = m_pPlayer->getVideoFps();
|
||||
info.iWidth = m_pPlayer->getVideoWidth();
|
||||
info.iHeight = m_pPlayer->getVideoHeight();
|
||||
m_pChn->initVideo(info);
|
||||
}
|
||||
if (m_pPlayer->containAudio()) {
|
||||
AudioInfo info;
|
||||
info.iSampleRate = m_pPlayer->getAudioSampleRate();
|
||||
info.iChannel = m_pPlayer->getAudioChannel();
|
||||
info.iSampleBit = m_pPlayer->getAudioSampleBit();
|
||||
m_pChn->initAudio(info);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerProxy::checkExpired() {
|
||||
if(m_aliveSecond && m_aliveTicker.elapsedTime() > m_aliveSecond * 1000){
|
||||
//到期
|
||||
expired();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerProxy::expired() {
|
||||
if(onExpired){
|
||||
onExpired();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace Player */
|
||||
} /* namespace ZL */
|
48
src/Device/PlayerProxy.h
Normal file
48
src/Device/PlayerProxy.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* PlyerProxy.h
|
||||
*
|
||||
* Created on: 2016年12月6日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_DEVICE_PLAYERPROXY_H_
|
||||
#define SRC_DEVICE_PLAYERPROXY_H_
|
||||
|
||||
#include "Device.h"
|
||||
#include <memory>
|
||||
#include "Player/MediaPlayer.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Player;
|
||||
|
||||
namespace ZL {
|
||||
namespace DEV {
|
||||
|
||||
class PlayerProxy : public std::enable_shared_from_this<PlayerProxy>{
|
||||
public:
|
||||
typedef std::shared_ptr<PlayerProxy> Ptr;
|
||||
PlayerProxy(const char *strApp, const char *strSrc);
|
||||
void play(const char* strUrl, const char *strUser = "", const char *strPwd = "",PlayerBase::eRtpType eType = PlayerBase::RTP_TCP,uint32_t iSecond = 0);
|
||||
virtual ~PlayerProxy();
|
||||
void setOnExpired(const function<void()> &cb){
|
||||
onExpired = cb;
|
||||
}
|
||||
private :
|
||||
MediaPlayer::Ptr m_pPlayer;
|
||||
DevChannel::Ptr m_pChn;
|
||||
Ticker m_aliveTicker;
|
||||
uint32_t m_aliveSecond = 0;
|
||||
function<void()> onExpired;
|
||||
string m_strApp;
|
||||
string m_strSrc;
|
||||
void initMedia();
|
||||
void rePlay(const string &strUrl, const string &strUser, const string &strPwd, PlayerBase::eRtpType eType,uint64_t iFailedCnt);
|
||||
void checkExpired();
|
||||
void expired();
|
||||
};
|
||||
|
||||
} /* namespace Player */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_DEVICE_PLAYERPROXY_H_ */
|
167
src/Device/base64.cpp
Normal file
167
src/Device/base64.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Base64 encode/decode
|
||||
* @author Ryan Martell <rdm4@martellventures.com> (with lots of Michael)
|
||||
*/
|
||||
|
||||
//#include "common.h"
|
||||
#include "stdio.h"
|
||||
#include "base64.h"
|
||||
|
||||
#include <limits.h>
|
||||
/* ---------------- private code */
|
||||
static const uint8_t map2[] =
|
||||
{
|
||||
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
|
||||
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
|
||||
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
|
||||
};
|
||||
|
||||
int av_base64_decode(uint8_t *out, const char *in, int out_size)
|
||||
{
|
||||
int i, v;
|
||||
uint8_t *dst = out;
|
||||
|
||||
v = 0;
|
||||
for (i = 0; in[i] && in[i] != '='; i++) {
|
||||
unsigned int index= in[i]-43;
|
||||
if (index>=FF_ARRAY_ELEMS(map2) || map2[index] == 0xff)
|
||||
return -1;
|
||||
v = (v << 6) + map2[index];
|
||||
if (i & 3) {
|
||||
if (dst - out < out_size) {
|
||||
*dst++ = v >> (6 - 2 * (i & 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst - out;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* b64_encode: Stolen from VLC's http.c.
|
||||
* Simplified by Michael.
|
||||
* Fixed edge cases and made it work from data (vs. strings) by Ryan.
|
||||
*****************************************************************************/
|
||||
|
||||
char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
|
||||
{
|
||||
static const char b64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char *ret, *dst;
|
||||
unsigned i_bits = 0;
|
||||
int i_shift = 0;
|
||||
int bytes_remaining = in_size;
|
||||
|
||||
if (in_size >= UINT_MAX / 4 || out_size < AV_BASE64_SIZE(in_size))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ret = dst = out;
|
||||
while (bytes_remaining) {
|
||||
i_bits = (i_bits << 8) + *in++;
|
||||
bytes_remaining--;
|
||||
i_shift += 8;
|
||||
|
||||
do {
|
||||
*dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
|
||||
i_shift -= 6;
|
||||
} while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0));
|
||||
}
|
||||
while ((dst - ret) & 3)
|
||||
*dst++ = '=';
|
||||
*dst = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
#undef printf
|
||||
|
||||
#define MAX_DATA_SIZE 1024
|
||||
#define MAX_ENCODED_SIZE 2048
|
||||
|
||||
static int test_encode_decode(const uint8_t *data, unsigned int data_size,
|
||||
const char *encoded_ref)
|
||||
{
|
||||
char encoded[MAX_ENCODED_SIZE];
|
||||
uint8_t data2[MAX_DATA_SIZE];
|
||||
int data2_size, max_data2_size = MAX_DATA_SIZE;
|
||||
|
||||
if (!av_base64_encode(encoded, MAX_ENCODED_SIZE, data, data_size)) {
|
||||
printf("Failed: cannot encode the input data\n");
|
||||
return 1;
|
||||
}
|
||||
if (encoded_ref && strcmp(encoded, encoded_ref)) {
|
||||
printf("Failed: encoded string differs from reference\n"
|
||||
"Encoded:\n%s\nReference:\n%s\n", encoded, encoded_ref);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((data2_size = av_base64_decode(data2, encoded, max_data2_size)) < 0) {
|
||||
printf("Failed: cannot decode the encoded string\n"
|
||||
"Encoded:\n%s\n", encoded);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(data2, data, data_size)) {
|
||||
printf("Failed: encoded/decoded data differs from original data\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Passed!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i, error_count = 0;
|
||||
struct test {
|
||||
const uint8_t *data;
|
||||
const char *encoded_ref;
|
||||
} tests[] = {
|
||||
{ "", ""},
|
||||
{ "1", "MQ=="},
|
||||
{ "22", "MjI="},
|
||||
{ "333", "MzMz"},
|
||||
{ "4444", "NDQ0NA=="},
|
||||
{ "55555", "NTU1NTU="},
|
||||
{ "666666", "NjY2NjY2"},
|
||||
{ "abc:def", "YWJjOmRlZg=="},
|
||||
};
|
||||
|
||||
printf("Encoding/decoding tests\n");
|
||||
for (i = 0; i < FF_ARRAY_ELEMS(tests); i++)
|
||||
error_count += test_encode_decode(tests[i].data, strlen(tests[i].data), tests[i].encoded_ref);
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
#endif
|
55
src/Device/base64.h
Normal file
55
src/Device/base64.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#ifndef AVUTIL_BASE64_H
|
||||
#define AVUTIL_BASE64_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Decode a base64-encoded string.
|
||||
*
|
||||
* @param out buffer for decoded data
|
||||
* @param in null-terminated input string
|
||||
* @param out_size size in bytes of the out buffer, must be at
|
||||
* least 3/4 of the length of in
|
||||
* @return number of bytes written, or a negative value in case of
|
||||
* invalid input
|
||||
*/
|
||||
int av_base64_decode(uint8_t *out, const char *in, int out_size);
|
||||
|
||||
/**
|
||||
* Encode data to base64 and null-terminate.
|
||||
*
|
||||
* @param out buffer for encoded data
|
||||
* @param out_size size in bytes of the output buffer, must be at
|
||||
* least AV_BASE64_SIZE(in_size)
|
||||
* @param in_size size in bytes of the 'in' buffer
|
||||
* @return 'out' or NULL in case of error
|
||||
*/
|
||||
char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size);
|
||||
|
||||
/**
|
||||
* Calculate the output size needed to base64-encode x bytes.
|
||||
*/
|
||||
#define AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1)
|
||||
|
||||
#endif /* AVUTIL_BASE64_H */
|
100
src/H264/H264Parser.cpp
Normal file
100
src/H264/H264Parser.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
//
|
||||
// H264Parser.cpp
|
||||
// MediaPlayer
|
||||
//
|
||||
// Created by xzl on 2017/1/16.
|
||||
// Copyright © 2017年 jizan. All rights reserved.
|
||||
//
|
||||
|
||||
#include "H264Parser.h"
|
||||
#include "Util/logger.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
H264Parser::H264Parser(){
|
||||
|
||||
}
|
||||
H264Parser::~H264Parser(){
|
||||
|
||||
}
|
||||
void H264Parser::inputH264(const string &h264,uint32_t dts){
|
||||
|
||||
m_parser.SetStream((const uint8_t *)h264.data(), h264.size());
|
||||
while (true) {
|
||||
if(media::H264Parser::kOk != m_parser.AdvanceToNextNALU(&m_nalu)){
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_nalu.nal_unit_type) {
|
||||
case media::H264NALU::kNonIDRSlice:
|
||||
case media::H264NALU::kIDRSlice:{
|
||||
if(media::H264Parser::kOk == m_parser.ParseSliceHeader(m_nalu, &m_shdr)){
|
||||
const media::H264SPS *pPps = m_parser.GetSPS(m_shdr.pic_parameter_set_id);
|
||||
if (pPps) {
|
||||
m_poc.ComputePicOrderCnt(pPps, m_shdr, &m_iNowPOC);
|
||||
computePts(dts);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case media::H264NALU::kSPS:{
|
||||
int sps_id;
|
||||
m_parser.ParseSPS(&sps_id);
|
||||
}
|
||||
break;
|
||||
case media::H264NALU::kPPS:{
|
||||
int pps_id;
|
||||
m_parser.ParsePPS(&pps_id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void H264Parser::computePts(uint32_t iNowDTS) {
|
||||
auto iPOCInc = m_iNowPOC - m_iLastPOC;
|
||||
if (m_shdr.slice_type % 5 == 1) {
|
||||
//这是B帧
|
||||
m_iNowPTS = m_iLastPTS + m_iMsPerPOC * (iPOCInc);
|
||||
} else {
|
||||
//这是I帧或者P帧
|
||||
m_iNowPTS = iNowDTS;
|
||||
//计算每一POC的时间
|
||||
if(iPOCInc == 0){
|
||||
WarnL << "iPOCInc = 0," << m_iNowPOC << " " << m_iLastPOC;
|
||||
}else{
|
||||
m_iMsPerPOC = (m_iNowPTS - m_iLastPTS) / iPOCInc;
|
||||
}
|
||||
m_iLastPTS = m_iNowPTS;
|
||||
m_iLastPOC = m_iNowPOC;
|
||||
}
|
||||
|
||||
|
||||
// DebugL << m_shdr.slice_type
|
||||
// <<"\r\nNOW:"
|
||||
// << m_iNowPOC << " "
|
||||
// << m_iNowPTS << " "
|
||||
// << iNowDTS << " "
|
||||
// << "\r\nLST:"
|
||||
// << m_iLastPOC << " "
|
||||
// << m_iLastPTS << " "
|
||||
// << m_iMsPerPOC << endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
56
src/H264/H264Parser.h
Normal file
56
src/H264/H264Parser.h
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// H264Parser.hpp
|
||||
// MediaPlayer
|
||||
//
|
||||
// Created by xzl on 2017/1/16.
|
||||
// Copyright © 2017年 jizan. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef H264Parser_h
|
||||
#define H264Parser_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include "h264_parser.h"
|
||||
#include "h264_poc.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
class H264Parser{
|
||||
public:
|
||||
H264Parser();
|
||||
virtual ~H264Parser();
|
||||
void inputH264(const string &h264,uint32_t dts);
|
||||
|
||||
int32_t getPOC() const{
|
||||
return m_iNowPOC;
|
||||
}
|
||||
int getSliceType() const{
|
||||
return m_shdr.slice_type;
|
||||
}
|
||||
int getNaluType() const{
|
||||
return m_nalu.nal_unit_type;
|
||||
}
|
||||
uint32_t getPts() const{
|
||||
return m_iNowPTS;
|
||||
}
|
||||
private:
|
||||
media::H264Parser m_parser;
|
||||
media::H264POC m_poc;
|
||||
media::H264NALU m_nalu;
|
||||
media::H264SliceHeader m_shdr;
|
||||
|
||||
int32_t m_iNowPOC = INT32_MAX;
|
||||
int32_t m_iLastPOC = INT32_MAX;
|
||||
|
||||
uint32_t m_iNowPTS = INT32_MAX;
|
||||
uint32_t m_iLastPTS = INT32_MAX;
|
||||
int32_t m_iMsPerPOC = 30;
|
||||
|
||||
void computePts(uint32_t dts);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* H264Parser_hpp */
|
1039
src/H264/SPSParser.c
Normal file
1039
src/H264/SPSParser.c
Normal file
File diff suppressed because it is too large
Load Diff
192
src/H264/SPSParser.h
Normal file
192
src/H264/SPSParser.h
Normal file
@ -0,0 +1,192 @@
|
||||
/***************************************************************************************
|
||||
***************************************************************************************/
|
||||
#ifndef _JZAN_SPS_PPS_H_
|
||||
#define _JZAN_SPS_PPS_H_
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define QP_MAX_NUM (51 + 6*6) // The maximum supported qp
|
||||
|
||||
/**
|
||||
* Chromaticity coordinates of the source primaries.
|
||||
*/
|
||||
enum T_AVColorPrimaries {
|
||||
AVCOL_PRI_RESERVED0 = 0,
|
||||
AVCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B
|
||||
AVCOL_PRI_UNSPECIFIED = 2,
|
||||
AVCOL_PRI_RESERVED = 3,
|
||||
AVCOL_PRI_BT470M = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
|
||||
|
||||
AVCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM
|
||||
AVCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
|
||||
AVCOL_PRI_SMPTE240M = 7, ///< functionally identical to above
|
||||
AVCOL_PRI_FILM = 8, ///< colour filters using Illuminant C
|
||||
AVCOL_PRI_BT2020 = 9, ///< ITU-R BT2020
|
||||
AVCOL_PRI_NB, ///< Not part of ABI
|
||||
};
|
||||
|
||||
/**
|
||||
* Color Transfer Characteristic.
|
||||
*/
|
||||
enum T_AVColorTransferCharacteristic {
|
||||
AVCOL_TRC_RESERVED0 = 0,
|
||||
AVCOL_TRC_BT709 = 1, ///< also ITU-R BT1361
|
||||
AVCOL_TRC_UNSPECIFIED = 2,
|
||||
AVCOL_TRC_RESERVED = 3,
|
||||
AVCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
|
||||
AVCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG
|
||||
AVCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
|
||||
AVCOL_TRC_SMPTE240M = 7,
|
||||
AVCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics"
|
||||
AVCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)"
|
||||
AVCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
|
||||
AVCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4
|
||||
AVCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut
|
||||
AVCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC)
|
||||
AVCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10 bit system
|
||||
AVCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12 bit system
|
||||
AVCOL_TRC_NB, ///< Not part of ABI
|
||||
};
|
||||
|
||||
/**
|
||||
* YUV tColorspace type.
|
||||
*/
|
||||
enum T_AVColorSpace {
|
||||
AVCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB)
|
||||
AVCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B
|
||||
AVCOL_SPC_UNSPECIFIED = 2,
|
||||
AVCOL_SPC_RESERVED = 3,
|
||||
AVCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
|
||||
AVCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601
|
||||
AVCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above
|
||||
AVCOL_SPC_SMPTE240M = 7,
|
||||
AVCOL_SPC_YCOCG = 8, ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16
|
||||
AVCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system
|
||||
AVCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system
|
||||
AVCOL_SPC_NB, ///< Not part of ABI
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* rational number numerator/denominator
|
||||
*/
|
||||
typedef struct T_AVRational{
|
||||
int num; ///< numerator
|
||||
int den; ///< denominator
|
||||
} T_AVRational;
|
||||
|
||||
|
||||
/***
|
||||
* Sequence parameter set
|
||||
* ¿É²Î¿¼H264±ê×¼µÚ7½ÚºÍ¸½Â¼D E
|
||||
*/
|
||||
#define Extended_SAR 255
|
||||
|
||||
/**
|
||||
* Sequence parameter set
|
||||
*/
|
||||
typedef struct T_SPS {
|
||||
unsigned int uiSpsId;
|
||||
int iProfileIdc;
|
||||
int iLevelIdc;
|
||||
int iChromaFormatIdc;
|
||||
int iTransformBypass; ///< qpprime_y_zero_transform_bypass_flag
|
||||
int iLog2MaxFrameNum; ///< log2_max_frame_num_minus4 + 4
|
||||
int iPocType; ///< pic_order_cnt_type
|
||||
int iLog2MaxPocLsb; ///< log2_max_pic_order_cnt_lsb_minus4
|
||||
int iDeltaPicOrderAlwaysZeroFlag;
|
||||
int iOffsetForNonRefPic;
|
||||
int iOffsetForTopToBottomField;
|
||||
int iPocCycleLength; ///< num_ref_frames_in_pic_order_cnt_cycle
|
||||
int iRefFrameCount; ///< num_ref_frames
|
||||
int iGapsInFrameNumAllowedFlag;
|
||||
int iMbWidth; ///< pic_width_in_mbs_minus1 + 1
|
||||
int iMbHeight; ///< pic_height_in_map_units_minus1 + 1
|
||||
int iFrameMbsOnlyFlag;
|
||||
int iMbAff; ///< mb_adaptive_frame_field_flag
|
||||
int iDirect8x8InferenceFlag;
|
||||
int iCrop; ///< frame_cropping_flag
|
||||
|
||||
/* those 4 are already in luma samples */
|
||||
unsigned int uiCropLeft; ///< frame_cropping_rect_left_offset
|
||||
unsigned int uiCropRight; ///< frame_cropping_rect_right_offset
|
||||
unsigned int uiCropTop; ///< frame_cropping_rect_top_offset
|
||||
unsigned int uiCropBottom; ///< frame_cropping_rect_bottom_offset
|
||||
int iVuiParametersPresentFlag;
|
||||
T_AVRational tSar;
|
||||
int iVideoSignalTypePresentFlag;
|
||||
int iFullRange;
|
||||
int iColourDescriptionPresentFlag;
|
||||
enum T_AVColorPrimaries tColorPrimaries;
|
||||
enum T_AVColorTransferCharacteristic tColorTrc;
|
||||
enum T_AVColorSpace tColorspace;
|
||||
int iTimingInfoPresentFlag;
|
||||
uint32_t u32NumUnitsInTick;
|
||||
uint32_t u32TimeScale;
|
||||
int iFixedFrameRateFlag;
|
||||
short asOffsetForRefFrame[256]; // FIXME dyn aloc?
|
||||
int iBitstreamRestrictionFlag;
|
||||
int iNumReorderFrames;
|
||||
int iScalingMatrixPresent;
|
||||
uint8_t aau8ScalingMatrix4[6][16];
|
||||
uint8_t aau8ScalingMatrix8[6][64];
|
||||
int iNalHrdParametersPresentFlag;
|
||||
int iVclHrdParametersPresentFlag;
|
||||
int iPicStructPresentFlag;
|
||||
int iTimeOffsetLength;
|
||||
int iCpbCnt; ///< See H.264 E.1.2
|
||||
int iInitialCpbRemovalDelayLength; ///< initial_cpb_removal_delay_length_minus1 + 1
|
||||
int iCpbRemovalDelayLength; ///< cpb_removal_delay_length_minus1 + 1
|
||||
int iDpbOutputDelayLength; ///< dpb_output_delay_length_minus1 + 1
|
||||
int iBitDepthLuma; ///< bit_depth_luma_minus8 + 8
|
||||
int iBitDepthChroma; ///< bit_depth_chroma_minus8 + 8
|
||||
int iResidualColorTransformFlag; ///< residual_colour_transform_flag
|
||||
int iConstraintSetFlags; ///< constraint_set[0-3]_flag
|
||||
int iNew; ///< flag to keep track if the decoder context needs re-init due to changed SPS
|
||||
} T_SPS;
|
||||
|
||||
/**
|
||||
* Picture parameter set
|
||||
*/
|
||||
typedef struct T_PPS {
|
||||
unsigned int uiSpsId;
|
||||
int iCabac; ///< entropy_coding_mode_flag
|
||||
int iPicOrderPresent; ///< pic_order_present_flag
|
||||
int iSliceGroupCount; ///< num_slice_groups_minus1 + 1
|
||||
int iMbSliceGroupMapType;
|
||||
unsigned int auiRefCount[2]; ///< num_ref_idx_l0/1_active_minus1 + 1
|
||||
int iWeightedPred; ///< weighted_pred_flag
|
||||
int iWeightedBipredIdc;
|
||||
int iInitQp; ///< pic_init_qp_minus26 + 26
|
||||
int iInitQs; ///< pic_init_qs_minus26 + 26
|
||||
int aiChromaQpIndexOffset[2];
|
||||
int iDeblockingFilterParametersPresent; ///< deblocking_filter_parameters_present_flag
|
||||
int iConstrainedIntraPred; ///< constrained_intra_pred_flag
|
||||
int iRedundantPicCntPresent; ///< redundant_pic_cnt_present_flag
|
||||
int iTransform8x8Mode; ///< transform_8x8_mode_flag
|
||||
uint8_t aau8ScalingMatrix4[6][16];
|
||||
uint8_t aau8ScalingMatrix8[6][64];
|
||||
uint8_t u8ChromaQpTable[2][QP_MAX_NUM+1]; ///< pre-scaled (with aiChromaQpIndexOffset) version of qp_table
|
||||
int iChromaQpDiff;
|
||||
} T_PPS;
|
||||
|
||||
typedef struct T_GetBitContext{
|
||||
uint8_t *pu8Buf; /*Ö¸ÏòSPS start*/
|
||||
int iBufSize; /*SPS ³¤¶È*/
|
||||
int iBitPos; /*bitÒѶÁȡλÖÃ*/
|
||||
int iTotalBit; /*bit×ܳ¤¶È*/
|
||||
int iCurBitPos; /*µ±Ç°¶ÁȡλÖÃ*/
|
||||
}T_GetBitContext;
|
||||
|
||||
|
||||
int h264DecSeqParameterSet(void *pvBuf, T_SPS *ptSps);
|
||||
void h264GetWidthHeight(T_SPS *ptSps, int *piWidth, int *piHeight);
|
||||
void h264GeFramerate(T_SPS *ptSps, float *pfFramerate);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
121
src/H264/h264_bit_reader.cpp
Normal file
121
src/H264/h264_bit_reader.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "h264_bit_reader.h"
|
||||
#include "macros.h"
|
||||
namespace media {
|
||||
|
||||
H264BitReader::H264BitReader()
|
||||
: data_(NULL),
|
||||
bytes_left_(0),
|
||||
curr_byte_(0),
|
||||
num_remaining_bits_in_curr_byte_(0),
|
||||
prev_two_bytes_(0),
|
||||
emulation_prevention_bytes_(0) {}
|
||||
|
||||
H264BitReader::~H264BitReader() {}
|
||||
|
||||
bool H264BitReader::Initialize(const uint8_t* data, off_t size) {
|
||||
DCHECK(data);
|
||||
|
||||
if (size < 1)
|
||||
return false;
|
||||
|
||||
data_ = data;
|
||||
bytes_left_ = size;
|
||||
num_remaining_bits_in_curr_byte_ = 0;
|
||||
// Initially set to 0xffff to accept all initial two-byte sequences.
|
||||
prev_two_bytes_ = 0xffff;
|
||||
emulation_prevention_bytes_ = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool H264BitReader::UpdateCurrByte() {
|
||||
if (bytes_left_ < 1)
|
||||
return false;
|
||||
|
||||
// Emulation prevention three-byte detection.
|
||||
// If a sequence of 0x000003 is found, skip (ignore) the last byte (0x03).
|
||||
if (*data_ == 0x03 && (prev_two_bytes_ & 0xffff) == 0) {
|
||||
// Detected 0x000003, skip last byte.
|
||||
++data_;
|
||||
--bytes_left_;
|
||||
++emulation_prevention_bytes_;
|
||||
// Need another full three bytes before we can detect the sequence again.
|
||||
prev_two_bytes_ = 0xffff;
|
||||
|
||||
if (bytes_left_ < 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load a new byte and advance pointers.
|
||||
curr_byte_ = *data_++ & 0xff;
|
||||
--bytes_left_;
|
||||
num_remaining_bits_in_curr_byte_ = 8;
|
||||
|
||||
prev_two_bytes_ = ((prev_two_bytes_ & 0xff) << 8) | curr_byte_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read |num_bits| (1 to 31 inclusive) from the stream and return them
|
||||
// in |out|, with first bit in the stream as MSB in |out| at position
|
||||
// (|num_bits| - 1).
|
||||
bool H264BitReader::ReadBits(int num_bits, int* out) {
|
||||
int bits_left = num_bits;
|
||||
*out = 0;
|
||||
DCHECK(num_bits <= 31);
|
||||
|
||||
while (num_remaining_bits_in_curr_byte_ < bits_left) {
|
||||
// Take all that's left in current byte, shift to make space for the rest.
|
||||
*out |= (curr_byte_ << (bits_left - num_remaining_bits_in_curr_byte_));
|
||||
bits_left -= num_remaining_bits_in_curr_byte_;
|
||||
|
||||
if (!UpdateCurrByte())
|
||||
return false;
|
||||
}
|
||||
|
||||
*out |= (curr_byte_ >> (num_remaining_bits_in_curr_byte_ - bits_left));
|
||||
*out &= ((1u << num_bits) - 1u);
|
||||
num_remaining_bits_in_curr_byte_ -= bits_left;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
off_t H264BitReader::NumBitsLeft() {
|
||||
return (num_remaining_bits_in_curr_byte_ + bytes_left_ * 8);
|
||||
}
|
||||
|
||||
bool H264BitReader::HasMoreRBSPData() {
|
||||
// Make sure we have more bits, if we are at 0 bits in current byte and
|
||||
// updating current byte fails, we don't have more data anyway.
|
||||
if (num_remaining_bits_in_curr_byte_ == 0 && !UpdateCurrByte())
|
||||
return false;
|
||||
|
||||
// If there is no more RBSP data, then |curr_byte_| contains the stop bit and
|
||||
// zero padding. Check to see if there is other data instead.
|
||||
// (We don't actually check for the stop bit itself, instead treating the
|
||||
// invalid case of all trailing zeros identically).
|
||||
if ((curr_byte_ & ((1 << (num_remaining_bits_in_curr_byte_ - 1)) - 1)) != 0)
|
||||
return true;
|
||||
|
||||
// While the spec disallows it (7.4.1: "The last byte of the NAL unit shall
|
||||
// not be equal to 0x00"), some streams have trailing null bytes anyway. We
|
||||
// don't handle emulation prevention sequences because HasMoreRBSPData() is
|
||||
// not used when parsing slices (where cabac_zero_word elements are legal).
|
||||
for (off_t i = 0; i < bytes_left_; i++) {
|
||||
if (data_[i] != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
bytes_left_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t H264BitReader::NumEmulationPreventionBytesRead() {
|
||||
return emulation_prevention_bytes_;
|
||||
}
|
||||
|
||||
} // namespace media
|
81
src/H264/h264_bit_reader.h
Normal file
81
src/H264/h264_bit_reader.h
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// This file contains an implementation of an H264 Annex-B video stream parser.
|
||||
|
||||
#ifndef MEDIA_FILTERS_H264_BIT_READER_H_
|
||||
#define MEDIA_FILTERS_H264_BIT_READER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include "macros.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace media {
|
||||
|
||||
// A class to provide bit-granularity reading of H.264 streams.
|
||||
// This is not a generic bit reader class, as it takes into account
|
||||
// H.264 stream-specific constraints, such as skipping emulation-prevention
|
||||
// bytes and stop bits. See spec for more details.
|
||||
class MEDIA_EXPORT H264BitReader {
|
||||
public:
|
||||
H264BitReader();
|
||||
~H264BitReader();
|
||||
|
||||
// Initialize the reader to start reading at |data|, |size| being size
|
||||
// of |data| in bytes.
|
||||
// Return false on insufficient size of stream..
|
||||
// TODO(posciak,fischman): consider replacing Initialize() with
|
||||
// heap-allocating and creating bit readers on demand instead.
|
||||
bool Initialize(const uint8_t* data, off_t size);
|
||||
|
||||
// Read |num_bits| next bits from stream and return in |*out|, first bit
|
||||
// from the stream starting at |num_bits| position in |*out|.
|
||||
// |num_bits| may be 1-32, inclusive.
|
||||
// Return false if the given number of bits cannot be read (not enough
|
||||
// bits in the stream), true otherwise.
|
||||
bool ReadBits(int num_bits, int* out);
|
||||
|
||||
// Return the number of bits left in the stream.
|
||||
off_t NumBitsLeft();
|
||||
|
||||
// See the definition of more_rbsp_data() in spec.
|
||||
bool HasMoreRBSPData();
|
||||
|
||||
// Return the number of emulation prevention bytes already read.
|
||||
size_t NumEmulationPreventionBytesRead();
|
||||
|
||||
private:
|
||||
// Advance to the next byte, loading it into curr_byte_.
|
||||
// Return false on end of stream.
|
||||
bool UpdateCurrByte();
|
||||
|
||||
// Pointer to the next unread (not in curr_byte_) byte in the stream.
|
||||
const uint8_t* data_;
|
||||
|
||||
// Bytes left in the stream (without the curr_byte_).
|
||||
off_t bytes_left_;
|
||||
|
||||
// Contents of the current byte; first unread bit starting at position
|
||||
// 8 - num_remaining_bits_in_curr_byte_ from MSB.
|
||||
int curr_byte_;
|
||||
|
||||
// Number of bits remaining in curr_byte_
|
||||
int num_remaining_bits_in_curr_byte_;
|
||||
|
||||
// Used in emulation prevention three byte detection (see spec).
|
||||
// Initially set to 0xffff to accept all initial two-byte sequences.
|
||||
int prev_two_bytes_;
|
||||
|
||||
// Number of emulation preventation bytes (0x000003) we met.
|
||||
size_t emulation_prevention_bytes_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(H264BitReader);
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_FILTERS_H264_BIT_READER_H_
|
1372
src/H264/h264_parser.cpp
Normal file
1372
src/H264/h264_parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
491
src/H264/h264_parser.h
Normal file
491
src/H264/h264_parser.h
Normal file
@ -0,0 +1,491 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// This file contains an implementation of an H264 Annex-B video stream parser.
|
||||
|
||||
#ifndef MEDIA_FILTERS_H264_PARSER_H_
|
||||
#define MEDIA_FILTERS_H264_PARSER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "macros.h"
|
||||
#include "h264_bit_reader.h"
|
||||
#include "ranges.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace media {
|
||||
|
||||
struct SubsampleEntry {
|
||||
uint32_t clear_bytes;
|
||||
uint32_t cypher_bytes;
|
||||
};
|
||||
|
||||
// For explanations of each struct and its members, see H.264 specification
|
||||
// at http://www.itu.int/rec/T-REC-H.264.
|
||||
struct MEDIA_EXPORT H264NALU {
|
||||
H264NALU();
|
||||
|
||||
enum Type {
|
||||
kUnspecified = 0,
|
||||
kNonIDRSlice = 1,
|
||||
kSliceDataA = 2,
|
||||
kSliceDataB = 3,
|
||||
kSliceDataC = 4,
|
||||
kIDRSlice = 5,
|
||||
kSEIMessage = 6,
|
||||
kSPS = 7,
|
||||
kPPS = 8,
|
||||
kAUD = 9,
|
||||
kEOSeq = 10,
|
||||
kEOStream = 11,
|
||||
kFiller = 12,
|
||||
kSPSExt = 13,
|
||||
kReserved14 = 14,
|
||||
kReserved15 = 15,
|
||||
kReserved16 = 16,
|
||||
kReserved17 = 17,
|
||||
kReserved18 = 18,
|
||||
kCodedSliceAux = 19,
|
||||
kCodedSliceExtension = 20,
|
||||
};
|
||||
|
||||
// After (without) start code; we don't own the underlying memory
|
||||
// and a shallow copy should be made when copying this struct.
|
||||
const uint8_t* data;
|
||||
off_t size; // From after start code to start code of next NALU (or EOS).
|
||||
|
||||
int nal_ref_idc;
|
||||
int nal_unit_type;
|
||||
};
|
||||
|
||||
enum {
|
||||
kH264ScalingList4x4Length = 16,
|
||||
kH264ScalingList8x8Length = 64,
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264SPS {
|
||||
H264SPS();
|
||||
|
||||
enum H264ProfileIDC {
|
||||
kProfileIDCBaseline = 66,
|
||||
kProfileIDCConstrainedBaseline = kProfileIDCBaseline,
|
||||
kProfileIDCMain = 77,
|
||||
kProfileIDScalableBaseline = 83,
|
||||
kProfileIDScalableHigh = 86,
|
||||
kProfileIDCHigh = 100,
|
||||
kProfileIDHigh10 = 110,
|
||||
kProfileIDSMultiviewHigh = 118,
|
||||
kProfileIDHigh422 = 122,
|
||||
kProfileIDStereoHigh = 128,
|
||||
kProfileIDHigh444Predictive = 244,
|
||||
};
|
||||
|
||||
enum AspectRatioIdc {
|
||||
kExtendedSar = 255,
|
||||
};
|
||||
|
||||
enum {
|
||||
// Constants for HRD parameters (spec ch. E.2.2).
|
||||
kBitRateScaleConstantTerm = 6, // Equation E-37.
|
||||
kCPBSizeScaleConstantTerm = 4, // Equation E-38.
|
||||
kDefaultInitialCPBRemovalDelayLength = 24,
|
||||
kDefaultDPBOutputDelayLength = 24,
|
||||
kDefaultTimeOffsetLength = 24,
|
||||
};
|
||||
|
||||
int profile_idc;
|
||||
bool constraint_set0_flag;
|
||||
bool constraint_set1_flag;
|
||||
bool constraint_set2_flag;
|
||||
bool constraint_set3_flag;
|
||||
bool constraint_set4_flag;
|
||||
bool constraint_set5_flag;
|
||||
int level_idc;
|
||||
int seq_parameter_set_id;
|
||||
|
||||
int chroma_format_idc;
|
||||
bool separate_colour_plane_flag;
|
||||
int bit_depth_luma_minus8;
|
||||
int bit_depth_chroma_minus8;
|
||||
bool qpprime_y_zero_transform_bypass_flag;
|
||||
|
||||
bool seq_scaling_matrix_present_flag;
|
||||
int scaling_list4x4[6][kH264ScalingList4x4Length];
|
||||
int scaling_list8x8[6][kH264ScalingList8x8Length];
|
||||
|
||||
int log2_max_frame_num_minus4;
|
||||
int pic_order_cnt_type;
|
||||
int log2_max_pic_order_cnt_lsb_minus4;
|
||||
bool delta_pic_order_always_zero_flag;
|
||||
int offset_for_non_ref_pic;
|
||||
int offset_for_top_to_bottom_field;
|
||||
int num_ref_frames_in_pic_order_cnt_cycle;
|
||||
int expected_delta_per_pic_order_cnt_cycle; // calculated
|
||||
int offset_for_ref_frame[255];
|
||||
int max_num_ref_frames;
|
||||
bool gaps_in_frame_num_value_allowed_flag;
|
||||
int pic_width_in_mbs_minus1;
|
||||
int pic_height_in_map_units_minus1;
|
||||
bool frame_mbs_only_flag;
|
||||
bool mb_adaptive_frame_field_flag;
|
||||
bool direct_8x8_inference_flag;
|
||||
bool frame_cropping_flag;
|
||||
int frame_crop_left_offset;
|
||||
int frame_crop_right_offset;
|
||||
int frame_crop_top_offset;
|
||||
int frame_crop_bottom_offset;
|
||||
|
||||
bool vui_parameters_present_flag;
|
||||
int sar_width; // Set to 0 when not specified.
|
||||
int sar_height; // Set to 0 when not specified.
|
||||
bool bitstream_restriction_flag;
|
||||
int max_num_reorder_frames;
|
||||
int max_dec_frame_buffering;
|
||||
bool timing_info_present_flag;
|
||||
int num_units_in_tick;
|
||||
int time_scale;
|
||||
bool fixed_frame_rate_flag;
|
||||
|
||||
// TODO(posciak): actually parse these instead of ParseAndIgnoreHRDParameters.
|
||||
bool nal_hrd_parameters_present_flag;
|
||||
int cpb_cnt_minus1;
|
||||
int bit_rate_scale;
|
||||
int cpb_size_scale;
|
||||
int bit_rate_value_minus1[32];
|
||||
int cpb_size_value_minus1[32];
|
||||
bool cbr_flag[32];
|
||||
int initial_cpb_removal_delay_length_minus_1;
|
||||
int cpb_removal_delay_length_minus1;
|
||||
int dpb_output_delay_length_minus1;
|
||||
int time_offset_length;
|
||||
|
||||
bool low_delay_hrd_flag;
|
||||
|
||||
int chroma_array_type;
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264PPS {
|
||||
H264PPS();
|
||||
|
||||
int pic_parameter_set_id;
|
||||
int seq_parameter_set_id;
|
||||
bool entropy_coding_mode_flag;
|
||||
bool bottom_field_pic_order_in_frame_present_flag;
|
||||
int num_slice_groups_minus1;
|
||||
// TODO(posciak): Slice groups not implemented, could be added at some point.
|
||||
int num_ref_idx_l0_default_active_minus1;
|
||||
int num_ref_idx_l1_default_active_minus1;
|
||||
bool weighted_pred_flag;
|
||||
int weighted_bipred_idc;
|
||||
int pic_init_qp_minus26;
|
||||
int pic_init_qs_minus26;
|
||||
int chroma_qp_index_offset;
|
||||
bool deblocking_filter_control_present_flag;
|
||||
bool constrained_intra_pred_flag;
|
||||
bool redundant_pic_cnt_present_flag;
|
||||
bool transform_8x8_mode_flag;
|
||||
|
||||
bool pic_scaling_matrix_present_flag;
|
||||
int scaling_list4x4[6][kH264ScalingList4x4Length];
|
||||
int scaling_list8x8[6][kH264ScalingList8x8Length];
|
||||
|
||||
int second_chroma_qp_index_offset;
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264ModificationOfPicNum {
|
||||
int modification_of_pic_nums_idc;
|
||||
union {
|
||||
int abs_diff_pic_num_minus1;
|
||||
int long_term_pic_num;
|
||||
};
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264WeightingFactors {
|
||||
bool luma_weight_flag;
|
||||
bool chroma_weight_flag;
|
||||
int luma_weight[32];
|
||||
int luma_offset[32];
|
||||
int chroma_weight[32][2];
|
||||
int chroma_offset[32][2];
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264DecRefPicMarking {
|
||||
int memory_mgmnt_control_operation;
|
||||
int difference_of_pic_nums_minus1;
|
||||
int long_term_pic_num;
|
||||
int long_term_frame_idx;
|
||||
int max_long_term_frame_idx_plus1;
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264SliceHeader {
|
||||
H264SliceHeader();
|
||||
|
||||
enum {
|
||||
kRefListSize = 32,
|
||||
kRefListModSize = kRefListSize
|
||||
};
|
||||
|
||||
enum Type {
|
||||
kPSlice = 0,
|
||||
kBSlice = 1,
|
||||
kISlice = 2,
|
||||
kSPSlice = 3,
|
||||
kSISlice = 4,
|
||||
};
|
||||
|
||||
bool IsPSlice() const;
|
||||
bool IsBSlice() const;
|
||||
bool IsISlice() const;
|
||||
bool IsSPSlice() const;
|
||||
bool IsSISlice() const;
|
||||
|
||||
bool idr_pic_flag; // from NAL header
|
||||
int nal_ref_idc; // from NAL header
|
||||
const uint8_t* nalu_data; // from NAL header
|
||||
off_t nalu_size; // from NAL header
|
||||
off_t header_bit_size; // calculated
|
||||
|
||||
int first_mb_in_slice;
|
||||
int slice_type;
|
||||
int pic_parameter_set_id;
|
||||
int colour_plane_id; // TODO(posciak): use this! http://crbug.com/139878
|
||||
int frame_num;
|
||||
bool field_pic_flag;
|
||||
bool bottom_field_flag;
|
||||
int idr_pic_id;
|
||||
int pic_order_cnt_lsb;
|
||||
int delta_pic_order_cnt_bottom;
|
||||
int delta_pic_order_cnt0;
|
||||
int delta_pic_order_cnt1;
|
||||
int redundant_pic_cnt;
|
||||
bool direct_spatial_mv_pred_flag;
|
||||
|
||||
bool num_ref_idx_active_override_flag;
|
||||
int num_ref_idx_l0_active_minus1;
|
||||
int num_ref_idx_l1_active_minus1;
|
||||
bool ref_pic_list_modification_flag_l0;
|
||||
bool ref_pic_list_modification_flag_l1;
|
||||
H264ModificationOfPicNum ref_list_l0_modifications[kRefListModSize];
|
||||
H264ModificationOfPicNum ref_list_l1_modifications[kRefListModSize];
|
||||
|
||||
int luma_log2_weight_denom;
|
||||
int chroma_log2_weight_denom;
|
||||
|
||||
bool luma_weight_l0_flag;
|
||||
bool chroma_weight_l0_flag;
|
||||
H264WeightingFactors pred_weight_table_l0;
|
||||
|
||||
bool luma_weight_l1_flag;
|
||||
bool chroma_weight_l1_flag;
|
||||
H264WeightingFactors pred_weight_table_l1;
|
||||
|
||||
bool no_output_of_prior_pics_flag;
|
||||
bool long_term_reference_flag;
|
||||
|
||||
bool adaptive_ref_pic_marking_mode_flag;
|
||||
H264DecRefPicMarking ref_pic_marking[kRefListSize];
|
||||
|
||||
int cabac_init_idc;
|
||||
int slice_qp_delta;
|
||||
bool sp_for_switch_flag;
|
||||
int slice_qs_delta;
|
||||
int disable_deblocking_filter_idc;
|
||||
int slice_alpha_c0_offset_div2;
|
||||
int slice_beta_offset_div2;
|
||||
|
||||
// Calculated.
|
||||
// Size in bits of dec_ref_pic_marking() syntax element.
|
||||
size_t dec_ref_pic_marking_bit_size;
|
||||
size_t pic_order_cnt_bit_size;
|
||||
};
|
||||
|
||||
struct H264SEIRecoveryPoint {
|
||||
int recovery_frame_cnt;
|
||||
bool exact_match_flag;
|
||||
bool broken_link_flag;
|
||||
int changing_slice_group_idc;
|
||||
};
|
||||
|
||||
struct MEDIA_EXPORT H264SEIMessage {
|
||||
H264SEIMessage();
|
||||
|
||||
enum Type {
|
||||
kSEIRecoveryPoint = 6,
|
||||
};
|
||||
|
||||
int type;
|
||||
int payload_size;
|
||||
union {
|
||||
// Placeholder; in future more supported types will contribute to more
|
||||
// union members here.
|
||||
H264SEIRecoveryPoint recovery_point;
|
||||
};
|
||||
};
|
||||
|
||||
// Class to parse an Annex-B H.264 stream,
|
||||
// as specified in chapters 7 and Annex B of the H.264 spec.
|
||||
class MEDIA_EXPORT H264Parser {
|
||||
public:
|
||||
enum Result {
|
||||
kOk,
|
||||
kInvalidStream, // error in stream
|
||||
kUnsupportedStream, // stream not supported by the parser
|
||||
kEOStream, // end of stream
|
||||
};
|
||||
|
||||
// Find offset from start of data to next NALU start code
|
||||
// and size of found start code (3 or 4 bytes).
|
||||
// If no start code is found, offset is pointing to the first unprocessed byte
|
||||
// (i.e. the first byte that was not considered as a possible start of a start
|
||||
// code) and |*start_code_size| is set to 0.
|
||||
// Preconditions:
|
||||
// - |data_size| >= 0
|
||||
// Postconditions:
|
||||
// - |*offset| is between 0 and |data_size| included.
|
||||
// It is strictly less than |data_size| if |data_size| > 0.
|
||||
// - |*start_code_size| is either 0, 3 or 4.
|
||||
static bool FindStartCode(const uint8_t* data,
|
||||
off_t data_size,
|
||||
off_t* offset,
|
||||
off_t* start_code_size);
|
||||
|
||||
// Wrapper for FindStartCode() that skips over start codes that
|
||||
// may appear inside of |encrypted_ranges_|.
|
||||
// Returns true if a start code was found. Otherwise returns false.
|
||||
static bool FindStartCodeInClearRanges(const uint8_t* data,
|
||||
off_t data_size,
|
||||
const Ranges<const uint8_t*>& ranges,
|
||||
off_t* offset,
|
||||
off_t* start_code_size);
|
||||
H264Parser();
|
||||
~H264Parser();
|
||||
|
||||
void Reset();
|
||||
// Set current stream pointer to |stream| of |stream_size| in bytes,
|
||||
// |stream| owned by caller.
|
||||
// |subsamples| contains information about what parts of |stream| are
|
||||
// encrypted.
|
||||
void SetStream(const uint8_t* stream, off_t stream_size);
|
||||
void SetEncryptedStream(const uint8_t* stream,
|
||||
off_t stream_size,
|
||||
const std::vector<SubsampleEntry>& subsamples);
|
||||
|
||||
// Read the stream to find the next NALU, identify it and return
|
||||
// that information in |*nalu|. This advances the stream to the beginning
|
||||
// of this NALU, but not past it, so subsequent calls to NALU-specific
|
||||
// parsing functions (ParseSPS, etc.) will parse this NALU.
|
||||
// If the caller wishes to skip the current NALU, it can call this function
|
||||
// again, instead of any NALU-type specific parse functions below.
|
||||
Result AdvanceToNextNALU(H264NALU* nalu);
|
||||
|
||||
// NALU-specific parsing functions.
|
||||
// These should be called after AdvanceToNextNALU().
|
||||
|
||||
// SPSes and PPSes are owned by the parser class and the memory for their
|
||||
// structures is managed here, not by the caller, as they are reused
|
||||
// across NALUs.
|
||||
//
|
||||
// Parse an SPS/PPS NALU and save their data in the parser, returning id
|
||||
// of the parsed structure in |*pps_id|/|*sps_id|.
|
||||
// To get a pointer to a given SPS/PPS structure, use GetSPS()/GetPPS(),
|
||||
// passing the returned |*sps_id|/|*pps_id| as parameter.
|
||||
// TODO(posciak,fischman): consider replacing returning Result from Parse*()
|
||||
// methods with a scoped_ptr and adding an AtEOS() function to check for EOS
|
||||
// if Parse*() return NULL.
|
||||
Result ParseSPS(int* sps_id);
|
||||
Result ParsePPS(int* pps_id);
|
||||
|
||||
// Return a pointer to SPS/PPS with given |sps_id|/|pps_id| or NULL if not
|
||||
// present.
|
||||
const H264SPS* GetSPS(int sps_id) const;
|
||||
const H264PPS* GetPPS(int pps_id) const;
|
||||
|
||||
// Slice headers and SEI messages are not used across NALUs by the parser
|
||||
// and can be discarded after current NALU, so the parser does not store
|
||||
// them, nor does it manage their memory.
|
||||
// The caller has to provide and manage it instead.
|
||||
|
||||
// Parse a slice header, returning it in |*shdr|. |*nalu| must be set to
|
||||
// the NALU returned from AdvanceToNextNALU() and corresponding to |*shdr|.
|
||||
Result ParseSliceHeader(const H264NALU& nalu, H264SliceHeader* shdr);
|
||||
|
||||
// Parse a SEI message, returning it in |*sei_msg|, provided and managed
|
||||
// by the caller.
|
||||
Result ParseSEI(H264SEIMessage* sei_msg);
|
||||
|
||||
private:
|
||||
// Move the stream pointer to the beginning of the next NALU,
|
||||
// i.e. pointing at the next start code.
|
||||
// Return true if a NALU has been found.
|
||||
// If a NALU is found:
|
||||
// - its size in bytes is returned in |*nalu_size| and includes
|
||||
// the start code as well as the trailing zero bits.
|
||||
// - the size in bytes of the start code is returned in |*start_code_size|.
|
||||
bool LocateNALU(off_t* nalu_size, off_t* start_code_size);
|
||||
|
||||
// Exp-Golomb code parsing as specified in chapter 9.1 of the spec.
|
||||
// Read one unsigned exp-Golomb code from the stream and return in |*val|.
|
||||
Result ReadUE(int* val);
|
||||
|
||||
// Read one signed exp-Golomb code from the stream and return in |*val|.
|
||||
Result ReadSE(int* val);
|
||||
|
||||
// Parse scaling lists (see spec).
|
||||
Result ParseScalingList(int size, int* scaling_list, bool* use_default);
|
||||
Result ParseSPSScalingLists(H264SPS* sps);
|
||||
Result ParsePPSScalingLists(const H264SPS& sps, H264PPS* pps);
|
||||
|
||||
// Parse optional VUI parameters in SPS (see spec).
|
||||
Result ParseVUIParameters(H264SPS* sps);
|
||||
// Set |hrd_parameters_present| to true only if they are present.
|
||||
Result ParseAndIgnoreHRDParameters(bool* hrd_parameters_present);
|
||||
|
||||
// Parse reference picture lists' modifications (see spec).
|
||||
Result ParseRefPicListModifications(H264SliceHeader* shdr);
|
||||
Result ParseRefPicListModification(int num_ref_idx_active_minus1,
|
||||
H264ModificationOfPicNum* ref_list_mods);
|
||||
|
||||
// Parse prediction weight table (see spec).
|
||||
Result ParsePredWeightTable(const H264SPS& sps, H264SliceHeader* shdr);
|
||||
|
||||
// Parse weighting factors (see spec).
|
||||
Result ParseWeightingFactors(int num_ref_idx_active_minus1,
|
||||
int chroma_array_type,
|
||||
int luma_log2_weight_denom,
|
||||
int chroma_log2_weight_denom,
|
||||
H264WeightingFactors* w_facts);
|
||||
|
||||
// Parse decoded reference picture marking information (see spec).
|
||||
Result ParseDecRefPicMarking(H264SliceHeader* shdr);
|
||||
|
||||
// Pointer to the current NALU in the stream.
|
||||
const uint8_t* stream_;
|
||||
|
||||
// Bytes left in the stream after the current NALU.
|
||||
off_t bytes_left_;
|
||||
|
||||
H264BitReader br_;
|
||||
|
||||
// PPSes and SPSes stored for future reference.
|
||||
typedef std::map<int, H264SPS*> SPSById;
|
||||
typedef std::map<int, H264PPS*> PPSById;
|
||||
SPSById active_SPSes_;
|
||||
PPSById active_PPSes_;
|
||||
|
||||
// Ranges of encrypted bytes in the buffer passed to
|
||||
// SetEncryptedStream().
|
||||
Ranges<const uint8_t*> encrypted_ranges_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(H264Parser);
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_FILTERS_H264_PARSER_H_
|
232
src/H264/h264_poc.cpp
Normal file
232
src/H264/h264_poc.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
#include "Util/logger.h"
|
||||
#include <algorithm>
|
||||
#include "macros.h"
|
||||
#include "h264_parser.h"
|
||||
#include "h264_poc.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace media {
|
||||
|
||||
H264POC::H264POC() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
H264POC::~H264POC() {
|
||||
}
|
||||
|
||||
void H264POC::Reset() {
|
||||
// It shouldn't be necessary to reset these values, but doing so will improve
|
||||
// reproducibility for buggy streams.
|
||||
ref_pic_order_cnt_msb_ = 0;
|
||||
ref_pic_order_cnt_lsb_ = 0;
|
||||
prev_frame_num_ = 0;
|
||||
prev_frame_num_offset_ = 0;
|
||||
}
|
||||
|
||||
// Check if a slice includes memory management control operation 5, which
|
||||
// results in some |pic_order_cnt| state being cleared.
|
||||
static bool HasMMCO5(const media::H264SliceHeader& slice_hdr) {
|
||||
// Require that the frame actually has memory management control operations.
|
||||
if (slice_hdr.nal_ref_idc == 0 ||
|
||||
slice_hdr.idr_pic_flag ||
|
||||
!slice_hdr.adaptive_ref_pic_marking_mode_flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < arraysize(slice_hdr.ref_pic_marking); i++) {
|
||||
int32_t op = slice_hdr.ref_pic_marking[i].memory_mgmnt_control_operation;
|
||||
if (op == 5)
|
||||
return true;
|
||||
|
||||
// Stop at the end of the list.
|
||||
if (op == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Should not get here, the list is always zero terminated.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool H264POC::ComputePicOrderCnt(
|
||||
const H264SPS* sps,
|
||||
const H264SliceHeader& slice_hdr,
|
||||
int32_t *pic_order_cnt) {
|
||||
if (slice_hdr.field_pic_flag) {
|
||||
DebugL << "Interlaced frames are not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mmco5 = HasMMCO5(slice_hdr);
|
||||
int32_t max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4);
|
||||
int32_t max_pic_order_cnt_lsb =
|
||||
1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
|
||||
|
||||
// Check for invalid (including duplicate) |frame_num| values. All cases are
|
||||
// treated as gaps, which is to say that nothing is done. (Gaps don't affect
|
||||
// POC computation.)
|
||||
if (!slice_hdr.idr_pic_flag &&
|
||||
slice_hdr.frame_num != (prev_frame_num_ + 1) % max_frame_num) {
|
||||
if (!sps->gaps_in_frame_num_value_allowed_flag){
|
||||
//WarnL << "Invalid gap in frame_num";
|
||||
}
|
||||
}
|
||||
|
||||
// Based on T-REC-H.264 8.2.1, "Decoding process for picture order
|
||||
// count", available from http://www.itu.int/rec/T-REC-H.264.
|
||||
//
|
||||
// Reorganized slightly from spec pseudocode to handle MMCO5 when storing
|
||||
// state instead of when loading it.
|
||||
switch (sps->pic_order_cnt_type) {
|
||||
case 0: {
|
||||
int32_t prev_pic_order_cnt_msb = ref_pic_order_cnt_msb_;
|
||||
int32_t prev_pic_order_cnt_lsb = ref_pic_order_cnt_lsb_;
|
||||
|
||||
// For an IDR picture, clear the state.
|
||||
if (slice_hdr.idr_pic_flag) {
|
||||
prev_pic_order_cnt_msb = 0;
|
||||
prev_pic_order_cnt_lsb = 0;
|
||||
}
|
||||
|
||||
// 8-3. Derive |pic_order_cnt_msb|, accounting for wrapping which is
|
||||
// detected when |pic_order_cnt_lsb| increases or decreases by at
|
||||
// least half of its maximum.
|
||||
int32_t pic_order_cnt_msb;
|
||||
if ((slice_hdr.pic_order_cnt_lsb < prev_pic_order_cnt_lsb) &&
|
||||
(prev_pic_order_cnt_lsb - slice_hdr.pic_order_cnt_lsb >=
|
||||
max_pic_order_cnt_lsb / 2)) {
|
||||
pic_order_cnt_msb = prev_pic_order_cnt_msb + max_pic_order_cnt_lsb;
|
||||
} else if ((slice_hdr.pic_order_cnt_lsb > prev_pic_order_cnt_lsb) &&
|
||||
(slice_hdr.pic_order_cnt_lsb - prev_pic_order_cnt_lsb >
|
||||
max_pic_order_cnt_lsb / 2)) {
|
||||
pic_order_cnt_msb = prev_pic_order_cnt_msb - max_pic_order_cnt_lsb;
|
||||
} else {
|
||||
pic_order_cnt_msb = prev_pic_order_cnt_msb;
|
||||
}
|
||||
|
||||
// 8-4, 8-5. Derive |top_field_order_count| and |bottom_field_order_cnt|
|
||||
// (assuming no interlacing).
|
||||
int32_t top_foc = pic_order_cnt_msb + slice_hdr.pic_order_cnt_lsb;
|
||||
int32_t bottom_foc = top_foc + slice_hdr.delta_pic_order_cnt_bottom;
|
||||
*pic_order_cnt = std::min(top_foc, bottom_foc);
|
||||
|
||||
// Store state.
|
||||
prev_frame_num_ = slice_hdr.frame_num;
|
||||
if (slice_hdr.nal_ref_idc != 0) {
|
||||
if (mmco5) {
|
||||
ref_pic_order_cnt_msb_ = 0;
|
||||
ref_pic_order_cnt_lsb_ = top_foc;
|
||||
} else {
|
||||
ref_pic_order_cnt_msb_ = pic_order_cnt_msb;
|
||||
ref_pic_order_cnt_lsb_ = slice_hdr.pic_order_cnt_lsb;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
// 8-6. Derive |frame_num_offset|.
|
||||
int32_t frame_num_offset;
|
||||
if (slice_hdr.idr_pic_flag)
|
||||
frame_num_offset = 0;
|
||||
else if (prev_frame_num_ > slice_hdr.frame_num)
|
||||
frame_num_offset = prev_frame_num_offset_ + max_frame_num;
|
||||
else
|
||||
frame_num_offset = prev_frame_num_offset_;
|
||||
|
||||
// 8-7. Derive |abs_frame_num|.
|
||||
int32_t abs_frame_num;
|
||||
if (sps->num_ref_frames_in_pic_order_cnt_cycle != 0)
|
||||
abs_frame_num = frame_num_offset + slice_hdr.frame_num;
|
||||
else
|
||||
abs_frame_num = 0;
|
||||
|
||||
if (slice_hdr.nal_ref_idc == 0 && abs_frame_num > 0)
|
||||
abs_frame_num--;
|
||||
|
||||
// 8-9. Derive |expected_pic_order_cnt| (the |pic_order_cnt| indicated
|
||||
// by the cycle described in the SPS).
|
||||
int32_t expected_pic_order_cnt = 0;
|
||||
if (abs_frame_num > 0) {
|
||||
// 8-8. Derive pic_order_cnt_cycle_cnt and
|
||||
// frame_num_in_pic_order_cnt_cycle.
|
||||
// Moved inside 8-9 to avoid division when this check is not done.
|
||||
if (sps->num_ref_frames_in_pic_order_cnt_cycle == 0) {
|
||||
ErrorL << "Invalid num_ref_frames_in_pic_order_cnt_cycle";
|
||||
return false;
|
||||
}
|
||||
|
||||
// H264Parser checks that num_ref_frames_in_pic_order_cnt_cycle < 255.
|
||||
int32_t pic_order_cnt_cycle_cnt =
|
||||
(abs_frame_num - 1) / sps->num_ref_frames_in_pic_order_cnt_cycle;
|
||||
int32_t frame_num_in_pic_order_cnt_cycle =
|
||||
(abs_frame_num - 1) % sps->num_ref_frames_in_pic_order_cnt_cycle;
|
||||
|
||||
// 8-9 continued.
|
||||
expected_pic_order_cnt = pic_order_cnt_cycle_cnt *
|
||||
sps->expected_delta_per_pic_order_cnt_cycle;
|
||||
for (int32_t i = 0; i <= frame_num_in_pic_order_cnt_cycle; i++)
|
||||
expected_pic_order_cnt += sps->offset_for_ref_frame[i];
|
||||
}
|
||||
if (slice_hdr.nal_ref_idc == 0)
|
||||
expected_pic_order_cnt += sps->offset_for_non_ref_pic;
|
||||
|
||||
// 8-10. Derive |top_field_order_cnt| and |bottom_field_order_cnt|
|
||||
// (assuming no interlacing).
|
||||
int32_t top_foc = expected_pic_order_cnt + slice_hdr.delta_pic_order_cnt0;
|
||||
int32_t bottom_foc = top_foc + sps->offset_for_top_to_bottom_field +
|
||||
slice_hdr.delta_pic_order_cnt1;
|
||||
*pic_order_cnt = std::min(top_foc, bottom_foc);
|
||||
|
||||
// Store state.
|
||||
prev_frame_num_ = slice_hdr.frame_num;
|
||||
prev_frame_num_offset_ = frame_num_offset;
|
||||
if (mmco5)
|
||||
prev_frame_num_offset_ = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
// 8-11. Derive |frame_num_offset|.
|
||||
int32_t frame_num_offset;
|
||||
if (slice_hdr.idr_pic_flag)
|
||||
frame_num_offset = 0;
|
||||
else if (prev_frame_num_ > slice_hdr.frame_num)
|
||||
frame_num_offset = prev_frame_num_offset_ + max_frame_num;
|
||||
else
|
||||
frame_num_offset = prev_frame_num_offset_;
|
||||
|
||||
// 8-12, 8-13. Derive |temp_pic_order_count| (it's always the
|
||||
// |pic_order_cnt|, regardless of interlacing).
|
||||
if (slice_hdr.idr_pic_flag)
|
||||
*pic_order_cnt = 0;
|
||||
else if (slice_hdr.nal_ref_idc == 0)
|
||||
*pic_order_cnt = 2 * (frame_num_offset + slice_hdr.frame_num) - 1;
|
||||
else
|
||||
*pic_order_cnt = 2 * (frame_num_offset + slice_hdr.frame_num);
|
||||
|
||||
// Store state.
|
||||
prev_frame_num_ = slice_hdr.frame_num;
|
||||
prev_frame_num_offset_ = frame_num_offset;
|
||||
if (mmco5)
|
||||
prev_frame_num_offset_ = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ErrorL << "Invalid pic_order_cnt_type: " << sps->pic_order_cnt_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace media
|
45
src/H264/h264_poc.h
Normal file
45
src/H264/h264_poc.h
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef MEDIA_VIDEO_H264_POC_H_
|
||||
#define MEDIA_VIDEO_H264_POC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "macros.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace media {
|
||||
|
||||
struct H264SPS;
|
||||
struct H264SliceHeader;
|
||||
|
||||
class MEDIA_EXPORT H264POC {
|
||||
public:
|
||||
H264POC();
|
||||
~H264POC();
|
||||
|
||||
// Compute the picture order count for a slice, storing the result into
|
||||
// |*pic_order_cnt|.
|
||||
bool ComputePicOrderCnt(
|
||||
const H264SPS* sps,
|
||||
const H264SliceHeader& slice_hdr,
|
||||
int32_t* pic_order_cnt);
|
||||
|
||||
// Reset computation state. It's best (although not strictly required) to call
|
||||
// this after a seek.
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
int32_t ref_pic_order_cnt_msb_;
|
||||
int32_t ref_pic_order_cnt_lsb_;
|
||||
int32_t prev_frame_num_;
|
||||
int32_t prev_frame_num_offset_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(H264POC);
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_VIDEO_H264_POC_H_
|
100
src/H264/macros.h
Normal file
100
src/H264/macros.h
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file contains macros and macro-like constructs (e.g., templates) that
|
||||
// are commonly used throughout Chromium source. (It may also contain things
|
||||
// that are closely related to things that are commonly used that belong in this
|
||||
// file.)
|
||||
|
||||
#ifndef BASE_MACROS_H_
|
||||
#define BASE_MACROS_H_
|
||||
|
||||
#include <stddef.h> // For size_t.
|
||||
|
||||
// Put this in the declarations for a class to be uncopyable.
|
||||
#define DISALLOW_COPY(TypeName) \
|
||||
TypeName(const TypeName&) = delete
|
||||
|
||||
// Put this in the declarations for a class to be unassignable.
|
||||
#define DISALLOW_ASSIGN(TypeName) \
|
||||
void operator=(const TypeName&) = delete
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions.
|
||||
// This should be used in the private: declarations for a class.
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
void operator=(const TypeName&) = delete
|
||||
|
||||
// A macro to disallow all the implicit constructors, namely the
|
||||
// default constructor, copy constructor and operator= functions.
|
||||
//
|
||||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName() = delete; \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr. The
|
||||
// expression is a compile-time constant, and therefore can be used in defining
|
||||
// new arrays, for example. If you use arraysize on a pointer by mistake, you
|
||||
// will get a compile-time error. For the technical details, refer to
|
||||
// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx.
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
// Used to explicitly mark the return value of a function as unused. If you are
|
||||
// really sure you don't want to do anything with the return value of a function
|
||||
// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
|
||||
//
|
||||
// std::unique_ptr<MyType> my_var = ...;
|
||||
// if (TakeOwnership(my_var.get()) == SUCCESS)
|
||||
// ignore_result(my_var.release());
|
||||
//
|
||||
template<typename T>
|
||||
inline void ignore_result(const T&) {
|
||||
}
|
||||
|
||||
// The following enum should be used only as a constructor argument to indicate
|
||||
// that the variable has static storage class, and that the constructor should
|
||||
// do nothing to its state. It indicates to the reader that it is legal to
|
||||
// declare a static instance of the class, provided the constructor is given
|
||||
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
|
||||
// static variable that has a constructor or a destructor because invocation
|
||||
// order is undefined. However, IF the type can be initialized by filling with
|
||||
// zeroes (which the loader does for static variables), AND the destructor also
|
||||
// does nothing to the storage, AND there are no virtual methods, then a
|
||||
// constructor declared as
|
||||
// explicit MyClass(base::LinkerInitialized x) {}
|
||||
// and invoked as
|
||||
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
|
||||
namespace base {
|
||||
enum LinkerInitialized { LINKER_INITIALIZED };
|
||||
|
||||
// Use these to declare and define a static local variable (static T;) so that
|
||||
// it is leaked so that its destructors are not called at exit. If you need
|
||||
// thread-safe initialization, use base/lazy_instance.h instead.
|
||||
#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \
|
||||
static type& name = *new type arguments
|
||||
|
||||
} // base
|
||||
|
||||
#define MEDIA_EXPORT
|
||||
|
||||
#include <assert.h>
|
||||
#include "Util/logger.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
#define DCHECK(x) if(!(x)) { ErrorL << "DCHECK " << #x <<endl; }
|
||||
#define DCHECK_GT(x,y) if(!((x) > (y))) { ErrorL << "DCHECK_GT:" << #x << #y << endl; }
|
||||
#define DCHECK_GE(x,y) if(!((x) >= (y))) { ErrorL << "DCHECK_GE:" << #x << #y << endl; }
|
||||
#define DCHECK_LT(x,y) if(!((x) < (y))) { ErrorL << "DCHECK_LT:" << #x << #y << endl; }
|
||||
#define NOTREACHED() ErrorL << "NOTREACHED" << endl;
|
||||
|
||||
|
||||
#endif // BASE_MACROS_H_
|
9
src/H264/ranges.cpp
Normal file
9
src/H264/ranges.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
//
|
||||
// ranges.cpp
|
||||
// MediaPlayer
|
||||
//
|
||||
// Created by xzl on 2017/1/13.
|
||||
// Copyright © 2017年 jizan. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ranges.h"
|
154
src/H264/ranges.h
Normal file
154
src/H264/ranges.h
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef MEDIA_BASE_RANGES_H_
|
||||
#define MEDIA_BASE_RANGES_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace media {
|
||||
|
||||
// Ranges allows holding an ordered list of ranges of [start,end) intervals.
|
||||
// The canonical example use-case is holding the list of ranges of buffered
|
||||
// bytes or times in a <video> tag.
|
||||
template <typename T> // Endpoint type; typically a base::TimeDelta or an int64_t.
|
||||
class Ranges {
|
||||
public:
|
||||
// Allow copy & assign.
|
||||
|
||||
// Add (start,end) to this object, coallescing overlaps as appropriate.
|
||||
// Returns the number of stored ranges, post coallescing.
|
||||
size_t Add(T start, T end);
|
||||
|
||||
// Return the number of disjoint ranges.
|
||||
size_t size() const;
|
||||
|
||||
// Return the "i"'th range's start & end (0-based).
|
||||
T start(size_t i) const;
|
||||
T end(size_t i) const;
|
||||
|
||||
// Clear all ranges.
|
||||
void clear();
|
||||
|
||||
// Computes the intersection between this range and |other|.
|
||||
Ranges<T> IntersectionWith(const Ranges<T>& other) const;
|
||||
|
||||
private:
|
||||
// Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's.
|
||||
void DCheckLT(const T& lhs, const T& rhs) const;
|
||||
|
||||
// Disjoint, in increasing order of start.
|
||||
std::vector<std::pair<T, T> > ranges_;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// EVERYTHING BELOW HERE IS IMPLEMENTATION DETAIL!!
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class T>
|
||||
size_t Ranges<T>::Add(T start, T end) {
|
||||
if (start == end) // Nothing to be done with empty ranges.
|
||||
return ranges_.size();
|
||||
|
||||
//DCheckLT(start, end);
|
||||
size_t i;
|
||||
// Walk along the array of ranges until |start| is no longer larger than the
|
||||
// current interval's end.
|
||||
for (i = 0; i < ranges_.size() && ranges_[i].second < start; ++i) {
|
||||
// Empty body
|
||||
}
|
||||
|
||||
// Now we know |start| belongs in the i'th slot.
|
||||
// If i is the end of the range, append new range and done.
|
||||
if (i == ranges_.size()) {
|
||||
ranges_.push_back(std::make_pair(start, end));
|
||||
return ranges_.size();
|
||||
}
|
||||
|
||||
// If |end| is less than i->first, then [start,end) is a new (non-overlapping)
|
||||
// i'th entry pushing everyone else back, and done.
|
||||
if (end < ranges_[i].first) {
|
||||
ranges_.insert(ranges_.begin() + i, std::make_pair(start, end));
|
||||
return ranges_.size();
|
||||
}
|
||||
|
||||
// Easy cases done. Getting here means there is overlap between [start,end)
|
||||
// and the existing ranges.
|
||||
|
||||
// Now: start <= i->second && i->first <= end
|
||||
if (start < ranges_[i].first)
|
||||
ranges_[i].first = start;
|
||||
if (ranges_[i].second < end)
|
||||
ranges_[i].second = end;
|
||||
|
||||
// Now: [start,end) is contained in the i'th range, and we'd be done, except
|
||||
// for the fact that the newly-extended i'th range might now overlap
|
||||
// subsequent ranges. Merge until discontinuities appear. Note that there's
|
||||
// no need to test/merge previous ranges, since needing that would mean the
|
||||
// original loop went too far.
|
||||
while ((i + 1) < ranges_.size() &&
|
||||
ranges_[i + 1].first <= ranges_[i].second) {
|
||||
ranges_[i].second = std::max(ranges_[i].second, ranges_[i + 1].second);
|
||||
ranges_.erase(ranges_.begin() + i + 1);
|
||||
}
|
||||
|
||||
return ranges_.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
size_t Ranges<T>::size() const {
|
||||
return ranges_.size();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Ranges<T>::start(size_t i) const {
|
||||
return ranges_[i].first;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Ranges<T>::end(size_t i) const {
|
||||
return ranges_[i].second;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Ranges<T>::clear() {
|
||||
ranges_.clear();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) const {
|
||||
Ranges<T> result;
|
||||
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
|
||||
while (i < size() && j < other.size()) {
|
||||
T max_start = std::max(start(i), other.start(j));
|
||||
T min_end = std::min(end(i), other.end(j));
|
||||
|
||||
// Add an intersection range to the result if the ranges overlap.
|
||||
if (max_start < min_end)
|
||||
result.Add(max_start, min_end);
|
||||
|
||||
if (end(i) < other.end(j))
|
||||
++i;
|
||||
else
|
||||
++j;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_BASE_RANGES_H_
|
410
src/Http/HttpSession.cpp
Normal file
410
src/Http/HttpSession.cpp
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* HttpSession.cpp
|
||||
*
|
||||
* Created on: 2016年9月22日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <algorithm>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "strCoding.h"
|
||||
#include "HttpSession.h"
|
||||
#include "Util/File.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Util/onceToken.h"
|
||||
#include "Util/mini.hpp"
|
||||
#include "Util/NoticeCenter.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Http {
|
||||
|
||||
unordered_map<string, HttpSession::HttpCMDHandle> HttpSession::g_mapCmdIndex;
|
||||
string dateStr() {
|
||||
char buf[64];
|
||||
time_t tt = time(NULL);
|
||||
strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
|
||||
return buf;
|
||||
}
|
||||
static const char*
|
||||
get_mime_type(const char* name) {
|
||||
const char* dot;
|
||||
dot = strrchr(name, '.');
|
||||
static unordered_map<string, string> mapType;
|
||||
static onceToken token([&]() {
|
||||
mapType.emplace(".html","text/html");
|
||||
mapType.emplace(".htm","text/html");
|
||||
mapType.emplace(".mp4","video/mp4");
|
||||
mapType.emplace(".m3u8","application/vnd.apple.mpegurl");
|
||||
mapType.emplace(".jpg","image/jpeg");
|
||||
mapType.emplace(".jpeg","image/jpeg");
|
||||
mapType.emplace(".gif","image/gif");
|
||||
mapType.emplace(".png","image/png");
|
||||
mapType.emplace(".ico","image/x-icon");
|
||||
mapType.emplace(".css","text/css");
|
||||
mapType.emplace(".js","application/javascript");
|
||||
mapType.emplace(".au","audio/basic");
|
||||
mapType.emplace(".wav","audio/wav");
|
||||
mapType.emplace(".avi","video/x-msvideo");
|
||||
mapType.emplace(".mov","video/quicktime");
|
||||
mapType.emplace(".qt","video/quicktime");
|
||||
mapType.emplace(".mpeg","video/mpeg");
|
||||
mapType.emplace(".mpe","video/mpeg");
|
||||
mapType.emplace(".vrml","model/vrml");
|
||||
mapType.emplace(".wrl","model/vrml");
|
||||
mapType.emplace(".midi","audio/midi");
|
||||
mapType.emplace(".mid","audio/midi");
|
||||
mapType.emplace(".mp3","audio/mpeg");
|
||||
mapType.emplace(".ogg","application/ogg");
|
||||
mapType.emplace(".pac","application/x-ns-proxy-autoconfig");
|
||||
}, nullptr);
|
||||
if(!dot){
|
||||
return "text/plain";
|
||||
}
|
||||
string strDot(dot);
|
||||
transform(strDot.begin(), strDot.end(), strDot.begin(), (int (*)(int))tolower);
|
||||
auto it = mapType.find(strDot);
|
||||
if (it == mapType.end()) {
|
||||
return "text/plain";
|
||||
}
|
||||
return it->second.data();
|
||||
}
|
||||
|
||||
|
||||
HttpSession::HttpSession(const std::shared_ptr<ThreadPool> &pTh, const Socket::Ptr &pSock) :
|
||||
TcpLimitedSession(pTh, pSock) {
|
||||
static string rootPath = mINI::Instance()[Config::Http::kRootPath];
|
||||
m_strPath = rootPath;
|
||||
static onceToken token([]() {
|
||||
g_mapCmdIndex.emplace("GET",&HttpSession::Handle_Req_GET);
|
||||
g_mapCmdIndex.emplace("POST",&HttpSession::Handle_Req_POST);
|
||||
}, nullptr);
|
||||
}
|
||||
|
||||
HttpSession::~HttpSession() {
|
||||
//DebugL;
|
||||
}
|
||||
|
||||
void HttpSession::onRecv(const Socket::Buffer::Ptr&pBuf) {
|
||||
static uint32_t reqSize = mINI::Instance()[Config::Http::kMaxReqSize].as<uint32_t>();
|
||||
m_ticker.resetTime();
|
||||
if (m_strRcvBuf.size() + pBuf->size() >= reqSize) {
|
||||
WarnL << "接收缓冲区溢出!";
|
||||
shutdown();
|
||||
return;
|
||||
}
|
||||
m_strRcvBuf.append(pBuf->data(), pBuf->size());
|
||||
size_t index;
|
||||
string onePkt;
|
||||
while ((index = m_strRcvBuf.find("\r\n\r\n")) != std::string::npos) {
|
||||
onePkt = m_strRcvBuf.substr(0, index + 4);
|
||||
m_strRcvBuf.erase(0, index + 4);
|
||||
switch (parserHttpReq(onePkt)) {
|
||||
case 0:
|
||||
//失败
|
||||
shutdown();
|
||||
return;
|
||||
case 1:
|
||||
//成功
|
||||
break;
|
||||
case 2:
|
||||
//需要更多数据,恢复数据并退出
|
||||
m_strRcvBuf = onePkt + m_strRcvBuf;
|
||||
m_parser.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_parser.Clear();
|
||||
}
|
||||
inline int HttpSession::parserHttpReq(const string &str) {
|
||||
m_parser.Parse(str.data());
|
||||
string cmd = m_parser.Method();
|
||||
auto it = g_mapCmdIndex.find(cmd);
|
||||
if (it == g_mapCmdIndex.end()) {
|
||||
WarnL << cmd;
|
||||
sendResponse("403 Forbidden", makeHttpHeader(true), "");
|
||||
return false;
|
||||
}
|
||||
auto fun = it->second;
|
||||
return (this->*fun)();
|
||||
}
|
||||
void HttpSession::onError(const SockException& err) {
|
||||
//WarnL << err.what();
|
||||
}
|
||||
|
||||
void HttpSession::onManager() {
|
||||
static uint32_t keepAliveSec = mINI::Instance()[Config::Http::kKeepAliveSecond].as<uint32_t>();
|
||||
if(m_ticker.elapsedTime() > keepAliveSec * 1000){
|
||||
//1分钟超时
|
||||
WarnL<<"HttpSession超时断开!";
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
inline int HttpSession::Handle_Req_GET() {
|
||||
string strUrl = strCoding::UrlUTF8Decode(m_parser.Url());
|
||||
string strFile = m_strPath + strUrl;
|
||||
string strConType = m_parser["Connection"];
|
||||
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
||||
bool bClose = (strcasecmp(strConType.data(),"close") == 0) && ( ++m_iReqCnt < reqCnt);
|
||||
if (strFile.back() == '/') {
|
||||
//index the folder
|
||||
string strMeun;
|
||||
if (!makeMeun(strFile, strMeun)) {
|
||||
sendNotFound(bClose);
|
||||
return !bClose;
|
||||
}
|
||||
sendResponse("200 OK", makeHttpHeader(bClose,strMeun.size() ), strMeun);
|
||||
return !bClose;
|
||||
}
|
||||
//download the file
|
||||
struct stat tFileStat;
|
||||
if (0 != stat(strFile.data(), &tFileStat)) {
|
||||
sendNotFound(bClose);
|
||||
return !bClose;
|
||||
}
|
||||
|
||||
TimeTicker();
|
||||
FILE *pFile = fopen(strFile.data(), "rb");
|
||||
if (pFile == NULL) {
|
||||
sendNotFound(bClose);
|
||||
return !bClose;
|
||||
}
|
||||
|
||||
auto &strRange = m_parser["Range"];
|
||||
int64_t iRangeStart = 0, iRangeEnd = 0;
|
||||
iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data());
|
||||
iRangeEnd = atoll(FindField(strRange.data(), "-", "\r\n").data());
|
||||
if (iRangeEnd == 0) {
|
||||
iRangeEnd = tFileStat.st_size - 1;
|
||||
}
|
||||
const char *pcHttpResult = NULL;
|
||||
if (strRange.size() == 0) {
|
||||
pcHttpResult = "200 OK";
|
||||
} else {
|
||||
pcHttpResult = "206 Partial Content";
|
||||
fseek(pFile, iRangeStart, SEEK_SET);
|
||||
}
|
||||
|
||||
auto httpHeader=makeHttpHeader(bClose, iRangeEnd - iRangeStart + 1, get_mime_type(strUrl.data()));
|
||||
if (strRange.size() != 0) {
|
||||
httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
|
||||
}
|
||||
|
||||
sendResponse(pcHttpResult, httpHeader, "");
|
||||
if (iRangeEnd - iRangeStart < 0) {
|
||||
//file is empty!
|
||||
return !bClose;
|
||||
}
|
||||
|
||||
//send the file
|
||||
std::shared_ptr<int64_t> piLeft(new int64_t(iRangeEnd - iRangeStart + 1));
|
||||
std::shared_ptr<FILE> pFilePtr(pFile, [](FILE *pFp) {
|
||||
fclose(pFp);
|
||||
});
|
||||
static uint32_t sendBufSize = mINI::Instance()[Config::Http::kSendBufSize].as<uint32_t>();
|
||||
std::shared_ptr<char> pacSendBuf(new char[sendBufSize],[](char *ptr){
|
||||
delete [] ptr;
|
||||
});
|
||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||
auto onFlush = [pFilePtr,bClose,weakSelf,piLeft,pacSendBuf]() {
|
||||
TimeTicker();
|
||||
auto strongSelf = weakSelf.lock();
|
||||
while(*piLeft && strongSelf){
|
||||
strongSelf->m_ticker.resetTime();
|
||||
int64_t iReq = MIN(sendBufSize,*piLeft);
|
||||
int64_t iRead = fread(pacSendBuf.get(), 1, iReq, pFilePtr.get());
|
||||
*piLeft -= iRead;
|
||||
//InfoL << "Send file :" << iReq << " " << *piLeft;
|
||||
if (iRead < iReq || !*piLeft) {
|
||||
//send completed!
|
||||
//FatalL << "send completed!";
|
||||
if(iRead>0) {
|
||||
strongSelf->send(pacSendBuf.get(), iRead);
|
||||
}
|
||||
if(bClose) {
|
||||
strongSelf->shutdown();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int iSent=strongSelf->send(pacSendBuf.get(), iRead);
|
||||
if(iSent == -1) {
|
||||
//send error
|
||||
//FatalL << "send error";
|
||||
return false;
|
||||
}
|
||||
if(iSent < iRead) {
|
||||
//send wait
|
||||
//FatalL << "send wait";
|
||||
return true;
|
||||
}
|
||||
//send success
|
||||
}
|
||||
return false;
|
||||
};
|
||||
onFlush();
|
||||
sock->setOnFlush(onFlush);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool HttpSession::makeMeun(const string &strFullPath, string &strRet) {
|
||||
string strPathPrefix(strFullPath);
|
||||
strPathPrefix = strPathPrefix.substr(0, strPathPrefix.length() - 1);
|
||||
if (!File::is_dir(strPathPrefix.data())) {
|
||||
return false;
|
||||
}
|
||||
strRet = "<html>\r\n"
|
||||
"<head>\r\n"
|
||||
"<title>文件索引</title>\r\n"
|
||||
"</head>\r\n"
|
||||
"<body>\r\n"
|
||||
"<h1>文件索引:";
|
||||
|
||||
string strPath = strFullPath;
|
||||
strPath = strPath.substr(m_strPath.length(), strFullPath.length() - m_strPath.length());
|
||||
strRet += strPath;
|
||||
strRet += "</h1>\r\n";
|
||||
if (strPath != "/") {
|
||||
strRet += "<li><a href=\"";
|
||||
strRet += "/";
|
||||
strRet += "\">";
|
||||
strRet += "根目录";
|
||||
strRet += "</a></li>\r\n";
|
||||
|
||||
strRet += "<li><a href=\"";
|
||||
strRet += "../";
|
||||
strRet += "\">";
|
||||
strRet += "上一级目录";
|
||||
strRet += "</a></li>\r\n";
|
||||
}
|
||||
|
||||
DIR *pDir;
|
||||
dirent *pDirent;
|
||||
if ((pDir = opendir(strPathPrefix.data())) == NULL) {
|
||||
return false;
|
||||
}
|
||||
set<string> setFile;
|
||||
while ((pDirent = readdir(pDir)) != NULL) {
|
||||
if (File::is_special_dir(pDirent->d_name)) {
|
||||
continue;
|
||||
}
|
||||
if(pDirent->d_name[0] == '.'){
|
||||
continue;
|
||||
}
|
||||
setFile.emplace(pDirent->d_name);
|
||||
}
|
||||
for(auto &strFile :setFile ){
|
||||
string strAbsolutePath = strPathPrefix + "/" + strFile;
|
||||
if (File::is_dir(strAbsolutePath.data())) {
|
||||
strRet += "<li><a href=\"";
|
||||
strRet += strFile;
|
||||
strRet += "/\">";
|
||||
strRet += strFile;
|
||||
strRet += "/</a></li>\r\n";
|
||||
} else { //是文件
|
||||
strRet += "<li><a href=\"";
|
||||
strRet += strFile;
|
||||
strRet += "\">";
|
||||
strRet += strFile;
|
||||
struct stat fileData;
|
||||
if (0 == stat(strAbsolutePath.data(), &fileData)) {
|
||||
auto &fileSize = fileData.st_size;
|
||||
if (fileSize < 1024) {
|
||||
strRet += StrPrinter << " (" << fileData.st_size << "B)" << endl;
|
||||
} else if (fileSize < 1024 * 1024) {
|
||||
strRet += StrPrinter << " (" << fileData.st_size / 1024 << "KB)" << endl;
|
||||
} else if (fileSize < 1024 * 1024 * 1024) {
|
||||
strRet += StrPrinter << " (" << fileData.st_size / 1024 / 1024 << "MB)" << endl;
|
||||
} else {
|
||||
strRet += StrPrinter << " (" << fileData.st_size / 1024 / 1024 / 1024 << "GB)" << endl;
|
||||
}
|
||||
}
|
||||
strRet += "</a></li>\r\n";
|
||||
}
|
||||
}
|
||||
closedir(pDir);
|
||||
strRet += "<ul>\r\n";
|
||||
strRet += "</ul>\r\n</body></html>";
|
||||
return true;
|
||||
}
|
||||
inline void HttpSession::sendResponse(const char* pcStatus, const KeyValue& header, const string& strContent) {
|
||||
_StrPrinter printer;
|
||||
printer << "HTTP/1.1 " << pcStatus << " \r\n";
|
||||
for (auto &pr : header) {
|
||||
printer << pr.first << ": " << pr.second << "\r\n";
|
||||
}
|
||||
printer << "\r\n" << strContent;
|
||||
auto strSend = printer << endl;
|
||||
//DebugL << strSend;
|
||||
send(strSend);
|
||||
m_ticker.resetTime();
|
||||
}
|
||||
inline HttpSession::KeyValue HttpSession::makeHttpHeader(bool bClose, int64_t iContentSize,const char* pcContentType) {
|
||||
KeyValue headerOut;
|
||||
static string serverName = mINI::Instance()[Config::Http::kServerName];
|
||||
static string charSet = mINI::Instance()[Config::Http::kCharSet];
|
||||
static uint32_t keepAliveSec = mINI::Instance()[Config::Http::kKeepAliveSecond].as<uint32_t>();
|
||||
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
||||
|
||||
headerOut.emplace("Server", serverName);
|
||||
headerOut.emplace("Connection", bClose ? "close" :
|
||||
StrPrinter << "keep-alive: timeout=" << keepAliveSec
|
||||
<< ", max=" << reqCnt << endl);
|
||||
headerOut.emplace("Date", dateStr());
|
||||
if(iContentSize >=0 && pcContentType !=nullptr){
|
||||
auto strContentType = StrPrinter << pcContentType << "; charset=" << charSet << endl;
|
||||
headerOut.emplace("Content-Type",strContentType.data());
|
||||
headerOut.emplace("Content-Length", StrPrinter<<iContentSize<<endl);
|
||||
}
|
||||
return headerOut;
|
||||
}
|
||||
inline int HttpSession::Handle_Req_POST() {
|
||||
int iContentLen = atoi(m_parser["Content-Length"].data());
|
||||
if (!iContentLen) {
|
||||
return false;
|
||||
}
|
||||
if ((int) m_strRcvBuf.size() < iContentLen) {
|
||||
return 2; //需要更多数据
|
||||
}
|
||||
auto strContent = m_strRcvBuf.substr(0, iContentLen);
|
||||
m_strRcvBuf.erase(0, iContentLen);
|
||||
|
||||
string strUrl = strCoding::UrlUTF8Decode(m_parser.Url());
|
||||
string strConType = m_parser["Connection"];
|
||||
static string charSet = mINI::Instance()[Config::Http::kCharSet];
|
||||
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
||||
|
||||
bool bClose = (strcasecmp(strConType.data(),"close") == 0) && ( ++m_iReqCnt < reqCnt);
|
||||
m_parser.setUrl(strUrl);
|
||||
m_parser.setContent(strContent);
|
||||
auto headerOut=makeHttpHeader(bClose);
|
||||
|
||||
static string notFound = mINI::Instance()[Config::Http::kNotFound];
|
||||
string strContentOut = notFound;
|
||||
string strCodeOut = "404 Not Found";
|
||||
NoticeCenter::Instance().emitEvent(
|
||||
Config::Broadcast::kBroadcastHttpRequest,
|
||||
m_parser,strCodeOut,headerOut,strContentOut);
|
||||
|
||||
if(strContentOut.size()){
|
||||
headerOut.emplace("Content-Type",StrPrinter<<"text/json; charset=" << charSet <<endl);
|
||||
headerOut.emplace("Content-Length",StrPrinter<<strContentOut.size()<<endl);
|
||||
}
|
||||
sendResponse(strCodeOut.data(), headerOut, strContentOut);
|
||||
return !bClose;
|
||||
}
|
||||
|
||||
inline void HttpSession::sendNotFound(bool bClose) {
|
||||
static string notFound = mINI::Instance()[Config::Http::kNotFound];
|
||||
sendResponse("404 Not Found", makeHttpHeader(bClose, notFound.size()), notFound.data());
|
||||
}
|
||||
|
||||
} /* namespace Http */
|
||||
} /* namespace ZL */
|
51
src/Http/HttpSession.h
Normal file
51
src/Http/HttpSession.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* HttpSession.h
|
||||
*
|
||||
* Created on: 2016年9月22日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_HTTP_HTTPSESSION_H_
|
||||
#define SRC_HTTP_HTTPSESSION_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "Rtsp/Rtsp.h"
|
||||
#include "Network/TcpLimitedSession.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Network;
|
||||
|
||||
namespace ZL {
|
||||
namespace Http {
|
||||
|
||||
class HttpSession: public TcpLimitedSession<MAX_TCP_SESSION> {
|
||||
public:
|
||||
typedef map<string,string> KeyValue;
|
||||
HttpSession(const std::shared_ptr<ThreadPool> &pTh, const Socket::Ptr &pSock);
|
||||
virtual ~HttpSession();
|
||||
|
||||
void onRecv(const Socket::Buffer::Ptr &) override;
|
||||
void onError(const SockException &err) override;
|
||||
void onManager() override;
|
||||
private:
|
||||
typedef int (HttpSession::*HttpCMDHandle)();
|
||||
static unordered_map<string, HttpCMDHandle> g_mapCmdIndex;
|
||||
Parser m_parser;
|
||||
string m_strPath;
|
||||
string m_strRcvBuf;
|
||||
Ticker m_ticker;
|
||||
uint32_t m_iReqCnt = 0;
|
||||
|
||||
inline int parserHttpReq(const string &);
|
||||
inline int Handle_Req_GET();
|
||||
inline int Handle_Req_POST();
|
||||
inline bool makeMeun(const string &strFullPath, string &strRet);
|
||||
inline void sendNotFound(bool bClose);
|
||||
inline void sendResponse(const char *pcStatus,const KeyValue &header,const string &strContent);
|
||||
inline KeyValue makeHttpHeader(bool bClose=false,int64_t iContentSize=-1,const char *pcContentType="text/html");
|
||||
};
|
||||
|
||||
} /* namespace Http */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_HTTP_HTTPSESSION_H_ */
|
72
src/Http/strCoding.cpp
Normal file
72
src/Http/strCoding.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* strCoding.cpp
|
||||
*
|
||||
* Created on: 2016年9月22日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "strCoding.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace ZL {
|
||||
namespace Http {
|
||||
|
||||
inline char strCoding::CharToInt(char ch) {
|
||||
if (ch >= '0' && ch <= '9')
|
||||
return (char) (ch - '0');
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
return (char) (ch - 'a' + 10);
|
||||
if (ch >= 'A' && ch <= 'F')
|
||||
return (char) (ch - 'A' + 10);
|
||||
return -1;
|
||||
}
|
||||
inline char strCoding::StrToBin(const char *str) {
|
||||
char tempWord[2];
|
||||
char chn;
|
||||
tempWord[0] = CharToInt(str[0]); //make the B to 11 -- 00001011
|
||||
tempWord[1] = CharToInt(str[1]); //make the 0 to 0 -- 00000000
|
||||
chn = (tempWord[0] << 4) | tempWord[1]; //to change the BO to 10110000
|
||||
return chn;
|
||||
}
|
||||
string strCoding::UrlUTF8Encode(const char * str) {
|
||||
string dd;
|
||||
size_t len = strlen(str);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (isalnum((uint8_t) str[i])) {
|
||||
char tempbuff[2];
|
||||
sprintf(tempbuff, "%c", str[i]);
|
||||
dd.append(tempbuff);
|
||||
} else if (isspace((uint8_t) str[i])) {
|
||||
dd.append("+");
|
||||
} else {
|
||||
char tempbuff[4];
|
||||
sprintf(tempbuff, "%%%X%X", ((uint8_t*) str)[i] >> 4,
|
||||
((uint8_t*) str)[i] % 16);
|
||||
dd.append(tempbuff);
|
||||
}
|
||||
}
|
||||
return dd;
|
||||
}
|
||||
string strCoding::UrlUTF8Decode(const string &str) {
|
||||
string output = "";
|
||||
char tmp[2];
|
||||
int i = 0, len = str.length();
|
||||
while (i < len) {
|
||||
if (str[i] == '%') {
|
||||
tmp[0] = str[i + 1];
|
||||
tmp[1] = str[i + 2];
|
||||
output += StrToBin(tmp);
|
||||
i = i + 3;
|
||||
} else if (str[i] == '+') {
|
||||
output += ' ';
|
||||
i++;
|
||||
} else {
|
||||
output += str[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
} /* namespace Http */
|
||||
} /* namespace ZL */
|
37
src/Http/strCoding.h
Normal file
37
src/Http/strCoding.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* strCoding.h
|
||||
*
|
||||
* Created on: 2016年9月22日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_HTTP_STRCODING_H_
|
||||
#define SRC_HTTP_STRCODING_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace ZL {
|
||||
namespace Http {
|
||||
|
||||
class strCoding {
|
||||
public:
|
||||
static string UrlUTF8Encode(const char * str); //urlutf8 编码
|
||||
static string UrlUTF8Decode(const string &str); //urlutf8解码
|
||||
private:
|
||||
strCoding(void);
|
||||
virtual ~strCoding(void);
|
||||
static inline char CharToInt(char ch);
|
||||
static inline char StrToBin(const char *str);
|
||||
};
|
||||
|
||||
} /* namespace Http */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_HTTP_STRCODING_H_ */
|
97
src/MedaiFile/CRC/crc32.cpp
Normal file
97
src/MedaiFile/CRC/crc32.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* crc32.cpp
|
||||
*
|
||||
* Created on: 2013-9-11
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
/*
|
||||
* crc32.h
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#include "crc32.h"
|
||||
|
||||
#define BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff))
|
||||
#define BSWAP32C(x) (BSWAP16C(x) << 16 | BSWAP16C((x) >> 16))
|
||||
#define BSWAP64C(x) (BSWAP32C(x) << 32 | BSWAP32C((x) >> 32))
|
||||
|
||||
static const unsigned int crc_tab[256] = {
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||||
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
|
||||
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||||
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
|
||||
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
|
||||
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||||
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
|
||||
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||||
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||||
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
|
||||
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||||
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
|
||||
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
|
||||
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||||
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
|
||||
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||||
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||||
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
|
||||
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||||
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
|
||||
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
|
||||
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||||
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
|
||||
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||||
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||||
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
|
||||
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||||
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
|
||||
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
|
||||
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||||
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
|
||||
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||||
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||||
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
|
||||
unsigned int calc_crc32 (unsigned char *data, unsigned int datalen)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int crc = 0xffffffff;
|
||||
|
||||
for (i=0; i<datalen; i++)
|
||||
{
|
||||
crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
unsigned int Zwg_ntohl(unsigned int s)
|
||||
{
|
||||
union
|
||||
{
|
||||
int i;
|
||||
char buf;
|
||||
}a;
|
||||
a.i = 0x01;
|
||||
if(a.buf)
|
||||
{
|
||||
// 小端
|
||||
s = BSWAP32C(s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
16
src/MedaiFile/CRC/crc32.h
Normal file
16
src/MedaiFile/CRC/crc32.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* crc32.h
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef CRC32_H_
|
||||
#define CRC32_H_
|
||||
|
||||
unsigned int calc_crc32 (unsigned char *data, unsigned int datalen);
|
||||
unsigned int Zwg_ntohl(unsigned int s);
|
||||
|
||||
|
||||
|
||||
#endif /* CRC32_H_ */
|
159
src/MedaiFile/HLSMaker.cpp
Normal file
159
src/MedaiFile/HLSMaker.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* HLSMaker.cpp
|
||||
*
|
||||
* Created on: 2013-6-24
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#include "HLSMaker.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
HLSMaker::HLSMaker(const string& strM3u8File, const string& strHttpUrl,
|
||||
uint32_t ui32BufSize, uint32_t ui32Duration, uint32_t ui32Num) {
|
||||
if (ui32BufSize < 16 * 1024) {
|
||||
ui32BufSize = 16 * 1024;
|
||||
}
|
||||
m_ui32BufSize = ui32BufSize;
|
||||
if (ui32Duration < 5) {
|
||||
ui32Duration = 5;
|
||||
}
|
||||
if (ui32Num < 2) {
|
||||
ui32Num = 2;
|
||||
}
|
||||
|
||||
m_ui64TsCnt = 0;
|
||||
m_strM3u8File = strM3u8File;
|
||||
m_strHttpUrl = strHttpUrl.substr(0, strHttpUrl.find_last_of('/') + 1);
|
||||
m_ui32NumSegments = ui32Num;
|
||||
m_ui32SegmentDuration = ui32Duration;
|
||||
|
||||
m_strOutputPrefix = strM3u8File.substr(0, strM3u8File.find_last_of('.'));
|
||||
m_strFileName = m_strOutputPrefix.substr(m_strOutputPrefix.find_last_of('/') + 1);
|
||||
m_strTmpFileName = m_strOutputPrefix + "-0.ts";
|
||||
m_ts.init(m_strTmpFileName, m_ui32BufSize);
|
||||
}
|
||||
|
||||
|
||||
HLSMaker::~HLSMaker() {
|
||||
m_ts.clear();
|
||||
string strDir = m_strOutputPrefix.substr(0,m_strOutputPrefix.find_last_of('/'));
|
||||
File::delete_file(strDir.data());
|
||||
}
|
||||
|
||||
int HLSMaker::write_index_file(int iFirstSegment, unsigned int uiLastSegment,
|
||||
int iEnd) {
|
||||
FILE *pIndexFp;
|
||||
char *pcWriteBuf;
|
||||
const char *pcTmpM3u8File = (m_strM3u8File).c_str();
|
||||
pIndexFp = File::createfile_file(pcTmpM3u8File, "w");
|
||||
if (pIndexFp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (iFirstSegment < 0) {
|
||||
iFirstSegment = 0;
|
||||
}
|
||||
if (!pIndexFp) {
|
||||
WarnL << "Could not open temporary m3u8 index file (" << pcTmpM3u8File
|
||||
<< "), no index file will be created";
|
||||
return -1;
|
||||
}
|
||||
|
||||
pcWriteBuf = (char *) malloc(sizeof(char) * 1024);
|
||||
if (!pcWriteBuf) {
|
||||
WarnL << "Could not allocate write buffer for index file, index file will be invalid";
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_ui32NumSegments) {
|
||||
snprintf(pcWriteBuf, 1024,
|
||||
"#EXTM3U\n#EXT-X-TARGETDURATION:%u\n#EXT-X-MEDIA-SEQUENCE:%u\n",
|
||||
m_ui32SegmentDuration, iFirstSegment);
|
||||
} else {
|
||||
snprintf(pcWriteBuf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%u\n",
|
||||
m_ui32SegmentDuration);
|
||||
}
|
||||
if (fwrite(pcWriteBuf, strlen(pcWriteBuf), 1, pIndexFp) != 1) {
|
||||
WarnL << "Could not write to m3u8 index file, will not continue writing to index file";
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned int i = iFirstSegment; i < uiLastSegment; i++) {
|
||||
snprintf(pcWriteBuf, 1024, "#EXTINF:%u,\n%s%s-%u.ts\n",
|
||||
m_ui32SegmentDuration, m_strHttpUrl.c_str(),
|
||||
m_strFileName.c_str(), i);
|
||||
//printf(options.output_prefix);
|
||||
if (fwrite(pcWriteBuf, strlen(pcWriteBuf), 1, pIndexFp) != 1) {
|
||||
WarnL << "Could not write to m3u8 index file, will not continue writing to index file";
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (iEnd) {
|
||||
snprintf(pcWriteBuf, 1024, "#EXT-X-ENDLIST\n");
|
||||
if (fwrite(pcWriteBuf, strlen(pcWriteBuf), 1, pIndexFp) != 1) {
|
||||
WarnL << "Could not write last file and endlist tag to m3u8 index file";
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void HLSMaker::inputH264(void *data, uint32_t length, uint32_t timeStamp,
|
||||
int type) {
|
||||
switch (type) {
|
||||
case 7: //SPS
|
||||
if (m_Timer.elapsedTime() >= m_ui32SegmentDuration * 1000) {
|
||||
m_ts.clear();
|
||||
m_strTmpFileName = StrPrinter << m_strOutputPrefix << '-' << (++m_ui64TsCnt) << ".ts" << endl;
|
||||
if (!m_ts.init(m_strTmpFileName, m_ui32BufSize)) {
|
||||
//创建文件失败
|
||||
return;
|
||||
}
|
||||
m_Timer.resetTime();
|
||||
removets();
|
||||
if (write_index_file(m_ui64TsCnt - m_ui32NumSegments, m_ui64TsCnt, 0) == -1) {
|
||||
WarnL << "write_index_file error :" << strerror(errno);
|
||||
}
|
||||
}
|
||||
case 1: //P
|
||||
//insert aud frame before p and SPS frame
|
||||
m_ts.inputH264("\x0\x0\x0\x1\x9\xf0", 6, timeStamp);
|
||||
case 5: //IDR
|
||||
case 8: //PPS
|
||||
m_ts.inputH264((char *) data, length, timeStamp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HLSMaker::inputAAC(void *data, uint32_t length, uint32_t timeStamp) {
|
||||
m_ts.inputAAC((char *) data, length, timeStamp);
|
||||
}
|
||||
|
||||
void HLSMaker::removets() {
|
||||
if (m_ui64TsCnt <= m_ui32NumSegments) {
|
||||
return;
|
||||
}
|
||||
File::delete_file( (StrPrinter << m_strOutputPrefix << "-"
|
||||
<< m_ui64TsCnt - m_ui32NumSegments - 1
|
||||
<< ".ts" << endl).data());
|
||||
}
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
62
src/MedaiFile/HLSMaker.h
Normal file
62
src/MedaiFile/HLSMaker.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* HLSMaker.h
|
||||
*
|
||||
* Created on: 2013-6-24
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef HLSMAKER_H_
|
||||
#define HLSMAKER_H_
|
||||
|
||||
#include "TSMaker.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Util/File.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/logger.h"
|
||||
#include "config.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
class HLSMaker {
|
||||
public:
|
||||
HLSMaker(const string &strM3u8File,
|
||||
const string &strHttpUrl,
|
||||
uint32_t ui32BufSize = 64 * 1024,
|
||||
uint32_t ui32Duration = 5,
|
||||
uint32_t ui32Num = 3);
|
||||
|
||||
virtual ~HLSMaker();
|
||||
|
||||
//时间戳:参考频率90000
|
||||
void inputH264( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp,
|
||||
int iType);
|
||||
|
||||
//时间戳:参考频率90000
|
||||
void inputAAC( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp);
|
||||
private:
|
||||
TSMaker m_ts;
|
||||
string m_strM3u8File;
|
||||
string m_strHttpUrl;
|
||||
string m_strFileName;
|
||||
string m_strOutputPrefix;
|
||||
string m_strTmpFileName;
|
||||
uint32_t m_ui32SegmentDuration;
|
||||
uint32_t m_ui32NumSegments;
|
||||
uint64_t m_ui64TsCnt;
|
||||
uint32_t m_ui32BufSize;
|
||||
Ticker m_Timer;
|
||||
|
||||
int write_index_file(int iFirstSegment, unsigned int uiLastSegment, int iEnd);
|
||||
void removets();
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* HLSMAKER_H_ */
|
349
src/MedaiFile/MediaReader.cpp
Normal file
349
src/MedaiFile/MediaReader.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
* MediaReader.cpp
|
||||
*
|
||||
* Created on: 2016年12月14日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "MediaReader.h"
|
||||
#include "Http/HttpSession.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
MediaReader::MediaReader(const string &strApp, const string &strId) {
|
||||
static string recordPath = mINI::Instance()[Config::Record::kFilePath];
|
||||
auto strFileName = recordPath + "/" + strApp + "/" + strId;
|
||||
|
||||
m_hMP4File = MP4Read(strFileName.data());
|
||||
if(m_hMP4File == MP4_INVALID_FILE_HANDLE){
|
||||
throw runtime_error(StrPrinter << "打开MP4文件失败:" << strFileName << endl);
|
||||
}
|
||||
m_video_trId = MP4FindTrackId(m_hMP4File, 0, MP4_VIDEO_TRACK_TYPE, 0);
|
||||
if(m_video_trId != MP4_INVALID_TRACK_ID){
|
||||
if(strcmp(MP4GetTrackMediaDataName(m_hMP4File, m_video_trId),"avc1") ==0){
|
||||
auto m_video_timescale = MP4GetTrackTimeScale(m_hMP4File, m_video_trId);
|
||||
auto m_video_duration = MP4GetTrackDuration(m_hMP4File, m_video_trId);
|
||||
m_video_num_samples = MP4GetTrackNumberOfSamples(m_hMP4File, m_video_trId);
|
||||
m_video_sample_max_size = MP4GetTrackMaxSampleSize(m_hMP4File, m_video_trId);
|
||||
m_video_width = MP4GetTrackVideoWidth(m_hMP4File, m_video_trId);
|
||||
m_video_height = MP4GetTrackVideoHeight(m_hMP4File, m_video_trId);
|
||||
m_video_framerate = MP4GetTrackVideoFrameRate(m_hMP4File, m_video_trId);
|
||||
m_pcVideoSample = std::shared_ptr<uint8_t> (new uint8_t[m_video_sample_max_size],[](uint8_t *ptr){
|
||||
delete [] ptr;
|
||||
});
|
||||
uint8_t **seqheader;
|
||||
uint8_t **pictheader;
|
||||
uint32_t *pictheadersize;
|
||||
uint32_t *seqheadersize;
|
||||
uint32_t ix;
|
||||
if(MP4GetTrackH264SeqPictHeaders(m_hMP4File, m_video_trId, &seqheader, &seqheadersize, &pictheader, &pictheadersize)){
|
||||
for (ix = 0; seqheadersize[ix] != 0; ix++) {
|
||||
m_strSps.assign((char *)(seqheader[ix]), seqheadersize[ix]);
|
||||
float framerate;
|
||||
getAVCInfo(m_strSps, (int &)m_video_width, (int &)m_video_height, framerate);
|
||||
m_video_framerate = framerate;
|
||||
m_strSps = string("\x0\x0\x0\x1",4) + m_strSps;
|
||||
free(seqheader[ix]);
|
||||
}
|
||||
free(seqheader);
|
||||
free(seqheadersize);
|
||||
for (ix = 0; pictheadersize[ix] != 0; ix++) {
|
||||
m_strPps.assign("\x0\x0\x0\x1",4);
|
||||
m_strPps.append((char *)(pictheader[ix]), pictheadersize[ix]);
|
||||
free(pictheader[ix]);
|
||||
}
|
||||
free(pictheader);
|
||||
free(pictheadersize);
|
||||
}
|
||||
m_video_ms = 1000.0 * m_video_duration / m_video_timescale;
|
||||
/*InfoL << "\r\n"
|
||||
<< m_video_ms << "\r\n"
|
||||
<< m_video_num_samples << "\r\n"
|
||||
<< m_video_framerate << "\r\n"
|
||||
<< m_video_width << "\r\n"
|
||||
<< m_video_height << "\r\n";*/
|
||||
} else {
|
||||
//如果不是h264,则忽略
|
||||
m_video_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_audio_trId = MP4FindTrackId(m_hMP4File, 0, MP4_AUDIO_TRACK_TYPE, 0);
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
if (strcmp(MP4GetTrackMediaDataName(m_hMP4File, m_audio_trId), "mp4a") == 0) {
|
||||
m_audio_sample_rate = MP4GetTrackTimeScale(m_hMP4File, m_audio_trId);
|
||||
auto m_audio_duration = MP4GetTrackDuration(m_hMP4File, m_audio_trId);
|
||||
m_audio_num_samples = MP4GetTrackNumberOfSamples(m_hMP4File,m_audio_trId);
|
||||
m_audio_num_channels = MP4GetTrackAudioChannels(m_hMP4File, m_audio_trId);
|
||||
m_audio_sample_max_size = MP4GetTrackMaxSampleSize(m_hMP4File,m_audio_trId);
|
||||
uint8_t *ppConfig;
|
||||
uint32_t pConfigSize;
|
||||
if(MP4GetTrackESConfiguration(m_hMP4File,m_audio_trId,&ppConfig,&pConfigSize)){
|
||||
m_strAacCfg.assign((char *)ppConfig, pConfigSize);
|
||||
makeAdtsHeader(m_strAacCfg, m_adts);
|
||||
writeAdtsHeader(m_adts,m_adts.data);
|
||||
getAACInfo(m_adts, (int &)m_audio_sample_rate, (int &)m_audio_num_channels);
|
||||
free(ppConfig);
|
||||
}
|
||||
m_audio_ms = 1000.0 * m_audio_duration / m_audio_sample_rate;
|
||||
/*InfoL << "\r\n"
|
||||
<< m_audio_ms << "\r\n"
|
||||
<< m_audio_num_samples << "\r\n"
|
||||
<< m_audio_num_channels << "\r\n"
|
||||
<< m_audio_sample_rate << "\r\n";*/
|
||||
}else{
|
||||
m_audio_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
if(m_audio_trId == MP4_INVALID_TRACK_ID && m_video_trId == MP4_INVALID_TRACK_ID){
|
||||
MP4Close(m_hMP4File);
|
||||
m_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
throw runtime_error(StrPrinter << "该MP4文件音视频格式不支持:" << strFileName << endl);
|
||||
}
|
||||
|
||||
m_iDuration = MAX(m_video_ms,m_audio_ms);
|
||||
m_pChn.reset(new DevChannel(strApp.data(),strId.data(),m_iDuration/1000.0,false));
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
AudioInfo info;
|
||||
info.iChannel = m_audio_num_channels;
|
||||
info.iSampleBit = 16;
|
||||
info.iSampleRate = m_audio_sample_rate;
|
||||
m_pChn->initAudio(info);
|
||||
}
|
||||
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
VideoInfo info;
|
||||
info.iFrameRate = m_video_framerate;
|
||||
info.iWidth = m_video_width;
|
||||
info.iHeight = m_video_height;
|
||||
m_pChn->initVideo(info);
|
||||
}
|
||||
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
m_pChn->inputAAC((char *)m_adts.data, 7, 0);
|
||||
}
|
||||
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
//m_pChn->initVideo(info);
|
||||
m_pChn->inputH264((char *) m_strSps.data(), m_strSps.size(), 0);
|
||||
m_pChn->inputH264((char *) m_strPps.data(), m_strPps.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MediaReader::~MediaReader() {
|
||||
if (m_hMP4File != MP4_INVALID_FILE_HANDLE) {
|
||||
MP4Close(m_hMP4File);
|
||||
m_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MediaReader::startReadMP4() {
|
||||
auto strongSelf = shared_from_this();
|
||||
static uint32_t sampleMS = mINI::Instance()[Config::Record::kSampleMS].as<uint32_t>();
|
||||
|
||||
AsyncTaskThread::Instance().DoTaskDelay(reinterpret_cast<uint64_t>(this), sampleMS, [strongSelf](){
|
||||
return strongSelf->readSample();
|
||||
});
|
||||
weak_ptr<MediaReader> weakSelf = strongSelf;
|
||||
m_pChn->setOnSeek([weakSelf](uint32_t ui32Stamp){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf){
|
||||
return false;
|
||||
}
|
||||
strongSelf->seek(ui32Stamp);
|
||||
return true;
|
||||
});
|
||||
m_pChn->setOnStamp([weakSelf]() -> uint32_t {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return 0;
|
||||
}
|
||||
return strongSelf-> m_iSeekTime + strongSelf->m_ticker.elapsedTime();
|
||||
});
|
||||
}
|
||||
|
||||
bool MediaReader::readSample(int iTimeInc) {
|
||||
TimeTicker();
|
||||
lock_guard<recursive_mutex> lck(m_mtx);
|
||||
auto bFlag0 = readVideoSample(iTimeInc);//数据没读完
|
||||
auto bFlag1 = readAudioSample(iTimeInc);//数据没读完
|
||||
auto bFlag2 = m_pChn->readerCount() > 0;//读取者大于0
|
||||
if((bFlag0 || bFlag1) && bFlag2){
|
||||
m_alive.resetTime();
|
||||
}
|
||||
//DebugL << "alive ...";
|
||||
//3秒延时关闭
|
||||
return m_alive.elapsedTime() < 3 * 1000;
|
||||
}
|
||||
inline bool MediaReader::readVideoSample(int iTimeInc) {
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getVideoSampleId(iTimeInc);
|
||||
MP4SampleId iIdx = m_video_current;
|
||||
for (iIdx = m_video_current; iIdx < iNextSample; iIdx++) {
|
||||
uint8_t *pBytes = m_pcVideoSample.get();
|
||||
uint32_t numBytes = m_video_sample_max_size;
|
||||
if(MP4ReadSample(m_hMP4File, m_video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,NULL,&m_bSyncSample)){
|
||||
if (!iTimeInc) {
|
||||
uint32_t iOffset = 0;
|
||||
while (iOffset < numBytes) {
|
||||
uint32_t iFrameLen;
|
||||
memcpy(&iFrameLen,pBytes + iOffset,4);
|
||||
iFrameLen = ntohl(iFrameLen);
|
||||
if(iFrameLen + iOffset + 4> numBytes){
|
||||
break;
|
||||
}
|
||||
memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
|
||||
writeH264(pBytes + iOffset, iFrameLen + 4, (double) m_video_ms * iIdx / m_video_num_samples);
|
||||
iOffset += (iFrameLen + 4);
|
||||
}
|
||||
}else if(m_bSyncSample){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
FatalL << "读取视频失败:" << iIdx + 1;
|
||||
}
|
||||
}
|
||||
m_video_current = iIdx;
|
||||
return m_video_current < m_video_num_samples;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool MediaReader::readAudioSample(int iTimeInc) {
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getAudioSampleId(iTimeInc);
|
||||
for (auto i = m_audio_current; i < iNextSample; i++) {
|
||||
uint32_t numBytes = m_audio_sample_max_size;
|
||||
uint8_t *pBytes = m_adts.data + 7;
|
||||
if(MP4ReadSample(m_hMP4File, m_audio_trId, i + 1, &pBytes, &numBytes)){
|
||||
if (!iTimeInc) {
|
||||
m_adts.aac_frame_length = 7 + numBytes;
|
||||
writeAdtsHeader(m_adts, m_adts.data);
|
||||
writeAAC(m_adts.data, m_adts.aac_frame_length, (double) m_audio_ms * i / m_audio_num_samples);
|
||||
}
|
||||
}else{
|
||||
FatalL << "读取音频失败:" << i+ 1;
|
||||
}
|
||||
}
|
||||
m_audio_current = iNextSample;
|
||||
return m_audio_current < m_audio_num_samples;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void MediaReader::writeH264(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
||||
m_pChn->inputH264((char *)pucData, iLen, uiStamp);
|
||||
}
|
||||
|
||||
inline void MediaReader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
||||
m_pChn->inputAAC((char *)pucData, iLen, uiStamp);
|
||||
}
|
||||
|
||||
inline MP4SampleId MediaReader::getVideoSampleId(int iTimeInc ) {
|
||||
MP4SampleId video_current = (double)m_video_num_samples * (m_iSeekTime + m_ticker.elapsedTime() + iTimeInc) / m_video_ms;
|
||||
video_current = MAX(0,MIN(m_video_num_samples, video_current));
|
||||
return video_current;
|
||||
|
||||
}
|
||||
|
||||
inline MP4SampleId MediaReader::getAudioSampleId(int iTimeInc) {
|
||||
MP4SampleId audio_current = (double)m_audio_num_samples * (m_iSeekTime + m_ticker.elapsedTime() + iTimeInc) / m_audio_ms ;
|
||||
audio_current = MAX(0,MIN(m_audio_num_samples,audio_current));
|
||||
return audio_current;
|
||||
}
|
||||
inline void MediaReader::setSeekTime(int iSeekTime){
|
||||
m_iSeekTime = MAX(0, MIN(iSeekTime,m_iDuration));
|
||||
m_ticker.resetTime();
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
m_audio_current = getAudioSampleId();
|
||||
}
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
m_video_current = getVideoSampleId();
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t MediaReader::getVideoCurrentTime(){
|
||||
return (double)m_video_current * m_video_ms /m_video_num_samples;
|
||||
}
|
||||
void MediaReader::seek(int iSeekTime,bool bReStart){
|
||||
lock_guard<recursive_mutex> lck(m_mtx);
|
||||
if(iSeekTime == 0 || m_video_trId == MP4_INVALID_TRACK_ID){
|
||||
setSeekTime(iSeekTime);
|
||||
}else{
|
||||
setSeekTime(iSeekTime - 5000);
|
||||
//在之后的10秒查找关键帧
|
||||
readVideoSample(10000);
|
||||
if (m_bSyncSample) {
|
||||
//找到关键帧
|
||||
auto iIdr = m_video_current;
|
||||
setSeekTime(getVideoCurrentTime());
|
||||
m_video_current = iIdr;
|
||||
}else{
|
||||
//未找到关键帧
|
||||
setSeekTime(iSeekTime);
|
||||
}
|
||||
}
|
||||
m_pChn->updateTimeStamp(m_iSeekTime);
|
||||
|
||||
if(bReStart){
|
||||
AsyncTaskThread::Instance().CancelTask(reinterpret_cast<uint64_t>(this));
|
||||
startReadMP4();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
RtspMediaSource::Ptr MediaReader::onMakeRtsp(const string &strApp, const string &strId) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
static string appName = mINI::Instance()[Config::Record::kAppName];
|
||||
if (strApp != appName) {
|
||||
return nullptr;
|
||||
}
|
||||
try{
|
||||
MediaReader::Ptr pReader(new MediaReader(strApp,strId));
|
||||
pReader->startReadMP4();
|
||||
return RtspMediaSource::find(strApp, strId, false);
|
||||
}catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
}
|
||||
|
||||
RtmpMediaSource::Ptr MediaReader::onMakeRtmp(const string &strApp, const string &strId) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
static string appName = mINI::Instance()[Config::Record::kAppName];
|
||||
if (strApp != appName) {
|
||||
return nullptr;
|
||||
}
|
||||
try {
|
||||
MediaReader::Ptr pReader(new MediaReader(strApp, strId));
|
||||
pReader->startReadMP4();
|
||||
return RtmpMediaSource::find(strApp, strId, false);
|
||||
} catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
85
src/MedaiFile/MediaReader.h
Normal file
85
src/MedaiFile/MediaReader.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* MediaReader.h
|
||||
*
|
||||
* Created on: 2016年12月14日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_MEDAIFILE_MEDIAREADER_H_
|
||||
#define SRC_MEDAIFILE_MEDIAREADER_H_
|
||||
|
||||
#include "Rtsp/RtspMediaSource.h"
|
||||
#include "Rtmp/RtmpMediaSource.h"
|
||||
#include "Device/Device.h"
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
using namespace ZL::DEV;
|
||||
using namespace ZL::Rtsp;
|
||||
using namespace ZL::Rtmp;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
class MediaReader : public std::enable_shared_from_this<MediaReader>{
|
||||
public:
|
||||
typedef std::shared_ptr<MediaReader> Ptr;
|
||||
MediaReader(const string &strApp, const string &strId);
|
||||
virtual ~MediaReader();
|
||||
static RtspMediaSource::Ptr onMakeRtsp(const string &strApp, const string &strId);
|
||||
static RtmpMediaSource::Ptr onMakeRtmp(const string &strApp, const string &strId);
|
||||
private:
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
MP4FileHandle m_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
MP4TrackId m_video_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t m_video_ms = 0;
|
||||
uint32_t m_video_num_samples = 0;
|
||||
uint32_t m_video_sample_max_size = 0;
|
||||
uint32_t m_video_width = 0;
|
||||
uint32_t m_video_height = 0;
|
||||
uint32_t m_video_framerate = 0;
|
||||
string m_strPps;
|
||||
string m_strSps;
|
||||
bool m_bSyncSample = false;
|
||||
|
||||
MP4TrackId m_audio_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t m_audio_ms = 0;
|
||||
uint32_t m_audio_num_samples = 0;
|
||||
uint32_t m_audio_sample_max_size = 0;
|
||||
uint32_t m_audio_sample_rate = 0;
|
||||
uint32_t m_audio_num_channels = 0;
|
||||
string m_strAacCfg;
|
||||
AdtsFrame m_adts;
|
||||
|
||||
int m_iDuration = 0;
|
||||
DevChannel::Ptr m_pChn;
|
||||
MP4SampleId m_video_current = 0;
|
||||
MP4SampleId m_audio_current = 0;
|
||||
std::shared_ptr<uint8_t> m_pcVideoSample;
|
||||
|
||||
int m_iSeekTime = 0 ;
|
||||
Ticker m_ticker;
|
||||
Ticker m_alive;
|
||||
recursive_mutex m_mtx;
|
||||
|
||||
void seek(int iSeekTime,bool bReStart = true);
|
||||
inline void setSeekTime(int iSeekTime);
|
||||
inline uint32_t getVideoCurrentTime();
|
||||
void startReadMP4();
|
||||
inline MP4SampleId getVideoSampleId(int iTimeInc = 0);
|
||||
inline MP4SampleId getAudioSampleId(int iTimeInc = 0);
|
||||
bool readSample(int iTimeInc = 0);
|
||||
inline bool readVideoSample(int iTimeInc = 0);
|
||||
inline bool readAudioSample(int iTimeInc = 0);
|
||||
inline void writeH264(uint8_t *pucData,int iLen,uint32_t uiStamp);
|
||||
inline void writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp);
|
||||
#endif
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_MEDAIFILE_MEDIAREADER_H_ */
|
62
src/MedaiFile/MediaRecorder.cpp
Normal file
62
src/MedaiFile/MediaRecorder.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MediaRecorder.cpp
|
||||
*
|
||||
* Created on: 2016年12月8日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "Network/sockutil.h"
|
||||
#include "MediaRecorder.h"
|
||||
#include "Util/util.h"
|
||||
#include "Http/HttpSession.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Network;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
MediaRecorder::MediaRecorder(const string &strApp,const string &strId,const std::shared_ptr<PlayerBase> &pPlayer) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
static string hlsPrefix = mINI::Instance()[Config::Hls::kHttpPrefix];
|
||||
static string hlsPath = mINI::Instance()[Config::Hls::kFilePath];
|
||||
static uint32_t hlsBufSize = mINI::Instance()[Config::Hls::kFileBufSize].as<uint32_t>();
|
||||
static uint32_t hlsDuration = mINI::Instance()[Config::Hls::kSegmentDuration].as<uint32_t>();
|
||||
static uint32_t hlsNum = mINI::Instance()[Config::Hls::kSegmentNum].as<uint32_t>();
|
||||
|
||||
static string recordPath = mINI::Instance()[Config::Record::kFilePath];
|
||||
static string recordAppName = mINI::Instance()[Config::Record::kAppName];
|
||||
|
||||
|
||||
m_hlsMaker.reset(new HLSMaker(hlsPath + "/" + strApp + "/" + strId + "/hls.m3u8",
|
||||
hlsPrefix + "/" + strApp + "/" + strId + "/",
|
||||
hlsBufSize,hlsDuration,hlsNum));
|
||||
|
||||
m_mp4Maker.reset(new Mp4Maker(recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/",
|
||||
strApp,strId,pPlayer));
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
}
|
||||
|
||||
MediaRecorder::~MediaRecorder() {
|
||||
}
|
||||
|
||||
void MediaRecorder::inputH264(void* pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
m_hlsMaker->inputH264(pData, ui32Length, ui32TimeStamp * 90, iType);
|
||||
m_mp4Maker->inputH264(pData, ui32Length, ui32TimeStamp, iType);
|
||||
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
}
|
||||
|
||||
void MediaRecorder::inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32TimeStamp) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
m_hlsMaker->inputAAC(pData, ui32Length, ui32TimeStamp * 90);
|
||||
m_mp4Maker->inputAAC(pData, ui32Length, ui32TimeStamp);
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
}
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
50
src/MedaiFile/MediaRecorder.h
Normal file
50
src/MedaiFile/MediaRecorder.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* MediaRecorder.h
|
||||
*
|
||||
* Created on: 2016年12月8日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_MEDAIFILE_MEDIARECORDER_H_
|
||||
#define SRC_MEDAIFILE_MEDIARECORDER_H_
|
||||
|
||||
#include "Player/PlayerBase.h"
|
||||
#include <memory>
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
#include "Mp4Maker.h"
|
||||
#include "HLSMaker.h"
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Player;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
|
||||
class MediaRecorder {
|
||||
public:
|
||||
typedef std::shared_ptr<MediaRecorder> Ptr;
|
||||
MediaRecorder(const string &strApp,const string &strId,const std::shared_ptr<PlayerBase> &pPlayer);
|
||||
virtual ~MediaRecorder();
|
||||
|
||||
void inputH264( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp,
|
||||
int iType);
|
||||
|
||||
void inputAAC( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp);
|
||||
private:
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
std::shared_ptr<HLSMaker> m_hlsMaker;
|
||||
std::shared_ptr<Mp4Maker> m_mp4Maker;
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_MEDAIFILE_MEDIARECORDER_H_ */
|
190
src/MedaiFile/Mp4Maker.cpp
Normal file
190
src/MedaiFile/Mp4Maker.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
|
||||
#include "MediaRecorder.h"
|
||||
#include <netinet/in.h>
|
||||
#include <sys/stat.h>
|
||||
#include "Util/File.h"
|
||||
#include "Mp4Maker.h"
|
||||
#include "Util/NoticeCenter.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
string timeStr(const char *fmt) {
|
||||
std::tm tm_snapshot;
|
||||
auto time = ::time(NULL);
|
||||
#if defined(WIN32)
|
||||
localtime_s(&tm_snapshot, &time); // thread-safe?
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(const_cast<std::tm*>(&tm_snapshot), fmt);
|
||||
return oss.str();
|
||||
#else
|
||||
localtime_r(&time, &tm_snapshot); // POSIX
|
||||
const size_t size = 1024;
|
||||
char buffer[size];
|
||||
auto success = std::strftime(buffer, size, fmt, &tm_snapshot);
|
||||
if (0 == success)
|
||||
return string(fmt);
|
||||
return buffer;
|
||||
#endif
|
||||
}
|
||||
|
||||
Mp4Maker::Mp4Maker(const string& strPath,const string &strApp,const string &strStreamId, const PlayerBase::Ptr &pPlayer) {
|
||||
DebugL << strPath;
|
||||
m_pPlayer = pPlayer;
|
||||
m_strPath = strPath;
|
||||
|
||||
/////record 业务逻辑//////
|
||||
m_info.strAppName = strApp;
|
||||
m_info.strStreamId = strStreamId;
|
||||
m_info.strFolder = strPath;
|
||||
//----record 业务逻辑----//
|
||||
}
|
||||
Mp4Maker::~Mp4Maker() {
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType){
|
||||
switch (iType) {
|
||||
case 1: //P
|
||||
case 5: { //IDR
|
||||
if (m_strLastVideo.size()) {
|
||||
_inputH264((char *) m_strLastVideo.data(), m_strLastVideo.size(), ui32TimeStamp - m_ui32LastVideoTime, m_iLastVideoType);
|
||||
}
|
||||
//m_strLastVideo.assign(("\x0\x0\x0\x2\x9\xf0"), 6);
|
||||
uint32_t *p = (uint32_t *) pData;
|
||||
*p = htonl(ui32Length - 4);
|
||||
m_strLastVideo.assign((char *) pData, ui32Length);
|
||||
memcpy(pData, "\x00\x00\x00\x01", 4);
|
||||
|
||||
m_ui32LastVideoTime = ui32TimeStamp;
|
||||
m_iLastVideoType = iType;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Mp4Maker::inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp){
|
||||
if (m_strLastAudio.size()) {
|
||||
_inputAAC((char *)m_strLastAudio.data(), m_strLastAudio.size(), ui32TimeStamp - m_ui32LastAudioTime);
|
||||
}
|
||||
m_strLastAudio.assign((char *)pData, ui32Length);
|
||||
m_ui32LastAudioTime = ui32TimeStamp;
|
||||
}
|
||||
|
||||
void Mp4Maker::_inputH264(void* pData, uint32_t ui32Length, uint32_t ui32Duration, int iType) {
|
||||
static uint32_t recordMS = 1000 * mINI::Instance()[Config::Record::kFileSecond].as<uint32_t>();
|
||||
if(iType == 5 && (m_hMp4 == MP4_INVALID_FILE_HANDLE || m_ticker.elapsedTime() > recordMS)){
|
||||
//在I帧率处新建MP4文件
|
||||
//如果文件未创建或者文件超过10分钟则创建新文件
|
||||
createFile();
|
||||
}
|
||||
if (m_hVideo != MP4_INVALID_TRACK_ID) {
|
||||
MP4WriteSample(m_hMp4, m_hVideo, (uint8_t *) pData, ui32Length,ui32Duration * 90,0,iType == 5);
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::_inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32Duration) {
|
||||
static uint32_t recordMS = 1000 * mINI::Instance()[Config::Record::kFileSecond].as<uint32_t>();
|
||||
if (!m_pPlayer->containVideo() && (m_hMp4 == MP4_INVALID_FILE_HANDLE || m_ticker.elapsedTime() > recordMS)) {
|
||||
//在I帧率处新建MP4文件
|
||||
//如果文件未创建或者文件超过10分钟则创建新文件
|
||||
createFile();
|
||||
}
|
||||
if (m_hAudio != MP4_INVALID_TRACK_ID) {
|
||||
auto duration = ui32Duration * m_pPlayer->getAudioSampleRate() /1000.0;
|
||||
MP4WriteSample(m_hMp4, m_hAudio, (uint8_t*)pData + 7, ui32Length - 7,duration,0,false);
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::createFile() {
|
||||
if(!m_pPlayer->isInited()){
|
||||
return;
|
||||
}
|
||||
closeFile();
|
||||
|
||||
|
||||
auto strDate = timeStr("%Y-%m-%d");
|
||||
auto strTime = timeStr("%H-%M-%S");
|
||||
auto strFileTmp = m_strPath + strDate + "/." + strTime + ".mp4";
|
||||
auto strFile = m_strPath + strDate + "/" + strTime + ".mp4";
|
||||
|
||||
/////record 业务逻辑//////
|
||||
m_info.ui64StartedTime = ::time(NULL);
|
||||
m_info.strFileName = strTime + ".mp4";
|
||||
m_info.strFilePath = strFile;
|
||||
static string appName = mINI::Instance()[Config::Record::kAppName];
|
||||
m_info.strUrl = appName + "/"
|
||||
+ m_info.strAppName + "/"
|
||||
+ m_info.strStreamId + "/"
|
||||
+ strDate + "/"
|
||||
+ strTime + ".mp4";
|
||||
//----record 业务逻辑----//
|
||||
|
||||
File::createfile_path(strFileTmp.data(), S_IRWXO | S_IRWXG | S_IRWXU);
|
||||
m_hMp4 = MP4Create(strFileTmp.data());
|
||||
if (m_hMp4 == MP4_INVALID_FILE_HANDLE) {
|
||||
WarnL << "创建MP4文件失败:" << strFileTmp;
|
||||
return;
|
||||
}
|
||||
//MP4SetTimeScale(m_hMp4, 90000);
|
||||
m_strFileTmp = strFileTmp;
|
||||
m_strFile = strFile;
|
||||
m_ticker.resetTime();
|
||||
if(m_pPlayer->containVideo()){
|
||||
auto &sps = m_pPlayer->getSps();
|
||||
auto &pps = m_pPlayer->getPps();
|
||||
m_hVideo = MP4AddH264VideoTrack(m_hMp4, 90000, MP4_INVALID_DURATION,
|
||||
m_pPlayer->getVideoWidth(), m_pPlayer->getVideoHeight(),
|
||||
sps[5], sps[6], sps[7], 3);
|
||||
if(m_hVideo !=MP4_INVALID_TRACK_ID){
|
||||
MP4AddH264SequenceParameterSet(m_hMp4, m_hVideo, (uint8_t *)sps.data() + 4, sps.size() - 4);
|
||||
MP4AddH264PictureParameterSet(m_hMp4, m_hVideo, (uint8_t *)pps.data() + 4, pps.size() - 4);
|
||||
}else{
|
||||
WarnL << "添加视频通道失败:" << strFileTmp;
|
||||
}
|
||||
}
|
||||
if(m_pPlayer->containAudio()){
|
||||
m_hAudio = MP4AddAudioTrack(m_hMp4, m_pPlayer->getAudioSampleRate(), MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE);
|
||||
if (m_hAudio != MP4_INVALID_TRACK_ID) {
|
||||
auto &cfg = m_pPlayer->getAudioCfg();
|
||||
MP4SetTrackESConfiguration(m_hMp4, m_hAudio,(uint8_t *)cfg.data(), cfg.size());
|
||||
}else{
|
||||
WarnL << "添加音频通道失败:" << strFileTmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::closeFile() {
|
||||
if (m_hMp4 != MP4_INVALID_FILE_HANDLE) {
|
||||
{
|
||||
TimeTicker();
|
||||
MP4Close(m_hMp4,MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
|
||||
}
|
||||
rename(m_strFileTmp.data(),m_strFile.data());
|
||||
m_hMp4 = MP4_INVALID_FILE_HANDLE;
|
||||
m_hVideo = MP4_INVALID_TRACK_ID;
|
||||
m_hAudio = MP4_INVALID_TRACK_ID;
|
||||
|
||||
/////record 业务逻辑//////
|
||||
m_info.ui64TimeLen = ::time(NULL) - m_info.ui64StartedTime;
|
||||
//获取文件大小
|
||||
struct stat fileData;
|
||||
stat(m_strFile.data(), &fileData);
|
||||
m_info.ui64FileSize = fileData.st_size;
|
||||
//----record 业务逻辑----//
|
||||
NoticeCenter::Instance().emitEvent(Config::Broadcast::kBroadcastRecordMP4,(const Mp4Info &)m_info);
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
|
||||
#endif //ENABLE_MEDIAFILE
|
82
src/MedaiFile/Mp4Maker.h
Normal file
82
src/MedaiFile/Mp4Maker.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Mp4Maker.h
|
||||
*
|
||||
* Created on: 2013-9-18
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef MP4MAKER_H_
|
||||
#define MP4MAKER_H_
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
|
||||
#include <mutex>
|
||||
#include "Player/PlayerBase.h"
|
||||
#include <memory>
|
||||
#include "Util/logger.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#include "Util/TimeTicker.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Player;
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
class Mp4Info
|
||||
{
|
||||
public:
|
||||
time_t ui64StartedTime; //GMT标准时间,单位秒
|
||||
time_t ui64TimeLen;//录像长度,单位秒
|
||||
__off_t ui64FileSize;//文件大小,单位BYTE
|
||||
string strFilePath;//文件路径
|
||||
string strFileName;//文件名称
|
||||
string strFolder;//文件夹路径
|
||||
string strUrl;//播放路径
|
||||
string strAppName;//应用名称
|
||||
string strStreamId;//流ID
|
||||
};
|
||||
class Mp4Maker {
|
||||
public:
|
||||
typedef std::shared_ptr<Mp4Maker> Ptr;
|
||||
Mp4Maker(const string &strPath,const string &strApp,const string &strStreamId, const PlayerBase::Ptr &pPlayer);
|
||||
virtual ~Mp4Maker();
|
||||
//时间戳:参考频率1000
|
||||
void inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType);
|
||||
//时间戳:参考频率1000
|
||||
void inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
|
||||
private:
|
||||
MP4FileHandle m_hMp4 = MP4_INVALID_FILE_HANDLE;
|
||||
MP4TrackId m_hVideo = MP4_INVALID_TRACK_ID;
|
||||
MP4TrackId m_hAudio = MP4_INVALID_TRACK_ID;
|
||||
PlayerBase::Ptr m_pPlayer;
|
||||
string m_strPath;
|
||||
string m_strFile;
|
||||
string m_strFileTmp;
|
||||
Ticker m_ticker;
|
||||
SmoothTicker m_mediaTicker[2];
|
||||
|
||||
void createFile();
|
||||
void closeFile();
|
||||
void _inputH264(void *pData, uint32_t ui32Length, uint32_t ui64Duration, int iType);
|
||||
void _inputAAC(void *pData, uint32_t ui32Length, uint32_t ui64Duration);
|
||||
|
||||
string m_strLastVideo;
|
||||
string m_strLastAudio;
|
||||
|
||||
uint32_t m_ui32LastVideoTime = 0;
|
||||
uint32_t m_ui32LastAudioTime = 0;
|
||||
int m_iLastVideoType = 0;
|
||||
|
||||
Mp4Info m_info;
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif ///ENABLE_MEDIAFILE
|
||||
|
||||
#endif /* MP4MAKER_H_ */
|
585
src/MedaiFile/TSMaker.cpp
Normal file
585
src/MedaiFile/TSMaker.cpp
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* TSMaker.cpp
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#include "TSMaker.h"
|
||||
#include "Util/logger.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
TSMaker::TSMaker() {
|
||||
m_pOutVideoTs = NULL;
|
||||
m_pcFileBuf = NULL;
|
||||
m_uiWritePacketNum = 0;
|
||||
m_pVideo_pes = new TsPes();
|
||||
m_pAudio_pes = new TsPes();
|
||||
m_pVideo_pes->ESlen = 0;
|
||||
m_pAudio_pes->ESlen = 0;
|
||||
memset(&m_continuityCounter, 0, sizeof m_continuityCounter);
|
||||
}
|
||||
|
||||
TSMaker::~TSMaker() {
|
||||
flush();
|
||||
if (m_pOutVideoTs != NULL) {
|
||||
fflush(m_pOutVideoTs);
|
||||
fclose(m_pOutVideoTs);
|
||||
}
|
||||
if (m_pcFileBuf != NULL) {
|
||||
delete[] m_pcFileBuf;
|
||||
}
|
||||
delete m_pVideo_pes;
|
||||
delete m_pAudio_pes;
|
||||
}
|
||||
void TSMaker::clear() {
|
||||
flush();
|
||||
if (m_pOutVideoTs != NULL) {
|
||||
fflush(m_pOutVideoTs);
|
||||
fclose(m_pOutVideoTs);
|
||||
m_pOutVideoTs = NULL;
|
||||
}
|
||||
m_uiWritePacketNum = 0;
|
||||
m_pVideo_pes->ESlen = 0;
|
||||
memset(&m_continuityCounter, 0, sizeof m_continuityCounter);
|
||||
|
||||
}
|
||||
void TSMaker::flush() {
|
||||
unsigned char acTSbuf[TS_PACKET_SIZE];
|
||||
TsPacketHeader ts_header;
|
||||
if (m_pVideo_pes->ESlen == 0)
|
||||
return;
|
||||
unsigned char *pucTs = acTSbuf;
|
||||
if ((m_uiWritePacketNum % 40) == 0) //每40个包打一个 pat,一个pmt
|
||||
{
|
||||
CreatePAT(); //创建PAT
|
||||
CreatePMT(); //创建PMT
|
||||
}
|
||||
memset(acTSbuf, 0, TS_PACKET_SIZE);
|
||||
CreateTsHeader(&ts_header, TS_H264_PID, 0x00, 0x03); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x03,含有调整字段和有效负载;
|
||||
TsHeader2buffer(&ts_header, acTSbuf);
|
||||
pucTs += 4;
|
||||
pucTs[0] = 184 - m_pVideo_pes->ESlen - 1;
|
||||
pucTs[1] = 0x00;
|
||||
pucTs += 2;
|
||||
memset(pucTs, 0xFF, (184 - m_pVideo_pes->ESlen - 2));
|
||||
pucTs += (184 - m_pVideo_pes->ESlen - 2);
|
||||
memcpy(pucTs, m_pVideo_pes->ES, m_pVideo_pes->ESlen);
|
||||
m_pVideo_pes->ESlen = 0;
|
||||
fwrite(acTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件
|
||||
m_uiWritePacketNum++;
|
||||
return;
|
||||
}
|
||||
|
||||
bool TSMaker::init(const string& filename, uint32_t bufsize) {
|
||||
m_strFilename = filename;
|
||||
if (m_pOutVideoTs == NULL) {
|
||||
m_pOutVideoTs = File::createfile_file(filename.c_str(), "wb");
|
||||
if (m_pOutVideoTs == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pcFileBuf == NULL) {
|
||||
m_pcFileBuf = new char[bufsize];
|
||||
setvbuf(m_pOutVideoTs, m_pcFileBuf, _IOFBF, bufsize);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void TSMaker::CreatePAT() {
|
||||
TsPacketHeader ts_header;
|
||||
TsPat ts_pat;
|
||||
unsigned char aucPat[TS_PACKET_SIZE];
|
||||
unsigned char * pucPat;
|
||||
uint32_t ui32PAT_CRC = 0xFFFFFFFF;
|
||||
|
||||
memset(aucPat, 0xFF, TS_PACKET_SIZE);
|
||||
pucPat = aucPat;
|
||||
CreateTsHeader(&ts_header, TS_PAT_PID, 0x01, 0x01); //PID = 0x00,有效荷载单元起始指示符_play_init = 0x01, ada_field_C,0x01,仅有有效负载 ;
|
||||
TsHeader2buffer(&ts_header, aucPat);
|
||||
pucPat[4] = 0; //自适应段的长度为0
|
||||
pucPat += 5;
|
||||
ts_pat.table_id = 0x00;
|
||||
ts_pat.section_syntax_indicator = 0x01;
|
||||
ts_pat.zero = 0x00;
|
||||
ts_pat.reserved_1 = 0x03; //设置为11;
|
||||
ts_pat.section_length = 0x0d; //pat结构体长度 16个字节减去上面的3个字节
|
||||
ts_pat.transport_stream_id = 0x01;
|
||||
ts_pat.reserved_2 = 0x03; //设置为11;
|
||||
ts_pat.version_number = 0x00;
|
||||
ts_pat.current_next_indicator = 0x01; //当前的pat 有效
|
||||
ts_pat.section_number = 0x00;
|
||||
ts_pat.last_section_number = 0x00;
|
||||
ts_pat.program_number = 0x01;
|
||||
ts_pat.reserved_3 = 0x07; //设置为111;
|
||||
ts_pat.program_map_PID = TS_PMT_PID; //PMT的PID
|
||||
ts_pat.CRC_32 = ui32PAT_CRC; //传输过程中检测的一种算法值 先设定一个填充值
|
||||
|
||||
pucPat[0] = ts_pat.table_id;
|
||||
pucPat[1] = ts_pat.section_syntax_indicator << 7 | ts_pat.zero << 6
|
||||
| ts_pat.reserved_1 << 4 | ((ts_pat.section_length >> 8) & 0x0F);
|
||||
pucPat[2] = ts_pat.section_length & 0x00FF;
|
||||
pucPat[3] = ts_pat.transport_stream_id >> 8;
|
||||
pucPat[4] = ts_pat.transport_stream_id & 0x00FF;
|
||||
pucPat[5] = ts_pat.reserved_2 << 6 | ts_pat.version_number << 1
|
||||
| ts_pat.current_next_indicator;
|
||||
pucPat[6] = ts_pat.section_number;
|
||||
pucPat[7] = ts_pat.last_section_number;
|
||||
pucPat[8] = ts_pat.program_number >> 8;
|
||||
pucPat[9] = ts_pat.program_number & 0x00FF;
|
||||
pucPat[10] = ts_pat.reserved_3 << 5
|
||||
| ((ts_pat.program_map_PID >> 8) & 0x0F);
|
||||
pucPat[11] = ts_pat.program_map_PID & 0x00FF;
|
||||
pucPat += 12;
|
||||
ui32PAT_CRC = Zwg_ntohl(calc_crc32(aucPat + 5, pucPat - aucPat - 5));
|
||||
memcpy(pucPat, (unsigned char *) &ui32PAT_CRC, 4);
|
||||
fwrite(aucPat, 188, 1, m_pOutVideoTs); //将PAT包写入文件
|
||||
return;
|
||||
}
|
||||
|
||||
void TSMaker::CreatePMT() {
|
||||
TsPacketHeader ts_header;
|
||||
TsPmt ts_pmt;
|
||||
unsigned char aucPmt[TS_PACKET_SIZE];
|
||||
unsigned char * pucPmt;
|
||||
uint32_t ui32PMT_CRC = 0xFFFFFFFF;
|
||||
int iLen = 0;
|
||||
|
||||
memset(aucPmt, 0xFF, TS_PACKET_SIZE); //将一个包填成0xFF
|
||||
pucPmt = aucPmt;
|
||||
|
||||
CreateTsHeader(&ts_header, TS_PMT_PID, 0x01, 0x01); //PID = 0x00,有效荷载单元起始指示符_play_init = 0x01, ada_field_C,0x01,仅有有效负载;
|
||||
TsHeader2buffer(&ts_header, aucPmt);
|
||||
pucPmt[4] = 0; //自适应段的长度为0
|
||||
pucPmt += 5;
|
||||
ts_pmt.table_id = 0x02;
|
||||
ts_pmt.section_syntax_indicator = 0x01;
|
||||
ts_pmt.zero = 0x00;
|
||||
ts_pmt.reserved_1 = 0x03;
|
||||
ts_pmt.section_length = 0x17; //PMT结构体长度 16 + 5 + 5个字节减去上面的3个字节
|
||||
ts_pmt.program_number = 01; //只有一个节目
|
||||
ts_pmt.reserved_2 = 0x03;
|
||||
ts_pmt.version_number = 0x00;
|
||||
ts_pmt.current_next_indicator = 0x01; //当前的PMT有效
|
||||
ts_pmt.section_number = 0x00;
|
||||
ts_pmt.last_section_number = 0x00;
|
||||
ts_pmt.reserved_3 = 0x07;
|
||||
ts_pmt.PCR_PID = TS_H264_PID; //视频PID
|
||||
ts_pmt.reserved_4 = 0x0F;
|
||||
ts_pmt.program_info_length = 0x00; //后面无 节目信息描述
|
||||
ts_pmt.stream_type_video = PMT_STREAM_TYPE_VIDEO; //视频的类型
|
||||
ts_pmt.reserved_5_video = 0x07;
|
||||
ts_pmt.elementary_PID_video = TS_H264_PID; //视频的PID
|
||||
ts_pmt.reserved_6_video = 0x0F;
|
||||
ts_pmt.ES_info_length_video = 0x00; //视频无跟随的相关信息
|
||||
ts_pmt.stream_type_audio = PMT_STREAM_TYPE_AUDIO; //音频类型
|
||||
ts_pmt.reserved_5_audio = 0x07;
|
||||
ts_pmt.elementary_PID_audio = TS_AAC_PID; //音频PID
|
||||
ts_pmt.reserved_6_audio = 0x0F;
|
||||
ts_pmt.ES_info_length_audio = 0x00; //音频无跟随的相关信息
|
||||
|
||||
ts_pmt.CRC_32 = ui32PMT_CRC;
|
||||
|
||||
pucPmt[0] = ts_pmt.table_id;
|
||||
pucPmt[1] = ts_pmt.section_syntax_indicator << 7 | ts_pmt.zero << 6
|
||||
| ts_pmt.reserved_1 << 4 | ((ts_pmt.section_length >> 8) & 0x0F);
|
||||
pucPmt[2] = ts_pmt.section_length & 0x00FF;
|
||||
pucPmt[3] = ts_pmt.program_number >> 8;
|
||||
pucPmt[4] = ts_pmt.program_number & 0x00FF;
|
||||
pucPmt[5] = ts_pmt.reserved_2 << 6 | ts_pmt.version_number << 1
|
||||
| ts_pmt.current_next_indicator;
|
||||
pucPmt[6] = ts_pmt.section_number;
|
||||
pucPmt[7] = ts_pmt.last_section_number;
|
||||
pucPmt[8] = ts_pmt.reserved_3 << 5 | ((ts_pmt.PCR_PID >> 8) & 0x1F);
|
||||
pucPmt[9] = ts_pmt.PCR_PID & 0x0FF;
|
||||
pucPmt[10] = ts_pmt.reserved_4 << 4
|
||||
| ((ts_pmt.program_info_length >> 8) & 0x0F);
|
||||
pucPmt[11] = ts_pmt.program_info_length & 0xFF;
|
||||
pucPmt[12] = ts_pmt.stream_type_video; //视频流的stream_type
|
||||
pucPmt[13] = ts_pmt.reserved_5_video << 5
|
||||
| ((ts_pmt.elementary_PID_video >> 8) & 0x1F);
|
||||
pucPmt[14] = ts_pmt.elementary_PID_video & 0x00FF;
|
||||
pucPmt[15] = ts_pmt.reserved_6_video << 4
|
||||
| ((ts_pmt.ES_info_length_video >> 8) & 0x0F);
|
||||
pucPmt[16] = ts_pmt.ES_info_length_video & 0x0FF;
|
||||
pucPmt[17] = ts_pmt.stream_type_audio; //音频流的stream_type
|
||||
pucPmt[18] = ts_pmt.reserved_5_audio << 5
|
||||
| ((ts_pmt.elementary_PID_audio >> 8) & 0x1F);
|
||||
pucPmt[19] = ts_pmt.elementary_PID_audio & 0x00FF;
|
||||
pucPmt[20] = ts_pmt.reserved_6_audio << 4
|
||||
| ((ts_pmt.ES_info_length_audio >> 8) & 0x0F);
|
||||
pucPmt[21] = ts_pmt.ES_info_length_audio & 0x0FF;
|
||||
pucPmt += 22;
|
||||
|
||||
iLen = pucPmt - aucPmt - 8 + 4;
|
||||
iLen = iLen > 0xffff ? 0 : iLen;
|
||||
*(aucPmt + 6) = 0xb0 | (iLen >> 8);
|
||||
*(aucPmt + 7) = iLen;
|
||||
|
||||
ui32PMT_CRC = Zwg_ntohl(calc_crc32(aucPmt + 5, pucPmt - aucPmt - 5));
|
||||
memcpy(pucPmt, (unsigned char *) &ui32PMT_CRC, 4);
|
||||
fwrite(aucPmt, 188, 1, m_pOutVideoTs); //将PAT包写入文件
|
||||
}
|
||||
|
||||
void TSMaker::CreateTsHeader(TsPacketHeader* pTsHeader, unsigned int uiPID, unsigned char ucPlayInit, unsigned char ucAdaFieldC) {
|
||||
pTsHeader->sync_byte = TS_SYNC_BYTE;
|
||||
pTsHeader->tras_error = 0x00;
|
||||
pTsHeader->play_init = ucPlayInit;
|
||||
pTsHeader->tras_prio = 0x00;
|
||||
pTsHeader->PID = uiPID;
|
||||
pTsHeader->tras_scramb = 0x00;
|
||||
pTsHeader->ada_field_C = ucAdaFieldC;
|
||||
|
||||
if (uiPID == TS_PAT_PID) { //这是pat的包
|
||||
pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_pat % 16);
|
||||
m_continuityCounter.continuity_counter_pat++;
|
||||
} else if (uiPID == TS_PMT_PID) { //这是pmt的包
|
||||
pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_pmt % 16);
|
||||
m_continuityCounter.continuity_counter_pmt++;
|
||||
} else if (uiPID == TS_H264_PID) { //这是H264的包
|
||||
pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_video % 16);
|
||||
m_continuityCounter.continuity_counter_video++;
|
||||
} else if (uiPID == TS_AAC_PID) { //这是MP3的包
|
||||
pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_audio % 16);
|
||||
m_continuityCounter.continuity_counter_audio++;
|
||||
} else { //其他包出错,或可扩展
|
||||
WarnL << "continuity_counter error packet";
|
||||
}
|
||||
}
|
||||
|
||||
void TSMaker::TsHeader2buffer(TsPacketHeader* pTsHeader, unsigned char* pucBuffer) {
|
||||
pucBuffer[0] = pTsHeader->sync_byte;
|
||||
pucBuffer[1] = pTsHeader->tras_error << 7 | pTsHeader->play_init << 6 |
|
||||
pTsHeader->tras_prio << 5 | ((pTsHeader->PID >> 8) & 0x1f);
|
||||
pucBuffer[2] = (pTsHeader->PID & 0x00ff);
|
||||
pucBuffer[3] = pTsHeader->tras_scramb << 6 | pTsHeader->ada_field_C << 4 | pTsHeader->conti_cter;
|
||||
|
||||
}
|
||||
|
||||
void TSMaker::WriteAdaptive_flags_Head( Ts_Adaptation_field * pTsAdaptationField, uint64_t ui64VideoPts) {
|
||||
//填写自适应段
|
||||
pTsAdaptationField->discontinuty_indicator = 0;
|
||||
pTsAdaptationField->random_access_indicator = 0;
|
||||
pTsAdaptationField->elementary_stream_priority_indicator = 0;
|
||||
pTsAdaptationField->PCR_flag = 1; //只用到这个
|
||||
pTsAdaptationField->OPCR_flag = 0;
|
||||
pTsAdaptationField->splicing_point_flag = 0;
|
||||
pTsAdaptationField->transport_private_data_flag = 0;
|
||||
pTsAdaptationField->adaptation_field_extension_flag = 0;
|
||||
|
||||
//需要自己算
|
||||
pTsAdaptationField->pcr = ui64VideoPts * 300;
|
||||
pTsAdaptationField->adaptation_field_length = 7; //占用7位
|
||||
|
||||
pTsAdaptationField->opcr = 0;
|
||||
pTsAdaptationField->splice_countdown = 0;
|
||||
pTsAdaptationField->private_data_len = 0;
|
||||
}
|
||||
|
||||
int TSMaker::inputH264(const char* pcData, uint32_t ui32Len, uint64_t ui64Time) {
|
||||
if (m_pOutVideoTs == NULL) {
|
||||
return false;
|
||||
}
|
||||
m_pVideo_pes->ES = const_cast<char *>(pcData);
|
||||
m_pVideo_pes->ESlen = ui32Len;
|
||||
Ts_Adaptation_field ts_adaptation_field_Head;
|
||||
WriteAdaptive_flags_Head(&ts_adaptation_field_Head, ui64Time); //填写自适应段标志帧头
|
||||
m_pVideo_pes->packet_start_code_prefix = 0x000001;
|
||||
m_pVideo_pes->stream_id = TS_H264_STREAM_ID; //E0~EF表示是视频的,C0~DF是音频,H264-- E0
|
||||
m_pVideo_pes->marker_bit = 0x02;
|
||||
m_pVideo_pes->PES_scrambling_control = 0x00; //人选字段 存在,不加扰
|
||||
m_pVideo_pes->PES_priority = 0x00;
|
||||
m_pVideo_pes->data_alignment_indicator = 0x00;
|
||||
m_pVideo_pes->copyright = 0x00;
|
||||
m_pVideo_pes->original_or_copy = 0x00;
|
||||
m_pVideo_pes->PTS_DTS_flags = 0x03;
|
||||
m_pVideo_pes->ESCR_flag = 0x00;
|
||||
m_pVideo_pes->ES_rate_flag = 0x00;
|
||||
m_pVideo_pes->DSM_trick_mode_flag = 0x00;
|
||||
m_pVideo_pes->additional_copy_info_flag = 0x00;
|
||||
m_pVideo_pes->PES_CRC_flag = 0x00;
|
||||
m_pVideo_pes->PES_extension_flag = 0x00;
|
||||
m_pVideo_pes->PES_header_data_length = 0x0A; //后面的数据包括了PTS和 DTS所占的字节数
|
||||
PES2TS(m_pVideo_pes, TS_H264_PID, &ts_adaptation_field_Head, ui64Time);
|
||||
m_pVideo_pes->ESlen = 0;
|
||||
return ui32Len;
|
||||
}
|
||||
|
||||
int TSMaker::inputAAC(const char* pcData, uint32_t ui32Len, uint64_t ui64Pts) {
|
||||
if (m_pOutVideoTs == NULL) {
|
||||
return 0;
|
||||
}
|
||||
m_pAudio_pes->ES = const_cast<char *>(pcData);
|
||||
m_pAudio_pes->ESlen = ui32Len;
|
||||
Ts_Adaptation_field ts_adaptation_field_Head;
|
||||
WriteAdaptive_flags_Tail(&ts_adaptation_field_Head); //填写自适应段标志帧头
|
||||
m_pAudio_pes->packet_start_code_prefix = 0x000001;
|
||||
m_pAudio_pes->stream_id = TS_AAC_STREAM_ID; //E0~EF表示是视频的,C0~DF是音频,H264-- E0
|
||||
m_pAudio_pes->marker_bit = 0x02;
|
||||
m_pAudio_pes->PES_scrambling_control = 0x00; //人选字段 存在,不加扰
|
||||
m_pAudio_pes->PES_priority = 0x00;
|
||||
m_pAudio_pes->data_alignment_indicator = 0x00;
|
||||
m_pAudio_pes->copyright = 0x00;
|
||||
m_pAudio_pes->original_or_copy = 0x00;
|
||||
m_pAudio_pes->PTS_DTS_flags = 0x03;
|
||||
m_pAudio_pes->ESCR_flag = 0x00;
|
||||
m_pAudio_pes->ES_rate_flag = 0x00;
|
||||
m_pAudio_pes->DSM_trick_mode_flag = 0x00;
|
||||
m_pAudio_pes->additional_copy_info_flag = 0x00;
|
||||
m_pAudio_pes->PES_CRC_flag = 0x00;
|
||||
m_pAudio_pes->PES_extension_flag = 0x00;
|
||||
m_pAudio_pes->PES_header_data_length = 0x0A; //后面的数据包括了PTS
|
||||
PES2TS(m_pAudio_pes, TS_AAC_PID, &ts_adaptation_field_Head, ui64Pts);
|
||||
m_pAudio_pes->ESlen = 0;
|
||||
return ui32Len;
|
||||
}
|
||||
|
||||
void TSMaker::WriteAdaptive_flags_Tail(Ts_Adaptation_field* pTsAdaptationField) {
|
||||
//填写自适应段
|
||||
pTsAdaptationField->discontinuty_indicator = 0;
|
||||
pTsAdaptationField->random_access_indicator = 0;
|
||||
pTsAdaptationField->elementary_stream_priority_indicator = 0;
|
||||
pTsAdaptationField->PCR_flag = 0; //只用到这个
|
||||
pTsAdaptationField->OPCR_flag = 0;
|
||||
pTsAdaptationField->splicing_point_flag = 0;
|
||||
pTsAdaptationField->transport_private_data_flag = 0;
|
||||
pTsAdaptationField->adaptation_field_extension_flag = 0;
|
||||
|
||||
//需要自己算
|
||||
pTsAdaptationField->pcr = 0;
|
||||
pTsAdaptationField->adaptation_field_length = 1; //占用1位标志所用的位
|
||||
|
||||
pTsAdaptationField->opcr = 0;
|
||||
pTsAdaptationField->splice_countdown = 0;
|
||||
pTsAdaptationField->private_data_len = 0;
|
||||
}
|
||||
|
||||
void TSMaker::CreateAdaptive_Ts(Ts_Adaptation_field * pTsAdaptationField, unsigned char * pucTs, unsigned int uiAdaptiveLength) {
|
||||
unsigned int uiCurrentAdaptiveLength = 1; //当前已经用的自适应段长度
|
||||
unsigned char ucAdaptiveflags = 0; //自适应段的标志
|
||||
unsigned char *pucTmp = pucTs;
|
||||
//填写自适应字段
|
||||
if (pTsAdaptationField->adaptation_field_length > 0) {
|
||||
pucTs += 1; //自适应段的一些标志所占用的1个字节
|
||||
uiCurrentAdaptiveLength += 1;
|
||||
|
||||
if (pTsAdaptationField->discontinuty_indicator) {
|
||||
ucAdaptiveflags |= 0x80;
|
||||
}
|
||||
if (pTsAdaptationField->random_access_indicator) {
|
||||
ucAdaptiveflags |= 0x40;
|
||||
}
|
||||
if (pTsAdaptationField->elementary_stream_priority_indicator) {
|
||||
ucAdaptiveflags |= 0x20;
|
||||
}
|
||||
if (pTsAdaptationField->PCR_flag) {
|
||||
unsigned long long pcr_base;
|
||||
unsigned int pcr_ext;
|
||||
|
||||
pcr_base = (pTsAdaptationField->pcr / 300);
|
||||
pcr_ext = (pTsAdaptationField->pcr % 300);
|
||||
|
||||
ucAdaptiveflags |= 0x10;
|
||||
|
||||
pucTs[0] = (pcr_base >> 25) & 0xff;
|
||||
pucTs[1] = (pcr_base >> 17) & 0xff;
|
||||
pucTs[2] = (pcr_base >> 9) & 0xff;
|
||||
pucTs[3] = (pcr_base >> 1) & 0xff;
|
||||
pucTs[4] = pcr_base << 7 | pcr_ext >> 8 | 0x7e;
|
||||
pucTs[5] = (pcr_ext) & 0xff;
|
||||
pucTs += 6;
|
||||
|
||||
uiCurrentAdaptiveLength += 6;
|
||||
}
|
||||
if (pTsAdaptationField->OPCR_flag) {
|
||||
unsigned long long opcr_base;
|
||||
unsigned int opcr_ext;
|
||||
|
||||
opcr_base = (pTsAdaptationField->opcr / 300);
|
||||
opcr_ext = (pTsAdaptationField->opcr % 300);
|
||||
|
||||
ucAdaptiveflags |= 0x08;
|
||||
|
||||
pucTs[0] = (opcr_base >> 25) & 0xff;
|
||||
pucTs[1] = (opcr_base >> 17) & 0xff;
|
||||
pucTs[2] = (opcr_base >> 9) & 0xff;
|
||||
pucTs[3] = (opcr_base >> 1) & 0xff;
|
||||
pucTs[4] = ((opcr_base << 7) & 0x80)
|
||||
| ((opcr_ext >> 8) & 0x01);
|
||||
pucTs[5] = (opcr_ext) & 0xff;
|
||||
pucTs += 6;
|
||||
uiCurrentAdaptiveLength += 6;
|
||||
}
|
||||
if (pTsAdaptationField->splicing_point_flag) {
|
||||
pucTs[0] = pTsAdaptationField->splice_countdown;
|
||||
|
||||
ucAdaptiveflags |= 0x04;
|
||||
|
||||
pucTs += 1;
|
||||
uiCurrentAdaptiveLength += 1;
|
||||
}
|
||||
if (pTsAdaptationField->private_data_len > 0) {
|
||||
ucAdaptiveflags |= 0x02;
|
||||
if (1 + pTsAdaptationField->private_data_len
|
||||
> static_cast<unsigned char>(uiAdaptiveLength
|
||||
- uiCurrentAdaptiveLength)) {
|
||||
WarnL << "private_data_len error !";
|
||||
return;
|
||||
} else {
|
||||
pucTs[0] = pTsAdaptationField->private_data_len;
|
||||
pucTs += 1;
|
||||
memcpy(pucTs, pTsAdaptationField->private_data,
|
||||
pTsAdaptationField->private_data_len);
|
||||
pucTs += pTsAdaptationField->private_data_len;
|
||||
|
||||
uiCurrentAdaptiveLength += (1
|
||||
+ pTsAdaptationField->private_data_len);
|
||||
}
|
||||
}
|
||||
if (pTsAdaptationField->adaptation_field_extension_flag) {
|
||||
ucAdaptiveflags |= 0x01;
|
||||
pucTs[1] = 1;
|
||||
pucTs[2] = 0;
|
||||
uiCurrentAdaptiveLength += 2;
|
||||
}
|
||||
*pucTmp = ucAdaptiveflags; //将标志放入内存
|
||||
}
|
||||
return;
|
||||
}
|
||||
void TSMaker::PES2TS(TsPes * pTsPes, unsigned int uiPID, Ts_Adaptation_field * pTsAdaptationFieldHead, uint64_t ui64Dts) {
|
||||
TsPacketHeader ts_header;
|
||||
unsigned int uiAdaptiveLength = 0; //要填写0XFF的长度
|
||||
unsigned int uiFirstPacketLoadLength = 188 - 4 - 1 - pTsAdaptationFieldHead->adaptation_field_length - 19; //分片包的第一个包的负载长度
|
||||
const char * pcNeafBuf = pTsPes->ES; //分片包 总负载的指针
|
||||
unsigned char aucTSbuf[TS_PACKET_SIZE];
|
||||
unsigned char * pucTSBuf;
|
||||
bool bFirstPkt = true;
|
||||
while (true) {
|
||||
if ((m_uiWritePacketNum++ % 40) == 0) //每40个包打一个 pat,一个pmt
|
||||
{
|
||||
CreatePAT(); //创建PAT
|
||||
CreatePMT(); //创建PMT
|
||||
}
|
||||
if (bFirstPkt) {
|
||||
bFirstPkt = false;
|
||||
//memset(TSbuf,0,TS_PACKET_SIZE);
|
||||
pucTSBuf = aucTSbuf;
|
||||
CreateTsHeader(&ts_header, uiPID, 0x01, 0x03); //PID = TS_H264_PID,有效荷载单元起始指示符_play_init = 0x01, ada_field_C,0x03,含有调整字段和有效负载 ;
|
||||
TsHeader2buffer(&ts_header, aucTSbuf);
|
||||
pucTSBuf += 4; //写入TS 头
|
||||
if (pTsPes->ESlen > uiFirstPacketLoadLength) {
|
||||
//计算分片包的第一个包的负载长度
|
||||
uiAdaptiveLength = 188 - 4 - 1 - ((pTsPes->ESlen - uiFirstPacketLoadLength) % 184); //要填写0XFF的长度,最后一个包有自适应
|
||||
pucTSBuf[0] = pTsAdaptationFieldHead->adaptation_field_length; //自适应字段的长度,自己填写的
|
||||
pucTSBuf += 1;
|
||||
CreateAdaptive_Ts(pTsAdaptationFieldHead, pucTSBuf, (uiAdaptiveLength)); //填写自适应字段
|
||||
pucTSBuf += pTsAdaptationFieldHead->adaptation_field_length; //填写自适应段所需要的长度
|
||||
} else {
|
||||
uiAdaptiveLength = uiFirstPacketLoadLength - pTsPes->ESlen;
|
||||
pucTSBuf[0] = pTsAdaptationFieldHead->adaptation_field_length + uiAdaptiveLength; //自适应字段的长度,自己填写的
|
||||
pucTSBuf += 1;
|
||||
CreateAdaptive_Ts(pTsAdaptationFieldHead, pucTSBuf, uiAdaptiveLength); //填写自适应字段
|
||||
pucTSBuf += pTsAdaptationFieldHead->adaptation_field_length;
|
||||
memset(pucTSBuf, 0xFF, uiAdaptiveLength);
|
||||
pucTSBuf += uiAdaptiveLength;
|
||||
uiFirstPacketLoadLength = pTsPes->ESlen;
|
||||
}
|
||||
|
||||
pTsPes->PES_packet_length = pTsPes->ESlen + pTsPes->PES_header_data_length + 3;
|
||||
if (TS_H264_PID==uiPID || pTsPes->PES_packet_length > 0xFFFF) {
|
||||
pTsPes->PES_packet_length = 0;
|
||||
}
|
||||
pucTSBuf[0] = (pTsPes->packet_start_code_prefix >> 16) & 0xFF;
|
||||
pucTSBuf[1] = (pTsPes->packet_start_code_prefix >> 8) & 0xFF;
|
||||
pucTSBuf[2] = pTsPes->packet_start_code_prefix & 0xFF;
|
||||
pucTSBuf[3] = pTsPes->stream_id;
|
||||
pucTSBuf[4] = (pTsPes->PES_packet_length >> 8) & 0xFF;
|
||||
pucTSBuf[5] = pTsPes->PES_packet_length & 0xFF;
|
||||
pucTSBuf[6] = pTsPes->marker_bit << 6
|
||||
| pTsPes->PES_scrambling_control << 4
|
||||
| pTsPes->PES_priority << 3
|
||||
| pTsPes->data_alignment_indicator << 2
|
||||
| pTsPes->copyright << 1 | pTsPes->original_or_copy;
|
||||
pucTSBuf[7] = pTsPes->PTS_DTS_flags << 6 | pTsPes->ESCR_flag << 5
|
||||
| pTsPes->ES_rate_flag << 4
|
||||
| pTsPes->DSM_trick_mode_flag << 3
|
||||
| pTsPes->additional_copy_info_flag << 2
|
||||
| pTsPes->PES_CRC_flag << 1 | pTsPes->PES_extension_flag;
|
||||
pucTSBuf += 8;
|
||||
switch (pTsPes->PTS_DTS_flags) {
|
||||
case 0x03: //both pts and ui64Dts
|
||||
pucTSBuf[6] = (((0x1 << 4) | ((ui64Dts >> 29) & 0x0E) | 0x01) & 0xff);
|
||||
pucTSBuf[7] = (((((ui64Dts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff);
|
||||
pucTSBuf[8] = ((((ui64Dts >> 14) & 0xfffe) | 0x01) & 0xff);
|
||||
pucTSBuf[9] = ((((ui64Dts << 1) & 0xfffe) | 0x01) >> 8) & 0xff;
|
||||
pucTSBuf[10] = (((ui64Dts << 1) & 0xfffe) | 0x01) & 0xff;
|
||||
case 0x02: //pts only
|
||||
pucTSBuf[1] = (((0x3 << 4) | ((ui64Dts >> 29) & 0x0E) | 0x01) & 0xff);
|
||||
pucTSBuf[2] = (((((ui64Dts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff);
|
||||
pucTSBuf[3] = ((((ui64Dts >> 14) & 0xfffe) | 0x01) & 0xff);
|
||||
pucTSBuf[4] = (((((ui64Dts << 1) & 0xfffe) | 0x01) >> 8) & 0xff);
|
||||
pucTSBuf[5] = ((((ui64Dts << 1) & 0xfffe) | 0x01) & 0xff);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pucTSBuf[0] = pTsPes->PES_header_data_length;
|
||||
pucTSBuf += (1 + pucTSBuf[0]);
|
||||
memcpy(pucTSBuf, pcNeafBuf, uiFirstPacketLoadLength);
|
||||
pcNeafBuf += uiFirstPacketLoadLength;
|
||||
pTsPes->ESlen -= uiFirstPacketLoadLength;
|
||||
//将包写入文件
|
||||
fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件
|
||||
continue;
|
||||
}
|
||||
if (pTsPes->ESlen >= 184) {
|
||||
//处理中间包
|
||||
//memset(TSbuf,0,TS_PACKET_SIZE);
|
||||
pucTSBuf = aucTSbuf;
|
||||
CreateTsHeader(&ts_header, uiPID, 0x00, 0x01); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x01,仅有有效负载;
|
||||
TsHeader2buffer(&ts_header, aucTSbuf);
|
||||
pucTSBuf += 4;
|
||||
memcpy(pucTSBuf, pcNeafBuf, 184);
|
||||
pcNeafBuf += 184;
|
||||
pTsPes->ESlen -= 184;
|
||||
fwrite(aucTSbuf, 188, 1, m_pOutVideoTs);
|
||||
continue;
|
||||
}
|
||||
if (pTsPes->ESlen == 183) {
|
||||
//memset(TSbuf,0,TS_PACKET_SIZE);
|
||||
pucTSBuf = aucTSbuf;
|
||||
CreateTsHeader(&ts_header, uiPID, 0x00, 0x03); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x03,含有调整字段和有效负载;
|
||||
TsHeader2buffer(&ts_header, aucTSbuf);
|
||||
pucTSBuf += 4;
|
||||
pucTSBuf[0] = 1;
|
||||
pucTSBuf[1] = 0x00;
|
||||
pucTSBuf += 2;
|
||||
memcpy(pucTSBuf, pcNeafBuf, 182);
|
||||
pTsPes->ESlen = 1;
|
||||
fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件
|
||||
|
||||
}
|
||||
//memset(TSbuf,0,TS_PACKET_SIZE);
|
||||
pucTSBuf = aucTSbuf;
|
||||
CreateTsHeader(&ts_header, uiPID, 0x00, 0x03); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x03,含有调整字段和有效负载;
|
||||
TsHeader2buffer(&ts_header, aucTSbuf);
|
||||
pucTSBuf += 4;
|
||||
pucTSBuf[0] = 184 - pTsPes->ESlen - 1;
|
||||
pucTSBuf[1] = 0x00;
|
||||
pucTSBuf += 2;
|
||||
|
||||
memset(pucTSBuf, 0xFF, (184 - pTsPes->ESlen - 2));
|
||||
pucTSBuf += (184 - pTsPes->ESlen - 2);
|
||||
|
||||
memcpy(pucTSBuf, pcNeafBuf, pTsPes->ESlen); //183就丢弃一字节
|
||||
pTsPes->ESlen = 0;
|
||||
fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
249
src/MedaiFile/TSMaker.h
Normal file
249
src/MedaiFile/TSMaker.h
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* TSMaker.h
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef TSMAKER_H_
|
||||
#define TSMAKER_H_
|
||||
|
||||
#include "CRC/crc32.h"
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include "Util/File.h"
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
|
||||
|
||||
#define TS_PACKET_SIZE 188
|
||||
#define TS_PACKET_HEADER 4
|
||||
#define TS_SYNC_BYTE 0x47
|
||||
#define TS_PAT_PID 0x00
|
||||
#define TS_PMT_PID 0xFFF
|
||||
#define TS_H264_PID 0x100
|
||||
#define TS_AAC_PID 0x101
|
||||
#define TS_H264_STREAM_ID 0xE0
|
||||
#define TS_AAC_STREAM_ID 0xC0
|
||||
#define PMT_STREAM_TYPE_VIDEO 0x1B
|
||||
#define PMT_STREAM_TYPE_AUDIO 0x0F
|
||||
|
||||
//#define ES_BUF_SIZE 256*1024
|
||||
|
||||
//ts 包头
|
||||
typedef struct Tag_PacketHeader {
|
||||
unsigned char sync_byte :8; //同步字节, 固定为0x47,表示后面的是一个TS分组
|
||||
unsigned char tras_error :1; //传输误码指示符
|
||||
unsigned char play_init :1; //有效荷载单元起始指示符
|
||||
unsigned char tras_prio :1; //传输优先, 1表示高优先级,传输机制可能用到,解码用不着
|
||||
unsigned int PID :13; //PID
|
||||
unsigned char tras_scramb :2; //传输加扰控制
|
||||
unsigned char ada_field_C :2; //自适应控制 01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载,先调整字段然后有效负载。为00解码器不进行处理
|
||||
unsigned char conti_cter :4; //连续计数器 一个4bit的计数器,范围0-15
|
||||
} TsPacketHeader;
|
||||
|
||||
//连续性计数器,也就是说 有多少个 pat包,几个pmt包 ,几个MP3 包,几个 h264包,0x00 - 0x0f ,然后折回到0x00
|
||||
typedef struct Tag_Continuity_Counter {
|
||||
unsigned char continuity_counter_pat;
|
||||
unsigned char continuity_counter_pmt;
|
||||
unsigned char continuity_counter_video;
|
||||
unsigned char continuity_counter_audio;
|
||||
} Continuity_Counter;
|
||||
|
||||
//自适应段标志
|
||||
typedef struct Tag_Ts_Adaptation_field {
|
||||
unsigned char discontinuty_indicator :1; //1表明当前传送流分组的不连续状态为真
|
||||
unsigned char random_access_indicator :1; //表明下一个有相同PID的PES分组应该含有PTS字段和一个原始流访问点
|
||||
unsigned char elementary_stream_priority_indicator :1; //优先级
|
||||
unsigned char PCR_flag :1; //包含pcr字段
|
||||
unsigned char OPCR_flag :1; //包含opcr字段
|
||||
unsigned char splicing_point_flag :1; //拼接点标志
|
||||
unsigned char transport_private_data_flag :1; //私用字节
|
||||
unsigned char adaptation_field_extension_flag :1; //调整字段有扩展
|
||||
|
||||
unsigned char adaptation_field_length; //自适应段长度
|
||||
unsigned long long pcr; //自适应段中用到的的pcr
|
||||
unsigned long long opcr; //自适应段中用到的的opcr
|
||||
unsigned char splice_countdown;
|
||||
unsigned char private_data_len;
|
||||
unsigned char private_data[256];
|
||||
} Ts_Adaptation_field;
|
||||
|
||||
//PAT结构体:节目相关表
|
||||
typedef struct Tag_TsPat {
|
||||
unsigned char table_id :8; //固定为0x00 ,标志是该表是PAT
|
||||
unsigned char section_syntax_indicator :1; //段语法标志位,固定为1
|
||||
unsigned char zero :1; //0
|
||||
unsigned char reserved_1 :2; //保留位
|
||||
unsigned int section_length :12; //表示这个字节后面有用的字节数,包括CRC32
|
||||
unsigned int transport_stream_id :16; //该传输流的ID,区别于一个网络中其它多路复用的流
|
||||
unsigned char reserved_2 :2; //保留位
|
||||
unsigned char version_number :5; //范围0-31,表示PAT的版本号
|
||||
unsigned char current_next_indicator :1; //发送的PAT是当前有效还是下一个PAT有效
|
||||
unsigned char section_number :8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
|
||||
unsigned char last_section_number :8; //最后一个分段的号码
|
||||
unsigned int program_number :16; //节目号
|
||||
unsigned char reserved_3 :3; //保留位
|
||||
//unsigned int network_PID :13 ; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID,本例中不含有 networke_pid
|
||||
unsigned int program_map_PID :13; //节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个
|
||||
unsigned long long CRC_32 :32; //CRC32校验码
|
||||
} TsPat;
|
||||
|
||||
//PMT结构体:节目映射表
|
||||
typedef struct Tag_TsPmt {
|
||||
unsigned char table_id :8; //固定为0x02, 表示PMT表
|
||||
unsigned char section_syntax_indicator :1; //固定为0x01
|
||||
unsigned char zero :1; //0x00
|
||||
unsigned char reserved_1 :2; //0x03
|
||||
unsigned int section_length :12; //首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
|
||||
unsigned int program_number :16; // 指出该节目对应于可应用的Program map PID
|
||||
unsigned char reserved_2 :2; //0x03
|
||||
unsigned char version_number :5; //指出TS流中Program map section的版本号
|
||||
unsigned char current_next_indicator :1; //当该位置1时,当前传送的Program map section可用;当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
|
||||
unsigned char section_number :8; //固定为0x00
|
||||
unsigned char last_section_number :8; //固定为0x00
|
||||
unsigned char reserved_3 :3; //0x07
|
||||
unsigned int PCR_PID :13; //指明TS包的PID值,该TS包含有PCR域,该PCR值对应于由节目号指定的对应节目。如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
|
||||
unsigned char reserved_4 :4; //预留为0x0F
|
||||
unsigned int program_info_length :12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
|
||||
unsigned char stream_type_video :8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定
|
||||
unsigned char reserved_5_video :3; //0x07
|
||||
unsigned int elementary_PID_video :13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
|
||||
unsigned char reserved_6_video :4; //0x0F
|
||||
unsigned int ES_info_length_video :12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
|
||||
unsigned char stream_type_audio :8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定
|
||||
unsigned char reserved_5_audio :3; //0x07
|
||||
unsigned int elementary_PID_audio :13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
|
||||
unsigned char reserved_6_audio :4; //0x0F
|
||||
unsigned int ES_info_length_audio :12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
|
||||
unsigned long long CRC_32 :32; //CRC32校验码
|
||||
} TsPmt;
|
||||
|
||||
//PTS_DTS结构体:本程序设置都有 “11”
|
||||
typedef struct Tag_TsPtsDts {
|
||||
unsigned char reserved_1 :4;
|
||||
unsigned char pts_32_30 :3; //显示时间戳
|
||||
unsigned char marker_bit1 :1;
|
||||
unsigned int pts_29_15 :15;
|
||||
unsigned char marker_bit2 :1;
|
||||
unsigned int pts_14_0 :15;
|
||||
unsigned char marker_bit3 :1;
|
||||
unsigned char reserved_2 :4;
|
||||
unsigned char dts_32_30 :3; //解码时间戳
|
||||
unsigned char marker_bit4 :1;
|
||||
unsigned int dts_29_15 :15;
|
||||
unsigned char marker_bit5 :1;
|
||||
unsigned int dts_14_0 :15;
|
||||
unsigned char marker_bit6 :1;
|
||||
} TsPtsDts;
|
||||
|
||||
//PES包结构体,包括PES包头和ES数据 ,头 19 个字节
|
||||
typedef struct Tag_TsPes {
|
||||
unsigned int packet_start_code_prefix :24; //起始:0x000001
|
||||
unsigned char stream_id :8; //基本流的类型和编号
|
||||
unsigned int PES_packet_length :16; //包长度,就是帧数据的长度,可能为0,要自己算,做多16位,如果超出则需要自己算
|
||||
unsigned char marker_bit :2; //必须是:'10'
|
||||
unsigned char PES_scrambling_control :2; //pes包有效载荷的加扰方式
|
||||
unsigned char PES_priority :1; //有效负载的优先级
|
||||
unsigned char data_alignment_indicator :1; //如果设置为1表明PES包的头后面紧跟着视频或音频syncword开始的代码。
|
||||
unsigned char copyright :1; //1:靠版权保护,0:不靠
|
||||
unsigned char original_or_copy :1; //1;有效负载是原始的,0:有效负载时拷贝的
|
||||
unsigned char PTS_DTS_flags :2; //'10':PTS字段存在,‘11’:PTD和DTS都存在,‘00’:都没有,‘01’:禁用。
|
||||
unsigned char ESCR_flag :1; //1:escr基准字段 和 escr扩展字段均存在,0:无任何escr字段存在
|
||||
unsigned char ES_rate_flag :1; //1:es_rate字段存在,0 :不存在
|
||||
unsigned char DSM_trick_mode_flag :1; //1;8比特特接方式字段存在,0 :不存在
|
||||
unsigned char additional_copy_info_flag :1; //1:additional_copy_info存在,0: 不存在
|
||||
unsigned char PES_CRC_flag :1; //1:crc字段存在,0:不存在
|
||||
unsigned char PES_extension_flag :1; //1:扩展字段存在,0:不存在
|
||||
unsigned char PES_header_data_length :8; //后面数据的长度,
|
||||
//TsPtsDts tsptsdts; //ptsdts结构体对象,10个字节
|
||||
char *ES;
|
||||
unsigned long ESlen;
|
||||
} TsPes;
|
||||
|
||||
/*//H264一帧数据的结构体
|
||||
typedef struct Tag_NALU_t {
|
||||
unsigned char forbidden_bit; //! Should always be FALSE
|
||||
unsigned char nal_reference_idc; //! NALU_PRIORITY_xxxx
|
||||
unsigned char nal_unit_type; //! NALU_TYPE_xxxx
|
||||
unsigned int startcodeprefix_len; //! 前缀字节数
|
||||
unsigned int len; //! 包含nal 头的nal 长度,从第一个00000001到下一个000000001的长度
|
||||
unsigned int max_size; //! 做多一个nal 的长度
|
||||
unsigned char * buf; //! 包含nal 头的nal 数据
|
||||
unsigned int lost_packets; //! 预留
|
||||
} NALU_t;*/
|
||||
|
||||
//nal类型
|
||||
typedef enum {
|
||||
NALU_TYPE_SLICE = 1,
|
||||
NALU_TYPE_DPA = 2,
|
||||
NALU_TYPE_DPB = 3,
|
||||
NALU_TYPE_DPC = 4,
|
||||
NALU_TYPE_IDR = 5,
|
||||
NALU_TYPE_SEI = 6,
|
||||
NALU_TYPE_SPS = 7,
|
||||
NALU_TYPE_PPS = 8,
|
||||
NALU_TYPE_AUD = 9,
|
||||
NALU_TYPE_EOSEQ = 10,
|
||||
NALU_TYPE_EOSTREAM = 11,
|
||||
NALU_TYPE_FILL = 12,
|
||||
#if (MVC_EXTENSION_ENABLE)
|
||||
NALU_TYPE_PREFIX = 14,
|
||||
NALU_TYPE_SUB_SPS = 15,
|
||||
NALU_TYPE_SLC_EXT = 20,
|
||||
NALU_TYPE_VDRD = 24 // View and Dependency Representation Delimiter NAL Unit
|
||||
#endif
|
||||
} NaluType;
|
||||
|
||||
/*//MP3头结构体
|
||||
typedef struct Tag_Mp3_Header {
|
||||
unsigned int sync :11; //同步信息
|
||||
unsigned char version :2; //版本
|
||||
unsigned char layer :2; //层
|
||||
unsigned char error_protection :1; //CRC校验
|
||||
unsigned char bitrate_index :4; //位率
|
||||
unsigned char sampling_frequency :2; //采样频率
|
||||
unsigned char padding :1; //帧长调节
|
||||
unsigned char private_t :1; //保留字
|
||||
unsigned char mode :2; //声道模式
|
||||
unsigned char mode_extension :2; //扩展模式
|
||||
unsigned char copyright :1; //版权
|
||||
unsigned char original :1; //原版标志
|
||||
unsigned char emphasis :2; //强调模式
|
||||
} Mp3_Header;*/
|
||||
|
||||
|
||||
class TSMaker {
|
||||
public:
|
||||
TSMaker();
|
||||
virtual ~TSMaker();
|
||||
bool init(const string &strFilename, uint32_t ui32BufSize);
|
||||
int inputH264(const char *pcData, uint32_t ui32Len, uint64_t ui64Time);
|
||||
int inputAAC(const char *pcData, uint32_t ui32Len, uint64_t ui64Time);
|
||||
void clear();
|
||||
private:
|
||||
string m_strFilename;
|
||||
FILE *m_pOutVideoTs;
|
||||
Continuity_Counter m_continuityCounter;
|
||||
TsPes *m_pVideo_pes;
|
||||
TsPes *m_pAudio_pes;
|
||||
unsigned int m_uiWritePacketNum;
|
||||
char *m_pcFileBuf;
|
||||
|
||||
void flush();
|
||||
void CreateTsHeader(TsPacketHeader * pTsHeader, unsigned int uiPID, unsigned char ucPlayInit, unsigned char ucAdaFieldC);
|
||||
void TsHeader2buffer(TsPacketHeader * pTsHeader, unsigned char *pucBuffer);
|
||||
void CreatePAT();
|
||||
void CreatePMT();
|
||||
|
||||
void WriteAdaptive_flags_Head(Ts_Adaptation_field * pAdaptationField, uint64_t ui64VideoPts);
|
||||
void WriteAdaptive_flags_Tail(Ts_Adaptation_field * pAdaptationField); //填写自适应段标志帧尾的
|
||||
|
||||
void PES2TS(TsPes * pPes, unsigned int uiPID, Ts_Adaptation_field * pAdaptationField, uint64_t ui64Pts);
|
||||
void CreateAdaptive_Ts(Ts_Adaptation_field * pAdaptationField, unsigned char * pcTs, unsigned int uiAdaptiveLength);
|
||||
};
|
||||
|
||||
#endif /* TSMAKER_H_ */
|
24
src/MediaSender.h
Normal file
24
src/MediaSender.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* MediaSender.h
|
||||
*
|
||||
* Created on: 2016年9月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_MEDIASENDER_H_
|
||||
#define SRC_MEDIASENDER_H_
|
||||
#include "Thread/ThreadPool.hpp"
|
||||
using namespace ZL::Thread;
|
||||
|
||||
class MediaSender {
|
||||
public:
|
||||
static ThreadPool & sendThread() {
|
||||
static ThreadPool pool(1, ThreadPool::PRIORITY_HIGHEST);
|
||||
return pool;
|
||||
}
|
||||
private:
|
||||
MediaSender();
|
||||
virtual ~MediaSender();
|
||||
};
|
||||
|
||||
#endif /* SRC_MEDIASENDER_H_ */
|
57
src/Player/MediaPlayer.cpp
Normal file
57
src/Player/MediaPlayer.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* MediaPlayer.cpp
|
||||
*
|
||||
* Created on: 2016年12月5日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "Rtmp/RtmpPlayerImp.h"
|
||||
#include "Rtsp/RtspPlayerImp.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace ZL::Rtmp;
|
||||
using namespace ZL::Rtsp;
|
||||
|
||||
namespace ZL {
|
||||
namespace Player {
|
||||
|
||||
MediaPlayer::MediaPlayer() {
|
||||
}
|
||||
|
||||
MediaPlayer::~MediaPlayer() {
|
||||
if(!EventPoller::Instance().isMainThread()){
|
||||
FatalL << "未在主线程释放";
|
||||
}
|
||||
teardown();
|
||||
}
|
||||
|
||||
void MediaPlayer::play(const char* strUrl, const char* strUser, const char* strPwd, eRtpType eType) {
|
||||
string strPrefix = FindField(strUrl, NULL, "://");
|
||||
if ((strcasecmp(m_strPrefix.data(),strPrefix.data()) != 0) || strPrefix.empty()) {
|
||||
//协议切换
|
||||
m_strPrefix = strPrefix;
|
||||
m_parser = PlayerBase::createPlayer(strUrl);
|
||||
m_parser->setOnShutdown(m_shutdownCB);
|
||||
m_parser->setOnVideoCB(m_onGetVideoCB);
|
||||
m_parser->setOnAudioCB(m_onGetAudioCB);
|
||||
}
|
||||
m_parser->setOnPlayResult(m_playResultCB);
|
||||
m_parser->play(strUrl, strUser, strPwd, eType);
|
||||
}
|
||||
|
||||
void MediaPlayer::pause(bool bPause) {
|
||||
if (m_parser) {
|
||||
m_parser->pause(bPause);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaPlayer::teardown() {
|
||||
if (m_parser) {
|
||||
m_parser->teardown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} /* namespace Player */
|
||||
} /* namespace ZL */
|
39
src/Player/MediaPlayer.h
Normal file
39
src/Player/MediaPlayer.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* MediaPlayer.h
|
||||
*
|
||||
* Created on: 2016年12月5日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_PLAYER_MEDIAPLAYER_H_
|
||||
#define SRC_PLAYER_MEDIAPLAYER_H_
|
||||
|
||||
#include "Player.h"
|
||||
#include "PlayerBase.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
using namespace std;
|
||||
|
||||
|
||||
namespace ZL {
|
||||
namespace Player {
|
||||
|
||||
class MediaPlayer : public PlayerImp<PlayerBase,PlayerBase> {
|
||||
public:
|
||||
typedef std::shared_ptr<MediaPlayer> Ptr;
|
||||
|
||||
MediaPlayer();
|
||||
virtual ~MediaPlayer();
|
||||
|
||||
void play(const char* strUrl, const char *strUser = "", const char *strPwd = "", eRtpType eType = RTP_TCP) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
private:
|
||||
string m_strPrefix;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace Player */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_PLAYER_MEDIAPLAYER_H_ */
|
118
src/Player/Player.cpp
Normal file
118
src/Player/Player.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Player.cpp
|
||||
*
|
||||
* Created on: 2016年12月2日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "Player.h"
|
||||
#include "H264/SPSParser.h"
|
||||
#include <cstring>
|
||||
#include "Util/logger.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
|
||||
static unsigned const samplingFrequencyTable[16] = { 96000, 88200,
|
||||
64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025,
|
||||
8000, 7350, 0, 0, 0 };
|
||||
|
||||
void writeAdtsHeader(const AdtsFrame &hed, uint8_t *pcAdts) {
|
||||
pcAdts[0] = (hed.syncword >> 4 & 0xFF); //8bit
|
||||
pcAdts[1] = (hed.syncword << 4 & 0xF0); //4 bit
|
||||
pcAdts[1] |= (hed.id << 3 & 0x08); //1 bit
|
||||
pcAdts[1] |= (hed.layer << 1 & 0x06); //2bit
|
||||
pcAdts[1] |= (hed.protection_absent & 0x01); //1 bit
|
||||
|
||||
pcAdts[2] = (hed.profile << 6 & 0xC0); // 2 bit
|
||||
pcAdts[2] |= (hed.sf_index << 2 & 0x3C); //4bit
|
||||
pcAdts[2] |= (hed.private_bit << 1 & 0x02); //1 bit
|
||||
pcAdts[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit
|
||||
|
||||
pcAdts[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit
|
||||
pcAdts[3] |= (hed.original << 5 & 0x20); //1 bit
|
||||
pcAdts[3] |= (hed.home << 4 & 0x10); //1 bit
|
||||
pcAdts[3] |= (hed.copyright_identification_bit << 3 & 0x08); //1 bit
|
||||
pcAdts[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit
|
||||
pcAdts[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit
|
||||
|
||||
pcAdts[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit
|
||||
|
||||
pcAdts[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit
|
||||
pcAdts[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit
|
||||
|
||||
pcAdts[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit
|
||||
pcAdts[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 bit
|
||||
}
|
||||
string makeAdtsConfig(const uint8_t *pcAdts){
|
||||
if (!(pcAdts[0] == 0xFF && (pcAdts[1] & 0xF0) == 0xF0)) {
|
||||
return "";
|
||||
}
|
||||
// Get and check the 'profile':
|
||||
unsigned char profile = (pcAdts[2] & 0xC0) >> 6; // 2 bits
|
||||
if (profile == 3) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Get and check the 'sampling_frequency_index':
|
||||
unsigned char sampling_frequency_index = (pcAdts[2] & 0x3C) >> 2; // 4 bits
|
||||
if (samplingFrequencyTable[sampling_frequency_index] == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Get and check the 'channel_configuration':
|
||||
unsigned char channel_configuration = ((pcAdts[2] & 0x01) << 2)
|
||||
| ((pcAdts[3] & 0xC0) >> 6); // 3 bits
|
||||
|
||||
unsigned char audioSpecificConfig[2];
|
||||
unsigned char const audioObjectType = profile + 1;
|
||||
audioSpecificConfig[0] = (audioObjectType << 3) | (sampling_frequency_index >> 1);
|
||||
audioSpecificConfig[1] = (sampling_frequency_index << 7) | (channel_configuration << 3);
|
||||
return string((char *)audioSpecificConfig,2);
|
||||
}
|
||||
void makeAdtsHeader(const string &strAudioCfg,AdtsFrame &adts) {
|
||||
uint8_t cfg1 = strAudioCfg[0];
|
||||
uint8_t cfg2 = strAudioCfg[1];
|
||||
|
||||
int audioObjectType;
|
||||
int sampling_frequency_index;
|
||||
int channel_configuration;
|
||||
|
||||
audioObjectType = cfg1 >> 3;
|
||||
sampling_frequency_index = ((cfg1 & 0x07) << 1) | (cfg2 >> 7);
|
||||
channel_configuration = (cfg2 & 0x7F) >> 3;
|
||||
|
||||
adts.syncword = 0x0FFF;
|
||||
adts.id = 0;
|
||||
adts.layer = 0;
|
||||
adts.protection_absent = 1;
|
||||
adts.profile = audioObjectType - 1;
|
||||
adts.sf_index = sampling_frequency_index;
|
||||
adts.private_bit = 0;
|
||||
adts.channel_configuration = channel_configuration;
|
||||
adts.original = 0;
|
||||
adts.home = 0;
|
||||
adts.copyright_identification_bit = 0;
|
||||
adts.copyright_identification_start = 0;
|
||||
adts.aac_frame_length = 7;
|
||||
adts.adts_buffer_fullness = 2047;
|
||||
adts.no_raw_data_blocks_in_frame = 0;
|
||||
}
|
||||
void getAACInfo(const AdtsFrame &adts,int &iSampleRate,int &iChannel){
|
||||
iSampleRate = samplingFrequencyTable[adts.sf_index];
|
||||
iChannel = adts.channel_configuration;
|
||||
}
|
||||
bool getAVCInfo(const string& strSps,int &iVideoWidth, int &iVideoHeight, float &iVideoFps) {
|
||||
T_GetBitContext tGetBitBuf;
|
||||
T_SPS tH264SpsInfo;
|
||||
memset(&tGetBitBuf,0,sizeof(tGetBitBuf));
|
||||
memset(&tH264SpsInfo,0,sizeof(tH264SpsInfo));
|
||||
tGetBitBuf.pu8Buf = (uint8_t *)strSps.data() + 1;
|
||||
tGetBitBuf.iBufSize = strSps.size() - 1;
|
||||
if(0 != h264DecSeqParameterSet((void *) &tGetBitBuf, &tH264SpsInfo)){
|
||||
return false;
|
||||
}
|
||||
h264GetWidthHeight(&tH264SpsInfo, &iVideoWidth, &iVideoHeight);
|
||||
h264GeFramerate(&tH264SpsInfo, &iVideoFps);
|
||||
//FatalL << iVideoWidth << " " << iVideoHeight << " " << iVideoFps;
|
||||
return true;
|
||||
}
|
53
src/Player/Player.h
Normal file
53
src/Player/Player.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Player.h
|
||||
*
|
||||
* Created on: 2016年12月2日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_PLAYER_PLAYER_H_
|
||||
#define SRC_PLAYER_PLAYER_H_
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
typedef struct {
|
||||
uint16_t sequence;
|
||||
uint32_t timeStamp;
|
||||
unsigned char type;
|
||||
string data;
|
||||
} H264Frame;
|
||||
|
||||
//ADTS 头中相对有用的信息 采样率、声道数、帧长度
|
||||
typedef struct {
|
||||
unsigned int syncword; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
|
||||
unsigned int id; //1 bslbf MPEG 标示符, 设置为1
|
||||
unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’
|
||||
unsigned int protection_absent; //1 bslbf 表示是否误码校验
|
||||
unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC
|
||||
unsigned int sf_index; //4 uimsbf 表示使用的采样率下标
|
||||
unsigned int private_bit; //1 bslbf
|
||||
unsigned int channel_configuration; //3 uimsbf 表示声道数
|
||||
unsigned int original; //1 bslbf
|
||||
unsigned int home; //1 bslbf
|
||||
//下面的为改变的参数即每一帧都不同
|
||||
unsigned int copyright_identification_bit; //1 bslbf
|
||||
unsigned int copyright_identification_start; //1 bslbf
|
||||
unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block
|
||||
unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流
|
||||
//no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
|
||||
//所以说number_of_raw_data_blocks_in_frame == 0
|
||||
//表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
|
||||
unsigned int no_raw_data_blocks_in_frame; //2 uimsfb
|
||||
unsigned char data[2 * 1024 + 7];
|
||||
uint16_t sequence;
|
||||
uint32_t timeStamp;
|
||||
} AdtsFrame;
|
||||
|
||||
void makeAdtsHeader(const string &strAudioCfg,AdtsFrame &adts);
|
||||
void writeAdtsHeader(const AdtsFrame &adts, uint8_t *pcAdts) ;
|
||||
string makeAdtsConfig(const uint8_t *pcAdts);
|
||||
void getAACInfo(const AdtsFrame &adts,int &iSampleRate,int &iChannel);
|
||||
bool getAVCInfo(const string &strSps,int &iVideoWidth, int &iVideoHeight, float &iVideoFps);
|
||||
|
||||
#endif /* SRC_PLAYER_PLAYER_H_ */
|
34
src/Player/PlayerBase.cpp
Normal file
34
src/Player/PlayerBase.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* PlayerBase.cpp
|
||||
*
|
||||
* Created on: 2016年12月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "PlayerBase.h"
|
||||
#include "Rtmp/RtmpPlayerImp.h"
|
||||
#include "Rtsp/RtspPlayerImp.h"
|
||||
#include "Rtsp/Rtsp.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Rtmp;
|
||||
using namespace ZL::Rtsp;
|
||||
|
||||
namespace ZL {
|
||||
namespace Player {
|
||||
|
||||
|
||||
PlayerBase::Ptr PlayerBase::createPlayer(const char* strUrl) {
|
||||
string prefix = FindField(strUrl, NULL, "://");
|
||||
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new RtspPlayerImp());
|
||||
}
|
||||
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new RtmpPlayerImp());
|
||||
}
|
||||
return PlayerBase::Ptr(new RtspPlayerImp());
|
||||
}
|
||||
|
||||
} /* namespace Player */
|
||||
} /* namespace ZL */
|
219
src/Player/PlayerBase.h
Normal file
219
src/Player/PlayerBase.h
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* PlayerBase.h
|
||||
*
|
||||
* Created on: 2016年12月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_PLAYER_PLAYERBASE_H_
|
||||
#define SRC_PLAYER_PLAYERBASE_H_
|
||||
|
||||
#include "Player.h"
|
||||
#include "Network/Socket.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
using namespace ZL::Network;
|
||||
|
||||
namespace ZL {
|
||||
namespace Player {
|
||||
|
||||
|
||||
class PlayerBase{
|
||||
public:
|
||||
typedef std::shared_ptr<PlayerBase> Ptr;
|
||||
typedef enum {
|
||||
RTP_TCP = 0,
|
||||
RTP_UDP = 1,
|
||||
RTP_MULTICAST = 2,
|
||||
} eRtpType;
|
||||
static Ptr createPlayer(const char* strUrl);
|
||||
|
||||
PlayerBase(){};
|
||||
virtual ~PlayerBase(){};
|
||||
|
||||
virtual void play(const char* strUrl, const char *strUser = "", const char *strPwd = "", eRtpType eType = RTP_TCP) {};
|
||||
virtual void pause(bool bPause) {};
|
||||
virtual void teardown() {};
|
||||
|
||||
virtual void setOnShutdown( const function<void(const SockException &)> &cb) {};
|
||||
virtual void setOnPlayResult( const function<void(const SockException &ex)> &cb) {};
|
||||
virtual void setOnVideoCB( const function<void(const H264Frame &frame)> &cb) {};
|
||||
virtual void setOnAudioCB( const function<void(const AdtsFrame &frame)> &cb) {};
|
||||
|
||||
virtual int getVideoHeight() const { return 0; };
|
||||
virtual int getVideoWidth() const { return 0; };
|
||||
virtual float getVideoFps() const { return 0; };
|
||||
virtual int getAudioSampleRate() const { return 0; };
|
||||
virtual int getAudioSampleBit() const { return 0; };
|
||||
virtual int getAudioChannel() const { return 0; };
|
||||
virtual float getRtpLossRate(int iTrackId) const {return 0; };
|
||||
virtual const string& getPps() const { static string null;return null; };
|
||||
virtual const string& getSps() const { static string null;return null; };
|
||||
virtual const string& getAudioCfg() const { static string null;return null; };
|
||||
virtual bool containAudio() const { return false; };
|
||||
virtual bool containVideo() const { return false; };
|
||||
virtual bool isInited() const { return true; };
|
||||
virtual float getDuration() const { return 0;};
|
||||
virtual float getProgresss() const { return 0;};
|
||||
virtual void seekTo(float fProgress) {};
|
||||
|
||||
|
||||
protected:
|
||||
virtual void onShutdown(const SockException &ex) {};
|
||||
virtual void onPlayResult(const SockException &ex) {};
|
||||
};
|
||||
|
||||
template<typename Parent,typename Parser>
|
||||
class PlayerImp : public Parent
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<PlayerImp> Ptr;
|
||||
PlayerImp(){};
|
||||
virtual ~PlayerImp(){};
|
||||
void setOnShutdown(const function<void(const SockException &)> &cb) override {
|
||||
if (m_parser) {
|
||||
m_parser->setOnShutdown(cb);
|
||||
}
|
||||
m_shutdownCB = cb;
|
||||
}
|
||||
void setOnPlayResult(const function<void(const SockException &ex)> &cb) override {
|
||||
if (m_parser) {
|
||||
m_parser->setOnPlayResult(cb);
|
||||
}
|
||||
m_playResultCB = cb;
|
||||
}
|
||||
void setOnVideoCB(const function<void(const H264Frame &frame)> &cb) override{
|
||||
if (m_parser) {
|
||||
m_parser->setOnVideoCB(cb);
|
||||
}
|
||||
m_onGetVideoCB = cb;
|
||||
}
|
||||
void setOnAudioCB(const function<void(const AdtsFrame &frame)> &cb) override{
|
||||
if (m_parser) {
|
||||
m_parser->setOnAudioCB(cb);
|
||||
}
|
||||
m_onGetAudioCB = cb;
|
||||
}
|
||||
int getVideoHeight() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getVideoHeight();
|
||||
}
|
||||
return PlayerBase::getVideoHeight();
|
||||
}
|
||||
|
||||
int getVideoWidth() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getVideoWidth();
|
||||
}
|
||||
return PlayerBase::getVideoWidth();
|
||||
}
|
||||
|
||||
float getVideoFps() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getVideoFps();
|
||||
}
|
||||
return PlayerBase::getVideoFps();
|
||||
}
|
||||
|
||||
int getAudioSampleRate() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getAudioSampleRate();
|
||||
}
|
||||
return PlayerBase::getAudioSampleRate();
|
||||
}
|
||||
|
||||
int getAudioSampleBit() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getAudioSampleBit();
|
||||
}
|
||||
return PlayerBase::getAudioSampleBit();
|
||||
}
|
||||
|
||||
int getAudioChannel() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getAudioChannel();
|
||||
}
|
||||
return PlayerBase::getAudioChannel();
|
||||
}
|
||||
|
||||
const string& getPps() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getPps();
|
||||
}
|
||||
return PlayerBase::getPps();
|
||||
}
|
||||
|
||||
const string& getSps() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getSps();
|
||||
}
|
||||
return PlayerBase::getSps();
|
||||
}
|
||||
|
||||
const string& getAudioCfg() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getAudioCfg();
|
||||
}
|
||||
return PlayerBase::getAudioCfg();
|
||||
}
|
||||
bool containAudio() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->containAudio();
|
||||
}
|
||||
return PlayerBase::containAudio();
|
||||
}
|
||||
bool containVideo() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->containVideo();
|
||||
}
|
||||
return PlayerBase::containVideo();
|
||||
}
|
||||
bool isInited() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->isInited();
|
||||
}
|
||||
return PlayerBase::isInited();
|
||||
}
|
||||
float getDuration() const override {
|
||||
if (m_parser) {
|
||||
return m_parser->getDuration();
|
||||
}
|
||||
return PlayerBase::getDuration();
|
||||
}
|
||||
float getProgresss() const override{
|
||||
if (m_parser) {
|
||||
return m_parser->getProgresss();
|
||||
}
|
||||
return PlayerBase::getProgresss();
|
||||
};
|
||||
void seekTo(float fProgress) override{
|
||||
if (m_parser) {
|
||||
return m_parser->seekTo(fProgress);
|
||||
}
|
||||
return PlayerBase::seekTo(fProgress);
|
||||
};
|
||||
protected:
|
||||
void onShutdown(const SockException &ex) override {
|
||||
if (m_shutdownCB) {
|
||||
m_shutdownCB(ex);
|
||||
}
|
||||
}
|
||||
void onPlayResult(const SockException &ex) override {
|
||||
if (m_playResultCB) {
|
||||
m_playResultCB(ex);
|
||||
m_playResultCB = nullptr;
|
||||
}
|
||||
}
|
||||
function<void(const SockException &ex)> m_shutdownCB;
|
||||
function<void(const SockException &ex)> m_playResultCB;
|
||||
std::shared_ptr<Parser> m_parser;
|
||||
function<void(const H264Frame &frame)> m_onGetVideoCB;
|
||||
function<void(const AdtsFrame &frame)> m_onGetAudioCB;
|
||||
|
||||
};
|
||||
} /* namespace Player */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_PLAYER_PLAYERBASE_H_ */
|
85
src/RTP/RtpMaker.h
Normal file
85
src/RTP/RtpMaker.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* RtpMaker.h
|
||||
*
|
||||
* Created on: 2016年8月12日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef RTP_RTPMAKER_H_
|
||||
#define RTP_RTPMAKER_H_
|
||||
|
||||
#include "Rtsp/RtspMediaSource.h"
|
||||
#include "Util/ResourcePool.h"
|
||||
#include "Util/logger.h"
|
||||
#include "Util/RingBuffer.hpp"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Thread/ThreadPool.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Thread;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtsp {
|
||||
|
||||
class RtpMaker {
|
||||
public:
|
||||
typedef function<void(const RtpPacket::Ptr &pPkt, bool bKeyPos)> onGetRTP;
|
||||
RtpMaker(const onGetRTP &cb, uint32_t ui32Ssrc, int iMtuSize,int iSampleRate,
|
||||
uint8_t ui8PlayloadType, uint8_t ui8Interleaved) {
|
||||
callBack = cb;
|
||||
m_ui32Ssrc = ui32Ssrc;
|
||||
m_ui32SampleRate = iSampleRate;
|
||||
m_iMtuSize = iMtuSize;
|
||||
m_ui8PlayloadType = ui8PlayloadType;
|
||||
m_ui8Interleaved = ui8Interleaved;
|
||||
}
|
||||
virtual ~RtpMaker() {
|
||||
}
|
||||
|
||||
virtual void makeRtp(const char *pcData, int iDataLen, uint32_t uiStamp)=0;
|
||||
|
||||
int getInterleaved() const {
|
||||
return m_ui8Interleaved;
|
||||
}
|
||||
|
||||
int getPlayloadType() const {
|
||||
return m_ui8PlayloadType;
|
||||
}
|
||||
|
||||
int getSampleRate() const {
|
||||
return m_ui32SampleRate;
|
||||
}
|
||||
|
||||
uint32_t getSsrc() const {
|
||||
return m_ui32Ssrc;
|
||||
}
|
||||
|
||||
uint16_t getSeqence() const {
|
||||
return m_ui16Sequence;
|
||||
}
|
||||
uint32_t getTimestamp() const {
|
||||
return m_ui32TimeStamp;
|
||||
}
|
||||
protected:
|
||||
uint32_t m_ui32Ssrc;
|
||||
uint32_t m_ui32SampleRate;
|
||||
int m_iMtuSize;
|
||||
uint8_t m_ui8PlayloadType;
|
||||
uint8_t m_ui8Interleaved;
|
||||
uint16_t m_ui16Sequence = 0;
|
||||
uint32_t m_ui32TimeStamp = 0;
|
||||
virtual void onMakeRtp(const RtpPacket::Ptr &pkt, bool bKeyPos = true) {
|
||||
callBack(pkt, bKeyPos);
|
||||
}
|
||||
inline RtpPacket::Ptr obtainPkt() {
|
||||
return m_pktPool.obtain();
|
||||
}
|
||||
private:
|
||||
RtspMediaSource::PoolType m_pktPool;
|
||||
onGetRTP callBack;
|
||||
};
|
||||
|
||||
} /* namespace RTP */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* RTP_RTPMAKER_H_ */
|
81
src/RTP/RtpMakerAAC.cpp
Normal file
81
src/RTP/RtpMakerAAC.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* RtpMakerAAC.cpp
|
||||
*
|
||||
* Created on: 2016年8月12日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include "RtpMakerAAC.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtsp {
|
||||
|
||||
void RtpMaker_AAC::makeRtp(const char *pcData, int iLen, uint32_t uiStamp) {
|
||||
static uint32_t cycleMS = mINI::Instance()[Config::Rtp::kCycleMS].as<uint32_t>();
|
||||
uiStamp %= cycleMS;
|
||||
char *ptr = (char *) pcData;
|
||||
int iSize = iLen;
|
||||
while (iSize > 0 ) {
|
||||
if (iSize <= m_iMtuSize - 20) {
|
||||
m_aucSectionBuf[0] = 0;
|
||||
m_aucSectionBuf[1] = 16;
|
||||
m_aucSectionBuf[2] = iLen >> 5;
|
||||
m_aucSectionBuf[3] = (iLen & 0x1F) << 3;
|
||||
memcpy(m_aucSectionBuf + 4, ptr, iSize);
|
||||
makeAACRtp(m_aucSectionBuf, iSize + 4, true, uiStamp);
|
||||
break;
|
||||
}
|
||||
m_aucSectionBuf[0] = 0;
|
||||
m_aucSectionBuf[1] = 16;
|
||||
m_aucSectionBuf[2] = (iLen) >> 5;
|
||||
m_aucSectionBuf[3] = (iLen & 0x1F) << 3;
|
||||
memcpy(m_aucSectionBuf + 4, ptr, m_iMtuSize - 20);
|
||||
makeAACRtp(m_aucSectionBuf, m_iMtuSize - 16, false, uiStamp);
|
||||
ptr += (m_iMtuSize - 20);
|
||||
iSize -= (m_iMtuSize - 20);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void RtpMaker_AAC::makeAACRtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp) {
|
||||
uint16_t u16RtpLen = uiLen + 12;
|
||||
m_ui32TimeStamp = (m_ui32SampleRate / 1000) * uiStamp;
|
||||
uint32_t ts = htonl(m_ui32TimeStamp);
|
||||
uint16_t sq = htons(m_ui16Sequence);
|
||||
uint32_t sc = htonl(m_ui32Ssrc);
|
||||
auto pRtppkt = obtainPkt();
|
||||
auto &rtppkt = *(pRtppkt.get());
|
||||
unsigned char *pucRtp = rtppkt.payload;
|
||||
pucRtp[0] = '$';
|
||||
pucRtp[1] = m_ui8Interleaved;
|
||||
pucRtp[2] = u16RtpLen >> 8;
|
||||
pucRtp[3] = u16RtpLen & 0x00FF;
|
||||
pucRtp[4] = 0x80;
|
||||
pucRtp[5] = (bMark << 7) | m_ui8PlayloadType;
|
||||
memcpy(&pucRtp[6], &sq, 2);
|
||||
memcpy(&pucRtp[8], &ts, 4);
|
||||
//ssrc
|
||||
memcpy(&pucRtp[12], &sc, 4);
|
||||
memcpy(&pucRtp[16], pData, uiLen);
|
||||
rtppkt.PT = m_ui8PlayloadType;
|
||||
rtppkt.interleaved = m_ui8Interleaved;
|
||||
rtppkt.mark = bMark;
|
||||
rtppkt.length = uiLen + 16;
|
||||
rtppkt.sequence = m_ui16Sequence;
|
||||
rtppkt.timeStamp = m_ui32TimeStamp;
|
||||
rtppkt.ssrc = m_ui32Ssrc;
|
||||
rtppkt.type = TrackAudio;
|
||||
|
||||
memcpy(rtppkt.payload + 16, pData, uiLen);
|
||||
onMakeRtp(pRtppkt, false);
|
||||
m_ui16Sequence++;
|
||||
}
|
||||
|
||||
} /* namespace RTP */
|
||||
} /* namespace ZL */
|
42
src/RTP/RtpMakerAAC.h
Normal file
42
src/RTP/RtpMakerAAC.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* RtpMakerAAC.h
|
||||
*
|
||||
* Created on: 2016年8月12日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef RTP_RTPMAKERAAC_H_
|
||||
#define RTP_RTPMAKERAAC_H_
|
||||
#include "Rtsp/RtspMediaSource.h"
|
||||
#include "Util/logger.h"
|
||||
#include "Util/RingBuffer.hpp"
|
||||
#include "RtpMaker.h"
|
||||
#include <memory>
|
||||
#include "Util/ResourcePool.h"
|
||||
using namespace std;
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtsp {
|
||||
|
||||
class RtpMaker_AAC: public RtpMaker {
|
||||
public:
|
||||
typedef std::shared_ptr<RtpMaker_AAC> Ptr;
|
||||
RtpMaker_AAC(const onGetRTP &cb,
|
||||
uint32_t ui32Ssrc, int iMtuSize , int iSampleRate, uint8_t ui8PlayloadType = 97,
|
||||
uint8_t ui8Interleaved = 2) :
|
||||
RtpMaker(cb, ui32Ssrc, iMtuSize,iSampleRate, ui8PlayloadType, ui8Interleaved) {
|
||||
}
|
||||
virtual ~RtpMaker_AAC() {
|
||||
}
|
||||
void makeRtp(const char *pcData, int iDataLen, uint32_t uiStamp) override;
|
||||
private:
|
||||
inline void makeAACRtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp);
|
||||
unsigned char m_aucSectionBuf[1600];
|
||||
};
|
||||
|
||||
} /* namespace RTP */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* RTP_RTPMAKERAAC_H_ */
|
98
src/RTP/RtpMakerH264.cpp
Normal file
98
src/RTP/RtpMakerH264.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* RtpMakerH264.cpp
|
||||
*
|
||||
* Created on: 2016年8月12日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include "RtpMakerH264.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.hpp"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtsp {
|
||||
|
||||
void RtpMaker_H264::makeRtp(const char* pcData, int iLen, uint32_t uiStamp) {
|
||||
static uint32_t cycleMS = mINI::Instance()[Config::Rtp::kCycleMS].as<uint32_t>();
|
||||
uiStamp %= cycleMS;
|
||||
int iSize = m_iMtuSize - 2;
|
||||
if (iLen > iSize) { //超过MTU
|
||||
const unsigned char s_e_r_Start = 0x80;
|
||||
const unsigned char s_e_r_Mid = 0x00;
|
||||
const unsigned char s_e_r_End = 0x40;
|
||||
//获取帧头数据,1byte
|
||||
unsigned char naluType = *((unsigned char *) pcData) & 0x1f; //获取NALU的5bit 帧类型
|
||||
|
||||
unsigned char nal_ref_idc = *((unsigned char *) pcData) & 0x60; //获取NALU的2bit 帧重要程度 00 可以丢 11不能丢
|
||||
//nal_ref_idc = 0x60;
|
||||
//组装FU-A帧头数据 2byte
|
||||
unsigned char f_nri_type = nal_ref_idc + 28;//F为0 1bit,nri上面获取到2bit,28为FU-A分片类型5bit
|
||||
unsigned char s_e_r_type = naluType;
|
||||
bool bFirst = true;
|
||||
bool mark = false;
|
||||
int nOffset = 1;
|
||||
while (!mark) {
|
||||
if (iLen < nOffset + iSize) { //是否拆分结束
|
||||
iSize = iLen - nOffset;
|
||||
mark = true;
|
||||
s_e_r_type = s_e_r_End + naluType;
|
||||
} else {
|
||||
if (bFirst == true) {
|
||||
s_e_r_type = s_e_r_Start + naluType;
|
||||
bFirst = false;
|
||||
} else {
|
||||
s_e_r_type = s_e_r_Mid + naluType;
|
||||
}
|
||||
}
|
||||
memcpy(aucSectionBuf, &f_nri_type, 1);
|
||||
memcpy(aucSectionBuf + 1, &s_e_r_type, 1);
|
||||
memcpy(aucSectionBuf + 2, (unsigned char *) pcData + nOffset, iSize);
|
||||
nOffset += iSize;
|
||||
makeH264Rtp(aucSectionBuf, iSize + 2, mark, uiStamp);
|
||||
}
|
||||
} else {
|
||||
makeH264Rtp(pcData, iLen, true, uiStamp);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RtpMaker_H264::makeH264Rtp(const void* data, unsigned int len, bool mark, uint32_t uiStamp) {
|
||||
uint16_t ui16RtpLen = len + 12;
|
||||
m_ui32TimeStamp = (m_ui32SampleRate / 1000) * uiStamp;
|
||||
uint32_t ts = htonl(m_ui32TimeStamp);
|
||||
uint16_t sq = htons(m_ui16Sequence);
|
||||
uint32_t sc = htonl(m_ui32Ssrc);
|
||||
|
||||
auto pRtppkt = obtainPkt();
|
||||
auto &rtppkt = *(pRtppkt.get());
|
||||
unsigned char *pucRtp = rtppkt.payload;
|
||||
pucRtp[0] = '$';
|
||||
pucRtp[1] = m_ui8Interleaved;
|
||||
pucRtp[2] = ui16RtpLen >> 8;
|
||||
pucRtp[3] = ui16RtpLen & 0x00FF;
|
||||
pucRtp[4] = 0x80;
|
||||
pucRtp[5] = (mark << 7) | m_ui8PlayloadType;
|
||||
memcpy(&pucRtp[6], &sq, 2);
|
||||
memcpy(&pucRtp[8], &ts, 4);
|
||||
//ssrc
|
||||
memcpy(&pucRtp[12], &sc, 4);
|
||||
memcpy(&pucRtp[16], data, len);
|
||||
rtppkt.PT = m_ui8PlayloadType;
|
||||
rtppkt.interleaved = m_ui8Interleaved;
|
||||
rtppkt.mark = mark;
|
||||
rtppkt.length = len + 16;
|
||||
rtppkt.sequence = m_ui16Sequence;
|
||||
rtppkt.timeStamp = m_ui32TimeStamp;
|
||||
rtppkt.ssrc = m_ui32Ssrc;
|
||||
rtppkt.type = TrackVideo;
|
||||
uint8_t type = ((uint8_t *) (data))[0] & 0x1F;
|
||||
memcpy(rtppkt.payload + 16, data, len);
|
||||
onMakeRtp(pRtppkt, type == 5);
|
||||
m_ui16Sequence++;
|
||||
//InfoL<<timeStamp<<" "<<time<<" "<<sampleRate;
|
||||
}
|
||||
|
||||
} /* namespace RTP */
|
||||
} /* namespace ZL */
|
42
src/RTP/RtpMakerH264.h
Normal file
42
src/RTP/RtpMakerH264.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* RtpMakerH264.h
|
||||
*
|
||||
* Created on: 2016年8月12日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef RTP_RTPMAKERH264_H_
|
||||
#define RTP_RTPMAKERH264_H_
|
||||
|
||||
#include "Rtsp/RtspMediaSource.h"
|
||||
#include "Util/logger.h"
|
||||
#include "Util/RingBuffer.hpp"
|
||||
#include "RtpMaker.h"
|
||||
#include <memory>
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Rtsp;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtsp {
|
||||
|
||||
class RtpMaker_H264: public RtpMaker {
|
||||
public:
|
||||
typedef std::shared_ptr<RtpMaker_H264> Ptr;
|
||||
RtpMaker_H264(const onGetRTP &cb, uint32_t ui32Ssrc,int iMtuSize = 1400,int iSampleRate = 90000,
|
||||
uint8_t ui8PlayloadType = 96, uint8_t ui8Interleaved = 0) :
|
||||
RtpMaker(cb, ui32Ssrc, iMtuSize,iSampleRate, ui8PlayloadType, ui8Interleaved) {
|
||||
}
|
||||
virtual ~RtpMaker_H264() {
|
||||
}
|
||||
|
||||
void makeRtp(const char *pcData, int iDataLen, uint32_t uiStamp) override;
|
||||
private:
|
||||
inline void makeH264Rtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp);
|
||||
unsigned char aucSectionBuf[1600];
|
||||
};
|
||||
|
||||
} /* namespace RTP */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* RTP_RTPMAKERH264_H_ */
|
223
src/Rtmp/Rtmp.h
Normal file
223
src/Rtmp/Rtmp.h
Normal file
@ -0,0 +1,223 @@
|
||||
#ifndef __rtmp_h
|
||||
#define __rtmp_h
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include "Util/util.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "Util/logger.h"
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
|
||||
|
||||
#define PORT 1935
|
||||
|
||||
#define DEFAULT_CHUNK_LEN 128
|
||||
|
||||
#define PACKED __attribute__((packed))
|
||||
|
||||
#define HANDSHAKE_PLAINTEXT 0x03
|
||||
|
||||
#define RANDOM_LEN (1536 - 8)
|
||||
|
||||
#define MSG_SET_CHUNK 1 /*Set Chunk Size (1)*/
|
||||
#define MSG_ABORT 2 /*Abort Message (2)*/
|
||||
#define MSG_ACK 3 /*Acknowledgement (3)*/
|
||||
#define MSG_USER_CONTROL 4 /*User Control Messages (4)*/
|
||||
#define MSG_WIN_SIZE 5 /*Window Acknowledgement Size (5)*/
|
||||
#define MSG_SET_PEER_BW 6 /*Set Peer Bandwidth (6)*/
|
||||
#define MSG_AUDIO 8 /*Audio Message (8)*/
|
||||
#define MSG_VIDEO 9 /*Video Message (9)*/
|
||||
#define MSG_DATA 18 /*Data Message (18, 15) AMF0*/
|
||||
#define MSG_DATA3 15 /*Data Message (18, 15) AMF3*/
|
||||
#define MSG_CMD 20 /*Command Message AMF0 */
|
||||
#define MSG_CMD3 17 /*Command Message AMF3 */
|
||||
#define MSG_OBJECT3 16 /*Shared Object Message (19, 16) AMF3*/
|
||||
#define MSG_OBJECT 19 /*Shared Object Message (19, 16) AMF0*/
|
||||
#define MSG_AGGREGATE 22 /*Aggregate Message (22)*/
|
||||
|
||||
|
||||
#define CONTROL_STREAM_BEGIN 0
|
||||
#define CONTROL_STREAM_EOF 1
|
||||
#define CONTROL_STREAM_DRY 2
|
||||
#define CONTROL_SETBUFFER 3
|
||||
#define CONTROL_STREAM_ISRECORDED 4
|
||||
#define CONTROL_PING_REQUEST 6
|
||||
#define CONTROL_PING_RESPONSE 7
|
||||
|
||||
|
||||
#define STREAM_CONTROL 0
|
||||
#define STREAM_MEDIA 1
|
||||
|
||||
#define CHUNK_SERVER_REQUEST 2 /*服务器像客户端发出请求时的chunkID*/
|
||||
#define CHUNK_CLIENT_REQUEST_BEFORE 3 /*客户端在createStream前,向服务器发出请求的chunkID*/
|
||||
#define CHUNK_CLIENT_REQUEST_AFTER 4 /*客户端在createStream后,向服务器发出请求的chunkID*/
|
||||
|
||||
#define FLV_KEY_FRAME 1
|
||||
#define FLV_INTER_FRAME 2
|
||||
|
||||
class RtmpHandshake {
|
||||
public:
|
||||
RtmpHandshake(uint32_t _time, uint8_t *_random = nullptr) {
|
||||
_time = htonl(_time);
|
||||
memcpy(timeStamp, &_time, 4);
|
||||
if (!_random) {
|
||||
random_generate((char *) random, sizeof(random));
|
||||
} else {
|
||||
memcpy(random, _random, sizeof(random));
|
||||
}
|
||||
}
|
||||
uint8_t timeStamp[4];
|
||||
uint8_t zero[4] = { 0 };
|
||||
uint8_t random[RANDOM_LEN];
|
||||
private:
|
||||
void random_generate(char* bytes, int size) {
|
||||
static char cdata[] = { 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72,
|
||||
0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||
0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||
0x40, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6d };
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[i] = cdata[rand() % (sizeof(cdata) - 1)];
|
||||
}
|
||||
}
|
||||
}PACKED;
|
||||
|
||||
class RtmpHeader {
|
||||
public:
|
||||
uint8_t flags;
|
||||
uint8_t timeStamp[3];
|
||||
uint8_t bodySize[3];
|
||||
uint8_t typeId;
|
||||
uint8_t streamId[4]; /* Note, this is little-endian while others are BE */
|
||||
}PACKED;
|
||||
|
||||
|
||||
class RtmpPacket {
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPacket> Ptr;
|
||||
uint8_t typeId;
|
||||
uint32_t bodySize = 0;
|
||||
uint32_t timeStamp = 0;
|
||||
bool extStamp = false;
|
||||
uint32_t streamId;
|
||||
uint32_t chunkId;
|
||||
std::string strBuf;
|
||||
bool isVideoKeyFrame() const {
|
||||
return typeId == MSG_VIDEO && (uint8_t) strBuf[0] >> 4 == FLV_KEY_FRAME
|
||||
&& (uint8_t) strBuf[1] == 1;
|
||||
}
|
||||
bool isCfgFrame() const {
|
||||
return (typeId == MSG_VIDEO || typeId == MSG_AUDIO)
|
||||
&& (uint8_t) strBuf[1] == 0;
|
||||
}
|
||||
int getMediaType() const {
|
||||
switch (typeId) {
|
||||
case MSG_VIDEO: {
|
||||
return (uint8_t) strBuf[0] & 0x0F;
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO: {
|
||||
return (uint8_t) strBuf[0] >> 4;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int getAudioSampleRate() const {
|
||||
if (typeId != MSG_AUDIO) {
|
||||
return 0;
|
||||
}
|
||||
int flvSampleRate = ((uint8_t) strBuf[0] & 0x0C) >> 2;
|
||||
const static int sampleRate[] = { 5512, 11025, 22050, 44100 };
|
||||
return sampleRate[flvSampleRate];
|
||||
}
|
||||
int getAudioSampleBit() const {
|
||||
if (typeId != MSG_AUDIO) {
|
||||
return 0;
|
||||
}
|
||||
int flvSampleBit = ((uint8_t) strBuf[0] & 0x02) >> 1;
|
||||
const static int sampleBit[] = { 8, 16 };
|
||||
return sampleBit[flvSampleBit];
|
||||
}
|
||||
int getAudioChannel() const {
|
||||
if (typeId != MSG_AUDIO) {
|
||||
return 0;
|
||||
}
|
||||
int flvStereoOrMono = (uint8_t) strBuf[0] & 0x01;
|
||||
const static int channel[] = { 1, 2 };
|
||||
return channel[flvStereoOrMono];
|
||||
}
|
||||
string getH264SPS() const {
|
||||
string ret;
|
||||
if (getMediaType() != 7) {
|
||||
return ret;
|
||||
}
|
||||
if (!isCfgFrame()) {
|
||||
return ret;
|
||||
}
|
||||
if (strBuf.size() < 13) {
|
||||
WarnL << "bad H264 cfg!";
|
||||
return ret;
|
||||
}
|
||||
uint16_t sps_size ;
|
||||
memcpy(&sps_size,strBuf.data() + 11,2);
|
||||
sps_size = ntohs(sps_size);
|
||||
if ((int) strBuf.size() < 13 + sps_size) {
|
||||
WarnL << "bad H264 cfg!";
|
||||
return ret;
|
||||
}
|
||||
ret.assign(strBuf.data() + 13, sps_size);
|
||||
return ret;
|
||||
}
|
||||
string getH264PPS() const {
|
||||
string ret;
|
||||
if (getMediaType() != 7) {
|
||||
return ret;
|
||||
}
|
||||
if (!isCfgFrame()) {
|
||||
return ret;
|
||||
}
|
||||
if (strBuf.size() < 13) {
|
||||
WarnL << "bad H264 cfg!";
|
||||
return ret;
|
||||
}
|
||||
uint16_t sps_size ;
|
||||
memcpy(&sps_size,strBuf.data() + 11,2);
|
||||
sps_size = ntohs(sps_size);
|
||||
|
||||
if ((int) strBuf.size() < 13 + sps_size + 1 + 2) {
|
||||
WarnL << "bad H264 cfg!";
|
||||
return ret;
|
||||
}
|
||||
uint16_t pps_size ;
|
||||
memcpy(&pps_size,strBuf.data() + 13 + sps_size + 1,2);
|
||||
pps_size = ntohs(pps_size);
|
||||
|
||||
if ((int) strBuf.size() < 13 + sps_size + 1 + 2 + pps_size) {
|
||||
WarnL << "bad H264 cfg!";
|
||||
return ret;
|
||||
}
|
||||
ret.assign(strBuf.data() + 13 + sps_size + 1 + 2, pps_size);
|
||||
return ret;
|
||||
}
|
||||
string getAacCfg() const {
|
||||
string ret;
|
||||
if (getMediaType() != 10) {
|
||||
return ret;
|
||||
}
|
||||
if (!isCfgFrame()) {
|
||||
return ret;
|
||||
}
|
||||
if (strBuf.size() < 4) {
|
||||
WarnL << "bad aac cfg!";
|
||||
return ret;
|
||||
}
|
||||
ret = strBuf.substr(2, 2);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
42
src/Rtmp/RtmpMediaSource.cpp
Normal file
42
src/Rtmp/RtmpMediaSource.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* MediaSource.cpp
|
||||
*
|
||||
* Created on: 2016年9月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "RtmpMediaSource.h"
|
||||
#include "MedaiFile/MediaReader.h"
|
||||
using namespace ZL::MediaFile;
|
||||
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
recursive_mutex RtmpMediaSource::g_mtxMediaSrc;
|
||||
unordered_map<string, unordered_map<string,weak_ptr<RtmpMediaSource> > > RtmpMediaSource::g_mapMediaSrc;
|
||||
|
||||
RtmpMediaSource::Ptr RtmpMediaSource::find(const string &strApp, const string &strId, bool bMake) {
|
||||
//查找某一媒体源,找到后返回
|
||||
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
|
||||
auto itApp = g_mapMediaSrc.find(strApp);
|
||||
if (itApp == g_mapMediaSrc.end()) {
|
||||
return bMake ? MediaReader::onMakeRtmp(strApp, strId) : nullptr;
|
||||
}
|
||||
auto itId = itApp->second.find(strId);
|
||||
if (itId == itApp->second.end()) {
|
||||
return bMake ? MediaReader::onMakeRtmp(strApp, strId) : nullptr;
|
||||
}
|
||||
auto ret = itId->second.lock();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
itApp->second.erase(itId);
|
||||
if (itApp->second.size() == 0) {
|
||||
g_mapMediaSrc.erase(itApp);
|
||||
}
|
||||
return bMake ? MediaReader::onMakeRtmp(strApp, strId) : nullptr;
|
||||
}
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
163
src/Rtmp/RtmpMediaSource.h
Normal file
163
src/Rtmp/RtmpMediaSource.h
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* RtmpMediaSource.h
|
||||
*
|
||||
* Created on: 2016年9月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_RTMP_RTMPMEDIASOURCE_H_
|
||||
#define SRC_RTMP_RTMPMEDIASOURCE_H_
|
||||
|
||||
#include "config.h"
|
||||
#include <netinet/in.h>
|
||||
#include "Util/NoticeCenter.h"
|
||||
#include "Util/ResourcePool.h"
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "Util/RingBuffer.hpp"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include "Util/logger.h"
|
||||
#include "Thread/ThreadPool.hpp"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "amf.h"
|
||||
#include "Rtmp.h"
|
||||
#include "Util/util.h"
|
||||
#include "MediaSender.h"
|
||||
#include <mutex>
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Thread;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
|
||||
class RtmpMediaSource: public enable_shared_from_this<RtmpMediaSource> {
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpMediaSource> Ptr;
|
||||
typedef RingBuffer<RtmpPacket> RingType;
|
||||
RtmpMediaSource(const string &strApp, const string &strId) :
|
||||
m_strApp(strApp),
|
||||
m_strId(strId),
|
||||
m_pRing( new RingBuffer<RtmpPacket>(1)),
|
||||
m_thPool( MediaSender::sendThread()) {
|
||||
}
|
||||
virtual ~RtmpMediaSource() {
|
||||
unregist();
|
||||
}
|
||||
const RingType::Ptr &getRing() const {
|
||||
//获取媒体源的rtp环形缓冲
|
||||
return m_pRing;
|
||||
}
|
||||
virtual void regist() {
|
||||
//注册该源,注册后rtmp服务器才能找到该源
|
||||
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
|
||||
if (!g_mapMediaSrc[m_strApp].erase(m_strId)) {
|
||||
InfoL << "Rtmp src:" << m_strApp << " " << m_strId;
|
||||
}
|
||||
g_mapMediaSrc[m_strApp].emplace(m_strId, shared_from_this());
|
||||
NoticeCenter::Instance().emitEvent(Config::Broadcast::kBroadcastRtmpSrcRegisted,m_strApp.data(),m_strId.data());
|
||||
}
|
||||
virtual void unregist() {
|
||||
//反注册该源
|
||||
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
|
||||
auto it = g_mapMediaSrc.find(m_strApp);
|
||||
if (it == g_mapMediaSrc.end()) {
|
||||
return;
|
||||
}
|
||||
if (it->second.erase(m_strId)) {
|
||||
if (it->second.size() == 0) {
|
||||
g_mapMediaSrc.erase(it);
|
||||
}
|
||||
InfoL << "Rtmp src:" << m_strApp << " " << m_strId;
|
||||
}
|
||||
}
|
||||
static set<string> getMediaSet() {
|
||||
set<string> ret;
|
||||
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
|
||||
for (auto &pr0 : g_mapMediaSrc) {
|
||||
for (auto &pr1 : pr0.second) {
|
||||
if (pr1.second.lock()) {
|
||||
ret.emplace(pr0.first + "/" + pr1.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static Ptr find(const string &strApp, const string &strId, bool bMake = true) ;
|
||||
const string& getApp() const {
|
||||
//获取该源的id
|
||||
return m_strApp;
|
||||
}
|
||||
const string& getId() const {
|
||||
//获取该源的id
|
||||
return m_strId;
|
||||
}
|
||||
const AMFValue &getMetaData() const {
|
||||
return m_metadata;
|
||||
}
|
||||
template<typename FUN>
|
||||
void getConfigFrame(const FUN &f) {
|
||||
lock_guard<recursive_mutex> lock(m_mtxMap);
|
||||
for (auto &pr : m_mapCfgFrame) {
|
||||
f(pr.second);
|
||||
}
|
||||
}
|
||||
bool ready() const {
|
||||
lock_guard<recursive_mutex> lock(m_mtxMap);
|
||||
return (m_mapCfgFrame.size() != 0);
|
||||
}
|
||||
virtual void onGetMetaData(const AMFValue &_metadata) {
|
||||
m_metadata = _metadata;
|
||||
}
|
||||
virtual void onGetMedia(const RtmpPacket &_pkt) {
|
||||
RtmpPacket & pkt = const_cast<RtmpPacket &>(_pkt);
|
||||
if (pkt.isCfgFrame()) {
|
||||
lock_guard<recursive_mutex> lock(m_mtxMap);
|
||||
m_mapCfgFrame.emplace(pkt.typeId, pkt);
|
||||
}
|
||||
auto _ring = m_pRing;
|
||||
m_thPool.async([_ring,pkt]() {
|
||||
_ring->write(pkt);
|
||||
});
|
||||
}
|
||||
bool seekTo(uint32_t ui32Stamp) {
|
||||
if (!m_onSeek) {
|
||||
return false;
|
||||
}
|
||||
return m_onSeek(ui32Stamp);
|
||||
}
|
||||
virtual void setOnSeek(const function<bool(uint32_t)> &cb) {
|
||||
m_onSeek = cb;
|
||||
}
|
||||
uint32_t getStamp() {
|
||||
if (!m_onStamp) {
|
||||
return 0;
|
||||
}
|
||||
return m_onStamp();
|
||||
}
|
||||
virtual void setOnStamp(const function<uint32_t()> &cb) {
|
||||
m_onStamp = cb;
|
||||
}
|
||||
protected:
|
||||
function<bool(uint32_t)> m_onSeek;
|
||||
function<uint32_t()> m_onStamp;
|
||||
private:
|
||||
AMFValue m_metadata;
|
||||
unordered_map<int, RtmpPacket> m_mapCfgFrame;
|
||||
mutable recursive_mutex m_mtxMap;
|
||||
string m_strApp; //媒体app
|
||||
string m_strId; //媒体id
|
||||
RingBuffer<RtmpPacket>::Ptr m_pRing; //rtp环形缓冲
|
||||
ThreadPool &m_thPool;
|
||||
static unordered_map<string, unordered_map<string,weak_ptr<RtmpMediaSource> > > g_mapMediaSrc; //静态的媒体源表
|
||||
static recursive_mutex g_mtxMediaSrc; ///访问静态的媒体源表的互斥锁
|
||||
};
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_RTMP_RTMPMEDIASOURCE_H_ */
|
231
src/Rtmp/RtmpParser.cpp
Normal file
231
src/Rtmp/RtmpParser.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* RtmpParser.cpp
|
||||
*
|
||||
* Created on: 2016年12月2日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "RtmpParser.h"
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
RtmpParser::RtmpParser(const AMFValue &val) {
|
||||
auto videoCodec = val["videocodecid"];
|
||||
auto audioCodec = val["audiocodecid"];
|
||||
|
||||
if (videoCodec.type() == AMF_STRING) {
|
||||
if (videoCodec.as_string() == "avc1") {
|
||||
//264
|
||||
m_bHaveVideo = true;
|
||||
} else {
|
||||
InfoL << "不支持RTMP视频格式:" << videoCodec.as_string();
|
||||
}
|
||||
}else if (videoCodec.type() != AMF_NULL){
|
||||
if (videoCodec.as_integer() == 7) {
|
||||
//264
|
||||
m_bHaveVideo = true;
|
||||
} else {
|
||||
InfoL << "不支持RTMP视频格式:" << videoCodec.as_integer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (audioCodec.type() == AMF_STRING) {
|
||||
if (audioCodec.as_string() == "mp4a") {
|
||||
//aac
|
||||
m_bHaveAudio = true;
|
||||
} else {
|
||||
InfoL << "不支持RTMP音频格式:" << audioCodec.as_string();
|
||||
}
|
||||
}else if (audioCodec.type() != AMF_NULL) {
|
||||
if (audioCodec.as_integer() == 10) {
|
||||
//aac
|
||||
m_bHaveAudio = true;
|
||||
} else {
|
||||
InfoL << "不支持RTMP音频格式:" << audioCodec.as_integer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!m_bHaveVideo && !m_bHaveAudio) {
|
||||
throw std::runtime_error("不支持该RTMP媒体格式");
|
||||
}
|
||||
|
||||
onCheckMedia(val);
|
||||
}
|
||||
|
||||
RtmpParser::~RtmpParser() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
bool RtmpParser::inputRtmp(const RtmpPacket &pkt) {
|
||||
switch (pkt.typeId) {
|
||||
case MSG_VIDEO:
|
||||
if (m_bHaveVideo) {
|
||||
return inputVideo(pkt);
|
||||
}
|
||||
return false;
|
||||
case MSG_AUDIO:
|
||||
if (m_bHaveAudio) {
|
||||
return inputAudio(pkt);
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool RtmpParser::inputVideo(const RtmpPacket& pkt) {
|
||||
if (pkt.isCfgFrame()) {
|
||||
//WarnL << " got h264 cfg";
|
||||
if (m_strSPS.size()) {
|
||||
return false;
|
||||
}
|
||||
m_strSPS.assign("\x00\x00\x00\x01", 4);
|
||||
m_strSPS.append(pkt.getH264SPS());
|
||||
|
||||
m_strPPS.assign("\x00\x00\x00\x01", 4);
|
||||
m_strPPS.append(pkt.getH264PPS());
|
||||
|
||||
getAVCInfo(pkt.getH264SPS(), m_iVideoWidth, m_iVideoHeight, m_fVideoFps);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_strSPS.size()) {
|
||||
uint32_t iTotalLen = pkt.strBuf.size();
|
||||
uint32_t iOffset = 5;
|
||||
while(iOffset + 4 < iTotalLen){
|
||||
uint32_t iFrameLen;
|
||||
memcpy(&iFrameLen, pkt.strBuf.data() + iOffset, 4);
|
||||
iFrameLen = ntohl(iFrameLen);
|
||||
iOffset += 4;
|
||||
if(iFrameLen + iOffset > iTotalLen){
|
||||
break;
|
||||
}
|
||||
_onGetH264(pkt.strBuf.data() + iOffset, iFrameLen, pkt.timeStamp);
|
||||
iOffset += iFrameLen;
|
||||
}
|
||||
}
|
||||
return pkt.isVideoKeyFrame();
|
||||
}
|
||||
inline void RtmpParser::_onGetH264(const char* pcData, int iLen, uint32_t ui32TimeStamp) {
|
||||
switch (pcData[0] & 0x1F) {
|
||||
case 5: {
|
||||
onGetH264(m_strSPS.data() + 4, m_strSPS.length() - 4, ui32TimeStamp);
|
||||
onGetH264(m_strPPS.data() + 4, m_strPPS.length() - 4, ui32TimeStamp);
|
||||
}
|
||||
case 1: {
|
||||
onGetH264(pcData, iLen, ui32TimeStamp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL <<(int)(pcData[0] & 0x1F);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void RtmpParser::onGetH264(const char* pcData, int iLen, uint32_t ui32TimeStamp) {
|
||||
m_h264frame.type = pcData[0] & 0x1F;
|
||||
m_h264frame.timeStamp = ui32TimeStamp;
|
||||
m_h264frame.data.assign("\x0\x0\x0\x1", 4); //添加264头
|
||||
m_h264frame.data.append(pcData, iLen);
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(m_mtxCB);
|
||||
if (onVideo) {
|
||||
onVideo(m_h264frame);
|
||||
}
|
||||
}
|
||||
m_h264frame.data.clear();
|
||||
}
|
||||
|
||||
inline bool RtmpParser::inputAudio(const RtmpPacket& pkt) {
|
||||
if (pkt.isCfgFrame()) {
|
||||
if (m_strAudioCfg.size()) {
|
||||
return false;
|
||||
}
|
||||
m_strAudioCfg = pkt.getAacCfg();
|
||||
m_iSampleBit = pkt.getAudioSampleBit();
|
||||
makeAdtsHeader(m_strAudioCfg,m_adts);
|
||||
getAACInfo(m_adts, m_iSampleRate, m_iChannel);
|
||||
return false;
|
||||
}
|
||||
if (m_strAudioCfg.size()) {
|
||||
onGetAAC(pkt.strBuf.data() + 2, pkt.strBuf.size() - 2, pkt.timeStamp);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline void RtmpParser::onGetAAC(const char* pcData, int iLen, uint32_t ui32TimeStamp) {
|
||||
//添加adts头
|
||||
memcpy(m_adts.data + 7, pcData, iLen);
|
||||
m_adts.aac_frame_length = 7 + iLen;
|
||||
m_adts.timeStamp = ui32TimeStamp;
|
||||
writeAdtsHeader(m_adts, m_adts.data);
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(m_mtxCB);
|
||||
if (onAudio) {
|
||||
onAudio(m_adts);
|
||||
}
|
||||
}
|
||||
m_adts.aac_frame_length = 7;
|
||||
|
||||
}
|
||||
inline void RtmpParser::onCheckMedia(const AMFValue& obj) {
|
||||
obj.object_for_each([&](const string &key ,const AMFValue& val) {
|
||||
if(key == "duration") {
|
||||
m_fDuration = val.as_number();
|
||||
return;
|
||||
}
|
||||
if(key == "width") {
|
||||
m_iVideoWidth = val.as_number();
|
||||
return;
|
||||
}
|
||||
if(key == "height") {
|
||||
m_iVideoHeight = val.as_number();
|
||||
return;
|
||||
}
|
||||
if(key == "framerate") {
|
||||
m_fVideoFps = val.as_number();
|
||||
return;
|
||||
}
|
||||
if(key == "audiosamplerate") {
|
||||
m_iSampleRate = val.as_number();
|
||||
return;
|
||||
}
|
||||
if(key == "audiosamplesize") {
|
||||
m_iSampleBit = val.as_number();
|
||||
return;
|
||||
}
|
||||
if(key == "stereo") {
|
||||
m_iChannel = val.as_boolean() ? 2 :1;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
130
src/Rtmp/RtmpParser.h
Normal file
130
src/Rtmp/RtmpParser.h
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* RtmpParser.h
|
||||
*
|
||||
* Created on: 2016年12月2日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_RTMP_RTMPPARSER_H_
|
||||
#define SRC_RTMP_RTMPPARSER_H_
|
||||
|
||||
#include "Rtmp.h"
|
||||
#include "amf.h"
|
||||
#include "Player/Player.h"
|
||||
#include <unordered_map>
|
||||
#include "Util/TimeTicker.h"
|
||||
#include <functional>
|
||||
#include "Player/PlayerBase.h"
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Player;
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
class RtmpParser : public PlayerBase{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpParser> Ptr;
|
||||
RtmpParser(const AMFValue &val);
|
||||
virtual ~RtmpParser();
|
||||
|
||||
bool inputRtmp(const RtmpPacket &pkt);
|
||||
|
||||
void setOnVideoCB(const function<void(const H264Frame &frame)> &cb) override{
|
||||
lock_guard<recursive_mutex> lck(m_mtxCB);
|
||||
onVideo = cb;
|
||||
}
|
||||
void setOnAudioCB(const function<void(const AdtsFrame &frame)> &cb) override{
|
||||
lock_guard<recursive_mutex> lck(m_mtxCB);
|
||||
onAudio = cb;
|
||||
}
|
||||
|
||||
int getVideoHeight() const override{
|
||||
return m_iVideoHeight;
|
||||
}
|
||||
|
||||
int getVideoWidth() const override{
|
||||
return m_iVideoWidth;
|
||||
}
|
||||
|
||||
float getVideoFps() const override{
|
||||
return m_fVideoFps;
|
||||
}
|
||||
|
||||
int getAudioSampleRate() const override{
|
||||
return m_iSampleRate;
|
||||
}
|
||||
|
||||
int getAudioSampleBit() const override{
|
||||
return m_iSampleBit;
|
||||
}
|
||||
|
||||
int getAudioChannel() const override{
|
||||
return m_iChannel;
|
||||
}
|
||||
|
||||
const string& getPps() const override{
|
||||
return m_strPPS;
|
||||
}
|
||||
|
||||
const string& getSps() const override{
|
||||
return m_strSPS;
|
||||
}
|
||||
|
||||
const string& getAudioCfg() const override{
|
||||
return m_strAudioCfg;
|
||||
}
|
||||
bool containAudio() const override{
|
||||
return m_bHaveAudio;
|
||||
}
|
||||
bool containVideo () const override{
|
||||
return m_bHaveVideo;
|
||||
}
|
||||
bool isInited() const override{
|
||||
if (m_bHaveAudio && !m_strAudioCfg.size()) {
|
||||
return false;
|
||||
}
|
||||
if (m_bHaveVideo && !m_strSPS.size()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
float getDuration() const override{
|
||||
return m_fDuration;
|
||||
}
|
||||
private:
|
||||
inline void onCheckMedia(const AMFValue &obj);
|
||||
|
||||
//返回值:true 代表是i帧第一个rtp包
|
||||
inline bool inputVideo(const RtmpPacket &pkt);
|
||||
inline bool inputAudio(const RtmpPacket &pkt);
|
||||
inline void _onGetH264(const char *pcData, int iLen, uint32_t ui32TimeStamp);
|
||||
inline void onGetH264(const char *pcData, int iLen, uint32_t ui32TimeStamp);
|
||||
inline void onGetAAC(const char *pcData, int iLen, uint32_t ui32TimeStamp);
|
||||
//video
|
||||
H264Frame m_h264frame;
|
||||
//aduio
|
||||
AdtsFrame m_adts;
|
||||
|
||||
int m_iSampleRate = 44100;
|
||||
int m_iSampleBit = 16;
|
||||
int m_iChannel = 1;
|
||||
|
||||
string m_strSPS;
|
||||
string m_strPPS;
|
||||
string m_strAudioCfg;
|
||||
int m_iVideoWidth = 0;
|
||||
int m_iVideoHeight = 0;
|
||||
float m_fVideoFps = 0;
|
||||
bool m_bHaveAudio = false;
|
||||
bool m_bHaveVideo = false;
|
||||
float m_fDuration = 0;
|
||||
function<void(const H264Frame &frame)> onVideo;
|
||||
function<void(const AdtsFrame &frame)> onAudio;
|
||||
recursive_mutex m_mtxCB;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_RTMP_RTMPPARSER_H_ */
|
326
src/Rtmp/RtmpPlayer.cpp
Normal file
326
src/Rtmp/RtmpPlayer.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* RtmpPlayer2.cpp
|
||||
*
|
||||
* Created on: 2016Äê11ÔÂ29ÈÕ
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "RtmpPlayer.h"
|
||||
#include "Thread/ThreadPool.hpp"
|
||||
#include "Util/util.h"
|
||||
#include "Util/onceToken.h"
|
||||
#include "Rtmp/utils.h"
|
||||
#include "Rtsp/Rtsp.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
unordered_map<string, RtmpPlayer::rtmpCMDHandle> RtmpPlayer::g_mapCmd;
|
||||
RtmpPlayer::RtmpPlayer() {
|
||||
static onceToken token([]() {
|
||||
g_mapCmd.emplace("_error",&RtmpPlayer::onCmd_result);
|
||||
g_mapCmd.emplace("_result",&RtmpPlayer::onCmd_result);
|
||||
g_mapCmd.emplace("onStatus",&RtmpPlayer::onCmd_onStatus);
|
||||
g_mapCmd.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData);
|
||||
}, []() {});
|
||||
|
||||
}
|
||||
|
||||
RtmpPlayer::~RtmpPlayer() {
|
||||
teardown();
|
||||
DebugL << endl;
|
||||
}
|
||||
void RtmpPlayer::teardown() {
|
||||
if (alive()) {
|
||||
m_strApp.clear();
|
||||
m_strStream.clear();
|
||||
m_strTcUrl.clear();
|
||||
m_mapOnResultCB.clear();
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(m_mtxDeque);
|
||||
m_dqOnStatusCB.clear();
|
||||
}
|
||||
m_pBeatTimer.reset();
|
||||
m_pPlayTimer.reset();
|
||||
m_pMediaTimer.reset();
|
||||
m_fSeekTo = 0;
|
||||
CLEAR_ARR(m_adFistStamp);
|
||||
CLEAR_ARR(m_adNowStamp);
|
||||
clear();
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::play(const char* strUrl, const char * , const char *, eRtpType) {
|
||||
teardown();
|
||||
string strHost = FindField(strUrl, "://", "/");
|
||||
m_strApp = FindField(strUrl, (strHost + "/").data(), "/");
|
||||
m_strStream = FindField(strUrl, (strHost + "/" + m_strApp + "/").data(), NULL);
|
||||
m_strTcUrl = string("rtmp://") + strHost + "/" + m_strApp;
|
||||
|
||||
if (!m_strApp.size() || !m_strStream.size()) {
|
||||
_onPlayResult(SockException(Err_other,"rtmp url非法"));
|
||||
return;
|
||||
}
|
||||
DebugL << strHost << " " << m_strApp << " " << m_strStream;
|
||||
|
||||
auto iPort = atoi(FindField(strHost.c_str(), ":", NULL).c_str());
|
||||
if (iPort <= 0) {
|
||||
//rtmp 默认端口1935
|
||||
iPort = 1935;
|
||||
} else {
|
||||
//服务器域名
|
||||
strHost = FindField(strHost.c_str(), NULL, ":");
|
||||
}
|
||||
startConnect(strHost, iPort);
|
||||
}
|
||||
void RtmpPlayer::onErr(const SockException &ex){
|
||||
_onShutdown(ex);
|
||||
}
|
||||
void RtmpPlayer::onConnect(const SockException &err){
|
||||
if(err.getErrCode()!=Err_success) {
|
||||
_onPlayResult(err);
|
||||
return;
|
||||
}
|
||||
|
||||
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
m_pPlayTimer.reset( new Timer(10, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
strongSelf->_onPlayResult(SockException(Err_timeout,"play rtmp timeout"));
|
||||
strongSelf->teardown();
|
||||
return false;
|
||||
}));
|
||||
startClientSession([weakSelf](){
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->send_connect();
|
||||
});
|
||||
}
|
||||
void RtmpPlayer::onRecv(const Socket::Buffer::Ptr &pBuf){
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
SockException ex(Err_other, e.what());
|
||||
_onPlayResult(ex);
|
||||
_onShutdown(ex);
|
||||
teardown();
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::pause(bool bPause) {
|
||||
send_pause(bPause);
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_connect() {
|
||||
AMFValue obj(AMF_OBJECT);
|
||||
obj.set("app", m_strApp);
|
||||
obj.set("tcUrl", m_strTcUrl);
|
||||
obj.set("fpad", false);
|
||||
obj.set("capabilities", 15);
|
||||
obj.set("videoFunction", 1);
|
||||
//只支持aac
|
||||
obj.set("audioCodecs", 3191);
|
||||
//只支持H264
|
||||
obj.set("videoCodecs", 252);
|
||||
sendInvoke("connect", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "connect result";
|
||||
dec.load<AMFValue>();
|
||||
auto val = dec.load<AMFValue>();
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
||||
}
|
||||
send_createStream();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_createStream() {
|
||||
AMFValue obj(AMF_NULL);
|
||||
sendInvoke("createStream", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "createStream result";
|
||||
dec.load<AMFValue>();
|
||||
m_ui32StreamId = dec.load<int>();
|
||||
send_play();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_play() {
|
||||
AMFEncoder enc;
|
||||
enc << "play" << ++m_iReqID << nullptr << m_strStream << (double)m_ui32StreamId;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
auto fun = [this](AMFValue &val){
|
||||
//TraceL << "play onStatus";
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"play 失败:" << level << " " << code << endl);
|
||||
}
|
||||
};
|
||||
addOnStatusCB(fun);
|
||||
addOnStatusCB(fun);
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_pause(bool bPause) {
|
||||
AMFEncoder enc;
|
||||
enc << "pause" << ++m_iReqID << nullptr << bPause;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
auto fun = [this,bPause](AMFValue &val){
|
||||
//TraceL << "pause onStatus";
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status") {
|
||||
if(!bPause){
|
||||
throw std::runtime_error(StrPrinter <<"pause 恢复播放失败:" << level << " " << code << endl);
|
||||
}
|
||||
}else{
|
||||
m_bPaused = bPause;
|
||||
if(!bPause){
|
||||
_onPlayResult(SockException(Err_success, "rtmp resum success"));
|
||||
}else{
|
||||
//暂停播放
|
||||
m_pMediaTimer.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
addOnStatusCB(fun);
|
||||
|
||||
m_pBeatTimer.reset();
|
||||
if(bPause){
|
||||
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
m_pBeatTimer.reset(new Timer(3,[weakSelf](){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf){
|
||||
return false;
|
||||
}
|
||||
uint32_t timeStamp = ::time(NULL);
|
||||
strongSelf->sendUserControl(CONTROL_PING_REQUEST, timeStamp);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::onCmd_result(AMFDecoder &dec){
|
||||
auto iReqId = dec.load<int>();
|
||||
auto it = m_mapOnResultCB.find(iReqId);
|
||||
if(it != m_mapOnResultCB.end()){
|
||||
it->second(dec);
|
||||
m_mapOnResultCB.erase(it);
|
||||
}else{
|
||||
WarnL << "unhandled _result";
|
||||
}
|
||||
}
|
||||
void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) {
|
||||
AMFValue val;
|
||||
while(true){
|
||||
val = dec.load<AMFValue>();
|
||||
if(val.type() == AMF_OBJECT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(val.type() != AMF_OBJECT){
|
||||
throw std::runtime_error("onStatus: 未找到结果对象");
|
||||
}
|
||||
|
||||
lock_guard<recursive_mutex> lck(m_mtxDeque);
|
||||
if(m_dqOnStatusCB.size()){
|
||||
m_dqOnStatusCB.front()(val);
|
||||
m_dqOnStatusCB.pop_front();
|
||||
}else{
|
||||
auto level = val["level"];
|
||||
auto code = val["code"].as_string();
|
||||
if(level.type() == AMF_STRING){
|
||||
if(level.as_string() != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||
}
|
||||
}
|
||||
//WarnL << "unhandled onStatus:" << code;
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) {
|
||||
//TraceL;
|
||||
auto val = dec.load<AMFValue>();
|
||||
if(!onCheckMeta(val)){
|
||||
throw std::runtime_error("onCheckMeta faied");
|
||||
}
|
||||
_onPlayResult(SockException(Err_success,"play rtmp success"));
|
||||
}
|
||||
|
||||
void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) {
|
||||
//TraceL << ui32StreamId;
|
||||
_onShutdown(SockException(Err_other,"rtmp stream dry"));
|
||||
}
|
||||
|
||||
|
||||
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3:
|
||||
case MSG_DATA:
|
||||
case MSG_DATA3: {
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
auto it = g_mapCmd.find(type);
|
||||
if(it != g_mapCmd.end()){
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}else{
|
||||
WarnL << "can not support cmd:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO:
|
||||
case MSG_VIDEO: {
|
||||
auto idx = chunkData.typeId%2;
|
||||
if (m_aNowStampTicker[idx].elapsedTime() > 500) {
|
||||
m_adNowStamp[idx] = chunkData.timeStamp;
|
||||
}
|
||||
_onMediaData(chunkData);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float RtmpPlayer::getProgressTime() const{
|
||||
double iTime[2] = {0,0};
|
||||
for(auto i = 0 ;i < 2 ;i++){
|
||||
iTime[i] = (m_adNowStamp[i] - m_adFistStamp[i]) / 1000.0;
|
||||
}
|
||||
return m_fSeekTo + MAX(iTime[0],iTime[1]);
|
||||
}
|
||||
void RtmpPlayer::seekToTime(float fTime){
|
||||
if (m_bPaused) {
|
||||
pause(false);
|
||||
}
|
||||
AMFEncoder enc;
|
||||
enc << "seek" << ++m_iReqID << nullptr << fTime * 1000.0;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
addOnStatusCB([this,fTime](AMFValue &val) {
|
||||
//TraceL << "seek result";
|
||||
m_aNowStampTicker[0].resetTime();
|
||||
m_aNowStampTicker[1].resetTime();
|
||||
float iTimeInc = fTime - getProgressTime();
|
||||
for(auto i = 0 ;i < 2 ;i++){
|
||||
m_adFistStamp[i] = m_adNowStamp[i] + iTimeInc * 1000.0;
|
||||
m_adNowStamp[i] = m_adFistStamp[i];
|
||||
}
|
||||
m_fSeekTo = fTime;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
143
src/Rtmp/RtmpPlayer.h
Normal file
143
src/Rtmp/RtmpPlayer.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* RtmpPlayer2.h
|
||||
*
|
||||
* Created on: 2016年11月29日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_RTMP_RtmpPlayer2_H_
|
||||
#define SRC_RTMP_RtmpPlayer2_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include "Player/PlayerBase.h"
|
||||
#include <string>
|
||||
#include "Util/logger.h"
|
||||
#include "Util/util.h"
|
||||
#include "Network/Socket.hpp"
|
||||
#include "Network/TcpClient.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include <functional>
|
||||
#include "Rtmp.h"
|
||||
#include <memory>
|
||||
#include "amf.h"
|
||||
#include "RtmpProtocol.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Player;
|
||||
using namespace ZL::Network;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPlayer> Ptr;
|
||||
RtmpPlayer();
|
||||
virtual ~RtmpPlayer();
|
||||
|
||||
void play(const char* strUrl, const char *strUser, const char *strPwd,
|
||||
eRtpType eType) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
protected:
|
||||
virtual bool onCheckMeta(AMFValue &val) =0;
|
||||
virtual void onMediaData(RtmpPacket &chunkData) =0;
|
||||
float getProgressTime() const;
|
||||
void seekToTime(float fTime);
|
||||
private:
|
||||
void _onShutdown(const SockException &ex) {
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
m_pPlayTimer.reset();
|
||||
m_pMediaTimer.reset();
|
||||
m_pBeatTimer.reset();
|
||||
onShutdown(ex);
|
||||
}
|
||||
void _onMediaData(RtmpPacket &chunkData) {
|
||||
m_mediaTicker.resetTime();
|
||||
onMediaData(chunkData);
|
||||
}
|
||||
void _onPlayResult(const SockException &ex) {
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
m_pPlayTimer.reset();
|
||||
m_pMediaTimer.reset();
|
||||
if (!ex) {
|
||||
m_mediaTicker.resetTime();
|
||||
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
m_pMediaTimer.reset( new Timer(5, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
if(strongSelf->m_mediaTicker.elapsedTime()>10000) {
|
||||
//recv media timeout!
|
||||
strongSelf->_onShutdown(SockException(Err_timeout,"recv rtmp timeout"));
|
||||
strongSelf->teardown();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
onPlayResult(ex);
|
||||
}
|
||||
|
||||
//for Tcpclient
|
||||
void onRecv(const Socket::Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
//fro RtmpProtocol
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onStreamDry(uint32_t ui32StreamId) override;
|
||||
void onSendRawData(const char *pcRawData, int iSize) override {
|
||||
send(pcRawData, iSize);
|
||||
}
|
||||
|
||||
template<typename FUN>
|
||||
inline void addOnResultCB(const FUN &fun) {
|
||||
m_mapOnResultCB.emplace(m_iReqID, fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnStatusCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(m_mtxDeque);
|
||||
m_dqOnStatusCB.emplace_back(fun);
|
||||
}
|
||||
|
||||
void onCmd_result(AMFDecoder &dec);
|
||||
void onCmd_onStatus(AMFDecoder &dec);
|
||||
void onCmd_onMetaData(AMFDecoder &dec);
|
||||
|
||||
inline void send_connect();
|
||||
inline void send_createStream();
|
||||
inline void send_play();
|
||||
inline void send_pause(bool bPause);
|
||||
|
||||
string m_strApp;
|
||||
string m_strStream;
|
||||
string m_strTcUrl;
|
||||
bool m_bPaused = false;
|
||||
|
||||
unordered_map<int, function<void(AMFDecoder &dec)> > m_mapOnResultCB;
|
||||
deque<function<void(AMFValue &dec)> > m_dqOnStatusCB;
|
||||
recursive_mutex m_mtxDeque;
|
||||
|
||||
typedef void (RtmpPlayer::*rtmpCMDHandle)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
||||
|
||||
//超时功能实现
|
||||
Ticker m_mediaTicker;
|
||||
std::shared_ptr<Timer> m_pMediaTimer;
|
||||
std::shared_ptr<Timer> m_pPlayTimer;
|
||||
//心跳定时器
|
||||
std::shared_ptr<Timer> m_pBeatTimer;
|
||||
|
||||
//播放进度控制
|
||||
float m_fSeekTo = 0;
|
||||
double m_adFistStamp[2] = { 0, 0 };
|
||||
double m_adNowStamp[2] = { 0, 0 };
|
||||
Ticker m_aNowStampTicker[2];
|
||||
};
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_RTMP_RtmpPlayer2_H_ */
|
23
src/Rtmp/RtmpPlayerImp.cpp
Normal file
23
src/Rtmp/RtmpPlayerImp.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* RtmpPlayerImp.cpp
|
||||
*
|
||||
* Created on: 2016年12月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "RtmpPlayerImp.h"
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
RtmpPlayerImp::RtmpPlayerImp() {
|
||||
|
||||
}
|
||||
|
||||
RtmpPlayerImp::~RtmpPlayerImp() {
|
||||
DebugL<<endl;
|
||||
teardown();
|
||||
}
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
62
src/Rtmp/RtmpPlayerImp.h
Normal file
62
src/Rtmp/RtmpPlayerImp.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* RtmpPlayerImp.h
|
||||
*
|
||||
* Created on: 2016年12月1日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_RTMP_RTMPPLAYERIMP_H_
|
||||
#define SRC_RTMP_RTMPPLAYERIMP_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "RtmpPlayer.h"
|
||||
#include "RtmpParser.h"
|
||||
#include "config.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Poller/Timer.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Player;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
class RtmpPlayerImp: public PlayerImp<RtmpPlayer,RtmpParser> {
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPlayerImp> Ptr;
|
||||
RtmpPlayerImp();
|
||||
virtual ~RtmpPlayerImp();
|
||||
float getProgresss() const override{
|
||||
if(getDuration() > 0){
|
||||
return getProgressTime() / getDuration();
|
||||
}
|
||||
return PlayerBase::getProgresss();
|
||||
};
|
||||
void seekTo(float fProgress) override{
|
||||
fProgress = MAX(float(0),MIN(fProgress,float(1.0)));
|
||||
seekToTime(fProgress * getDuration());
|
||||
};
|
||||
private:
|
||||
//派生类回调函数
|
||||
bool onCheckMeta(AMFValue &val) override {
|
||||
try {
|
||||
m_parser.reset(new RtmpParser(val));
|
||||
m_parser->setOnVideoCB(m_onGetVideoCB);
|
||||
m_parser->setOnAudioCB(m_onGetAudioCB);
|
||||
return true;
|
||||
} catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void onMediaData(RtmpPacket &chunkData) override {
|
||||
m_parser->inputRtmp(chunkData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_RTMP_RTMPPLAYERIMP_H_ */
|
435
src/Rtmp/RtmpProtocol.cpp
Normal file
435
src/Rtmp/RtmpProtocol.cpp
Normal file
@ -0,0 +1,435 @@
|
||||
/*
|
||||
* RtmpProtocol.cpp
|
||||
*
|
||||
* Created on: 2017年2月7日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "RtmpProtocol.h"
|
||||
#include "Thread/ThreadPool.hpp"
|
||||
#include "Util/util.h"
|
||||
#include "Util/onceToken.h"
|
||||
#include "Rtmp/utils.h"
|
||||
#include "Rtsp/Rtsp.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
RtmpProtocol::RtmpProtocol() {
|
||||
m_nextHandle = [this](){
|
||||
handle_C0C1();
|
||||
};
|
||||
}
|
||||
RtmpProtocol::~RtmpProtocol() {
|
||||
clear();
|
||||
}
|
||||
void RtmpProtocol::clear() {
|
||||
////////////ChunkSize////////////
|
||||
m_iChunkLenIn = DEFAULT_CHUNK_LEN;
|
||||
m_iChunkLenOut = DEFAULT_CHUNK_LEN;
|
||||
////////////Acknowledgement////////////
|
||||
m_ui32ByteSent = 0;
|
||||
m_ui32LastSent = 0;
|
||||
m_ui32WinSize = 0;
|
||||
///////////PeerBandwidth///////////
|
||||
m_ui32Bandwidth = 2500000;
|
||||
m_ui8LimitType = 2;
|
||||
////////////Chunk////////////
|
||||
m_mapChunkData.clear();
|
||||
m_iNowStreamID = 0;
|
||||
m_iNowChunkID = 0;
|
||||
//////////Invoke Request//////////
|
||||
m_iReqID = 0;
|
||||
//////////Rtmp parser//////////
|
||||
m_strRcvBuf.clear();
|
||||
m_ui32StreamId = STREAM_CONTROL;
|
||||
m_nextHandle = [this]() {
|
||||
handle_C0C1();
|
||||
};
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendAcknowledgement(uint32_t ui32Size) {
|
||||
std::string control;
|
||||
uint32_t stream = htonl(ui32Size);
|
||||
control.append((char *) &stream, 4);
|
||||
sendRequest(MSG_ACK, control);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendAcknowledgementSize(uint32_t ui32Size) {
|
||||
uint32_t windowSize = htonl(ui32Size);
|
||||
std::string set_windowSize((char *) &windowSize, 4);
|
||||
sendRequest(MSG_WIN_SIZE, set_windowSize);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendPeerBandwidth(uint32_t ui32Size) {
|
||||
uint32_t peerBandwidth = htonl(ui32Size);
|
||||
std::string set_peerBandwidth((char *) &peerBandwidth, 4);
|
||||
set_peerBandwidth.push_back((char) 0x02);
|
||||
sendRequest(MSG_SET_PEER_BW, set_peerBandwidth);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendChunkSize(uint32_t ui32Size) {
|
||||
uint32_t len = htonl(ui32Size);
|
||||
std::string set_chunk((char *) &len, 4);
|
||||
sendRequest(MSG_SET_CHUNK, set_chunk);
|
||||
m_iChunkLenOut = ui32Size;
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendPingRequest(uint32_t ui32TimeStamp) {
|
||||
sendUserControl(CONTROL_PING_REQUEST, ui32TimeStamp);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendPingResponse(uint32_t ui32TimeStamp) {
|
||||
sendUserControl(CONTROL_PING_RESPONSE, ui32TimeStamp);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendSetBufferLength(uint32_t ui32StreamId,
|
||||
uint32_t ui32Length) {
|
||||
std::string control;
|
||||
ui32StreamId = htonl(ui32StreamId);
|
||||
control.append((char *) &ui32StreamId, 4);
|
||||
ui32Length = htonl(ui32Length);
|
||||
control.append((char *) &ui32Length, 4);
|
||||
sendUserControl(CONTROL_SETBUFFER, control);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendUserControl(uint16_t ui16EventType,
|
||||
uint32_t ui32EventData) {
|
||||
std::string control;
|
||||
uint16_t type = htons(ui16EventType);
|
||||
control.append((char *) &type, 2);
|
||||
uint32_t stream = htonl(ui32EventData);
|
||||
control.append((char *) &stream, 4);
|
||||
sendRequest(MSG_USER_CONTROL, control);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendUserControl(uint16_t ui16EventType,
|
||||
const string& strEventData) {
|
||||
std::string control;
|
||||
uint16_t type = htons(ui16EventType);
|
||||
control.append((char *) &type, 2);
|
||||
control.append(strEventData);
|
||||
sendRequest(MSG_USER_CONTROL, control);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendResponse(int iType, const string& str) {
|
||||
sendRtmp(iType, m_iNowStreamID, str, 0, m_iNowChunkID);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendInvoke(const string& strCmd, const AMFValue& val) {
|
||||
AMFEncoder enc;
|
||||
enc << strCmd << ++m_iReqID << val;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendRequest(int iCmd, const string& str) {
|
||||
sendRtmp(iCmd, m_ui32StreamId, str, 0, CHUNK_SERVER_REQUEST);
|
||||
}
|
||||
|
||||
void RtmpProtocol::sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId,
|
||||
const std::string& strBuf, uint32_t ui32TimeStamp, int iChunkId) {
|
||||
if (iChunkId < 2 || iChunkId > 63) {
|
||||
auto strErr = StrPrinter << "不支持发送该类型的块流 ID:" << iChunkId << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
|
||||
bool bExtStamp = ui32TimeStamp >= 0xFFFFFF;
|
||||
RtmpHeader header;
|
||||
header.flags = (iChunkId & 0x3f) | (0 << 6);
|
||||
header.typeId = ui8Type;
|
||||
set_be24(header.timeStamp, bExtStamp ? 0xFFFFFF : ui32TimeStamp);
|
||||
set_be24(header.bodySize, strBuf.size());
|
||||
set_le32(header.streamId, ui32StreamId);
|
||||
std::string strSend;
|
||||
strSend.append((char *) &header, sizeof header);
|
||||
char acExtStamp[4];
|
||||
if (bExtStamp) {
|
||||
//扩展时间戳
|
||||
set_be32(acExtStamp, ui32TimeStamp);
|
||||
}
|
||||
size_t pos = 0;
|
||||
while (pos < strBuf.size()) {
|
||||
if (pos) {
|
||||
uint8_t flags = (iChunkId & 0x3f) | (3 << 6);
|
||||
strSend += char(flags);
|
||||
}
|
||||
if (bExtStamp) {
|
||||
//扩展时间戳
|
||||
strSend.append(acExtStamp, 4);
|
||||
}
|
||||
size_t chunk = min(m_iChunkLenOut, strBuf.size() - pos);
|
||||
strSend.append(strBuf, pos, chunk);
|
||||
pos += chunk;
|
||||
}
|
||||
onSendRawData(strSend.data(),strSend.size());
|
||||
m_ui32ByteSent += strSend.size();
|
||||
if (m_ui32WinSize > 0 && m_ui32ByteSent - m_ui32LastSent >= m_ui32WinSize) {
|
||||
m_ui32LastSent = m_ui32ByteSent;
|
||||
sendAcknowledgement(m_ui32ByteSent);
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpProtocol::onParseRtmp(const char *pcRawData, int iSize) {
|
||||
m_strRcvBuf.append(pcRawData, iSize);
|
||||
auto cb = m_nextHandle;
|
||||
cb();
|
||||
}
|
||||
|
||||
////for client////
|
||||
void RtmpProtocol::startClientSession(const function<void()> &callBack) {
|
||||
//发送 C0C1
|
||||
char handshake_head = HANDSHAKE_PLAINTEXT;
|
||||
onSendRawData(&handshake_head, 1);
|
||||
RtmpHandshake c0c1(::time(NULL));
|
||||
onSendRawData((char *) (&c0c1), sizeof(RtmpHandshake));
|
||||
m_nextHandle = [this,callBack]() {
|
||||
//等待 S0+S1+S2
|
||||
handle_S0S1S2(callBack);
|
||||
};
|
||||
}
|
||||
void RtmpProtocol::handle_S0S1S2(const function<void()> &callBack) {
|
||||
if (m_strRcvBuf.size() < 1 + 2 * sizeof(RtmpHandshake)) {
|
||||
//数据不够
|
||||
return;
|
||||
}
|
||||
if (m_strRcvBuf[0] != HANDSHAKE_PLAINTEXT) {
|
||||
throw std::runtime_error("only plaintext[0x03] handshake supported");
|
||||
}
|
||||
//发送 C2
|
||||
const char *pcC2 = m_strRcvBuf.data() + 1;
|
||||
onSendRawData(pcC2, sizeof(RtmpHandshake));
|
||||
m_strRcvBuf.erase(0, 1 + 2 * sizeof(RtmpHandshake));
|
||||
//握手结束
|
||||
m_nextHandle = [this]() {
|
||||
//握手结束并且开始进入解析命令模式
|
||||
handle_rtmp();
|
||||
};
|
||||
callBack();
|
||||
}
|
||||
////for server ////
|
||||
void RtmpProtocol::handle_C0C1() {
|
||||
if (m_strRcvBuf.size() < 1 + sizeof(RtmpHandshake)) {
|
||||
//need more data!
|
||||
return;
|
||||
}
|
||||
if (m_strRcvBuf[0] != HANDSHAKE_PLAINTEXT) {
|
||||
throw std::runtime_error("only plaintext[0x03] handshake supported");
|
||||
}
|
||||
char handshake_head = HANDSHAKE_PLAINTEXT;
|
||||
onSendRawData(&handshake_head, 1);
|
||||
//发送S2
|
||||
RtmpHandshake s2(0);
|
||||
onSendRawData((char *) &s2, sizeof(RtmpHandshake));
|
||||
//发送S0S1
|
||||
onSendRawData(m_strRcvBuf.c_str() + 1, sizeof(RtmpHandshake));
|
||||
m_strRcvBuf.erase(0, 1 + sizeof(RtmpHandshake));
|
||||
//等待C2
|
||||
m_nextHandle = [this]() {
|
||||
handle_C2();
|
||||
};
|
||||
}
|
||||
|
||||
void RtmpProtocol::handle_C2() {
|
||||
if (m_strRcvBuf.size() < sizeof(RtmpHandshake)) {
|
||||
//need more data!
|
||||
return;
|
||||
}
|
||||
m_strRcvBuf.erase(0, sizeof(RtmpHandshake));
|
||||
//握手结束,进入命令模式
|
||||
if (!m_strRcvBuf.empty()) {
|
||||
handle_rtmp();
|
||||
}
|
||||
m_nextHandle = [this]() {
|
||||
handle_rtmp();
|
||||
};
|
||||
}
|
||||
|
||||
void RtmpProtocol::handle_rtmp() {
|
||||
while (!m_strRcvBuf.empty()) {
|
||||
uint8_t flags = m_strRcvBuf[0];
|
||||
int iOffset = 0;
|
||||
static const size_t HEADER_LENGTH[] = { 12, 8, 4, 1 };
|
||||
size_t iHeaderLen = HEADER_LENGTH[flags >> 6];
|
||||
m_iNowChunkID = flags & 0x3f;
|
||||
switch (m_iNowChunkID) {
|
||||
case 0: {
|
||||
//0 值表示二字节形式,并且 ID 范围 64 - 319
|
||||
//(第二个字节 + 64)。
|
||||
if (m_strRcvBuf.size() < 2) {
|
||||
//need more data
|
||||
return;
|
||||
}
|
||||
m_iNowChunkID = 64 + (uint8_t) (m_strRcvBuf[1]);
|
||||
iOffset = 1;
|
||||
}
|
||||
break;
|
||||
case 1: {
|
||||
//1 值表示三字节形式,并且 ID 范围为 64 - 65599
|
||||
//((第三个字节) * 256 + 第二个字节 + 64)。
|
||||
if (m_strRcvBuf.size() < 3) {
|
||||
//need more data
|
||||
return;
|
||||
}
|
||||
m_iNowChunkID = 64 + ((uint8_t) (m_strRcvBuf[2]) << 8) + (uint8_t) (m_strRcvBuf[1]);
|
||||
iOffset = 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_strRcvBuf.size() < iHeaderLen + iOffset) {
|
||||
//need more data
|
||||
return;
|
||||
}
|
||||
RtmpHeader &header = *((RtmpHeader *) (m_strRcvBuf.data() + iOffset));
|
||||
auto &chunkData = m_mapChunkData[m_iNowChunkID];
|
||||
chunkData.chunkId = m_iNowChunkID;
|
||||
switch (iHeaderLen) {
|
||||
case 12:
|
||||
chunkData.streamId = load_le32(header.streamId);
|
||||
case 8:
|
||||
chunkData.bodySize = load_be24(header.bodySize);
|
||||
chunkData.typeId = header.typeId;
|
||||
case 4:
|
||||
uint32_t ts = load_be24(header.timeStamp);
|
||||
if (ts == 0xFFFFFF) {
|
||||
chunkData.extStamp = true;
|
||||
}else{
|
||||
chunkData.extStamp = false;
|
||||
chunkData.timeStamp = ts + ((iHeaderLen == 12) ? 0 : chunkData.timeStamp);
|
||||
}
|
||||
}
|
||||
if (chunkData.extStamp) {
|
||||
if (m_strRcvBuf.size() < iHeaderLen + iOffset + 4) {
|
||||
//need more data
|
||||
return;
|
||||
}
|
||||
chunkData.timeStamp = load_be32( m_strRcvBuf.data() + iOffset + iHeaderLen);
|
||||
iOffset += 4;
|
||||
}
|
||||
|
||||
if (chunkData.bodySize == 0 || chunkData.bodySize < chunkData.strBuf.size()) {
|
||||
throw std::runtime_error("非法的bodySize");
|
||||
}
|
||||
|
||||
auto iMore = min(m_iChunkLenIn, chunkData.bodySize - chunkData.strBuf.size());
|
||||
if (m_strRcvBuf.size() < iHeaderLen + iOffset + iMore) {
|
||||
//need more data
|
||||
return;
|
||||
}
|
||||
chunkData.strBuf.append(m_strRcvBuf, iHeaderLen + iOffset, iMore);
|
||||
m_strRcvBuf.erase(0, iHeaderLen + iOffset + iMore);
|
||||
if (chunkData.strBuf.size() == chunkData.bodySize) {
|
||||
m_iNowStreamID = chunkData.streamId;
|
||||
handle_rtmpChunk(chunkData);
|
||||
chunkData.strBuf.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpProtocol::handle_rtmpChunk(RtmpPacket& chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_ACK: {
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("MSG_ACK: Not enough data");
|
||||
}
|
||||
//auto bytePeerRecv = load_be32(&chunkData.strBuf[0]);
|
||||
//TraceL << "MSG_ACK:" << bytePeerRecv;
|
||||
}
|
||||
break;
|
||||
case MSG_SET_CHUNK: {
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("MSG_SET_CHUNK :Not enough data");
|
||||
}
|
||||
m_iChunkLenIn = load_be32(&chunkData.strBuf[0]);
|
||||
TraceL << "MSG_SET_CHUNK:" << m_iChunkLenIn;
|
||||
}
|
||||
break;
|
||||
case MSG_USER_CONTROL: {
|
||||
//user control message
|
||||
if (chunkData.strBuf.size() < 2) {
|
||||
throw std::runtime_error("MSG_USER_CONTROL: Not enough data.");
|
||||
}
|
||||
uint16_t event_type = load_be16(&chunkData.strBuf[0]);
|
||||
chunkData.strBuf.erase(0, 2);
|
||||
switch (event_type) {
|
||||
case CONTROL_PING_REQUEST: {
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("CONTROL_PING_REQUEST: Not enough data.");
|
||||
}
|
||||
uint32_t timeStamp = load_be32(&chunkData.strBuf[0]);
|
||||
//TraceL << "CONTROL_PING_REQUEST:" << timeStamp;
|
||||
sendUserControl(CONTROL_PING_RESPONSE, timeStamp);
|
||||
}
|
||||
break;
|
||||
case CONTROL_PING_RESPONSE: {
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("CONTROL_PING_RESPONSE: Not enough data.");
|
||||
}
|
||||
//uint32_t timeStamp = load_be32(&chunkData.strBuf[0]);
|
||||
//TraceL << "CONTROL_PING_RESPONSE:" << timeStamp;
|
||||
}
|
||||
break;
|
||||
case CONTROL_STREAM_BEGIN: {
|
||||
//开始播放
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("CONTROL_STREAM_BEGIN: Not enough data.");
|
||||
}
|
||||
uint32_t stramId = load_be32(&chunkData.strBuf[0]);
|
||||
onStreamBegin(stramId);
|
||||
TraceL << "CONTROL_STREAM_BEGIN:" << stramId;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTROL_STREAM_EOF: {
|
||||
//暂停
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("CONTROL_STREAM_EOF: Not enough data.");
|
||||
}
|
||||
uint32_t stramId = load_be32(&chunkData.strBuf[0]);
|
||||
onStreamEof(stramId);
|
||||
TraceL << "CONTROL_STREAM_EOF:" << stramId;
|
||||
}
|
||||
break;
|
||||
case CONTROL_STREAM_DRY: {
|
||||
//停止播放
|
||||
if (chunkData.strBuf.size() < 4) {
|
||||
throw std::runtime_error("CONTROL_STREAM_DRY: Not enough data.");
|
||||
}
|
||||
uint32_t stramId = load_be32(&chunkData.strBuf[0]);
|
||||
onStreamDry(stramId);
|
||||
TraceL << "CONTROL_STREAM_DRY:" << stramId;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled user control:" << event_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_WIN_SIZE: {
|
||||
m_ui32WinSize = load_be32(&chunkData.strBuf[0]);
|
||||
TraceL << "MSG_WIN_SIZE:" << m_ui32WinSize;
|
||||
}
|
||||
break;
|
||||
case MSG_SET_PEER_BW: {
|
||||
m_ui32Bandwidth = load_be32(&chunkData.strBuf[0]);
|
||||
m_ui8LimitType = chunkData.strBuf[4];
|
||||
TraceL << "MSG_SET_PEER_BW:" << m_ui32WinSize;
|
||||
}
|
||||
break;
|
||||
case MSG_AGGREGATE:
|
||||
throw std::runtime_error("streaming FLV not supported");
|
||||
break;
|
||||
default:
|
||||
onRtmpChunk(chunkData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
93
src/Rtmp/RtmpProtocol.h
Normal file
93
src/Rtmp/RtmpProtocol.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* RtmpProtocol.h
|
||||
*
|
||||
* Created on: 2017年2月7日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_RTMP_RTMPPROTOCOL_H_
|
||||
#define SRC_RTMP_RTMPPROTOCOL_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <string>
|
||||
#include "Util/logger.h"
|
||||
#include "Util/util.h"
|
||||
#include "Network/Socket.hpp"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include <functional>
|
||||
#include "Rtmp.h"
|
||||
#include <memory>
|
||||
#include "amf.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Network;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
class RtmpProtocol {
|
||||
public:
|
||||
RtmpProtocol();
|
||||
virtual ~RtmpProtocol();
|
||||
//作为客户端发送c0c1,等待s0s1s2并且回调
|
||||
void startClientSession(const function<void()> &cb);
|
||||
void onParseRtmp(const char *pcRawData,int iSize);
|
||||
void clear();
|
||||
protected:
|
||||
virtual void onSendRawData(const char *pcRawData,int iSize) = 0;
|
||||
virtual void onRtmpChunk(RtmpPacket &chunkData) = 0;
|
||||
|
||||
virtual void onStreamBegin(uint32_t ui32StreamId){
|
||||
m_ui32StreamId = ui32StreamId;
|
||||
}
|
||||
virtual void onStreamEof(uint32_t ui32StreamId){};
|
||||
virtual void onStreamDry(uint32_t ui32StreamId){};
|
||||
protected:
|
||||
void sendAcknowledgement(uint32_t ui32Size);
|
||||
void sendAcknowledgementSize(uint32_t ui32Size);
|
||||
void sendPeerBandwidth(uint32_t ui32Size);
|
||||
void sendChunkSize(uint32_t ui32Size);
|
||||
void sendPingRequest(uint32_t ui32TimeStamp = ::time(NULL));
|
||||
void sendPingResponse(uint32_t ui32TimeStamp = ::time(NULL));
|
||||
void sendSetBufferLength(uint32_t ui32StreamId, uint32_t ui32Length);
|
||||
void sendUserControl(uint16_t ui16EventType, uint32_t ui32EventData);
|
||||
void sendUserControl(uint16_t ui16EventType, const string &strEventData);
|
||||
|
||||
void sendInvoke(const string &strCmd, const AMFValue &val);
|
||||
void sendRequest(int iCmd, const string &str);
|
||||
void sendResponse(int iType, const string &str);
|
||||
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const std::string &strBuf, uint32_t ui32TimeStamp, int iChunkID);
|
||||
protected:
|
||||
int m_iReqID = 0;
|
||||
uint32_t m_ui32StreamId = STREAM_CONTROL;
|
||||
private:
|
||||
void handle_S0S1S2(const function<void()> &cb);
|
||||
void handle_C0C1();
|
||||
void handle_C2();
|
||||
void handle_rtmp();
|
||||
void handle_rtmpChunk(RtmpPacket &chunkData);
|
||||
|
||||
////////////ChunkSize////////////
|
||||
size_t m_iChunkLenIn = DEFAULT_CHUNK_LEN;
|
||||
size_t m_iChunkLenOut = DEFAULT_CHUNK_LEN;
|
||||
////////////Acknowledgement////////////
|
||||
uint32_t m_ui32ByteSent = 0;
|
||||
uint32_t m_ui32LastSent = 0;
|
||||
uint32_t m_ui32WinSize = 0;
|
||||
///////////PeerBandwidth///////////
|
||||
uint32_t m_ui32Bandwidth = 2500000;
|
||||
uint8_t m_ui8LimitType = 2;
|
||||
////////////Chunk////////////
|
||||
unordered_map<int, RtmpPacket> m_mapChunkData;
|
||||
int m_iNowStreamID = 0;
|
||||
int m_iNowChunkID = 0;
|
||||
//////////Rtmp parser//////////
|
||||
string m_strRcvBuf;
|
||||
function<void()> m_nextHandle;
|
||||
};
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_RTMP_RTMPPROTOCOL_H_ */
|
260
src/Rtmp/RtmpPusher.cpp
Normal file
260
src/Rtmp/RtmpPusher.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* RtmpPusher.cpp
|
||||
*
|
||||
* Created on: 2017年2月13日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "RtmpPusher.h"
|
||||
#include "Thread/ThreadPool.hpp"
|
||||
#include "Util/util.h"
|
||||
#include "Util/onceToken.h"
|
||||
#include "Rtmp/utils.h"
|
||||
#include "Rtsp/Rtsp.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
unordered_map<string, RtmpPusher::rtmpCMDHandle> RtmpPusher::g_mapCmd;
|
||||
RtmpPusher::RtmpPusher(const char *strApp,const char *strStream) {
|
||||
static onceToken token([]() {
|
||||
g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
|
||||
g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
|
||||
g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
|
||||
}, []() {});
|
||||
auto src = RtmpMediaSource::find(strApp,strStream);
|
||||
if (!src) {
|
||||
auto strErr = StrPrinter << "媒体源:" << strApp << "/" << strStream << "不存在" << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
m_pMediaSrc = src;
|
||||
}
|
||||
|
||||
RtmpPusher::~RtmpPusher() {
|
||||
teardown();
|
||||
DebugL << endl;
|
||||
}
|
||||
void RtmpPusher::teardown() {
|
||||
if (alive()) {
|
||||
m_strApp.clear();
|
||||
m_strStream.clear();
|
||||
m_strTcUrl.clear();
|
||||
m_mapOnResultCB.clear();
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(m_mtxDeque);
|
||||
m_dqOnStatusCB.clear();
|
||||
}
|
||||
m_pPlayTimer.reset();
|
||||
clear();
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPusher::publish(const char* strUrl) {
|
||||
teardown();
|
||||
string strHost = FindField(strUrl, "://", "/");
|
||||
m_strApp = FindField(strUrl, (strHost + "/").data(), "/");
|
||||
m_strStream = FindField(strUrl, (strHost + "/" + m_strApp + "/").data(), NULL);
|
||||
m_strTcUrl = string("rtmp://") + strHost + "/" + m_strApp;
|
||||
|
||||
if (!m_strApp.size() || !m_strStream.size()) {
|
||||
_onPlayResult(SockException(Err_other,"rtmp url非法"));
|
||||
return;
|
||||
}
|
||||
DebugL << strHost << " " << m_strApp << " " << m_strStream;
|
||||
|
||||
auto iPort = atoi(FindField(strHost.c_str(), ":", NULL).c_str());
|
||||
if (iPort <= 0) {
|
||||
//rtmp 默认端口1935
|
||||
iPort = 1935;
|
||||
} else {
|
||||
//服务器域名
|
||||
strHost = FindField(strHost.c_str(), NULL, ":");
|
||||
}
|
||||
startConnect(strHost, iPort);
|
||||
}
|
||||
|
||||
void RtmpPusher::onErr(const SockException &ex){
|
||||
_onShutdown(ex);
|
||||
}
|
||||
void RtmpPusher::onConnect(const SockException &err){
|
||||
if(err.getErrCode()!=Err_success) {
|
||||
_onPlayResult(err);
|
||||
return;
|
||||
}
|
||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||
m_pPlayTimer.reset( new Timer(10, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
strongSelf->_onPlayResult(SockException(Err_timeout,"publish rtmp timeout"));
|
||||
strongSelf->teardown();
|
||||
return false;
|
||||
}));
|
||||
startClientSession([weakSelf](){
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
//strongSelf->sendChunkSize(60000);
|
||||
strongSelf->send_connect();
|
||||
});
|
||||
}
|
||||
void RtmpPusher::onRecv(const Socket::Buffer::Ptr &pBuf){
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
SockException ex(Err_other, e.what());
|
||||
_onPlayResult(ex);
|
||||
_onShutdown(ex);
|
||||
teardown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void RtmpPusher::send_connect() {
|
||||
AMFValue obj(AMF_OBJECT);
|
||||
obj.set("app", m_strApp);
|
||||
obj.set("type", "nonprivate");
|
||||
obj.set("tcUrl", m_strTcUrl);
|
||||
obj.set("swfUrl", m_strTcUrl);
|
||||
sendInvoke("connect", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "connect result";
|
||||
dec.load<AMFValue>();
|
||||
auto val = dec.load<AMFValue>();
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
||||
}
|
||||
send_createStream();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPusher::send_createStream() {
|
||||
AMFValue obj(AMF_NULL);
|
||||
sendInvoke("createStream", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "createStream result";
|
||||
dec.load<AMFValue>();
|
||||
m_ui32StreamId = dec.load<int>();
|
||||
send_publish();
|
||||
});
|
||||
}
|
||||
inline void RtmpPusher::send_publish() {
|
||||
AMFEncoder enc;
|
||||
enc << "publish" << ++m_iReqID << nullptr << m_strStream << m_strApp ;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
|
||||
addOnStatusCB([this](AMFValue &val) {
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status") {
|
||||
throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
|
||||
}
|
||||
//start send media
|
||||
send_metaData();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPusher::send_metaData(){
|
||||
auto src = m_pMediaSrc.lock();
|
||||
if (!src) {
|
||||
throw std::runtime_error("媒体源已被释放");
|
||||
}
|
||||
if (!src->ready()) {
|
||||
throw std::runtime_error("媒体源尚未准备就绪");
|
||||
}
|
||||
|
||||
AMFEncoder enc;
|
||||
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
|
||||
sendRequest(MSG_DATA, enc.data());
|
||||
|
||||
src->getConfigFrame([&](const RtmpPacket &pkt){
|
||||
sendRtmp(pkt.typeId, m_ui32StreamId, pkt.strBuf, pkt.timeStamp, pkt.chunkId);
|
||||
});
|
||||
|
||||
m_pRtmpReader = src->getRing()->attach();
|
||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||
m_pRtmpReader->setReadCB([weakSelf](const RtmpPacket &pkt){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->sendRtmp(pkt.typeId, strongSelf->m_ui32StreamId, pkt.strBuf, pkt.timeStamp, pkt.chunkId);
|
||||
});
|
||||
m_pRtmpReader->setDetachCB([weakSelf](){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(strongSelf){
|
||||
strongSelf->_onShutdown(SockException(Err_other,"媒体源被释放"));
|
||||
strongSelf->teardown();
|
||||
}
|
||||
});
|
||||
_onPlayResult(SockException(Err_success,"success"));
|
||||
}
|
||||
void RtmpPusher::onCmd_result(AMFDecoder &dec){
|
||||
auto iReqId = dec.load<int>();
|
||||
auto it = m_mapOnResultCB.find(iReqId);
|
||||
if(it != m_mapOnResultCB.end()){
|
||||
it->second(dec);
|
||||
m_mapOnResultCB.erase(it);
|
||||
}else{
|
||||
WarnL << "unhandled _result";
|
||||
}
|
||||
}
|
||||
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
|
||||
AMFValue val;
|
||||
while(true){
|
||||
val = dec.load<AMFValue>();
|
||||
if(val.type() == AMF_OBJECT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(val.type() != AMF_OBJECT){
|
||||
throw std::runtime_error("onStatus: 未找到结果对象");
|
||||
}
|
||||
|
||||
lock_guard<recursive_mutex> lck(m_mtxDeque);
|
||||
if(m_dqOnStatusCB.size()){
|
||||
m_dqOnStatusCB.front()(val);
|
||||
m_dqOnStatusCB.pop_front();
|
||||
}else{
|
||||
auto level = val["level"];
|
||||
auto code = val["code"].as_string();
|
||||
if(level.type() == AMF_STRING){
|
||||
if(level.as_string() != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPusher::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3: {
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
auto it = g_mapCmd.find(type);
|
||||
if(it != g_mapCmd.end()){
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}else{
|
||||
WarnL << "can not support cmd:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
95
src/Rtmp/RtmpPusher.h
Normal file
95
src/Rtmp/RtmpPusher.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* RtmpPusher.h
|
||||
*
|
||||
* Created on: 2017年2月13日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_RTMP_RTMPPUSHER_H_
|
||||
#define SRC_RTMP_RTMPPUSHER_H_
|
||||
|
||||
#include "RtmpProtocol.h"
|
||||
#include "Network/TcpClient.h"
|
||||
#include "RtmpMediaSource.h"
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
class RtmpPusher: public RtmpProtocol , public TcpClient{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPusher> Ptr;
|
||||
RtmpPusher(const char *strApp,const char *strStream);
|
||||
virtual ~RtmpPusher();
|
||||
|
||||
void publish(const char* strUrl);
|
||||
void teardown();
|
||||
|
||||
protected:
|
||||
|
||||
//for Tcpclient
|
||||
void onRecv(const Socket::Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
|
||||
//fro RtmpProtocol
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onSendRawData(const char *pcRawData, int iSize) override {
|
||||
send(pcRawData, iSize);
|
||||
}
|
||||
|
||||
virtual void onShutdown(const SockException &ex){}
|
||||
virtual void onPlayResult(const SockException &ex) {}
|
||||
private:
|
||||
void _onShutdown(const SockException &ex) {
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
m_pPlayTimer.reset();
|
||||
onShutdown(ex);
|
||||
}
|
||||
void _onPlayResult(const SockException &ex) {
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
m_pPlayTimer.reset();
|
||||
onPlayResult(ex);
|
||||
}
|
||||
|
||||
template<typename FUN>
|
||||
inline void addOnResultCB(const FUN &fun) {
|
||||
m_mapOnResultCB.emplace(m_iReqID, fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnStatusCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(m_mtxDeque);
|
||||
m_dqOnStatusCB.emplace_back(fun);
|
||||
}
|
||||
|
||||
void onCmd_result(AMFDecoder &dec);
|
||||
void onCmd_onStatus(AMFDecoder &dec);
|
||||
void onCmd_onMetaData(AMFDecoder &dec);
|
||||
|
||||
inline void send_connect();
|
||||
inline void send_createStream();
|
||||
inline void send_publish();
|
||||
inline void send_metaData();
|
||||
|
||||
string m_strApp;
|
||||
string m_strStream;
|
||||
string m_strTcUrl;
|
||||
|
||||
unordered_map<int, function<void(AMFDecoder &dec)> > m_mapOnResultCB;
|
||||
deque<function<void(AMFValue &dec)> > m_dqOnStatusCB;
|
||||
recursive_mutex m_mtxDeque;
|
||||
|
||||
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
||||
|
||||
//超时功能实现
|
||||
std::shared_ptr<Timer> m_pPlayTimer;
|
||||
|
||||
//源
|
||||
std::weak_ptr<RtmpMediaSource> m_pMediaSrc;
|
||||
RtmpMediaSource::RingType::RingReader::Ptr m_pRtmpReader;
|
||||
};
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_RTMP_RTMPPUSHER_H_ */
|
316
src/Rtmp/RtmpSession.cpp
Normal file
316
src/Rtmp/RtmpSession.cpp
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* RtmpSession.cpp
|
||||
*
|
||||
* Created on: 2017年2月10日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
|
||||
#include "RtmpSession.h"
|
||||
#include "Util/onceToken.h"
|
||||
|
||||
namespace ZL {
|
||||
namespace Rtmp {
|
||||
|
||||
unordered_map<string, RtmpSession::rtmpCMDHandle> RtmpSession::g_mapCmd;
|
||||
RtmpSession::RtmpSession(const std::shared_ptr<ThreadPool> &pTh, const Socket::Ptr &pSock) :
|
||||
TcpLimitedSession(pTh, pSock) {
|
||||
static onceToken token([]() {
|
||||
g_mapCmd.emplace("connect",&RtmpSession::onCmd_connect);
|
||||
g_mapCmd.emplace("createStream",&RtmpSession::onCmd_createStream);
|
||||
g_mapCmd.emplace("publish",&RtmpSession::onCmd_publish);
|
||||
g_mapCmd.emplace("deleteStream",&RtmpSession::onCmd_deleteStream);
|
||||
g_mapCmd.emplace("play",&RtmpSession::onCmd_play);
|
||||
g_mapCmd.emplace("seek",&RtmpSession::onCmd_seek);
|
||||
g_mapCmd.emplace("pause",&RtmpSession::onCmd_pause);}, []() {});
|
||||
DebugL << getPeerIp();
|
||||
}
|
||||
|
||||
RtmpSession::~RtmpSession() {
|
||||
DebugL << getPeerIp();
|
||||
}
|
||||
|
||||
void RtmpSession::onError(const SockException& err) {
|
||||
if (m_pPublisherSrc) {
|
||||
m_pPublisherSrc.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onManager() {
|
||||
if (m_ticker.createdTime() > 10 * 1000) {
|
||||
if (!m_pRingReader && !m_pPublisherSrc) {
|
||||
WarnL << "非法链接:" << getPeerIp();
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
if (m_pPublisherSrc) {
|
||||
//publisher
|
||||
if (m_ticker.elapsedTime() > 10 * 1000) {
|
||||
WarnL << "数据接收超时:" << getPeerIp();
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onRecv(const Socket::Buffer::Ptr &pBuf) {
|
||||
m_ticker.resetTime();
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
WarnL << e.what();
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_connect(AMFDecoder &dec) {
|
||||
auto params = dec.load<AMFValue>();
|
||||
m_strApp = params["app"].as_string();
|
||||
bool ok = true; //(app == APP_NAME);
|
||||
AMFValue version(AMF_OBJECT);
|
||||
version.set("fmsVer", "FMS/3,5,3,888");
|
||||
version.set("capabilities", 127.0);
|
||||
version.set("mode", 1.0);
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", ok ? "status" : "error");
|
||||
status.set("code", ok ? "NetConnection.Connect.Success" : "NetConnection.Connect.InvalidApp");
|
||||
status.set("description", ok ? "Connection succeeded." : "InvalidApp.");
|
||||
status.set("objectEncoding", (double) (dec.getVersion()));
|
||||
sendReply(ok ? "_result" : "_error", version, status);
|
||||
if (!ok) {
|
||||
throw std::runtime_error("Unsupported application: " + m_strApp);
|
||||
}
|
||||
|
||||
////////////window Acknowledgement size/////
|
||||
sendAcknowledgementSize(2500000);
|
||||
///////////set peerBandwidth////////////////
|
||||
sendPeerBandwidth(2500000);
|
||||
///////////set chunk size////////////////
|
||||
#ifndef _DEBUG
|
||||
sendChunkSize(60000);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_createStream(AMFDecoder &dec) {
|
||||
sendReply("_result", nullptr, double(STREAM_MEDIA));
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_publish(AMFDecoder &dec) {
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
m_strId = dec.load<std::string>();
|
||||
auto iPos = m_strId.find('?');
|
||||
if (iPos != string::npos) {
|
||||
m_strId.erase(iPos);
|
||||
}
|
||||
auto src = RtmpMediaSource::find(m_strApp,m_strId,false);
|
||||
bool ok = (!src && !m_pPublisherSrc);
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", ok ? "status" : "error");
|
||||
status.set("code", ok ? "NetStream.Publish.Start" : "NetStream.Publish.BadName");
|
||||
status.set("description", ok ? "Started publishing stream." : "Already publishing.");
|
||||
status.set("clientid", "ASAICiss");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
if (!ok) {
|
||||
throw std::runtime_error( StrPrinter << "Already publishing:" << m_strApp << "/" << m_strId << endl);
|
||||
}
|
||||
m_pPublisherSrc.reset(new RtmpToRtspMediaSource(m_strApp,m_strId));
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_deleteStream(AMFDecoder &dec) {
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Unpublish.Success");
|
||||
status.set("description", "Stop publishing.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
throw std::runtime_error(StrPrinter << "Stop publishing." << endl);
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_play(AMFDecoder &dec) {
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
m_strId = dec.load<std::string>();
|
||||
auto iPos = m_strId.find('?');
|
||||
if (iPos != string::npos) {
|
||||
m_strId.erase(iPos);
|
||||
}
|
||||
auto src = RtmpMediaSource::find(m_strApp,m_strId,true);
|
||||
bool ok = (src.operator bool());
|
||||
ok = ok && src->ready();
|
||||
// onStatus(NetStream.Play.Reset)
|
||||
AMFValue status(AMF_OBJECT);
|
||||
|
||||
status.set("level", ok ? "status" : "error");
|
||||
status.set("code", ok ? "NetStream.Play.Reset" : "NetStream.Play.StreamNotFound");
|
||||
status.set("description", ok ? "Resetting and playing stream." : "No such stream.");
|
||||
status.set("details", "stream");
|
||||
status.set("clientid", "ASAICiss");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
if (!ok) {
|
||||
throw std::runtime_error( StrPrinter << "No such stream:" << m_strApp << " " << m_strId << endl);
|
||||
}
|
||||
//stream begin
|
||||
sendUserControl(CONTROL_STREAM_BEGIN, STREAM_MEDIA);
|
||||
// onStatus(NetStream.Play.Start)
|
||||
status.clear();
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Play.Start");
|
||||
status.set("description", "Started playing stream.");
|
||||
status.set("details", "stream");
|
||||
status.set("clientid", "ASAICiss");
|
||||
sendReply("onStatus", AMFValue(), status);
|
||||
|
||||
// |RtmpSampleAccess(true, true)
|
||||
AMFEncoder invoke;
|
||||
invoke << "|RtmpSampleAccess" << true << true;
|
||||
sendResponse(MSG_DATA, invoke.data());
|
||||
|
||||
// onMetaData
|
||||
invoke.clear();
|
||||
invoke << "onMetaData" << src->getMetaData();
|
||||
sendResponse(MSG_DATA, invoke.data());
|
||||
|
||||
src->getConfigFrame([&](const RtmpPacket &pkt) {
|
||||
//DebugL<<"send initial frame";
|
||||
onSendMedia(pkt);
|
||||
});
|
||||
|
||||
m_pRingReader = src->getRing()->attach();
|
||||
weak_ptr<RtmpSession> weakSelf = dynamic_pointer_cast<RtmpSession>(shared_from_this());
|
||||
m_pRingReader->setReadCB([weakSelf](const RtmpPacket& pkt){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->async([weakSelf,pkt]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onSendMedia(pkt);
|
||||
});
|
||||
});
|
||||
m_pRingReader->setDetachCB([weakSelf]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->safeShutdown();
|
||||
});
|
||||
m_pPlayerSrc = src;
|
||||
if(src->getRing()->readerCount() == 1){
|
||||
src->seekTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_pause(AMFDecoder &dec) {
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
bool paused = dec.load<bool>();
|
||||
TraceL << paused;
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", "status");
|
||||
status.set("code", paused ? "NetStream.Pause.Notify" : "NetStream.Unpause.Notify");
|
||||
status.set("description", paused ? "Paused stream." : "Unpaused stream.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
//streamBegin
|
||||
sendUserControl(paused ? CONTROL_STREAM_EOF : CONTROL_STREAM_BEGIN,
|
||||
STREAM_MEDIA);
|
||||
if (!m_pRingReader) {
|
||||
throw std::runtime_error("Rtmp not started yet!");
|
||||
}
|
||||
if (paused) {
|
||||
m_pRingReader->setReadCB(nullptr);
|
||||
} else {
|
||||
weak_ptr<RtmpSession> weakSelf = dynamic_pointer_cast<RtmpSession>(shared_from_this());
|
||||
m_pRingReader->setReadCB([weakSelf](const RtmpPacket& pkt) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->async([weakSelf,pkt]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onSendMedia(pkt);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::setMetaData(AMFDecoder &dec) {
|
||||
if (!m_pPublisherSrc) {
|
||||
throw std::runtime_error("not a publisher");
|
||||
}
|
||||
std::string type = dec.load<std::string>();
|
||||
if (type != "onMetaData") {
|
||||
throw std::runtime_error("can only set metadata");
|
||||
}
|
||||
m_pPublisherSrc->onGetMetaData(dec.load<AMFValue>());
|
||||
m_pPublisherSrc->regist();
|
||||
}
|
||||
|
||||
void RtmpSession::onProcessCmd(AMFDecoder &dec) {
|
||||
std::string method = dec.load<std::string>();
|
||||
auto it = g_mapCmd.find(method);
|
||||
if (it == g_mapCmd.end()) {
|
||||
TraceL << "can not support cmd:" << method;
|
||||
return;
|
||||
}
|
||||
m_dNowReqID = dec.load<double>();
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}
|
||||
|
||||
void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3: {
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
onProcessCmd(dec);
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_DATA:
|
||||
case MSG_DATA3: {
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
TraceL << "notify:" << type;
|
||||
if (type == "@setDataFrame") {
|
||||
setMetaData(dec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO:
|
||||
case MSG_VIDEO: {
|
||||
if (!m_pPublisherSrc) {
|
||||
throw std::runtime_error("Not a rtmp publisher!");
|
||||
}
|
||||
m_pPublisherSrc->onGetMedia(chunkData);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_seek(AMFDecoder &dec) {
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
auto milliSeconds = dec.load<AMFValue>().as_number();
|
||||
InfoL << "rtmp seekTo:" << milliSeconds/1000.0;
|
||||
auto stongSrc = m_pPlayerSrc.lock();
|
||||
if (stongSrc) {
|
||||
stongSrc->seekTo(milliSeconds);
|
||||
}
|
||||
AMFValue status(AMF_OBJECT);
|
||||
AMFEncoder invoke;
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Seek.Notify");
|
||||
status.set("description", "Seeking.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
}
|
||||
|
||||
void RtmpSession::onSendMedia(const RtmpPacket& pkt) {
|
||||
sendRtmp(pkt.typeId, pkt.streamId, pkt.strBuf, pkt.timeStamp, pkt.chunkId);
|
||||
}
|
||||
|
||||
} /* namespace Rtmp */
|
||||
} /* namespace ZL */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user