初始提交

This commit is contained in:
xzl 2017-04-01 16:35:56 +08:00
parent aef0ecbcb9
commit 3f73024a9b
131 changed files with 18216 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

279
.cproject Normal file
View 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="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
<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="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
<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="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/extra/x264/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/extra/faac-1.28/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;E:\ServiceCenter\ZLToolKit\src&quot;"/>
</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="&quot;E:\ServiceCenter\ZLToolKit\WIN32&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/extra/x264/lib}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/extra/faac-1.28/lib}&quot;"/>
</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
View File

@ -27,3 +27,4 @@
*.exe *.exe
*.out *.out
*.app *.app
/X64/

27
.project Normal file
View 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>

View 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 &quot;${INPUTS}&quot;" 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 &quot;${INPUTS}&quot;" 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 &quot;${INPUTS}&quot;" 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
View 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
View File

@ -0,0 +1,8 @@
################################################################################
# Automatically-generated file. Do not edit!
################################################################################
USER_OBJS :=
LIBS :=

38
ARM/sources.mk Normal file
View 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
View 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
View 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
View 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
View 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 ' '

View 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 ' '

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

@ -0,0 +1,8 @@
################################################################################
# Automatically-generated file. Do not edit!
################################################################################
USER_OBJS :=
LIBS :=

38
X64/sources.mk Normal file
View 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
View 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
View 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
View 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
View 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 ' '

View 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 ' '

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

@ -0,0 +1,110 @@
/*
* AACEncoder.cpp
*
* Created on: 2016811
* 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
View File

@ -0,0 +1,38 @@
/*
* AACEncoder.h
*
* Created on: 2016811
* 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
View File

@ -0,0 +1,349 @@
/*
* H264Encoder.cpp
*
* Created on: 2016811
* 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/bt470bgsmpte170m/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~50
} 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~+100B帧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的块寻找合适的量化值CABAC0 01使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; kbit0
float f_vbv_buffer_init; <=1: fraction of buffer_size. >1: kbit码率控制缓冲区数据保留的最大数据量与缓冲区大小之比0~1.00.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-GOPB0,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的最后一帧5B帧
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
View File

@ -0,0 +1,48 @@
/*
* H264Encoder.h
*
* Created on: 2016811
* 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
View File

@ -0,0 +1,256 @@
/*
* Device.cpp
*
* Created on: 2016810
* 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
View File

@ -0,0 +1,108 @@
/*
* Device.h
*
* Created on: 2016810
* 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
View File

@ -0,0 +1,153 @@
/*
* PlyerProxy.cpp
*
* Created on: 2016126
* 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
View File

@ -0,0 +1,48 @@
/*
* PlyerProxy.h
*
* Created on: 2016126
* 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

192
src/H264/SPSParser.h Normal file
View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

491
src/H264/h264_parser.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,410 @@
/*
* HttpSession.cpp
*
* Created on: 2016922
* 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
View File

@ -0,0 +1,51 @@
/*
* HttpSession.h
*
* Created on: 2016922
* 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
View File

@ -0,0 +1,72 @@
/*
* strCoding.cpp
*
* Created on: 2016922
* 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
View File

@ -0,0 +1,37 @@
/*
* strCoding.h
*
* Created on: 2016922
* 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_ */

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

View File

@ -0,0 +1,349 @@
/*
* MediaReader.cpp
*
* Created on: 20161214
* 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 */

View File

@ -0,0 +1,85 @@
/*
* MediaReader.h
*
* Created on: 20161214
* 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_ */

View File

@ -0,0 +1,62 @@
/*
* MediaRecorder.cpp
*
* Created on: 2016128
* 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 */

View File

@ -0,0 +1,50 @@
/*
* MediaRecorder.h
*
* Created on: 2016128
* 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
View 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
View 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
View 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
View 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字段存在11PTD和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
View File

@ -0,0 +1,24 @@
/*
* MediaSender.h
*
* Created on: 201691
* 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_ */

View File

@ -0,0 +1,57 @@
/*
* MediaPlayer.cpp
*
* Created on: 2016125
* 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
View File

@ -0,0 +1,39 @@
/*
* MediaPlayer.h
*
* Created on: 2016125
* 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
View File

@ -0,0 +1,118 @@
/*
* Player.cpp
*
* Created on: 2016122
* 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
View File

@ -0,0 +1,53 @@
/*
* Player.h
*
* Created on: 2016122
* 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
View File

@ -0,0 +1,34 @@
/*
* PlayerBase.cpp
*
* Created on: 2016121
* 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
View File

@ -0,0 +1,219 @@
/*
* PlayerBase.h
*
* Created on: 2016121
* 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
View File

@ -0,0 +1,85 @@
/*
* RtpMaker.h
*
* Created on: 2016812
* 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
View File

@ -0,0 +1,81 @@
/*
* RtpMakerAAC.cpp
*
* Created on: 2016812
* 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
View File

@ -0,0 +1,42 @@
/*
* RtpMakerAAC.h
*
* Created on: 2016812
* 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
View File

@ -0,0 +1,98 @@
/*
* RtpMakerH264.cpp
*
* Created on: 2016812
* 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
View File

@ -0,0 +1,42 @@
/*
* RtpMakerH264.h
*
* Created on: 2016812
* 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
View 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

View File

@ -0,0 +1,42 @@
/*
* MediaSource.cpp
*
* Created on: 201691
* 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
View File

@ -0,0 +1,163 @@
/*
* RtmpMediaSource.h
*
* Created on: 201691
* 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
View File

@ -0,0 +1,231 @@
/*
* RtmpParser.cpp
*
* Created on: 2016122
* 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
View File

@ -0,0 +1,130 @@
/*
* RtmpParser.h
*
* Created on: 2016122
* 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
View 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
View File

@ -0,0 +1,143 @@
/*
* RtmpPlayer2.h
*
* Created on: 20161129
* 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_ */

View File

@ -0,0 +1,23 @@
/*
* RtmpPlayerImp.cpp
*
* Created on: 2016121
* 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
View File

@ -0,0 +1,62 @@
/*
* RtmpPlayerImp.h
*
* Created on: 2016121
* 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
View File

@ -0,0 +1,435 @@
/*
* RtmpProtocol.cpp
*
* Created on: 201727
* 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
View File

@ -0,0 +1,93 @@
/*
* RtmpProtocol.h
*
* Created on: 201727
* 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
View File

@ -0,0 +1,260 @@
/*
* RtmpPusher.cpp
*
* Created on: 2017213
* 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
View File

@ -0,0 +1,95 @@
/*
* RtmpPusher.h
*
* Created on: 2017213
* 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
View File

@ -0,0 +1,316 @@
/*
* RtmpSession.cpp
*
* Created on: 2017210
* 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