Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28d20814f6 | |||
| 4c385f6e89 | |||
| 8708e04c0d | |||
| e47f3154a8 | |||
| 7062cdcf7c | |||
| bbe11292de | |||
| 2b51c69007 | |||
| eb0edf85d2 | |||
| 5b097680f9 | |||
| fb9ecaabad |
+5
-8
@@ -2,24 +2,21 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "LFLiveKit"
|
||||
s.version = "1.9.0"
|
||||
s.version = "1.9.2"
|
||||
s.summary = "LaiFeng ios Live. LFLiveKit."
|
||||
s.homepage = "https://github.com/chenliming777"
|
||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||
s.author = { "chenliming" => "chenliming777@qq.com" }
|
||||
s.platform = :ios, "8.0"
|
||||
s.ios.deployment_target = "8.0"
|
||||
s.platform = :ios, "7.0"
|
||||
s.ios.deployment_target = "7.0"
|
||||
s.source = { :git => "https://github.com/LaiFengiOS/LFLiveKit.git", :tag => "#{s.version}" }
|
||||
s.source_files = "LFLiveKit/**/*.{h,m}"
|
||||
s.source_files = "LFLiveKit/**/*.{h,m,mm,cpp}"
|
||||
s.public_header_files = "LFLiveKit/**/*.h"
|
||||
|
||||
s.frameworks = "VideoToolbox", "AudioToolbox","AVFoundation","Foundation","UIKit"
|
||||
s.library = "z"
|
||||
s.libraries = "c++", "z"
|
||||
|
||||
s.requires_arc = true
|
||||
|
||||
s.dependency 'LMGPUImage', '~> 0.1.9'
|
||||
s.dependency "pili-librtmp", "~> 1.0.3"
|
||||
s.dependency "YYDispatchQueuePool"
|
||||
|
||||
end
|
||||
|
||||
@@ -46,6 +46,22 @@
|
||||
84001FFD1D0017680026C63F /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFC1D0017680026C63F /* AudioToolbox.framework */; };
|
||||
84001FFF1D00176C0026C63F /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFE1D00176C0026C63F /* VideoToolbox.framework */; };
|
||||
840020011D0017850026C63F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 840020001D0017850026C63F /* libz.tbd */; };
|
||||
849005B01D4B5C8400D2A3D5 /* amf.c in Sources */ = {isa = PBXBuildFile; fileRef = 849005A01D4B5C8400D2A3D5 /* amf.c */; };
|
||||
849005B11D4B5C8400D2A3D5 /* amf.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A11D4B5C8400D2A3D5 /* amf.h */; };
|
||||
849005B21D4B5C8400D2A3D5 /* bytes.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A21D4B5C8400D2A3D5 /* bytes.h */; };
|
||||
849005B31D4B5C8400D2A3D5 /* dh.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A31D4B5C8400D2A3D5 /* dh.h */; };
|
||||
849005B41D4B5C8400D2A3D5 /* dhgroups.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A41D4B5C8400D2A3D5 /* dhgroups.h */; };
|
||||
849005B51D4B5C8400D2A3D5 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 849005A51D4B5C8400D2A3D5 /* error.c */; };
|
||||
849005B61D4B5C8400D2A3D5 /* error.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A61D4B5C8400D2A3D5 /* error.h */; };
|
||||
849005B71D4B5C8400D2A3D5 /* handshake.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A71D4B5C8400D2A3D5 /* handshake.h */; };
|
||||
849005B81D4B5C8400D2A3D5 /* hashswf.c in Sources */ = {isa = PBXBuildFile; fileRef = 849005A81D4B5C8400D2A3D5 /* hashswf.c */; };
|
||||
849005B91D4B5C8400D2A3D5 /* http.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005A91D4B5C8400D2A3D5 /* http.h */; };
|
||||
849005BA1D4B5C8400D2A3D5 /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 849005AA1D4B5C8400D2A3D5 /* log.c */; };
|
||||
849005BB1D4B5C8400D2A3D5 /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005AB1D4B5C8400D2A3D5 /* log.h */; };
|
||||
849005BC1D4B5C8400D2A3D5 /* parseurl.c in Sources */ = {isa = PBXBuildFile; fileRef = 849005AC1D4B5C8400D2A3D5 /* parseurl.c */; };
|
||||
849005BD1D4B5C8400D2A3D5 /* rtmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 849005AD1D4B5C8400D2A3D5 /* rtmp.c */; };
|
||||
849005BE1D4B5C8400D2A3D5 /* rtmp.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005AE1D4B5C8400D2A3D5 /* rtmp.h */; };
|
||||
849005BF1D4B5C8400D2A3D5 /* rtmp_sys.h in Headers */ = {isa = PBXBuildFile; fileRef = 849005AF1D4B5C8400D2A3D5 /* rtmp_sys.h */; };
|
||||
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */; };
|
||||
B289F1DB1D3DE77F00D9C7A5 /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D41D3DE77F00D9C7A5 /* LFStreamingBuffer.h */; };
|
||||
B289F1DC1D3DE77F00D9C7A5 /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = B289F1D51D3DE77F00D9C7A5 /* LFStreamingBuffer.m */; };
|
||||
@@ -54,7 +70,17 @@
|
||||
B289F1DF1D3DE77F00D9C7A5 /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D81D3DE77F00D9C7A5 /* LFStreamSocket.h */; };
|
||||
B289F1E01D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D91D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h */; };
|
||||
B289F1E11D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = B289F1DA1D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m */; };
|
||||
BE55DA79155500CDEF87FB5C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B5758EB2A15DAA132D8BF380 /* libPods.a */; };
|
||||
B2CD146D1D45F18B008082E8 /* AVEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14621D45F18B008082E8 /* AVEncoder.h */; };
|
||||
B2CD146E1D45F18B008082E8 /* AVEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14631D45F18B008082E8 /* AVEncoder.mm */; };
|
||||
B2CD146F1D45F18B008082E8 /* LICENSE.markdown in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14641D45F18B008082E8 /* LICENSE.markdown */; };
|
||||
B2CD14701D45F18B008082E8 /* MP4Atom.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14651D45F18B008082E8 /* MP4Atom.h */; };
|
||||
B2CD14711D45F18B008082E8 /* MP4Atom.m in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14661D45F18B008082E8 /* MP4Atom.m */; };
|
||||
B2CD14721D45F18B008082E8 /* NALUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14671D45F18B008082E8 /* NALUnit.cpp */; };
|
||||
B2CD14731D45F18B008082E8 /* NALUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14681D45F18B008082E8 /* NALUnit.h */; };
|
||||
B2CD14741D45F18B008082E8 /* VideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14691D45F18B008082E8 /* VideoEncoder.h */; };
|
||||
B2CD14751D45F18B008082E8 /* VideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B2CD146A1D45F18B008082E8 /* VideoEncoder.m */; };
|
||||
B2CD14761D45F18B008082E8 /* LFH264VideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD146B1D45F18B008082E8 /* LFH264VideoEncoder.h */; };
|
||||
B2CD14771D45F18B008082E8 /* LFH264VideoEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2CD146C1D45F18B008082E8 /* LFH264VideoEncoder.mm */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -110,6 +136,22 @@
|
||||
84001FFC1D0017680026C63F /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
84001FFE1D00176C0026C63F /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
|
||||
840020001D0017850026C63F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
849005A01D4B5C8400D2A3D5 /* amf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf.c; sourceTree = "<group>"; };
|
||||
849005A11D4B5C8400D2A3D5 /* amf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = amf.h; sourceTree = "<group>"; };
|
||||
849005A21D4B5C8400D2A3D5 /* bytes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bytes.h; sourceTree = "<group>"; };
|
||||
849005A31D4B5C8400D2A3D5 /* dh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dh.h; sourceTree = "<group>"; };
|
||||
849005A41D4B5C8400D2A3D5 /* dhgroups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dhgroups.h; sourceTree = "<group>"; };
|
||||
849005A51D4B5C8400D2A3D5 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = "<group>"; };
|
||||
849005A61D4B5C8400D2A3D5 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = "<group>"; };
|
||||
849005A71D4B5C8400D2A3D5 /* handshake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = handshake.h; sourceTree = "<group>"; };
|
||||
849005A81D4B5C8400D2A3D5 /* hashswf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hashswf.c; sourceTree = "<group>"; };
|
||||
849005A91D4B5C8400D2A3D5 /* http.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http.h; sourceTree = "<group>"; };
|
||||
849005AA1D4B5C8400D2A3D5 /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = "<group>"; };
|
||||
849005AB1D4B5C8400D2A3D5 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
|
||||
849005AC1D4B5C8400D2A3D5 /* parseurl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parseurl.c; sourceTree = "<group>"; };
|
||||
849005AD1D4B5C8400D2A3D5 /* rtmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtmp.c; sourceTree = "<group>"; };
|
||||
849005AE1D4B5C8400D2A3D5 /* rtmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtmp.h; sourceTree = "<group>"; };
|
||||
849005AF1D4B5C8400D2A3D5 /* rtmp_sys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtmp_sys.h; sourceTree = "<group>"; };
|
||||
A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
B289F1D41D3DE77F00D9C7A5 /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LFStreamingBuffer.h; path = LFLiveKit/publish/LFStreamingBuffer.h; sourceTree = SOURCE_ROOT; };
|
||||
B289F1D51D3DE77F00D9C7A5 /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LFStreamingBuffer.m; path = LFLiveKit/publish/LFStreamingBuffer.m; sourceTree = SOURCE_ROOT; };
|
||||
@@ -118,7 +160,17 @@
|
||||
B289F1D81D3DE77F00D9C7A5 /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LFStreamSocket.h; path = LFLiveKit/publish/LFStreamSocket.h; sourceTree = SOURCE_ROOT; };
|
||||
B289F1D91D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+LFAdd.h"; path = "LFLiveKit/publish/NSMutableArray+LFAdd.h"; sourceTree = SOURCE_ROOT; };
|
||||
B289F1DA1D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+LFAdd.m"; path = "LFLiveKit/publish/NSMutableArray+LFAdd.m"; sourceTree = SOURCE_ROOT; };
|
||||
B5758EB2A15DAA132D8BF380 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B2CD14621D45F18B008082E8 /* AVEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVEncoder.h; sourceTree = "<group>"; };
|
||||
B2CD14631D45F18B008082E8 /* AVEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AVEncoder.mm; sourceTree = "<group>"; };
|
||||
B2CD14641D45F18B008082E8 /* LICENSE.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.markdown; sourceTree = "<group>"; };
|
||||
B2CD14651D45F18B008082E8 /* MP4Atom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MP4Atom.h; sourceTree = "<group>"; };
|
||||
B2CD14661D45F18B008082E8 /* MP4Atom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MP4Atom.m; sourceTree = "<group>"; };
|
||||
B2CD14671D45F18B008082E8 /* NALUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NALUnit.cpp; sourceTree = "<group>"; };
|
||||
B2CD14681D45F18B008082E8 /* NALUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NALUnit.h; sourceTree = "<group>"; };
|
||||
B2CD14691D45F18B008082E8 /* VideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoEncoder.h; sourceTree = "<group>"; };
|
||||
B2CD146A1D45F18B008082E8 /* VideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoEncoder.m; sourceTree = "<group>"; };
|
||||
B2CD146B1D45F18B008082E8 /* LFH264VideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFH264VideoEncoder.h; sourceTree = "<group>"; };
|
||||
B2CD146C1D45F18B008082E8 /* LFH264VideoEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LFH264VideoEncoder.mm; sourceTree = "<group>"; };
|
||||
B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LFLiveKit.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -135,7 +187,6 @@
|
||||
84001FF91D00175D0026C63F /* Foundation.framework in Frameworks */,
|
||||
84001FF71D0017590026C63F /* AVFoundation.framework in Frameworks */,
|
||||
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */,
|
||||
BE55DA79155500CDEF87FB5C /* libPods.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -160,7 +211,6 @@
|
||||
84001FF81D00175D0026C63F /* Foundation.framework */,
|
||||
84001FF61D0017590026C63F /* AVFoundation.framework */,
|
||||
B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */,
|
||||
B5758EB2A15DAA132D8BF380 /* libPods.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -225,6 +275,9 @@
|
||||
84001FA91D0016380026C63F /* coder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B2CD14611D45F18B008082E8 /* H264 */,
|
||||
B2CD146B1D45F18B008082E8 /* LFH264VideoEncoder.h */,
|
||||
B2CD146C1D45F18B008082E8 /* LFH264VideoEncoder.mm */,
|
||||
84001FAA1D0016380026C63F /* LFAudioEncoding.h */,
|
||||
84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */,
|
||||
84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */,
|
||||
@@ -277,6 +330,7 @@
|
||||
84001FC91D0016380026C63F /* publish */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8490059F1D4B5C8400D2A3D5 /* pili-librtmp */,
|
||||
B289F1D41D3DE77F00D9C7A5 /* LFStreamingBuffer.h */,
|
||||
B289F1D51D3DE77F00D9C7A5 /* LFStreamingBuffer.m */,
|
||||
B289F1D61D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h */,
|
||||
@@ -289,6 +343,46 @@
|
||||
path = upload;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8490059F1D4B5C8400D2A3D5 /* pili-librtmp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
849005A01D4B5C8400D2A3D5 /* amf.c */,
|
||||
849005A11D4B5C8400D2A3D5 /* amf.h */,
|
||||
849005A21D4B5C8400D2A3D5 /* bytes.h */,
|
||||
849005A31D4B5C8400D2A3D5 /* dh.h */,
|
||||
849005A41D4B5C8400D2A3D5 /* dhgroups.h */,
|
||||
849005A51D4B5C8400D2A3D5 /* error.c */,
|
||||
849005A61D4B5C8400D2A3D5 /* error.h */,
|
||||
849005A71D4B5C8400D2A3D5 /* handshake.h */,
|
||||
849005A81D4B5C8400D2A3D5 /* hashswf.c */,
|
||||
849005A91D4B5C8400D2A3D5 /* http.h */,
|
||||
849005AA1D4B5C8400D2A3D5 /* log.c */,
|
||||
849005AB1D4B5C8400D2A3D5 /* log.h */,
|
||||
849005AC1D4B5C8400D2A3D5 /* parseurl.c */,
|
||||
849005AD1D4B5C8400D2A3D5 /* rtmp.c */,
|
||||
849005AE1D4B5C8400D2A3D5 /* rtmp.h */,
|
||||
849005AF1D4B5C8400D2A3D5 /* rtmp_sys.h */,
|
||||
);
|
||||
name = "pili-librtmp";
|
||||
path = "LFLiveKit/publish/pili-librtmp";
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
B2CD14611D45F18B008082E8 /* H264 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B2CD14621D45F18B008082E8 /* AVEncoder.h */,
|
||||
B2CD14631D45F18B008082E8 /* AVEncoder.mm */,
|
||||
B2CD14641D45F18B008082E8 /* LICENSE.markdown */,
|
||||
B2CD14651D45F18B008082E8 /* MP4Atom.h */,
|
||||
B2CD14661D45F18B008082E8 /* MP4Atom.m */,
|
||||
B2CD14671D45F18B008082E8 /* NALUnit.cpp */,
|
||||
B2CD14681D45F18B008082E8 /* NALUnit.h */,
|
||||
B2CD14691D45F18B008082E8 /* VideoEncoder.h */,
|
||||
B2CD146A1D45F18B008082E8 /* VideoEncoder.m */,
|
||||
);
|
||||
path = H264;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDD4B76A07A6817C79BB4E5C /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -305,26 +399,41 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
849005B21D4B5C8400D2A3D5 /* bytes.h in Headers */,
|
||||
84001FDB1D0016380026C63F /* LFLiveAudioConfiguration.h in Headers */,
|
||||
B289F1DD1D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h in Headers */,
|
||||
84001FDD1D0016380026C63F /* LFLiveVideoConfiguration.h in Headers */,
|
||||
849005B61D4B5C8400D2A3D5 /* error.h in Headers */,
|
||||
B2CD14701D45F18B008082E8 /* MP4Atom.h in Headers */,
|
||||
84001FE31D0016380026C63F /* LFLiveSession.h in Headers */,
|
||||
B289F1DB1D3DE77F00D9C7A5 /* LFStreamingBuffer.h in Headers */,
|
||||
849005B71D4B5C8400D2A3D5 /* handshake.h in Headers */,
|
||||
849005BB1D4B5C8400D2A3D5 /* log.h in Headers */,
|
||||
84001FEB1D0016380026C63F /* LFLiveStreamInfo.h in Headers */,
|
||||
84001FE91D0016380026C63F /* LFLiveDebug.h in Headers */,
|
||||
84001FE71D0016380026C63F /* LFFrame.h in Headers */,
|
||||
B2CD14761D45F18B008082E8 /* LFH264VideoEncoder.h in Headers */,
|
||||
84001FD61D0016380026C63F /* LFHardwareAudioEncoder.h in Headers */,
|
||||
B289F1E01D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h in Headers */,
|
||||
84001FDF1D0016380026C63F /* LFGPUImageBeautyFilter.h in Headers */,
|
||||
84001FD31D0016380026C63F /* LFVideoCapture.h in Headers */,
|
||||
84001FD11D0016380026C63F /* LFAudioCapture.h in Headers */,
|
||||
849005B91D4B5C8400D2A3D5 /* http.h in Headers */,
|
||||
84001FE11D0016380026C63F /* LFGPUImageEmptyFilter.h in Headers */,
|
||||
849005B31D4B5C8400D2A3D5 /* dh.h in Headers */,
|
||||
84001FDA1D0016380026C63F /* LFVideoEncoding.h in Headers */,
|
||||
84001FE51D0016380026C63F /* LFAudioFrame.h in Headers */,
|
||||
84001FED1D0016380026C63F /* LFVideoFrame.h in Headers */,
|
||||
849005BF1D4B5C8400D2A3D5 /* rtmp_sys.h in Headers */,
|
||||
849005BE1D4B5C8400D2A3D5 /* rtmp.h in Headers */,
|
||||
B2CD14741D45F18B008082E8 /* VideoEncoder.h in Headers */,
|
||||
B2CD14731D45F18B008082E8 /* NALUnit.h in Headers */,
|
||||
84001FD81D0016380026C63F /* LFHardwareVideoEncoder.h in Headers */,
|
||||
849005B41D4B5C8400D2A3D5 /* dhgroups.h in Headers */,
|
||||
B289F1DF1D3DE77F00D9C7A5 /* LFStreamSocket.h in Headers */,
|
||||
84001FD51D0016380026C63F /* LFAudioEncoding.h in Headers */,
|
||||
849005B11D4B5C8400D2A3D5 /* amf.h in Headers */,
|
||||
B2CD146D1D45F18B008082E8 /* AVEncoder.h in Headers */,
|
||||
84001F8E1D0015D10026C63F /* LFLiveKit.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -336,14 +445,12 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84001F9E1D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKit" */;
|
||||
buildPhases = (
|
||||
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */,
|
||||
98F2C3F394BD79A6D6B8424F /* Check Pods Manifest.lock */,
|
||||
5ED199EAC89EE599F1E56B19 /* 📦 Check Pods Manifest.lock */,
|
||||
84001F851D0015D10026C63F /* Sources */,
|
||||
84001F861D0015D10026C63F /* Frameworks */,
|
||||
84001F871D0015D10026C63F /* Headers */,
|
||||
84001F881D0015D10026C63F /* Resources */,
|
||||
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */,
|
||||
8A5D8B623E50AAC1575D1741 /* Copy Pods Resources */,
|
||||
36D0848EAED7999C442A99BD /* 📦 Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -425,7 +532,7 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */ = {
|
||||
36D0848EAED7999C442A99BD /* 📦 Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -440,22 +547,7 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
8A5D8B623E50AAC1575D1741 /* Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */ = {
|
||||
5ED199EAC89EE599F1E56B19 /* 📦 Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -470,21 +562,6 @@
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
98F2C3F394BD79A6D6B8424F /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -494,21 +571,33 @@
|
||||
files = (
|
||||
84001FE21D0016380026C63F /* LFGPUImageEmptyFilter.m in Sources */,
|
||||
84001FE41D0016380026C63F /* LFLiveSession.m in Sources */,
|
||||
849005B81D4B5C8400D2A3D5 /* hashswf.c in Sources */,
|
||||
B2CD14711D45F18B008082E8 /* MP4Atom.m in Sources */,
|
||||
84001FE61D0016380026C63F /* LFAudioFrame.m in Sources */,
|
||||
84001FDC1D0016380026C63F /* LFLiveAudioConfiguration.m in Sources */,
|
||||
84001FD41D0016380026C63F /* LFVideoCapture.m in Sources */,
|
||||
84001FE81D0016380026C63F /* LFFrame.m in Sources */,
|
||||
B2CD14721D45F18B008082E8 /* NALUnit.cpp in Sources */,
|
||||
B289F1DC1D3DE77F00D9C7A5 /* LFStreamingBuffer.m in Sources */,
|
||||
849005B51D4B5C8400D2A3D5 /* error.c in Sources */,
|
||||
B289F1E11D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m in Sources */,
|
||||
B2CD14771D45F18B008082E8 /* LFH264VideoEncoder.mm in Sources */,
|
||||
849005BC1D4B5C8400D2A3D5 /* parseurl.c in Sources */,
|
||||
84001FDE1D0016380026C63F /* LFLiveVideoConfiguration.m in Sources */,
|
||||
84001FD21D0016380026C63F /* LFAudioCapture.m in Sources */,
|
||||
B2CD14751D45F18B008082E8 /* VideoEncoder.m in Sources */,
|
||||
B2CD146F1D45F18B008082E8 /* LICENSE.markdown in Sources */,
|
||||
B289F1DE1D3DE77F00D9C7A5 /* LFStreamRtmpSocket.m in Sources */,
|
||||
849005BA1D4B5C8400D2A3D5 /* log.c in Sources */,
|
||||
84001FD91D0016380026C63F /* LFHardwareVideoEncoder.m in Sources */,
|
||||
84001FEC1D0016380026C63F /* LFLiveStreamInfo.m in Sources */,
|
||||
849005B01D4B5C8400D2A3D5 /* amf.c in Sources */,
|
||||
84001FEA1D0016380026C63F /* LFLiveDebug.m in Sources */,
|
||||
84001FEE1D0016380026C63F /* LFVideoFrame.m in Sources */,
|
||||
84001FD71D0016380026C63F /* LFHardwareAudioEncoder.m in Sources */,
|
||||
849005BD1D4B5C8400D2A3D5 /* rtmp.c in Sources */,
|
||||
84001FE01D0016380026C63F /* LFGPUImageBeautyFilter.m in Sources */,
|
||||
B2CD146E1D45F18B008082E8 /* AVEncoder.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -634,6 +723,10 @@
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/LFLiveKit/publish/libpili-librtmp",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -653,6 +746,10 @@
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/LFLiveKit/publish/libpili-librtmp",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
||||
BIN
Binary file not shown.
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.9.0</string>
|
||||
<string>1.9.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
- (void)stopLive {
|
||||
self.uploading = NO;
|
||||
[self.socket stop];
|
||||
self.socket = nil;
|
||||
}
|
||||
|
||||
#pragma mark -- CaptureDelegate
|
||||
|
||||
@@ -46,8 +46,8 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
NSError *error = nil;
|
||||
|
||||
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
|
||||
|
||||
[session setMode:AVAudioSessionModeVideoRecording error:&error];
|
||||
|
||||
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
|
||||
|
||||
if (![session setActive:YES error:&error]) {
|
||||
[self handleAudioComponentCreationFailure];
|
||||
@@ -55,7 +55,7 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
|
||||
AudioComponentDescription acd;
|
||||
acd.componentType = kAudioUnitType_Output;
|
||||
acd.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
acd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
|
||||
acd.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
acd.componentFlags = 0;
|
||||
acd.componentFlagsMask = 0;
|
||||
|
||||
@@ -33,22 +33,23 @@
|
||||
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
_configuration = configuration;
|
||||
if([self pixelBufferImageSize].width < configuration.videoSize.width || [self pixelBufferImageSize].height < configuration.videoSize.height){
|
||||
@throw [NSException exceptionWithName:@"当前videoSize大小出错" reason:@"LFLiveVideoConfiguration videoSize error" userInfo:nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:_configuration.avSessionPreset cameraPosition:AVCaptureDevicePositionFront];
|
||||
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
if (configuration.landscape) {
|
||||
if (statusBar != UIInterfaceOrientationLandscapeLeft && statusBar != UIInterfaceOrientationLandscapeRight) {
|
||||
NSLog(@"当前设置方向出错");
|
||||
NSLog(@"当前设置方向出错");
|
||||
NSLog(@"当前设置方向出错");
|
||||
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
|
||||
_videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
|
||||
} else {
|
||||
_videoCamera.outputImageOrientation = statusBar;
|
||||
}
|
||||
} else {
|
||||
if (statusBar != UIInterfaceOrientationPortrait && statusBar != UIInterfaceOrientationPortraitUpsideDown) {
|
||||
NSLog(@"当前设置方向出错");
|
||||
NSLog(@"当前设置方向出错");
|
||||
NSLog(@"当前设置方向出错");
|
||||
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
|
||||
_videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
|
||||
} else {
|
||||
_videoCamera.outputImageOrientation = statusBar;
|
||||
@@ -203,7 +204,6 @@
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace {
|
||||
if (_beautyFace == beautyFace) return;
|
||||
|
||||
_beautyFace = beautyFace;
|
||||
[_filter removeAllTargets];
|
||||
@@ -227,18 +227,18 @@
|
||||
}];
|
||||
}
|
||||
|
||||
if (_configuration.isClipVideo) {
|
||||
if (_configuration.landscape) {
|
||||
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0.125, 0, 0.75, 1)];
|
||||
} else {
|
||||
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0, 0.125, 1, 0.75)];
|
||||
}
|
||||
CGSize imageSize = [self pixelBufferImageSize];
|
||||
CGFloat cropLeft = (imageSize.width - self.configuration.videoSize.width)/2.0/imageSize.width;
|
||||
CGFloat cropTop = (imageSize.height - self.configuration.videoSize.height)/2.0/imageSize.height;
|
||||
|
||||
if(cropLeft == 0 && cropTop == 0){
|
||||
[_videoCamera addTarget:_filter];
|
||||
}else{
|
||||
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(cropLeft, cropTop, 1 - cropLeft*2, 1 - cropTop*2)];
|
||||
[_videoCamera addTarget:_cropfilter];
|
||||
[_cropfilter addTarget:_filter];
|
||||
} else {
|
||||
[_videoCamera addTarget:_filter];
|
||||
}
|
||||
|
||||
|
||||
if (_beautyFace) {
|
||||
[_filter addTarget:_output];
|
||||
[_output addTarget:_gpuImageView];
|
||||
@@ -300,4 +300,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark --
|
||||
- (CGSize)pixelBufferImageSize{
|
||||
CGSize videoSize = CGSizeZero;
|
||||
switch (self.configuration.sessionPreset) {
|
||||
case LFCaptureSessionPreset360x640:
|
||||
{
|
||||
videoSize = CGSizeMake(480, 640);
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset540x960:
|
||||
{
|
||||
videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset720x1280:
|
||||
{
|
||||
videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(self.configuration.landscape){
|
||||
return CGSizeMake(videoSize.height, videoSize.width);
|
||||
}
|
||||
return videoSize;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// AVEncoder.h
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVAssetWriter.h>
|
||||
#import <AVFoundation/AVAssetWriterInput.h>
|
||||
#import <AVFoundation/AVMediaFormat.h>
|
||||
#import <AVFoundation/AVVideoSettings.h>
|
||||
#import "sys/stat.h"
|
||||
#import "VideoEncoder.h"
|
||||
#import "MP4Atom.h"
|
||||
|
||||
typedef int (^encoder_handler_t)(NSArray *data, CMTimeValue ptsValue);
|
||||
typedef int (^param_handler_t)(NSData *params);
|
||||
|
||||
@interface AVEncoder : NSObject
|
||||
|
||||
@property (atomic) NSUInteger bitrate;
|
||||
|
||||
+ (AVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate;
|
||||
|
||||
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler;
|
||||
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer;
|
||||
- (void)encodePixelBuffer:(CVPixelBufferRef)pixelBuffer pts:(CMTime)pts;
|
||||
- (NSData *)getConfigData;
|
||||
- (void)shutdown;
|
||||
|
||||
|
||||
@property (readonly, atomic) int bitspersecond;
|
||||
|
||||
@end
|
||||
Executable
+439
@@ -0,0 +1,439 @@
|
||||
//
|
||||
// AVEncoder.m
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import "AVEncoder.h"
|
||||
#import "NALUnit.h"
|
||||
|
||||
static void *AVEncoderContext = &AVEncoderContext;
|
||||
|
||||
static unsigned int to_host(unsigned char *p){
|
||||
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
|
||||
}
|
||||
|
||||
#define OUTPUT_FILE_SWITCH_POINT (50 * 1024 * 1024) // 50 MB switch point
|
||||
#define MAX_FILENAME_INDEX 5 // filenames "capture1.mp4" wraps at capture5.mp4
|
||||
|
||||
|
||||
@interface AVEncoder ()
|
||||
|
||||
{
|
||||
// initial writer, used to obtain SPS/PPS from header
|
||||
VideoEncoder *_headerWriter;
|
||||
|
||||
// main encoder/writer
|
||||
VideoEncoder *_writer;
|
||||
|
||||
// writer output file (input to our extractor) and monitoring
|
||||
NSFileHandle *_inputFile;
|
||||
dispatch_queue_t _readQueue;
|
||||
dispatch_source_t _readSource;
|
||||
|
||||
// index of current file name
|
||||
BOOL _swapping;
|
||||
int _currentFile;
|
||||
int _height;
|
||||
int _width;
|
||||
|
||||
// param set data
|
||||
NSData *_avcC;
|
||||
int _lengthSize;
|
||||
|
||||
// location of mdat
|
||||
BOOL _foundMDAT;
|
||||
uint64_t _posMDAT;
|
||||
int _bytesToNextAtom;
|
||||
BOOL _needParams;
|
||||
|
||||
// tracking if NALU is next frame
|
||||
int _prev_nal_idc;
|
||||
int _prev_nal_type;
|
||||
// array of NSData comprising a single frame. each data is one nalu with no start code
|
||||
NSMutableArray *_pendingNALU;
|
||||
|
||||
// FIFO for frame times
|
||||
NSMutableArray *_times;
|
||||
|
||||
encoder_handler_t _outputBlock;
|
||||
param_handler_t _paramsBlock;
|
||||
|
||||
// estimate bitrate over first second
|
||||
int _bitspersecond;
|
||||
CMTimeValue _firstpts;
|
||||
}
|
||||
|
||||
@property (atomic) BOOL bitrateChanged;
|
||||
|
||||
@end
|
||||
|
||||
@implementation AVEncoder
|
||||
|
||||
@synthesize bitspersecond = _bitspersecond;
|
||||
|
||||
+ (AVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
AVEncoder *enc = [AVEncoder alloc];
|
||||
[enc initForHeight:height andWidth:width bitrate:bitrate];
|
||||
return enc;
|
||||
}
|
||||
|
||||
- (NSString *)makeFilename {
|
||||
NSString *filename = [NSString stringWithFormat:@"capture%d.mp4", _currentFile];
|
||||
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
|
||||
return path;
|
||||
}
|
||||
|
||||
- (void)initForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
_height = height;
|
||||
_width = width;
|
||||
_bitrate = bitrate;
|
||||
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"params.mp4"];
|
||||
_headerWriter = [VideoEncoder encoderForPath:path Height:height andWidth:width bitrate:self.bitrate];
|
||||
_times = [NSMutableArray arrayWithCapacity:10];
|
||||
|
||||
// swap between 3 filenames
|
||||
_currentFile = 1;
|
||||
_writer = [VideoEncoder encoderForPath:[self makeFilename] Height:height andWidth:width bitrate:self.bitrate];
|
||||
|
||||
[self addObserver:self forKeyPath:NSStringFromSelector(@selector(bitrate)) options:0 context:AVEncoderContext];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context {
|
||||
if (context == AVEncoderContext && [keyPath isEqualToString:NSStringFromSelector(@selector(bitrate))]) {
|
||||
self.bitrateChanged = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler {
|
||||
_outputBlock = block;
|
||||
_paramsBlock = paramsHandler;
|
||||
_needParams = YES;
|
||||
_pendingNALU = nil;
|
||||
_firstpts = -1;
|
||||
_bitspersecond = 0;
|
||||
}
|
||||
|
||||
- (BOOL)parseParams:(NSString *)path {
|
||||
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
|
||||
struct stat s;
|
||||
fstat([file fileDescriptor], &s);
|
||||
MP4Atom *movie = [MP4Atom atomAt:0 size:s.st_size type:(OSType)('file') inFile:file];
|
||||
MP4Atom *moov = [movie childOfType:(OSType)('moov') startAt:0];
|
||||
MP4Atom *trak = nil;
|
||||
if (moov != nil) {
|
||||
for (;; ) {
|
||||
trak = [moov nextChild];
|
||||
if (trak == nil) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (trak.type == (OSType)('trak')) {
|
||||
MP4Atom *tkhd = [trak childOfType:(OSType)('tkhd') startAt:0];
|
||||
NSData *verflags = [tkhd readAt:0 size:4];
|
||||
unsigned char *p = (unsigned char *)[verflags bytes];
|
||||
if (p[3] & 1) {
|
||||
break;
|
||||
} else {
|
||||
tkhd = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MP4Atom *stsd = nil;
|
||||
if (trak != nil) {
|
||||
MP4Atom *media = [trak childOfType:(OSType)('mdia') startAt:0];
|
||||
if (media != nil) {
|
||||
MP4Atom *minf = [media childOfType:(OSType)('minf') startAt:0];
|
||||
if (minf != nil) {
|
||||
MP4Atom *stbl = [minf childOfType:(OSType)('stbl') startAt:0];
|
||||
if (stbl != nil) {
|
||||
stsd = [stbl childOfType:(OSType)('stsd') startAt:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stsd != nil) {
|
||||
MP4Atom *avc1 = [stsd childOfType:(OSType)('avc1') startAt:8];
|
||||
if (avc1 != nil) {
|
||||
MP4Atom *esd = [avc1 childOfType:(OSType)('avcC') startAt:78];
|
||||
if (esd != nil) {
|
||||
// this is the avcC record that we are looking for
|
||||
_avcC = [esd readAt:0 size:esd.length];
|
||||
if (_avcC != nil) {
|
||||
// extract size of length field
|
||||
unsigned char *p = (unsigned char *)[_avcC bytes];
|
||||
_lengthSize = (p[4] & 3) + 1;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)onParamsCompletion {
|
||||
// the initial one-frame-only file has been completed
|
||||
// Extract the avcC structure and then start monitoring the
|
||||
// main file to extract video from the mdat chunk.
|
||||
if ([self parseParams:_headerWriter.path]) {
|
||||
if (_paramsBlock) {
|
||||
_paramsBlock(_avcC);
|
||||
}
|
||||
_headerWriter = nil;
|
||||
_swapping = NO;
|
||||
_inputFile = [NSFileHandle fileHandleForReadingAtPath:_writer.path];
|
||||
_readQueue = dispatch_queue_create("uk.co.gdcl.avencoder.read", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [_inputFile fileDescriptor], 0, _readQueue);
|
||||
dispatch_source_set_event_handler(_readSource, ^{
|
||||
[self onFileUpdate];
|
||||
});
|
||||
dispatch_resume(_readSource);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer {
|
||||
CMTime prestime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
|
||||
NSNumber *pts = [NSNumber numberWithLongLong:prestime.value];
|
||||
|
||||
@synchronized(self){
|
||||
if (_needParams) {
|
||||
// the avcC record is needed for decoding and it's not written to the file until
|
||||
// completion. We get round that by writing the first frame to two files; the first
|
||||
// file (containing only one frame) is then finished, so we can extract the avcC record.
|
||||
// Only when we've got that do we start reading from the main file.
|
||||
_needParams = NO;
|
||||
if ([_headerWriter encodeFrame:sampleBuffer]) {
|
||||
[_headerWriter finishWithCompletionHandler:^{
|
||||
[self onParamsCompletion];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@synchronized(_times){
|
||||
[_times addObject:pts];
|
||||
}
|
||||
@synchronized(self){
|
||||
// switch output files when we reach a size limit
|
||||
// to avoid runaway storage use.
|
||||
if (!_swapping) {
|
||||
struct stat st;
|
||||
fstat([_inputFile fileDescriptor], &st);
|
||||
if (st.st_size > OUTPUT_FILE_SWITCH_POINT || self.bitrateChanged) {
|
||||
self.bitrateChanged = NO;
|
||||
_swapping = YES;
|
||||
VideoEncoder *oldVideo = _writer;
|
||||
|
||||
// construct a new writer to the next filename
|
||||
if (++_currentFile > MAX_FILENAME_INDEX) {
|
||||
_currentFile = 1;
|
||||
}
|
||||
//NSLog(@"Swap to file %d", _currentFile);
|
||||
_writer = [VideoEncoder encoderForPath:[self makeFilename] Height:_height andWidth:_width bitrate:self.bitrate];
|
||||
|
||||
// to do this seamlessly requires a few steps in the right order
|
||||
// first, suspend the read source
|
||||
if (_readSource) {
|
||||
dispatch_source_cancel(_readSource);
|
||||
// execute the next step as a block on the same queue, to be sure the suspend is done
|
||||
dispatch_async(_readQueue, ^{
|
||||
// finish the file, writing moov, before reading any more from the file
|
||||
// since we don't yet know where the mdat ends
|
||||
_readSource = nil;
|
||||
[oldVideo finishWithCompletionHandler:^{
|
||||
[self swapFiles:oldVideo.path];
|
||||
}];
|
||||
});
|
||||
} else {
|
||||
[self swapFiles:oldVideo.path];
|
||||
}
|
||||
}
|
||||
}
|
||||
[_writer encodeFrame:sampleBuffer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)swapFiles:(NSString *)oldPath {
|
||||
// save current position
|
||||
uint64_t pos = [_inputFile offsetInFile];
|
||||
|
||||
// re-read mdat length
|
||||
[_inputFile seekToFileOffset:_posMDAT];
|
||||
NSData *hdr = [_inputFile readDataOfLength:4];
|
||||
unsigned char *p = (unsigned char *)[hdr bytes];
|
||||
if (p) {
|
||||
int lenMDAT = to_host(p);
|
||||
|
||||
// extract nalus from saved position to mdat end
|
||||
uint64_t posEnd = _posMDAT + lenMDAT;
|
||||
uint32_t cRead = (uint32_t)(posEnd - pos);
|
||||
[_inputFile seekToFileOffset:pos];
|
||||
[self readAndDeliver:cRead];
|
||||
}
|
||||
|
||||
// close and remove file
|
||||
[_inputFile closeFile];
|
||||
_foundMDAT = false;
|
||||
_bytesToNextAtom = 0;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:oldPath error:nil];
|
||||
|
||||
|
||||
// open new file and set up dispatch source
|
||||
_inputFile = [NSFileHandle fileHandleForReadingAtPath:_writer.path];
|
||||
_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [_inputFile fileDescriptor], 0, _readQueue);
|
||||
dispatch_source_set_event_handler(_readSource, ^{
|
||||
[self onFileUpdate];
|
||||
});
|
||||
dispatch_resume(_readSource);
|
||||
_swapping = NO;
|
||||
}
|
||||
|
||||
- (void)readAndDeliver:(uint32_t)cReady {
|
||||
// Identify the individual NALUs and extract them
|
||||
while (cReady > _lengthSize) {
|
||||
NSData *lenField = [_inputFile readDataOfLength:_lengthSize];
|
||||
cReady -= _lengthSize;
|
||||
unsigned char *p = (unsigned char *)[lenField bytes];
|
||||
unsigned int lenNALU = to_host(p);
|
||||
|
||||
if (lenNALU > cReady) {
|
||||
// whole NALU not present -- seek back to start of NALU and wait for more
|
||||
[_inputFile seekToFileOffset:[_inputFile offsetInFile] - 4];
|
||||
break;
|
||||
}
|
||||
NSData *nalu = [_inputFile readDataOfLength:lenNALU];
|
||||
cReady -= lenNALU;
|
||||
|
||||
[self onNALU:nalu];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onFileUpdate {
|
||||
// called whenever there is more data to read in the main encoder output file.
|
||||
struct stat s;
|
||||
fstat([_inputFile fileDescriptor], &s);
|
||||
int cReady = s.st_size - [_inputFile offsetInFile];
|
||||
|
||||
// locate the mdat atom if needed
|
||||
while (!_foundMDAT && (cReady > 8)) {
|
||||
if (_bytesToNextAtom == 0) {
|
||||
NSData *hdr = [_inputFile readDataOfLength:8];
|
||||
cReady -= 8;
|
||||
unsigned char *p = (unsigned char *)[hdr bytes];
|
||||
int lenAtom = to_host(p);
|
||||
unsigned int nameAtom = to_host(p+4);
|
||||
if (nameAtom == (unsigned int)('mdat')) {
|
||||
_foundMDAT = true;
|
||||
_posMDAT = [_inputFile offsetInFile] - 8;
|
||||
} else {
|
||||
_bytesToNextAtom = lenAtom - 8;
|
||||
}
|
||||
}
|
||||
if (_bytesToNextAtom > 0) {
|
||||
int cThis = cReady < _bytesToNextAtom ? cReady : _bytesToNextAtom;
|
||||
_bytesToNextAtom -= cThis;
|
||||
[_inputFile seekToFileOffset:[_inputFile offsetInFile]+cThis];
|
||||
cReady -= cThis;
|
||||
}
|
||||
}
|
||||
if (!_foundMDAT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the mdat must be just encoded video.
|
||||
[self readAndDeliver:cReady];
|
||||
}
|
||||
|
||||
- (void)onEncodedFrame {
|
||||
CMTimeValue pts = 0;
|
||||
@synchronized(_times){
|
||||
if ([_times count] > 0) {
|
||||
NSNumber *time = _times[0];
|
||||
pts = [time longLongValue];
|
||||
[_times removeObjectAtIndex:0];
|
||||
if (_firstpts < 0) {
|
||||
_firstpts = pts;
|
||||
}
|
||||
if ((pts - _firstpts) < 1) {
|
||||
int bytes = 0;
|
||||
for (NSData *data in _pendingNALU) {
|
||||
bytes += [data length];
|
||||
}
|
||||
_bitspersecond += (bytes * 8);
|
||||
}
|
||||
} else {
|
||||
//NSLog(@"no pts for buffer");
|
||||
}
|
||||
}
|
||||
if (_outputBlock != nil) {
|
||||
_outputBlock(_pendingNALU, pts);
|
||||
}
|
||||
}
|
||||
|
||||
// combine multiple NALUs into a single frame, and in the process, convert to BSF
|
||||
// by adding 00 00 01 startcodes before each NALU.
|
||||
- (void)onNALU:(NSData *)nalu {
|
||||
unsigned char *pNal = (unsigned char *)[nalu bytes];
|
||||
int idc = pNal[0] & 0x60;
|
||||
int naltype = pNal[0] & 0x1f;
|
||||
|
||||
if (_pendingNALU) {
|
||||
NALUnit nal(pNal, [nalu length]);
|
||||
|
||||
// we have existing data —is this the same frame?
|
||||
// typically there are a couple of NALUs per frame in iOS encoding.
|
||||
// This is not general-purpose: it assumes that arbitrary slice ordering is not allowed.
|
||||
BOOL bNew = NO;
|
||||
if ((idc != _prev_nal_idc) && ((idc * _prev_nal_idc) == 0)) {
|
||||
bNew = YES;
|
||||
} else if ((naltype != _prev_nal_type) && ((naltype == 5) || (_prev_nal_type == 5))) {
|
||||
bNew = YES;
|
||||
} else if ((naltype >= 1) && (naltype <= 5)) {
|
||||
nal.Skip(8);
|
||||
int first_mb = nal.GetUE();
|
||||
if (first_mb == 0) {
|
||||
bNew = YES;
|
||||
}
|
||||
}
|
||||
if (bNew) {
|
||||
[self onEncodedFrame];
|
||||
_pendingNALU = nil;
|
||||
}
|
||||
}
|
||||
_prev_nal_type = naltype;
|
||||
_prev_nal_idc = idc;
|
||||
if (_pendingNALU == nil) {
|
||||
_pendingNALU = [NSMutableArray arrayWithCapacity:2];
|
||||
}
|
||||
[_pendingNALU addObject:nalu];
|
||||
}
|
||||
|
||||
- (NSData *)getConfigData {
|
||||
return [_avcC copy];
|
||||
}
|
||||
|
||||
- (void)shutdown {
|
||||
@synchronized(self){
|
||||
_readSource = nil;
|
||||
if (_headerWriter) {
|
||||
[_headerWriter finishWithCompletionHandler:^{
|
||||
_headerWriter = nil;
|
||||
}];
|
||||
}
|
||||
if (_writer) {
|
||||
[_writer finishWithCompletionHandler:^{
|
||||
_writer = nil;
|
||||
}];
|
||||
}
|
||||
// !! wait for these to finish before returning and delete temp files
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
# GDCL Source Code License
|
||||
|
||||
Last updated: 20th February 2013
|
||||
|
||||
**License Agreement for Source Code provided by GDCL**
|
||||
|
||||
This software is supplied to you by Geraint Davies Consulting Ltd ('GDCL') in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and subject to these terms, GDCL grants you a personal, non-exclusive license, to use, reproduce, modify and redistribute the software, with or without modifications, in source and/or binary forms; provided that if you redistribute the software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the software, and that in all cases attribution of GDCL as the original author of the source code shall be included in all such resulting software products or distributions.
|
||||
|
||||
Neither the name, trademarks, service marks or logos of Geraint Davies or GDCL may be used to endorse or promote products derived from the software without specific prior written permission from GDCL. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by GDCL herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the software may be incorporated.
|
||||
|
||||
The software is provided by GDCL on an "AS IS" basis. GDCL MAKE NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL GDCL BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF GDCL HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MP4Atom.h
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 15/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MP4Atom : NSObject
|
||||
|
||||
{
|
||||
NSFileHandle *_file;
|
||||
int64_t _offset;
|
||||
int64_t _length;
|
||||
OSType _type;
|
||||
int64_t _nextChild;
|
||||
}
|
||||
@property OSType type;
|
||||
@property int64_t length;
|
||||
|
||||
+ (MP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle;
|
||||
- (BOOL)init:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle;
|
||||
- (NSData *)readAt:(int64_t)offset size:(int)length;
|
||||
- (BOOL)setChildOffset:(int64_t)offset;
|
||||
- (MP4Atom *)nextChild;
|
||||
- (MP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset;
|
||||
|
||||
@end
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// MP4Atom.m
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 15/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import "MP4Atom.h"
|
||||
|
||||
static unsigned int to_host(unsigned char *p){
|
||||
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
|
||||
}
|
||||
|
||||
@implementation MP4Atom
|
||||
|
||||
@synthesize type = _type;
|
||||
@synthesize length = _length;
|
||||
|
||||
+ (MP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
|
||||
MP4Atom *atom = [MP4Atom alloc];
|
||||
if (![atom init:offset size:length type:fourcc inFile:handle]) {
|
||||
return nil;
|
||||
}
|
||||
return atom;
|
||||
}
|
||||
|
||||
- (BOOL)init:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
|
||||
_file = handle;
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
_type = fourcc;
|
||||
_nextChild = 0;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData *)readAt:(int64_t)offset size:(int)length {
|
||||
[_file seekToFileOffset:_offset + offset];
|
||||
return [_file readDataOfLength:length];
|
||||
}
|
||||
|
||||
- (BOOL)setChildOffset:(int64_t)offset {
|
||||
_nextChild = offset;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MP4Atom *)nextChild {
|
||||
if (_nextChild <= (_length - 8)) {
|
||||
[_file seekToFileOffset:_offset + _nextChild];
|
||||
NSData *data = [_file readDataOfLength:8];
|
||||
int cHeader = 8;
|
||||
unsigned char *p = (unsigned char *)[data bytes];
|
||||
int64_t len = to_host(p);
|
||||
OSType fourcc = to_host(p + 4);
|
||||
if (len == 1) {
|
||||
// 64-bit extended length
|
||||
cHeader += 8;
|
||||
data = [_file readDataOfLength:8];
|
||||
p = (unsigned char *)[data bytes];
|
||||
len = to_host(p);
|
||||
len = (len << 32) + to_host(p + 4);
|
||||
} else if (len == 0) {
|
||||
// whole remaining parent space
|
||||
len = _length - _nextChild;
|
||||
}
|
||||
if (fourcc == (OSType)('uuid')) {
|
||||
cHeader += 16;
|
||||
}
|
||||
if ((len < 0) || ((len + _nextChild) > _length)) {
|
||||
return nil;
|
||||
}
|
||||
int64_t offset = _nextChild + cHeader;
|
||||
_nextChild += len;
|
||||
len -= cHeader;
|
||||
return [MP4Atom atomAt:offset+_offset size:len type:fourcc inFile:_file];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (MP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset {
|
||||
[self setChildOffset:offset];
|
||||
MP4Atom *child = nil;
|
||||
do {
|
||||
child = [self nextChild];
|
||||
} while ((child != nil) && (child.type != fourcc));
|
||||
return child;
|
||||
}
|
||||
|
||||
@end
|
||||
Executable
+424
@@ -0,0 +1,424 @@
|
||||
|
||||
//
|
||||
// NALUnit.cpp
|
||||
//
|
||||
// Implementation of Basic parsing of H.264 NAL Units
|
||||
//
|
||||
// Geraint Davies, March 2004
|
||||
//
|
||||
// Copyright (c) GDCL 2004-2008 http://www.gdcl.co.uk/license.htm
|
||||
|
||||
|
||||
|
||||
#include "NALUnit.h"
|
||||
|
||||
|
||||
// --- core NAL Unit implementation ------------------------------
|
||||
|
||||
NALUnit::NALUnit()
|
||||
: m_pStart(NULL),
|
||||
m_cBytes(0){
|
||||
}
|
||||
|
||||
bool
|
||||
NALUnit::GetStartCode(const BYTE *& pBegin, const BYTE *& pStart, int& cRemain){
|
||||
// start code is any number of 00 followed by 00 00 01
|
||||
// We need to record the first 00 in pBegin and the first byte
|
||||
// following the startcode in pStart.
|
||||
// if no start code is found, pStart and cRemain should be unchanged.
|
||||
|
||||
const BYTE *pThis = pStart;
|
||||
int cBytes = cRemain;
|
||||
|
||||
pBegin = NULL;
|
||||
while (cBytes >= 4) {
|
||||
if (pThis[0] == 0) {
|
||||
// remember first 00
|
||||
if (pBegin == NULL) {
|
||||
pBegin = pThis;
|
||||
}
|
||||
if ((pThis[1] == 0) &&
|
||||
(pThis[2] == 1)) {
|
||||
// point to type byte of NAL unit
|
||||
pStart = pThis + 3;
|
||||
cRemain = cBytes - 3;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
pBegin = NULL;
|
||||
}
|
||||
cBytes--;
|
||||
pThis++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
NALUnit::Parse(const BYTE *pBuffer, int cSpace, int LengthSize, bool bEnd){
|
||||
// if we get the start code but not the whole
|
||||
// NALU, we can return false but still have the length property valid
|
||||
m_cBytes = 0;
|
||||
|
||||
ResetBitstream();
|
||||
|
||||
if (LengthSize > 0) {
|
||||
m_pStartCodeStart = pBuffer;
|
||||
|
||||
if (LengthSize > cSpace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cBytes = 0;
|
||||
for (int i = 0; i < LengthSize; i++) {
|
||||
m_cBytes <<= 8;
|
||||
m_cBytes += *pBuffer++;
|
||||
}
|
||||
|
||||
if ((m_cBytes+LengthSize) <= cSpace) {
|
||||
m_pStart = pBuffer;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// this is not length-delimited: we must look for start codes
|
||||
const BYTE *pBegin;
|
||||
if (GetStartCode(pBegin, pBuffer, cSpace)) {
|
||||
m_pStart = pBuffer;
|
||||
m_pStartCodeStart = pBegin;
|
||||
|
||||
// either we find another startcode, or we continue to the
|
||||
// buffer end (if this is the last block of data)
|
||||
if (GetStartCode(pBegin, pBuffer, cSpace)) {
|
||||
m_cBytes = int(pBegin - m_pStart);
|
||||
return true;
|
||||
} else if (bEnd) {
|
||||
// current element extends to end of buffer
|
||||
m_cBytes = cSpace;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// bitwise access to data
|
||||
void
|
||||
NALUnit::ResetBitstream(){
|
||||
m_idx = 0;
|
||||
m_nBits = 0;
|
||||
m_cZeros = 0;
|
||||
}
|
||||
|
||||
void
|
||||
NALUnit::Skip(int nBits){
|
||||
if (nBits < m_nBits) {
|
||||
m_nBits -= nBits;
|
||||
} else {
|
||||
nBits -= m_nBits;
|
||||
while (nBits >= 8) {
|
||||
GetBYTE();
|
||||
nBits -= 8;
|
||||
}
|
||||
if (nBits) {
|
||||
m_byte = GetBYTE();
|
||||
m_nBits = 8;
|
||||
|
||||
m_nBits -= nBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the next byte, removing emulation prevention bytes
|
||||
BYTE
|
||||
NALUnit::GetBYTE(){
|
||||
if (m_idx >= m_cBytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTE b = m_pStart[m_idx++];
|
||||
|
||||
// to avoid start-code emulation, a byte 0x03 is inserted
|
||||
// after any 00 00 pair. Discard that here.
|
||||
if (b == 0) {
|
||||
m_cZeros++;
|
||||
if ((m_idx < m_cBytes) && (m_cZeros == 2) && (m_pStart[m_idx] == 0x03)) {
|
||||
m_idx++;
|
||||
m_cZeros = 0;
|
||||
}
|
||||
} else {
|
||||
m_cZeros = 0;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
NALUnit::GetBit(){
|
||||
if (m_nBits == 0) {
|
||||
m_byte = GetBYTE();
|
||||
m_nBits = 8;
|
||||
}
|
||||
m_nBits--;
|
||||
return (m_byte >> m_nBits) & 0x1;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
NALUnit::GetWord(int nBits){
|
||||
unsigned long u = 0;
|
||||
while (nBits > 0) {
|
||||
u <<= 1;
|
||||
u |= GetBit();
|
||||
nBits--;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
NALUnit::GetUE(){
|
||||
// Exp-Golomb entropy coding: leading zeros, then a one, then
|
||||
// the data bits. The number of leading zeros is the number of
|
||||
// data bits, counting up from that number of 1s as the base.
|
||||
// That is, if you see
|
||||
// 0001010
|
||||
// You have three leading zeros, so there are three data bits (010)
|
||||
// counting up from a base of 111: thus 111 + 010 = 1001 = 9
|
||||
int cZeros = 0;
|
||||
while (GetBit() == 0) {
|
||||
cZeros++;
|
||||
}
|
||||
return GetWord(cZeros) + ((1 << cZeros)-1);
|
||||
}
|
||||
|
||||
long
|
||||
NALUnit::GetSE(){
|
||||
// same as UE but signed.
|
||||
// basically the unsigned numbers are used as codes to indicate signed numbers in pairs
|
||||
// in increasing value. Thus the encoded values
|
||||
// 0, 1, 2, 3, 4
|
||||
// mean
|
||||
// 0, 1, -1, 2, -2 etc
|
||||
|
||||
unsigned long UE = GetUE();
|
||||
bool bPositive = UE & 1;
|
||||
long SE = (UE + 1) >> 1;
|
||||
if (!bPositive) {
|
||||
SE = -SE;
|
||||
}
|
||||
return SE;
|
||||
}
|
||||
|
||||
// --- sequence params parsing ---------------
|
||||
SeqParamSet::SeqParamSet()
|
||||
: m_cx(0),
|
||||
m_cy(0),
|
||||
m_FrameBits(0){
|
||||
// SetRect(&m_rcFrame, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
ScalingList(int size, NALUnit *pnalu){
|
||||
long lastScale = 8;
|
||||
long nextScale = 8;
|
||||
for (int j = 0; j < size; j++) {
|
||||
if (nextScale != 0) {
|
||||
long delta = pnalu->GetSE();
|
||||
nextScale = (lastScale + delta + 256) %256;
|
||||
}
|
||||
int scaling_list_j = (nextScale == 0) ? lastScale : nextScale;
|
||||
lastScale = scaling_list_j;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SeqParamSet::Parse(NALUnit *pnalu){
|
||||
if (pnalu->Type() != NALUnit::NAL_Sequence_Params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// with the UE/SE type encoding, we must decode all the values
|
||||
// to get through to the ones we want
|
||||
pnalu->ResetBitstream();
|
||||
pnalu->Skip(8); // type
|
||||
m_Profile = pnalu->GetWord(8);
|
||||
m_Compatibility = (BYTE)pnalu->GetWord(8);
|
||||
m_Level = pnalu->GetWord(8);
|
||||
|
||||
/*int seq_param_id =*/ pnalu->GetUE();
|
||||
|
||||
if ((m_Profile == 100) || (m_Profile == 110) || (m_Profile == 122) || (m_Profile == 144)) {
|
||||
int chroma_fmt = pnalu->GetUE();
|
||||
if (chroma_fmt == 3) {
|
||||
pnalu->Skip(1);
|
||||
}
|
||||
/* int bit_depth_luma_minus8 = */ pnalu->GetUE();
|
||||
/* int bit_depth_chroma_minus8 = */ pnalu->GetUE();
|
||||
pnalu->Skip(1);
|
||||
int seq_scaling_matrix_present = pnalu->GetBit();
|
||||
if (seq_scaling_matrix_present) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (pnalu->GetBit()) {
|
||||
if (i < 6) {
|
||||
ScalingList(16, pnalu);
|
||||
} else {
|
||||
ScalingList(64, pnalu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int log2_frame_minus4 = pnalu->GetUE();
|
||||
m_FrameBits = log2_frame_minus4 + 4;
|
||||
int POCtype = pnalu->GetUE();
|
||||
if (POCtype == 0) {
|
||||
/*int log2_poc_minus4 =*/ pnalu->GetUE();
|
||||
} else if (POCtype == 1) {
|
||||
pnalu->Skip(1); // delta always zero
|
||||
/*int nsp_offset =*/ pnalu->GetSE();
|
||||
/*int nsp_top_to_bottom = */ pnalu->GetSE();
|
||||
int num_ref_in_cycle = pnalu->GetUE();
|
||||
for (int i = 0; i < num_ref_in_cycle; i++) {
|
||||
/*int sf_offset =*/ pnalu->GetSE();
|
||||
}
|
||||
} else if (POCtype != 2) {
|
||||
return false;
|
||||
}
|
||||
// else for POCtype == 2, no additional data in stream
|
||||
|
||||
/*int num_ref_frames =*/ pnalu->GetUE();
|
||||
/*int gaps_allowed =*/ pnalu->GetBit();
|
||||
|
||||
int mbs_width = pnalu->GetUE();
|
||||
int mbs_height = pnalu->GetUE();
|
||||
m_cx = (mbs_width+1) * 16;
|
||||
m_cy = (mbs_height+1) * 16;
|
||||
|
||||
// smoke test validation of sps
|
||||
if ((m_cx > 2000) || (m_cy > 2000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if this is false, then sizes are field sizes and need adjusting
|
||||
m_bFrameOnly = pnalu->GetBit() ? true : false;
|
||||
|
||||
if (!m_bFrameOnly) {
|
||||
pnalu->Skip(1); // adaptive frame/field
|
||||
}
|
||||
pnalu->Skip(1); // direct 8x8
|
||||
|
||||
#if 0
|
||||
SetRect(&m_rcFrame, 0, 0, 0, 0);
|
||||
bool bCrop = pnalu->GetBit() ? true : false;
|
||||
if (bCrop) {
|
||||
// get cropping rect
|
||||
// store as exclusive, pixel parameters relative to frame
|
||||
m_rcFrame.left = pnalu->GetUE() * 2;
|
||||
m_rcFrame.right = pnalu->GetUE() * 2;
|
||||
m_rcFrame.top = pnalu->GetUE() * 2;
|
||||
m_rcFrame.bottom = pnalu->GetUE() * 2;
|
||||
}
|
||||
|
||||
if (!IsRectEmpty(&m_rcFrame)) {
|
||||
m_rcFrame.right = m_cx - m_rcFrame.right;
|
||||
m_rcFrame.bottom = m_cy - m_rcFrame.bottom;
|
||||
}
|
||||
#endif
|
||||
// adjust rect from 2x2 units to pixels
|
||||
|
||||
if (!m_bFrameOnly) {
|
||||
// adjust heights from field to frame
|
||||
m_cy *= 2;
|
||||
#if 0
|
||||
m_rcFrame.top *= 2;
|
||||
m_rcFrame.bottom *= 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
// .. rest are not interesting yet
|
||||
m_nalu = *pnalu;
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- slice header --------------------
|
||||
bool
|
||||
SliceHeader::Parse(NALUnit *pnalu){
|
||||
switch (pnalu->Type()) {
|
||||
case NALUnit::NAL_IDR_Slice:
|
||||
case NALUnit::NAL_Slice:
|
||||
case NALUnit::NAL_PartitionA:
|
||||
// all these begin with a slice header
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// slice header has the 1-byte type, then one UE value,
|
||||
// then the frame number.
|
||||
pnalu->ResetBitstream();
|
||||
pnalu->Skip(8); // NALU type
|
||||
pnalu->GetUE(); // first mb in slice
|
||||
pnalu->GetUE(); // slice type
|
||||
pnalu->GetUE(); // pic param set id
|
||||
|
||||
m_framenum = pnalu->GetWord(m_nBitsFrame);
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- SEI ----------------------
|
||||
|
||||
|
||||
SEIMessage::SEIMessage(NALUnit *pnalu){
|
||||
m_pnalu = pnalu;
|
||||
const BYTE *p = pnalu->Start();
|
||||
p++; // nalu type byte
|
||||
m_type = 0;
|
||||
while (*p == 0xff) {
|
||||
m_type += 255;
|
||||
p++;
|
||||
}
|
||||
m_type += *p;
|
||||
p++;
|
||||
m_length = 0;
|
||||
while (*p == 0xff) {
|
||||
m_type += 255;
|
||||
p++;
|
||||
}
|
||||
m_length += *p;
|
||||
p++;
|
||||
m_idxPayload = int(p - m_pnalu->Start());
|
||||
}
|
||||
|
||||
avcCHeader::avcCHeader(const BYTE *header, int cBytes){
|
||||
if (cBytes < 8) {
|
||||
return;
|
||||
}
|
||||
const BYTE *pEnd = header + cBytes;
|
||||
|
||||
int cSeq = header[5] & 0x1f;
|
||||
header += 6;
|
||||
for (int i = 0; i < cSeq; i++) {
|
||||
if ((header+2) > pEnd) {
|
||||
return;
|
||||
}
|
||||
int cThis = (header[0] << 8) + header[1];
|
||||
header += 2;
|
||||
if ((header+cThis) > pEnd) {
|
||||
return;
|
||||
}
|
||||
if (i == 0) {
|
||||
NALUnit n(header, cThis);
|
||||
m_sps = n;
|
||||
}
|
||||
header += cThis;
|
||||
}
|
||||
if ((header + 3) >= pEnd) {
|
||||
return;
|
||||
}
|
||||
int cPPS = header[0];
|
||||
if (cPPS > 0) {
|
||||
int cThis = (header[1] << 8) + header[2];
|
||||
header += 3;
|
||||
NALUnit n(header, cThis);
|
||||
m_pps = n;
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
|
||||
//
|
||||
// NALUnit.h
|
||||
//
|
||||
// Basic parsing of H.264 NAL Units
|
||||
//
|
||||
// Geraint Davies, March 2004
|
||||
//
|
||||
// Copyright (c) GDCL 2004-2008 http://www.gdcl.co.uk/license.htm
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned long ULONG;
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
class NALUnit
|
||||
{
|
||||
public:
|
||||
NALUnit();
|
||||
NALUnit(const BYTE* pStart, int len){
|
||||
m_pStart = m_pStartCodeStart = pStart;
|
||||
m_cBytes = len;
|
||||
ResetBitstream();
|
||||
}
|
||||
virtual ~NALUnit() {
|
||||
}
|
||||
|
||||
// assignment copies a pointer into a fixed buffer managed elsewhere. We do not copy the data
|
||||
NALUnit(const NALUnit &r){
|
||||
m_pStart = r.m_pStart;
|
||||
m_cBytes = r.m_cBytes;
|
||||
ResetBitstream();
|
||||
}
|
||||
const NALUnit& operator = (const NALUnit &r)
|
||||
{
|
||||
m_pStart = r.m_pStart;
|
||||
m_cBytes = r.m_cBytes;
|
||||
ResetBitstream();
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum eNALType {
|
||||
NAL_Slice = 1,
|
||||
NAL_PartitionA = 2,
|
||||
NAL_PartitionB = 3,
|
||||
NAL_PartitionC = 4,
|
||||
NAL_IDR_Slice = 5,
|
||||
NAL_SEI = 6,
|
||||
NAL_Sequence_Params = 7,
|
||||
NAL_Picture_Params = 8,
|
||||
NAL_AUD = 9,
|
||||
};
|
||||
|
||||
// identify a NAL unit within a buffer.
|
||||
// If LengthSize is non-zero, it is the number of bytes
|
||||
// of length field we expect. Otherwise, we expect start-code
|
||||
// delimiters.
|
||||
bool Parse(const BYTE *pBuffer, int cSpace, int LengthSize, bool bEnd);
|
||||
|
||||
eNALType Type(){
|
||||
if (m_pStart == NULL) {
|
||||
return eNALType(0);
|
||||
}
|
||||
return eNALType(m_pStart[0] & 0x1F);
|
||||
}
|
||||
|
||||
int Length(){
|
||||
return m_cBytes;
|
||||
}
|
||||
|
||||
const BYTE *Start(){
|
||||
return m_pStart;
|
||||
}
|
||||
|
||||
// bitwise access to data
|
||||
void ResetBitstream();
|
||||
void Skip(int nBits);
|
||||
|
||||
unsigned long GetWord(int nBits);
|
||||
unsigned long GetUE();
|
||||
long GetSE();
|
||||
BYTE GetBYTE();
|
||||
unsigned long GetBit();
|
||||
|
||||
const BYTE *StartCodeStart() {
|
||||
return m_pStartCodeStart;
|
||||
}
|
||||
|
||||
private:
|
||||
bool GetStartCode(const BYTE *& pBegin, const BYTE *& pStart, int& cRemain);
|
||||
|
||||
private:
|
||||
const BYTE *m_pStartCodeStart;
|
||||
const BYTE *m_pStart;
|
||||
int m_cBytes;
|
||||
|
||||
// bitstream access
|
||||
int m_idx;
|
||||
int m_nBits;
|
||||
BYTE m_byte;
|
||||
int m_cZeros;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// simple parser for the Sequence parameter set things that we need
|
||||
class SeqParamSet
|
||||
{
|
||||
public:
|
||||
SeqParamSet();
|
||||
bool Parse(NALUnit *pnalu);
|
||||
int FrameBits(){
|
||||
return m_FrameBits;
|
||||
}
|
||||
|
||||
long EncodedWidth(){
|
||||
return m_cx;
|
||||
}
|
||||
|
||||
long EncodedHeight(){
|
||||
return m_cy;
|
||||
}
|
||||
|
||||
#if 0
|
||||
long CroppedWidth(){
|
||||
if (IsRectEmpty(&m_rcFrame)) {
|
||||
return EncodedWidth();
|
||||
}
|
||||
return m_rcFrame.right - m_rcFrame.left;
|
||||
}
|
||||
|
||||
long CroppedHeight(){
|
||||
if (IsRectEmpty(&m_rcFrame)) {
|
||||
return EncodedHeight();
|
||||
}
|
||||
return m_rcFrame.bottom - m_rcFrame.top;
|
||||
}
|
||||
|
||||
RECT *CropRect(){
|
||||
return &m_rcFrame;
|
||||
}
|
||||
|
||||
#endif
|
||||
bool Interlaced(){
|
||||
return !m_bFrameOnly;
|
||||
}
|
||||
|
||||
unsigned int Profile() {
|
||||
return m_Profile;
|
||||
}
|
||||
|
||||
unsigned int Level() {
|
||||
return m_Level;
|
||||
}
|
||||
|
||||
BYTE Compat() {
|
||||
return m_Compatibility;
|
||||
}
|
||||
|
||||
NALUnit *NALU() {
|
||||
return &m_nalu;
|
||||
}
|
||||
|
||||
private:
|
||||
NALUnit m_nalu;
|
||||
int m_FrameBits;
|
||||
long m_cx;
|
||||
long m_cy;
|
||||
// RECT m_rcFrame;
|
||||
bool m_bFrameOnly;
|
||||
|
||||
int m_Profile;
|
||||
int m_Level;
|
||||
BYTE m_Compatibility;
|
||||
};
|
||||
|
||||
// extract frame num from slice headers
|
||||
class SliceHeader
|
||||
{
|
||||
public:
|
||||
SliceHeader(int nBitsFrame)
|
||||
: m_framenum(0),
|
||||
m_nBitsFrame(nBitsFrame){
|
||||
}
|
||||
|
||||
bool Parse(NALUnit *pnalu);
|
||||
int FrameNum(){
|
||||
return m_framenum;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_framenum;
|
||||
int m_nBitsFrame;
|
||||
};
|
||||
|
||||
// SEI message structure
|
||||
class SEIMessage
|
||||
{
|
||||
public:
|
||||
SEIMessage(NALUnit* pnalu);
|
||||
int Type() {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
int Length() {
|
||||
return m_length;
|
||||
}
|
||||
|
||||
const BYTE *Payload() {
|
||||
return m_pnalu->Start() + m_idxPayload;
|
||||
}
|
||||
|
||||
private:
|
||||
NALUnit *m_pnalu;
|
||||
int m_type;
|
||||
int m_length;
|
||||
int m_idxPayload;
|
||||
};
|
||||
|
||||
// avcC structure from MP4
|
||||
class avcCHeader
|
||||
{
|
||||
public:
|
||||
avcCHeader(const BYTE* header, int cBytes);
|
||||
NALUnit *sps() {
|
||||
return &m_sps;
|
||||
}
|
||||
|
||||
NALUnit *pps() {
|
||||
return &m_pps;
|
||||
}
|
||||
|
||||
private:
|
||||
NALUnit m_sps;
|
||||
NALUnit m_pps;
|
||||
};
|
||||
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// VideoEncoder.h
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AVFoundation/AVAssetWriter.h"
|
||||
#import "AVFoundation/AVAssetWriterInput.h"
|
||||
#import "AVFoundation/AVMediaFormat.h"
|
||||
#import "AVFoundation/AVVideoSettings.h"
|
||||
|
||||
@interface VideoEncoder : NSObject
|
||||
|
||||
|
||||
@property NSString *path;
|
||||
@property (nonatomic, readonly) NSUInteger bitrate;
|
||||
|
||||
+ (VideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate;
|
||||
|
||||
- (void)initPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate;
|
||||
- (void)finishWithCompletionHandler:(void (^)(void))handler;
|
||||
- (BOOL)encodeFrame:(CMSampleBufferRef)sampleBuffer;
|
||||
|
||||
@end
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// VideoEncoder.m
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import "VideoEncoder.h"
|
||||
|
||||
@implementation VideoEncoder
|
||||
{
|
||||
AVAssetWriter *_writer;
|
||||
AVAssetWriterInput *_writerInput;
|
||||
NSString *_path;
|
||||
}
|
||||
|
||||
@synthesize path = _path;
|
||||
|
||||
+ (VideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
VideoEncoder *enc = [VideoEncoder alloc];
|
||||
[enc initPath:path Height:height andWidth:width bitrate:bitrate];
|
||||
return enc;
|
||||
}
|
||||
|
||||
- (void)initPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
self.path = path;
|
||||
_bitrate = bitrate;
|
||||
|
||||
[[NSFileManager defaultManager] removeItemAtPath:self.path error:nil];
|
||||
NSURL *url = [NSURL fileURLWithPath:self.path];
|
||||
|
||||
NSDictionary *settings = @{
|
||||
AVVideoCodecKey: AVVideoCodecH264,
|
||||
AVVideoWidthKey: @(width),
|
||||
AVVideoHeightKey: @(height),
|
||||
AVVideoCompressionPropertiesKey: @{
|
||||
AVVideoAverageBitRateKey: @(self.bitrate),
|
||||
AVVideoMaxKeyFrameIntervalKey: @(30 * 2),
|
||||
AVVideoProfileLevelKey: AVVideoProfileLevelH264Baseline41,
|
||||
AVVideoAllowFrameReorderingKey: @NO,
|
||||
}
|
||||
};
|
||||
_writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];
|
||||
_writerInput.expectsMediaDataInRealTime = YES;
|
||||
|
||||
_writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeQuickTimeMovie error:nil];
|
||||
[_writer addInput:_writerInput];
|
||||
}
|
||||
|
||||
- (void)finishWithCompletionHandler:(void (^)(void))handler {
|
||||
if (_writer.status == AVAssetWriterStatusWriting) {
|
||||
[_writer finishWritingWithCompletionHandler:handler];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)encodeFrame:(CMSampleBufferRef)sampleBuffer {
|
||||
if (CMSampleBufferDataIsReady(sampleBuffer)) {
|
||||
if (_writer.status == AVAssetWriterStatusUnknown) {
|
||||
CMTime startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
|
||||
[_writer startWriting];
|
||||
[_writer startSessionAtSourceTime:startTime];
|
||||
}
|
||||
if (_writer.status == AVAssetWriterStatusFailed) {
|
||||
//NSLog(@"AVAssetWriterStatusFailed");
|
||||
return NO;
|
||||
}
|
||||
if (_writerInput.readyForMoreMediaData == YES) {
|
||||
[_writerInput appendSampleBuffer:sampleBuffer];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// LFH264VideoEncoder
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by feng on 7/5/16.
|
||||
// Copyright (c) 2014 zhanqi.tv. All rights reserved.
|
||||
//
|
||||
#import "LFVideoEncoding.h"
|
||||
|
||||
@interface LFH264VideoEncoder : NSObject <LFVideoEncoding> {
|
||||
|
||||
}
|
||||
|
||||
- (void)shutdown;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,270 @@
|
||||
//
|
||||
// LFH264VideoEncoder
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by feng on 7/5/16.
|
||||
// Copyright (c) 2014 zhanqi.tv. All rights reserved.
|
||||
//
|
||||
|
||||
#import <CoreMedia/CoreMedia.h>
|
||||
#import <mach/mach_time.h>
|
||||
#import "NALUnit.h"
|
||||
#import "AVEncoder.h"
|
||||
#import "LFH264VideoEncoder.h"
|
||||
#import "LFVideoFrame.h"
|
||||
|
||||
@interface LFH264VideoEncoder() {
|
||||
FILE *fp;
|
||||
NSInteger frameCount;
|
||||
BOOL enabledWriteVideoFile;
|
||||
}
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
|
||||
@property (nonatomic, weak) id<LFVideoEncodingDelegate> h264Delegate;
|
||||
@property (nonatomic) BOOL isBackGround;
|
||||
@property (nonatomic) NSInteger currentVideoBitRate;
|
||||
@property (nonatomic, strong) dispatch_queue_t sendQueue;
|
||||
|
||||
@property (nonatomic, strong) AVEncoder *encoder;
|
||||
|
||||
@property (nonatomic, strong) NSData *naluStartCode;
|
||||
@property (nonatomic, strong) NSMutableData *videoSPSandPPS;
|
||||
@property (nonatomic, strong) NSMutableData *spsData;
|
||||
@property (nonatomic, strong) NSMutableData *ppsData;
|
||||
@property (nonatomic, strong) NSMutableData *sei;
|
||||
@property (nonatomic) CMTimeScale timescale;
|
||||
@property (nonatomic, strong) NSMutableArray *orphanedFrames;
|
||||
@property (nonatomic, strong) NSMutableArray *orphanedSEIFrames;
|
||||
@property (nonatomic) CMTime lastPTS;
|
||||
@end
|
||||
|
||||
@implementation LFH264VideoEncoder
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoStreamConfiguration:(LFLiveVideoConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"USE LF264VideoEncoder");
|
||||
_configuration = configuration;
|
||||
[self initCompressionSession];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initCompressionSession{
|
||||
_sendQueue = dispatch_queue_create("com.youku.laifeng.h264.sendframe", DISPATCH_QUEUE_SERIAL);
|
||||
[self initializeNALUnitStartCode];
|
||||
_lastPTS = kCMTimeInvalid;
|
||||
_timescale = 1000;
|
||||
frameCount = 0;
|
||||
#ifdef DEBUG
|
||||
enabledWriteVideoFile = NO;
|
||||
[self initForFilePath];
|
||||
#endif
|
||||
|
||||
_encoder = [AVEncoder encoderForHeight:_configuration.videoSize.height andWidth:_configuration.videoSize.width bitrate:_configuration.videoBitRate];
|
||||
[_encoder encodeWithBlock:^int(NSArray* dataArray, CMTimeValue ptsValue) {
|
||||
[self incomingVideoFrames:dataArray ptsValue:ptsValue];
|
||||
return 0;
|
||||
} onParams:^int(NSData *data) {
|
||||
[self generateSPSandPPS];
|
||||
return 0;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) initializeNALUnitStartCode {
|
||||
NSUInteger naluLength = 4;
|
||||
uint8_t *nalu = (uint8_t*)malloc(naluLength * sizeof(uint8_t));
|
||||
nalu[0] = 0x00;
|
||||
nalu[1] = 0x00;
|
||||
nalu[2] = 0x00;
|
||||
nalu[3] = 0x01;
|
||||
_naluStartCode = [NSData dataWithBytesNoCopy:nalu length:naluLength freeWhenDone:YES];
|
||||
}
|
||||
|
||||
- (void) generateSPSandPPS {
|
||||
NSData* config = _encoder.getConfigData;
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
avcCHeader avcC((const BYTE*)[config bytes], [config length]);
|
||||
SeqParamSet seqParams;
|
||||
seqParams.Parse(avcC.sps());
|
||||
|
||||
NSData* spsData = [NSData dataWithBytes:avcC.sps()->Start() length:avcC.sps()->Length()];
|
||||
NSData *ppsData = [NSData dataWithBytes:avcC.pps()->Start() length:avcC.pps()->Length()];
|
||||
|
||||
_spsData = [NSMutableData dataWithCapacity:avcC.sps()->Length()+_naluStartCode.length];
|
||||
_ppsData = [NSMutableData dataWithCapacity:avcC.pps()->Length()+_naluStartCode.length];
|
||||
|
||||
[_spsData appendData:_naluStartCode];
|
||||
[_spsData appendData:spsData];
|
||||
[_ppsData appendData:_naluStartCode];
|
||||
[_ppsData appendData:ppsData];
|
||||
|
||||
_videoSPSandPPS = [NSMutableData dataWithCapacity:avcC.sps()->Length() + avcC.pps()->Length() + _naluStartCode.length * 2];
|
||||
[_videoSPSandPPS appendData:_naluStartCode];
|
||||
[_videoSPSandPPS appendData:spsData];
|
||||
[_videoSPSandPPS appendData:_naluStartCode];
|
||||
[_videoSPSandPPS appendData:ppsData];
|
||||
}
|
||||
|
||||
- (void)setVideoBitRate:(NSInteger)videoBitRate{
|
||||
_currentVideoBitRate = videoBitRate;
|
||||
_encoder.bitrate = _currentVideoBitRate;
|
||||
}
|
||||
|
||||
- (NSInteger)videoBitRate{
|
||||
return _currentVideoBitRate;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate{
|
||||
_h264Delegate = delegate;
|
||||
}
|
||||
|
||||
- (void)encodeVideoData:(CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp {
|
||||
|
||||
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
||||
CMVideoFormatDescriptionRef videoInfo = NULL;
|
||||
CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo);
|
||||
|
||||
CMTime frameTime = CMTimeMake(timeStamp, 1000);
|
||||
CMTime duration = CMTimeMake(1, (int32_t)_configuration.videoFrameRate);
|
||||
CMSampleTimingInfo timing = {duration, frameTime, kCMTimeInvalid};
|
||||
|
||||
CMSampleBufferRef sampleBuffer = NULL;
|
||||
CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, YES, NULL, NULL, videoInfo, &timing, &sampleBuffer);
|
||||
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
||||
[_encoder encodeFrame:sampleBuffer];
|
||||
CFRelease(videoInfo);
|
||||
CFRelease(sampleBuffer);
|
||||
|
||||
frameCount++;
|
||||
}
|
||||
|
||||
- (void)addOrphanedFramesFromArray:(NSArray*)frames {
|
||||
for (NSData *data in frames) {
|
||||
unsigned char* pNal = (unsigned char*)[data bytes];
|
||||
int idc = pNal[0] & 0x60;
|
||||
int naltype = pNal[0] & 0x1f;
|
||||
if (idc == 0 && naltype == 6) { // SEI
|
||||
[self.orphanedSEIFrames addObject:data];
|
||||
} else {
|
||||
[self.orphanedFrames addObject:data];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeVideoFrames:(NSArray*)frames pts:(CMTime)pts {
|
||||
NSMutableArray *totalFrames = [NSMutableArray array];
|
||||
if (self.orphanedSEIFrames.count > 0) {
|
||||
[totalFrames addObjectsFromArray:self.orphanedSEIFrames];
|
||||
[self.orphanedSEIFrames removeAllObjects];
|
||||
}
|
||||
[totalFrames addObjectsFromArray:frames];
|
||||
|
||||
NSMutableData *aggregateFrameData = [NSMutableData data];
|
||||
BOOL hasKeyframe = NO;
|
||||
|
||||
for (NSData *data in totalFrames) {
|
||||
unsigned char* pNal = (unsigned char*)[data bytes];
|
||||
int idc = pNal[0] & 0x60;
|
||||
int naltype = pNal[0] & 0x1f;
|
||||
NSData *videoData = nil;
|
||||
|
||||
if (idc == 0 && naltype == 6) { // SEI
|
||||
_sei = [NSMutableData dataWithData:data];
|
||||
continue;
|
||||
} else if (naltype == 5) { // IDR
|
||||
hasKeyframe = YES;
|
||||
NSMutableData *IDRData = [NSMutableData dataWithData:_videoSPSandPPS];
|
||||
if (_sei) {
|
||||
[IDRData appendData:_naluStartCode];
|
||||
[IDRData appendData:_sei];
|
||||
_sei = nil;
|
||||
}
|
||||
[IDRData appendData:_naluStartCode];
|
||||
[IDRData appendData:data];
|
||||
videoData = IDRData;
|
||||
} else {
|
||||
NSMutableData *regularData = [NSMutableData dataWithData:_naluStartCode];
|
||||
[regularData appendData:data];
|
||||
videoData = regularData;
|
||||
}
|
||||
[aggregateFrameData appendData:videoData];
|
||||
|
||||
LFVideoFrame *videoFrame = [LFVideoFrame new];
|
||||
const char *dataBuffer = (const char *)aggregateFrameData.bytes;
|
||||
videoFrame.data = [NSMutableData dataWithBytes:dataBuffer + _naluStartCode.length length:aggregateFrameData.length - _naluStartCode.length];
|
||||
videoFrame.timestamp = pts.value;
|
||||
videoFrame.isKeyFrame = (naltype == 5);
|
||||
videoFrame.sps = _spsData;
|
||||
videoFrame.pps = _ppsData;
|
||||
|
||||
if(self.h264Delegate && [self.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]){
|
||||
[self.h264Delegate videoEncoder:self videoFrame:videoFrame];
|
||||
}
|
||||
}
|
||||
|
||||
if (self->enabledWriteVideoFile) {
|
||||
fwrite(aggregateFrameData.bytes, 1, aggregateFrameData.length, self->fp);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) incomingVideoFrames:(NSArray*)frames ptsValue:(CMTimeValue)ptsValue {
|
||||
if (ptsValue == 0) {
|
||||
[self addOrphanedFramesFromArray:frames];
|
||||
return;
|
||||
}
|
||||
if (!_videoSPSandPPS) {
|
||||
[self generateSPSandPPS];
|
||||
}
|
||||
CMTime pts = CMTimeMake(ptsValue, _timescale);
|
||||
if (self.orphanedFrames.count > 0) {
|
||||
CMTime ptsDiff = CMTimeSubtract(pts, _lastPTS);
|
||||
NSUInteger orphanedFramesCount = self.orphanedFrames.count;
|
||||
// NSLog(@"lastPTS before first orphaned frame: %lld", _lastPTS.value);
|
||||
for (NSData *frame in self.orphanedFrames) {
|
||||
CMTime fakePTSDiff = CMTimeMultiplyByFloat64(ptsDiff, 1.0/(orphanedFramesCount + 1));
|
||||
CMTime fakePTS = CMTimeAdd(_lastPTS, fakePTSDiff);
|
||||
// NSLog(@"orphan frame fakePTS: %lld", fakePTS.value);
|
||||
[self writeVideoFrames:@[frame] pts:fakePTS];
|
||||
}
|
||||
// NSLog(@"pts after orphaned frame: %lld", pts.value);
|
||||
[self.orphanedFrames removeAllObjects];
|
||||
}
|
||||
|
||||
[self writeVideoFrames:frames pts:pts];
|
||||
_lastPTS = pts;
|
||||
}
|
||||
|
||||
|
||||
- (void) dealloc {
|
||||
[_encoder shutdown];
|
||||
}
|
||||
|
||||
- (void)shutdown {
|
||||
[_encoder encodeWithBlock:nil onParams:nil];
|
||||
}
|
||||
|
||||
- (void)initForFilePath {
|
||||
char *path = [self GetFilePathByfileName:"IOSCamDemo.h264"];
|
||||
NSLog(@"%s", path);
|
||||
self->fp = fopen(path, "wb");
|
||||
}
|
||||
|
||||
- (char *)GetFilePathByfileName:(char *)filename {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *strName = [NSString stringWithFormat:@"%s", filename];
|
||||
|
||||
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:strName];
|
||||
|
||||
NSUInteger len = [writablePath length];
|
||||
|
||||
char *filepath = (char *)malloc(sizeof(char) * (len + 1));
|
||||
|
||||
[writablePath getCString:filepath maxLength:len + 1 encoding:[NSString defaultCStringEncoding]];
|
||||
|
||||
return filepath;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
- (instancetype)initWithAudioStreamConfiguration:(LFLiveAudioConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"USE LFHardwareAudioEncoder");
|
||||
_configuration = configuration;
|
||||
}
|
||||
return self;
|
||||
@@ -108,8 +109,6 @@
|
||||
}
|
||||
};
|
||||
OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);
|
||||
|
||||
|
||||
if (result != noErr) return NO;
|
||||
|
||||
return YES;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoStreamConfiguration:(LFLiveVideoConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"USE LFHardwareVideoEncoder");
|
||||
_configuration = configuration;
|
||||
[self initCompressionSession];
|
||||
|
||||
@@ -225,7 +226,7 @@ static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatu
|
||||
}
|
||||
|
||||
- (void)initForFilePath {
|
||||
char *path = [self GetFilePathByfileName:"IOSCamDemo.h264"];
|
||||
char *path = [self GetFilePathByfileName:"IOSCamDemo_HW.h264"];
|
||||
NSLog(@"%s", path);
|
||||
self->fp = fopen(path, "wb");
|
||||
}
|
||||
|
||||
@@ -90,7 +90,4 @@ typedef NS_ENUM (NSUInteger, LFLiveVideoQuality){
|
||||
///< ≈sde3分辨率
|
||||
@property (nonatomic, assign, readonly) NSString *avSessionPreset;
|
||||
|
||||
///< 是否裁剪
|
||||
@property (nonatomic, assign, readonly) BOOL isClipVideo;
|
||||
|
||||
@end
|
||||
|
||||
@@ -213,10 +213,6 @@
|
||||
return sessionPreset;
|
||||
}
|
||||
|
||||
- (BOOL)isClipVideo {
|
||||
return self.sessionPreset == LFCaptureSessionPreset360x640 ? YES : NO;
|
||||
}
|
||||
|
||||
#pragma mark -- encoder
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:[NSValue valueWithCGSize:self.videoSize] forKey:@"videoSize"];
|
||||
@@ -248,7 +244,6 @@
|
||||
@(self.videoBitRate),
|
||||
@(self.videoMaxBitRate),
|
||||
@(self.videoMinBitRate),
|
||||
@(self.isClipVideo),
|
||||
self.avSessionPreset,
|
||||
@(self.sessionPreset),
|
||||
@(self.landscape), ];
|
||||
@@ -274,7 +269,6 @@
|
||||
object.videoBitRate == self.videoBitRate &&
|
||||
object.videoMaxBitRate == self.videoMaxBitRate &&
|
||||
object.videoMinBitRate == self.videoMinBitRate &&
|
||||
object.isClipVideo == self.isClipVideo &&
|
||||
[object.avSessionPreset isEqualToString:self.avSessionPreset] &&
|
||||
object.sessionPreset == self.sessionPreset &&
|
||||
object.landscape == self.landscape;
|
||||
@@ -297,7 +291,6 @@
|
||||
[desc appendFormat:@" videoBitRate:%zi", self.videoBitRate];
|
||||
[desc appendFormat:@" videoMaxBitRate:%zi", self.videoMaxBitRate];
|
||||
[desc appendFormat:@" videoMinBitRate:%zi", self.videoMinBitRate];
|
||||
[desc appendFormat:@" isClipVideo:%zi", self.isClipVideo];
|
||||
[desc appendFormat:@" avSessionPreset:%@", self.avSessionPreset];
|
||||
[desc appendFormat:@" sessionPreset:%zi", self.sessionPreset];
|
||||
[desc appendFormat:@" landscape:%zi", self.landscape];
|
||||
|
||||
Executable
+1037
File diff suppressed because it is too large
Load Diff
Executable
+180
@@ -0,0 +1,180 @@
|
||||
#ifndef __AMF_H__
|
||||
#define __AMF_H__
|
||||
/*
|
||||
* Copyright (C) 2005-2008 Team XBMC
|
||||
* http://www.xbmc.org
|
||||
* Copyright (C) 2008-2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
AMF_NUMBER = 0,
|
||||
AMF_BOOLEAN,
|
||||
AMF_STRING,
|
||||
AMF_OBJECT,
|
||||
AMF_MOVIECLIP, /* reserved, not used */
|
||||
AMF_NULL,
|
||||
AMF_UNDEFINED,
|
||||
AMF_REFERENCE,
|
||||
AMF_ECMA_ARRAY,
|
||||
AMF_OBJECT_END,
|
||||
AMF_STRICT_ARRAY,
|
||||
AMF_DATE,
|
||||
AMF_LONG_STRING,
|
||||
AMF_UNSUPPORTED,
|
||||
AMF_RECORDSET, /* reserved, not used */
|
||||
AMF_XML_DOC,
|
||||
AMF_TYPED_OBJECT,
|
||||
AMF_AVMPLUS, /* switch to AMF3 */
|
||||
AMF_INVALID = 0xff
|
||||
} AMFDataType;
|
||||
|
||||
typedef enum {
|
||||
AMF3_UNDEFINED = 0,
|
||||
AMF3_NULL,
|
||||
AMF3_FALSE,
|
||||
AMF3_TRUE,
|
||||
AMF3_INTEGER,
|
||||
AMF3_DOUBLE,
|
||||
AMF3_STRING,
|
||||
AMF3_XML_DOC,
|
||||
AMF3_DATE,
|
||||
AMF3_ARRAY,
|
||||
AMF3_OBJECT,
|
||||
AMF3_XML,
|
||||
AMF3_BYTE_ARRAY
|
||||
} AMF3DataType;
|
||||
|
||||
typedef struct AVal {
|
||||
char *av_val;
|
||||
int av_len;
|
||||
} AVal;
|
||||
#define AVC(str) \
|
||||
{ str, sizeof(str) - 1 }
|
||||
#define AVMATCH(a1, a2) \
|
||||
((a1)->av_len == (a2)->av_len && \
|
||||
!memcmp((a1)->av_val, (a2)->av_val, (a1)->av_len))
|
||||
|
||||
struct AMFObjectProperty;
|
||||
|
||||
typedef struct AMFObject {
|
||||
int o_num;
|
||||
struct AMFObjectProperty *o_props;
|
||||
} AMFObject;
|
||||
|
||||
typedef struct AMFObjectProperty {
|
||||
AVal p_name;
|
||||
AMFDataType p_type;
|
||||
union {
|
||||
double p_number;
|
||||
AVal p_aval;
|
||||
AMFObject p_object;
|
||||
} p_vu;
|
||||
int16_t p_UTCoffset;
|
||||
} AMFObjectProperty;
|
||||
|
||||
char *AMF_EncodeString(char *output, char *outend, const AVal *str);
|
||||
char *AMF_EncodeNumber(char *output, char *outend, double dVal);
|
||||
char *AMF_EncodeInt16(char *output, char *outend, short nVal);
|
||||
char *AMF_EncodeInt24(char *output, char *outend, int nVal);
|
||||
char *AMF_EncodeInt32(char *output, char *outend, int nVal);
|
||||
char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
|
||||
|
||||
/* Shortcuts for AMFProp_Encode */
|
||||
char *AMF_EncodeNamedString(char *output, char *outend, const AVal *name,
|
||||
const AVal *value);
|
||||
char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal *name,
|
||||
double dVal);
|
||||
char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *name,
|
||||
int bVal);
|
||||
|
||||
unsigned short AMF_DecodeInt16(const char *data);
|
||||
unsigned int AMF_DecodeInt24(const char *data);
|
||||
unsigned int AMF_DecodeInt32(const char *data);
|
||||
void AMF_DecodeString(const char *data, AVal *str);
|
||||
void AMF_DecodeLongString(const char *data, AVal *str);
|
||||
int AMF_DecodeBoolean(const char *data);
|
||||
double AMF_DecodeNumber(const char *data);
|
||||
|
||||
char *AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd);
|
||||
int AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName);
|
||||
int AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
|
||||
int nArrayLen, int bDecodeName);
|
||||
int AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize,
|
||||
int bDecodeName);
|
||||
void AMF_Dump(AMFObject *obj);
|
||||
void AMF_Reset(AMFObject *obj);
|
||||
|
||||
void AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop);
|
||||
int AMF_CountProp(AMFObject *obj);
|
||||
AMFObjectProperty *AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex);
|
||||
|
||||
AMFDataType AMFProp_GetType(AMFObjectProperty *prop);
|
||||
void AMFProp_SetNumber(AMFObjectProperty *prop, double dval);
|
||||
void AMFProp_SetBoolean(AMFObjectProperty *prop, int bflag);
|
||||
void AMFProp_SetString(AMFObjectProperty *prop, AVal *str);
|
||||
void AMFProp_SetObject(AMFObjectProperty *prop, AMFObject *obj);
|
||||
|
||||
void AMFProp_GetName(AMFObjectProperty *prop, AVal *name);
|
||||
void AMFProp_SetName(AMFObjectProperty *prop, AVal *name);
|
||||
double AMFProp_GetNumber(AMFObjectProperty *prop);
|
||||
int AMFProp_GetBoolean(AMFObjectProperty *prop);
|
||||
void AMFProp_GetString(AMFObjectProperty *prop, AVal *str);
|
||||
void AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj);
|
||||
|
||||
int AMFProp_IsValid(AMFObjectProperty *prop);
|
||||
|
||||
char *AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd);
|
||||
int AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
|
||||
int bDecodeName);
|
||||
int AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
|
||||
int bDecodeName);
|
||||
|
||||
void AMFProp_Dump(AMFObjectProperty *prop);
|
||||
void AMFProp_Reset(AMFObjectProperty *prop);
|
||||
|
||||
typedef struct AMF3ClassDef {
|
||||
AVal cd_name;
|
||||
char cd_externalizable;
|
||||
char cd_dynamic;
|
||||
int cd_num;
|
||||
AVal *cd_props;
|
||||
} AMF3ClassDef;
|
||||
|
||||
void AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop);
|
||||
AVal *AMF3CD_GetProp(AMF3ClassDef *cd, int idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __AMF_H__ */
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2008 Team XBMC
|
||||
* http://www.xbmc.org
|
||||
* Copyright (C) 2008-2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifndef __BYTES_H__
|
||||
#define __BYTES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Windows is little endian only */
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#define __FLOAT_WORD_ORDER __BYTE_ORDER
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
|
||||
#else /* !_WIN32 */
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#if defined(BYTE_ORDER) && !defined(__BYTE_ORDER)
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#endif
|
||||
|
||||
#if defined(BIG_ENDIAN) && !defined(__BIG_ENDIAN)
|
||||
#define __BIG_ENDIAN BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined(LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN)
|
||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
/* define default endianness */
|
||||
#ifndef __LITTLE_ENDIAN
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#endif
|
||||
|
||||
#ifndef __BIG_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#endif
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
#warning "Byte order not defined on your system, assuming little endian!"
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/* ok, we assume to have the same float word order and byte order if float word
|
||||
* order is not defined */
|
||||
#ifndef __FLOAT_WORD_ORDER
|
||||
#warning "Float word order not defined, assuming the same as byte order!"
|
||||
#define __FLOAT_WORD_ORDER __BYTE_ORDER
|
||||
#endif
|
||||
|
||||
#if !defined(__BYTE_ORDER) || !defined(__FLOAT_WORD_ORDER)
|
||||
#error "Undefined byte or float word order!"
|
||||
#endif
|
||||
|
||||
#if __FLOAT_WORD_ORDER != __BIG_ENDIAN && __FLOAT_WORD_ORDER != __LITTLE_ENDIAN
|
||||
#error "Unknown/unsupported float word order!"
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
|
||||
#error "Unknown/unsupported byte order!"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Executable
+345
@@ -0,0 +1,345 @@
|
||||
/* RTMPDump - Diffie-Hellmann Key Exchange
|
||||
* Copyright (C) 2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef USE_POLARSSL
|
||||
#include <polarssl/dhm.h>
|
||||
typedef mpi *MP_t;
|
||||
#define MP_new(m) \
|
||||
m = malloc(sizeof(mpi)); \
|
||||
mpi_init(m, NULL)
|
||||
#define MP_set_w(mpi, w) mpi_lset(mpi, w)
|
||||
#define MP_cmp(u, v) mpi_cmp_mpi(u, v)
|
||||
#define MP_set(u, v) mpi_copy(u, v)
|
||||
#define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w)
|
||||
#define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1)
|
||||
#define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL)
|
||||
#define MP_free(mpi) \
|
||||
mpi_free(mpi, NULL); \
|
||||
free(mpi)
|
||||
#define MP_gethex(u, hex, res) \
|
||||
MP_new(u); \
|
||||
res = mpi_read_string(u, 16, hex) == 0
|
||||
#define MP_bytes(u) mpi_size(u)
|
||||
#define MP_setbin(u, buf, len) mpi_write_binary(u, buf, len)
|
||||
#define MP_getbin(u, buf, len) \
|
||||
MP_new(u); \
|
||||
mpi_read_binary(u, buf, len)
|
||||
|
||||
typedef struct MDH {
|
||||
MP_t p;
|
||||
MP_t g;
|
||||
MP_t pub_key;
|
||||
MP_t priv_key;
|
||||
long length;
|
||||
dhm_context ctx;
|
||||
} MDH;
|
||||
|
||||
#define MDH_new() calloc(1, sizeof(MDH))
|
||||
#define MDH_free(vp) \
|
||||
{ \
|
||||
MDH *dh = vp; \
|
||||
dhm_free(&dh->ctx); \
|
||||
MP_free(dh->p); \
|
||||
MP_free(dh->g); \
|
||||
MP_free(dh->pub_key); \
|
||||
MP_free(dh->priv_key); \
|
||||
free(dh); \
|
||||
}
|
||||
|
||||
static int MDH_generate_key(MDH *dh) {
|
||||
unsigned char out[2];
|
||||
MP_set(&dh->ctx.P, dh->p);
|
||||
MP_set(&dh->ctx.G, dh->g);
|
||||
dh->ctx.len = 128;
|
||||
dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
|
||||
MP_new(dh->pub_key);
|
||||
MP_new(dh->priv_key);
|
||||
MP_set(dh->pub_key, &dh->ctx.GX);
|
||||
MP_set(dh->priv_key, &dh->ctx.X);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) {
|
||||
int n = len;
|
||||
MP_set(&dh->ctx.GY, pub);
|
||||
dhm_calc_secret(&dh->ctx, secret, &n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif defined(USE_GNUTLS)
|
||||
#include <gcrypt.h>
|
||||
typedef gcry_mpi_t MP_t;
|
||||
#define MP_new(m) m = gcry_mpi_new(1)
|
||||
#define MP_set_w(mpi, w) gcry_mpi_set_ui(mpi, w)
|
||||
#define MP_cmp(u, v) gcry_mpi_cmp(u, v)
|
||||
#define MP_set(u, v) gcry_mpi_set(u, v)
|
||||
#define MP_sub_w(mpi, w) gcry_mpi_sub_ui(mpi, mpi, w)
|
||||
#define MP_cmp_1(mpi) gcry_mpi_cmp_ui(mpi, 1)
|
||||
#define MP_modexp(r, y, q, p) gcry_mpi_powm(r, y, q, p)
|
||||
#define MP_free(mpi) gcry_mpi_release(mpi)
|
||||
#define MP_gethex(u, hex, res) \
|
||||
res = (gcry_mpi_scan(&u, GCRYMPI_FMT_HEX, hex, 0, 0) == 0)
|
||||
#define MP_bytes(u) (gcry_mpi_get_nbits(u) + 7) / 8
|
||||
#define MP_setbin(u, buf, len) \
|
||||
gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, u)
|
||||
#define MP_getbin(u, buf, len) \
|
||||
gcry_mpi_scan(&u, GCRYMPI_FMT_USG, buf, len, NULL)
|
||||
|
||||
typedef struct MDH {
|
||||
MP_t p;
|
||||
MP_t g;
|
||||
MP_t pub_key;
|
||||
MP_t priv_key;
|
||||
long length;
|
||||
} MDH;
|
||||
|
||||
#define MDH_new() calloc(1, sizeof(MDH))
|
||||
#define MDH_free(dh) \
|
||||
do { \
|
||||
MP_free(((MDH *)(dh))->p); \
|
||||
MP_free(((MDH *)(dh))->g); \
|
||||
MP_free(((MDH *)(dh))->pub_key); \
|
||||
MP_free(((MDH *)(dh))->priv_key); \
|
||||
free(dh); \
|
||||
} while (0)
|
||||
|
||||
extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p);
|
||||
extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p);
|
||||
|
||||
#define MDH_generate_key(dh) \
|
||||
(dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p))
|
||||
static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) {
|
||||
MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p);
|
||||
if (sec) {
|
||||
MP_setbin(sec, secret, len);
|
||||
MP_free(sec);
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else /* USE_OPENSSL */
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
|
||||
typedef BIGNUM *MP_t;
|
||||
#define MP_new(m) m = BN_new()
|
||||
#define MP_set_w(mpi, w) BN_set_word(mpi, w)
|
||||
#define MP_cmp(u, v) BN_cmp(u, v)
|
||||
#define MP_set(u, v) BN_copy(u, v)
|
||||
#define MP_sub_w(mpi, w) BN_sub_word(mpi, w)
|
||||
#define MP_cmp_1(mpi) BN_cmp(mpi, BN_value_one())
|
||||
#define MP_modexp(r, y, q, p) \
|
||||
do { \
|
||||
BN_CTX *ctx = BN_CTX_new(); \
|
||||
BN_mod_exp(r, y, q, p, ctx); \
|
||||
BN_CTX_free(ctx); \
|
||||
} while (0)
|
||||
#define MP_free(mpi) BN_free(mpi)
|
||||
#define MP_gethex(u, hex, res) res = BN_hex2bn(&u, hex)
|
||||
#define MP_bytes(u) BN_num_bytes(u)
|
||||
#define MP_setbin(u, buf, len) BN_bn2bin(u, buf)
|
||||
#define MP_getbin(u, buf, len) u = BN_bin2bn(buf, len, 0)
|
||||
|
||||
#define MDH DH
|
||||
#define MDH_new() DH_new()
|
||||
#define MDH_free(dh) DH_free(dh)
|
||||
#define MDH_generate_key(dh) DH_generate_key(dh)
|
||||
#define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh)
|
||||
|
||||
#endif
|
||||
|
||||
#include "dhgroups.h"
|
||||
#include "log.h"
|
||||
|
||||
/* RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt */
|
||||
static int isValidPublicKey(MP_t y, MP_t p, MP_t q) {
|
||||
int ret = TRUE;
|
||||
MP_t bn;
|
||||
assert(y);
|
||||
|
||||
MP_new(bn);
|
||||
assert(bn);
|
||||
|
||||
/* y must lie in [2,p-1] */
|
||||
MP_set_w(bn, 1);
|
||||
if (MP_cmp(y, bn) < 0) {
|
||||
RTMP_Log(RTMP_LOGERROR, "DH public key must be at least 2");
|
||||
ret = FALSE;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* bn = p-2 */
|
||||
MP_set(bn, p);
|
||||
MP_sub_w(bn, 1);
|
||||
if (MP_cmp(y, bn) > 0) {
|
||||
RTMP_Log(RTMP_LOGERROR, "DH public key must be at most p-2");
|
||||
ret = FALSE;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Verify with Sophie-Germain prime
|
||||
*
|
||||
* This is a nice test to make sure the public key position is calculated
|
||||
* correctly. This test will fail in about 50% of the cases if applied to
|
||||
* random data.
|
||||
*/
|
||||
if (q) {
|
||||
/* y must fulfill y^q mod p = 1 */
|
||||
MP_modexp(bn, y, q, p);
|
||||
|
||||
if (MP_cmp_1(bn) != 0) {
|
||||
RTMP_Log(RTMP_LOGWARNING, "DH public key does not fulfill y^q mod p = 1");
|
||||
}
|
||||
}
|
||||
|
||||
failed:
|
||||
MP_free(bn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static MDH *DHInit(int nKeyBits) {
|
||||
size_t res;
|
||||
MDH *dh = MDH_new();
|
||||
|
||||
if (!dh)
|
||||
goto failed;
|
||||
|
||||
MP_new(dh->g);
|
||||
|
||||
if (!dh->g)
|
||||
goto failed;
|
||||
|
||||
MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */
|
||||
if (!res) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
MP_set_w(dh->g, 2); /* base 2 */
|
||||
|
||||
dh->length = nKeyBits;
|
||||
return dh;
|
||||
|
||||
failed:
|
||||
if (dh)
|
||||
MDH_free(dh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DHGenerateKey(MDH *dh) {
|
||||
size_t res = 0;
|
||||
if (!dh)
|
||||
return 0;
|
||||
|
||||
while (!res) {
|
||||
MP_t q1 = NULL;
|
||||
|
||||
if (!MDH_generate_key(dh))
|
||||
return 0;
|
||||
|
||||
MP_gethex(q1, Q1024, res);
|
||||
assert(res);
|
||||
|
||||
res = isValidPublicKey(dh->pub_key, dh->p, q1);
|
||||
if (!res) {
|
||||
MP_free(dh->pub_key);
|
||||
MP_free(dh->priv_key);
|
||||
dh->pub_key = dh->priv_key = 0;
|
||||
}
|
||||
|
||||
MP_free(q1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fill pubkey with the public key in BIG ENDIAN order
|
||||
* 00 00 00 00 00 x1 x2 x3 .....
|
||||
*/
|
||||
|
||||
static int DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen) {
|
||||
int len;
|
||||
if (!dh || !dh->pub_key)
|
||||
return 0;
|
||||
|
||||
len = MP_bytes(dh->pub_key);
|
||||
if (len <= 0 || len > (int)nPubkeyLen)
|
||||
return 0;
|
||||
|
||||
memset(pubkey, 0, nPubkeyLen);
|
||||
MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0 /* unused */
|
||||
static int
|
||||
DHGetPrivateKey(MDH *dh, uint8_t *privkey, size_t nPrivkeyLen)
|
||||
{
|
||||
if (!dh || !dh->priv_key)
|
||||
return 0;
|
||||
|
||||
int len = MP_bytes(dh->priv_key);
|
||||
if (len <= 0 || len > (int) nPrivkeyLen)
|
||||
return 0;
|
||||
|
||||
memset(privkey, 0, nPrivkeyLen);
|
||||
MP_setbin(dh->priv_key, privkey + (nPrivkeyLen - len), len);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* computes the shared secret key from the private MDH value and the
|
||||
* other party's public key (pubkey)
|
||||
*/
|
||||
static int DHComputeSharedSecretKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen,
|
||||
uint8_t *secret) {
|
||||
MP_t q1 = NULL, pubkeyBn = NULL;
|
||||
size_t len;
|
||||
int res;
|
||||
|
||||
if (!dh || !secret || nPubkeyLen >= INT_MAX)
|
||||
return -1;
|
||||
|
||||
MP_getbin(pubkeyBn, pubkey, nPubkeyLen);
|
||||
if (!pubkeyBn)
|
||||
return -1;
|
||||
|
||||
MP_gethex(q1, Q1024, len);
|
||||
assert(len);
|
||||
|
||||
if (isValidPublicKey(pubkeyBn, dh->p, q1))
|
||||
res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh);
|
||||
else
|
||||
res = -1;
|
||||
|
||||
MP_free(q1);
|
||||
MP_free(pubkeyBn);
|
||||
|
||||
return res;
|
||||
}
|
||||
Executable
+198
@@ -0,0 +1,198 @@
|
||||
/* librtmp - Diffie-Hellmann Key Exchange
|
||||
* Copyright (C) 2009 Andrej Stepanchuk
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
/* from RFC 3526, see http://www.ietf.org/rfc/rfc3526.txt */
|
||||
|
||||
/* 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */
|
||||
#define P768 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */
|
||||
#define P1024 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
|
||||
"FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* Group morder largest prime factor: */
|
||||
#define Q1024 \
|
||||
"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
|
||||
"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
|
||||
"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
|
||||
"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
|
||||
"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
|
||||
"FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */
|
||||
#define P1536 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
|
||||
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */
|
||||
#define P2048 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
|
||||
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
|
||||
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
|
||||
"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */
|
||||
#define P3072 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
|
||||
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
|
||||
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
|
||||
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
|
||||
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
|
||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
|
||||
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
|
||||
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
|
||||
"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */
|
||||
#define P4096 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
|
||||
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
|
||||
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
|
||||
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
|
||||
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
|
||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
|
||||
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
|
||||
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
|
||||
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
|
||||
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
|
||||
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
|
||||
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
|
||||
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
|
||||
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \
|
||||
"FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */
|
||||
#define P6144 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
|
||||
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
|
||||
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
|
||||
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
|
||||
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
|
||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
|
||||
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
|
||||
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
|
||||
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
|
||||
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
|
||||
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
|
||||
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
|
||||
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
|
||||
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
|
||||
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
|
||||
"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
|
||||
"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
|
||||
"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
|
||||
"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
|
||||
"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
|
||||
"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
|
||||
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
|
||||
"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
|
||||
"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
|
||||
"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"
|
||||
|
||||
/* 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */
|
||||
#define P8192 \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
|
||||
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
|
||||
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
|
||||
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
|
||||
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
|
||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
|
||||
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
|
||||
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
|
||||
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
|
||||
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
|
||||
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
|
||||
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
|
||||
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
|
||||
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
|
||||
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
|
||||
"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
|
||||
"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
|
||||
"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
|
||||
"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
|
||||
"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
|
||||
"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
|
||||
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
|
||||
"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
|
||||
"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
|
||||
"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \
|
||||
"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \
|
||||
"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \
|
||||
"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \
|
||||
"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \
|
||||
"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \
|
||||
"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \
|
||||
"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \
|
||||
"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \
|
||||
"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \
|
||||
"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \
|
||||
"60C980DD98EDD3DFFFFFFFFFFFFFFFFF"
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#include "error.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void RTMPError_Alloc(RTMPError *error, size_t msg_size) {
|
||||
RTMPError_Free(error);
|
||||
|
||||
error->code = 0;
|
||||
error->message = (char *)malloc(msg_size + 1);
|
||||
memset(error->message, 0, msg_size);
|
||||
}
|
||||
|
||||
void RTMPError_Free(RTMPError *error) {
|
||||
if (error) {
|
||||
if (error->message) {
|
||||
free(error->message);
|
||||
error->message = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTMPError_Message(RTMPError *error, int code, const char *message) {
|
||||
RTMPError_Alloc(error, strlen(message));
|
||||
error->code = code;
|
||||
strcpy(error->message, message);
|
||||
}
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#ifndef __ERROR_H__
|
||||
#define __ERROR_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct RTMPError {
|
||||
int code;
|
||||
char *message;
|
||||
} RTMPError;
|
||||
|
||||
void RTMPError_Alloc(RTMPError *error, size_t msg_size);
|
||||
void RTMPError_Free(RTMPError *error);
|
||||
void RTMPError_Message(RTMPError *error, int code, const char *message);
|
||||
|
||||
// error defines
|
||||
enum {
|
||||
RTMPErrorUnknow = -1, // "Unknow error"
|
||||
RTMPErrorUnknowOption = -999, // "Unknown option %s"
|
||||
RTMPErrorAccessDNSFailed = -1000, // "Failed to access the DNS. (addr: %s)"
|
||||
RTMPErrorFailedToConnectSocket =
|
||||
-1001, // "Failed to connect socket. %d (%s)"
|
||||
RTMPErrorSocksNegotiationFailed = -1002, // "Socks negotiation failed"
|
||||
RTMPErrorFailedToCreateSocket =
|
||||
-1003, // "Failed to create socket. %d (%s)"
|
||||
RTMPErrorHandshakeFailed = -1004, // "Handshake failed"
|
||||
RTMPErrorRTMPConnectFailed = -1005, // "RTMP connect failed"
|
||||
RTMPErrorSendFailed = -1006, // "Send error %d (%s), (%d bytes)"
|
||||
RTMPErrorServerRequestedClose = -1007, // "RTMP server requested close"
|
||||
RTMPErrorNetStreamFailed = -1008, // "NetStream failed"
|
||||
RTMPErrorNetStreamPlayFailed = -1009, // "NetStream play failed"
|
||||
RTMPErrorNetStreamPlayStreamNotFound =
|
||||
-1010, // "NetStream play stream not found"
|
||||
RTMPErrorNetConnectionConnectInvalidApp =
|
||||
-1011, // "NetConnection connect invalip app"
|
||||
RTMPErrorSanityFailed =
|
||||
-1012, // "Sanity failed. Trying to send header of type: 0x%02X"
|
||||
RTMPErrorSocketClosedByPeer = -1013, // "RTMP socket closed by peer"
|
||||
RTMPErrorRTMPConnectStreamFailed = -1014, // "RTMP connect stream failed"
|
||||
RTMPErrorSocketTimeout = -1015, // "RTMP socket timeout"
|
||||
|
||||
// SSL errors
|
||||
RTMPErrorTLSConnectFailed = -1200, // "TLS_Connect failed"
|
||||
RTMPErrorNoSSLOrTLSSupport = -1201, // "No SSL/TLS support"
|
||||
};
|
||||
|
||||
#endif
|
||||
Executable
+1034
File diff suppressed because it is too large
Load Diff
Executable
+626
@@ -0,0 +1,626 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "http.h"
|
||||
#include "log.h"
|
||||
#include "rtmp_sys.h"
|
||||
|
||||
#ifdef CRYPTO
|
||||
#ifdef USE_POLARSSL
|
||||
#include <polarssl/sha2.h>
|
||||
#ifndef SHA256_DIGEST_LENGTH
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
#endif
|
||||
#define HMAC_CTX sha2_context
|
||||
#define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
|
||||
#define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
|
||||
#define HMAC_finish(ctx, dig, dlen) \
|
||||
dlen = SHA256_DIGEST_LENGTH; \
|
||||
sha2_hmac_finish(&ctx, dig)
|
||||
#define HMAC_close(ctx)
|
||||
#elif defined(USE_GNUTLS)
|
||||
#include <gcrypt.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#ifndef SHA256_DIGEST_LENGTH
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
#endif
|
||||
#define HMAC_CTX gcry_md_hd_t
|
||||
#define HMAC_setup(ctx, key, len) \
|
||||
gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); \
|
||||
gcry_md_setkey(ctx, key, len)
|
||||
#define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len)
|
||||
#define HMAC_finish(ctx, dig, dlen) \
|
||||
dlen = SHA256_DIGEST_LENGTH; \
|
||||
memcpy(dig, gcry_md_read(ctx, 0), dlen)
|
||||
#define HMAC_close(ctx) gcry_md_close(ctx)
|
||||
#else /* USE_OPENSSL */
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/rc4.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ssl.h>
|
||||
#define HMAC_setup(ctx, key, len) \
|
||||
HMAC_CTX_init(&ctx); \
|
||||
HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
|
||||
#define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
|
||||
#define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
|
||||
#define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
|
||||
#endif
|
||||
|
||||
extern void RTMP_TLS_Init();
|
||||
extern TLS_CTX RTMP_TLS_ctx;
|
||||
|
||||
#endif /* CRYPTO */
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#define AGENT "Mozilla/5.0"
|
||||
|
||||
HTTPResult
|
||||
HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb) {
|
||||
char *host, *path;
|
||||
char *p1, *p2;
|
||||
char hbuf[256];
|
||||
int port = 80;
|
||||
#ifdef CRYPTO
|
||||
int ssl = 0;
|
||||
#endif
|
||||
int hlen, flen = 0;
|
||||
int rc, i;
|
||||
int len_known;
|
||||
HTTPResult ret = HTTPRES_OK;
|
||||
// struct sockaddr_in sa;
|
||||
PILI_RTMPSockBuf sb = {0};
|
||||
|
||||
http->status = -1;
|
||||
|
||||
// memset(&sa, 0, sizeof(struct sockaddr_in));
|
||||
// sa.sin_family = AF_INET;
|
||||
|
||||
/* we only handle http here */
|
||||
if (strncasecmp(url, "http", 4))
|
||||
return HTTPRES_BAD_REQUEST;
|
||||
|
||||
if (url[4] == 's') {
|
||||
#ifdef CRYPTO
|
||||
ssl = 1;
|
||||
port = 443;
|
||||
if (!RTMP_TLS_ctx)
|
||||
RTMP_TLS_Init();
|
||||
#else
|
||||
return HTTPRES_BAD_REQUEST;
|
||||
#endif
|
||||
}
|
||||
|
||||
p1 = strchr(url + 4, ':');
|
||||
if (!p1 || strncmp(p1, "://", 3))
|
||||
return HTTPRES_BAD_REQUEST;
|
||||
|
||||
host = p1 + 3;
|
||||
path = strchr(host, '/');
|
||||
hlen = path - host;
|
||||
strncpy(hbuf, host, hlen);
|
||||
hbuf[hlen] = '\0';
|
||||
host = hbuf;
|
||||
p1 = strrchr(host, ':');
|
||||
if (p1) {
|
||||
*p1++ = '\0';
|
||||
port = atoi(p1);
|
||||
}
|
||||
|
||||
// sa.sin_addr.s_addr = inet_addr(host);
|
||||
// if (sa.sin_addr.s_addr == INADDR_NONE)
|
||||
// {
|
||||
// struct hostent *hp = gethostbyname(host);
|
||||
// if (!hp || !hp->h_addr)
|
||||
// return HTTPRES_LOST_CONNECTION;
|
||||
// sa.sin_addr = *(struct in_addr *)hp->h_addr;
|
||||
// }
|
||||
// sa.sin_port = htons(port);
|
||||
struct addrinfo hints = {0}, *ai, *cur_ai;
|
||||
char portstr[10];
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||
ret = getaddrinfo(host, portstr, &hints, &ai);
|
||||
if (ret != 0) {
|
||||
return HTTPRES_LOST_CONNECTION;
|
||||
}
|
||||
|
||||
cur_ai = ai;
|
||||
|
||||
sb.sb_socket = socket(cur_ai->ai_family,
|
||||
cur_ai->ai_socktype,
|
||||
cur_ai->ai_protocol);
|
||||
// sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sb.sb_socket == -1) {
|
||||
freeaddrinfo(ai);
|
||||
return HTTPRES_LOST_CONNECTION;
|
||||
}
|
||||
i = sprintf(sb.sb_buf,
|
||||
"GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferrer: %.*s\r\n",
|
||||
path, AGENT, host, (int)(path - url + 1), url);
|
||||
if (http->date[0])
|
||||
i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
|
||||
i += sprintf(sb.sb_buf + i, "\r\n");
|
||||
|
||||
if (cur_ai->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)cur_ai->ai_addr;
|
||||
in6->sin6_port = htons(port);
|
||||
}
|
||||
|
||||
if (connect(sb.sb_socket, cur_ai->ai_addr, cur_ai->ai_addrlen) < 0) {
|
||||
ret = HTTPRES_LOST_CONNECTION;
|
||||
goto leave;
|
||||
}
|
||||
#ifdef CRYPTO
|
||||
if (ssl) {
|
||||
#ifdef NO_SSL
|
||||
RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
|
||||
ret = HTTPRES_BAD_REQUEST;
|
||||
goto leave;
|
||||
#else
|
||||
TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
|
||||
TLS_setfd(sb.sb_ssl, sb.sb_socket);
|
||||
if ((i = TLS_connect(sb.sb_ssl)) < 0) {
|
||||
RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
|
||||
ret = HTTPRES_LOST_CONNECTION;
|
||||
goto leave;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
PILI_RTMPSockBuf_Send(&sb, sb.sb_buf, i);
|
||||
|
||||
/* set timeout */
|
||||
#define HTTP_TIMEOUT 5
|
||||
{
|
||||
SET_RCVTIMEO(tv, HTTP_TIMEOUT);
|
||||
if (setsockopt(sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) {
|
||||
RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
|
||||
__FUNCTION__, HTTP_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
sb.sb_size = 0;
|
||||
sb.sb_timedout = FALSE;
|
||||
if (PILI_RTMPSockBuf_Fill(&sb, HTTP_TIMEOUT) < 1) {
|
||||
ret = HTTPRES_LOST_CONNECTION;
|
||||
goto leave;
|
||||
}
|
||||
if (strncmp(sb.sb_buf, "HTTP/1", 6)) {
|
||||
ret = HTTPRES_BAD_REQUEST;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
p1 = strchr(sb.sb_buf, ' ');
|
||||
rc = atoi(p1 + 1);
|
||||
http->status = rc;
|
||||
|
||||
if (rc >= 300) {
|
||||
if (rc == 304) {
|
||||
ret = HTTPRES_OK_NOT_MODIFIED;
|
||||
goto leave;
|
||||
} else if (rc == 404)
|
||||
ret = HTTPRES_NOT_FOUND;
|
||||
else if (rc >= 500)
|
||||
ret = HTTPRES_SERVER_ERROR;
|
||||
else if (rc >= 400)
|
||||
ret = HTTPRES_BAD_REQUEST;
|
||||
else
|
||||
ret = HTTPRES_REDIRECTED;
|
||||
}
|
||||
|
||||
p1 = memchr(sb.sb_buf, '\n', sb.sb_size);
|
||||
if (!p1) {
|
||||
ret = HTTPRES_BAD_REQUEST;
|
||||
goto leave;
|
||||
}
|
||||
sb.sb_start = p1 + 1;
|
||||
sb.sb_size -= sb.sb_start - sb.sb_buf;
|
||||
|
||||
while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size))) {
|
||||
if (*sb.sb_start == '\r') {
|
||||
sb.sb_start += 2;
|
||||
sb.sb_size -= 2;
|
||||
break;
|
||||
} else if (!strncasecmp(sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1)) {
|
||||
flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
|
||||
} else if (!strncasecmp(sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1)) {
|
||||
*p2 = '\0';
|
||||
strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
|
||||
}
|
||||
p2 += 2;
|
||||
sb.sb_size -= p2 - sb.sb_start;
|
||||
sb.sb_start = p2;
|
||||
if (sb.sb_size < 1) {
|
||||
if (PILI_RTMPSockBuf_Fill(&sb, HTTP_TIMEOUT) < 1) {
|
||||
ret = HTTPRES_LOST_CONNECTION;
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
len_known = flen > 0;
|
||||
while ((!len_known || flen > 0) &&
|
||||
(sb.sb_size > 0 || PILI_RTMPSockBuf_Fill(&sb, HTTP_TIMEOUT) > 0)) {
|
||||
cb(sb.sb_start, 1, sb.sb_size, http->data);
|
||||
if (len_known)
|
||||
flen -= sb.sb_size;
|
||||
http->size += sb.sb_size;
|
||||
sb.sb_size = 0;
|
||||
}
|
||||
|
||||
if (flen > 0)
|
||||
ret = HTTPRES_LOST_CONNECTION;
|
||||
|
||||
leave:
|
||||
PILI_RTMPSockBuf_Close(&sb);
|
||||
freeaddrinfo(ai);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CRYPTO
|
||||
|
||||
#define CHUNK 16384
|
||||
|
||||
struct info {
|
||||
z_stream *zs;
|
||||
HMAC_CTX ctx;
|
||||
int first;
|
||||
int zlib;
|
||||
int size;
|
||||
};
|
||||
|
||||
static size_t
|
||||
swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||
struct info *i = stream;
|
||||
char *p = ptr;
|
||||
size_t len = size * nmemb;
|
||||
|
||||
if (i->first) {
|
||||
i->first = 0;
|
||||
/* compressed? */
|
||||
if (!strncmp(p, "CWS", 3)) {
|
||||
*p = 'F';
|
||||
i->zlib = 1;
|
||||
}
|
||||
HMAC_crunch(i->ctx, (unsigned char *)p, 8);
|
||||
p += 8;
|
||||
len -= 8;
|
||||
i->size = 8;
|
||||
}
|
||||
|
||||
if (i->zlib) {
|
||||
unsigned char out[CHUNK];
|
||||
i->zs->next_in = (unsigned char *)p;
|
||||
i->zs->avail_in = len;
|
||||
do {
|
||||
i->zs->avail_out = CHUNK;
|
||||
i->zs->next_out = out;
|
||||
inflate(i->zs, Z_NO_FLUSH);
|
||||
len = CHUNK - i->zs->avail_out;
|
||||
i->size += len;
|
||||
HMAC_crunch(i->ctx, out, len);
|
||||
} while (i->zs->avail_out == 0);
|
||||
} else {
|
||||
i->size += len;
|
||||
HMAC_crunch(i->ctx, (unsigned char *)p, len);
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static int tzoff;
|
||||
static int tzchecked;
|
||||
|
||||
#define JAN02_1980 318340800
|
||||
|
||||
static const char *monthtab[12] = {"Jan", "Feb", "Mar",
|
||||
"Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep",
|
||||
"Oct", "Nov", "Dec"};
|
||||
static const char *days[] =
|
||||
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
|
||||
/* Parse an HTTP datestamp into Unix time */
|
||||
static time_t
|
||||
make_unix_time(char *s) {
|
||||
struct tm time;
|
||||
int i, ysub = 1900, fmt = 0;
|
||||
char *month;
|
||||
char *n;
|
||||
time_t res;
|
||||
|
||||
if (s[3] != ' ') {
|
||||
fmt = 1;
|
||||
if (s[3] != ',')
|
||||
ysub = 0;
|
||||
}
|
||||
for (n = s; *n; ++n)
|
||||
if (*n == '-' || *n == ':')
|
||||
*n = ' ';
|
||||
|
||||
time.tm_mon = 0;
|
||||
n = strchr(s, ' ');
|
||||
if (fmt) {
|
||||
/* Day, DD-MMM-YYYY HH:MM:SS GMT */
|
||||
time.tm_mday = strtol(n + 1, &n, 0);
|
||||
month = n + 1;
|
||||
n = strchr(month, ' ');
|
||||
time.tm_year = strtol(n + 1, &n, 0);
|
||||
time.tm_hour = strtol(n + 1, &n, 0);
|
||||
time.tm_min = strtol(n + 1, &n, 0);
|
||||
time.tm_sec = strtol(n + 1, NULL, 0);
|
||||
} else {
|
||||
/* Unix ctime() format. Does not conform to HTTP spec. */
|
||||
/* Day MMM DD HH:MM:SS YYYY */
|
||||
month = n + 1;
|
||||
n = strchr(month, ' ');
|
||||
while (isspace(*n))
|
||||
n++;
|
||||
time.tm_mday = strtol(n, &n, 0);
|
||||
time.tm_hour = strtol(n + 1, &n, 0);
|
||||
time.tm_min = strtol(n + 1, &n, 0);
|
||||
time.tm_sec = strtol(n + 1, &n, 0);
|
||||
time.tm_year = strtol(n + 1, NULL, 0);
|
||||
}
|
||||
if (time.tm_year > 100)
|
||||
time.tm_year -= ysub;
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
if (!strncasecmp(month, monthtab[i], 3)) {
|
||||
time.tm_mon = i;
|
||||
break;
|
||||
}
|
||||
time.tm_isdst = 0; /* daylight saving is never in effect in GMT */
|
||||
|
||||
/* this is normally the value of extern int timezone, but some
|
||||
* braindead C libraries don't provide it.
|
||||
*/
|
||||
if (!tzchecked) {
|
||||
struct tm *tc;
|
||||
time_t then = JAN02_1980;
|
||||
tc = localtime(&then);
|
||||
tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
|
||||
tzchecked = 1;
|
||||
}
|
||||
res = mktime(&time);
|
||||
/* Unfortunately, mktime() assumes the input is in local time,
|
||||
* not GMT, so we have to correct it here.
|
||||
*/
|
||||
if (res != -1)
|
||||
res += tzoff;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Convert a Unix time to a network time string
|
||||
* Weekday, DD-MMM-YYYY HH:MM:SS GMT
|
||||
*/
|
||||
void strtime(time_t *t, char *s) {
|
||||
struct tm *tm;
|
||||
|
||||
tm = gmtime((time_t *)t);
|
||||
sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT",
|
||||
days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
|
||||
tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
|
||||
#define HEX2BIN(a) (((a)&0x40) ? ((a)&0xf) + 9 : ((a)&0xf))
|
||||
|
||||
int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
|
||||
int age) {
|
||||
FILE *f = NULL;
|
||||
char *path, date[64], cctim[64];
|
||||
long pos = 0;
|
||||
time_t ctim = -1, cnow;
|
||||
int i, got = 0, ret = 0;
|
||||
unsigned int hlen;
|
||||
struct info in = {0};
|
||||
struct HTTP_ctx http = {0};
|
||||
HTTPResult httpres;
|
||||
z_stream zs = {0};
|
||||
AVal home, hpre;
|
||||
|
||||
date[0] = '\0';
|
||||
#ifdef _WIN32
|
||||
#ifdef _XBOX
|
||||
hpre.av_val = "Q:";
|
||||
hpre.av_len = 2;
|
||||
home.av_val = "\\UserData";
|
||||
#else
|
||||
hpre.av_val = getenv("HOMEDRIVE");
|
||||
hpre.av_len = strlen(hpre.av_val);
|
||||
home.av_val = getenv("HOMEPATH");
|
||||
#endif
|
||||
#define DIRSEP "\\"
|
||||
|
||||
#else /* !_WIN32 */
|
||||
hpre.av_val = "";
|
||||
hpre.av_len = 0;
|
||||
home.av_val = getenv("HOME");
|
||||
#define DIRSEP "/"
|
||||
#endif
|
||||
if (!home.av_val)
|
||||
home.av_val = ".";
|
||||
home.av_len = strlen(home.av_val);
|
||||
|
||||
/* SWF hash info is cached in a fixed-format file.
|
||||
* url: <url of SWF file>
|
||||
* ctim: HTTP datestamp of when we last checked it.
|
||||
* date: HTTP datestamp of the SWF's last modification.
|
||||
* size: SWF size in hex
|
||||
* hash: SWF hash in hex
|
||||
*
|
||||
* These fields must be present in this order. All fields
|
||||
* besides URL are fixed size.
|
||||
*/
|
||||
path = malloc(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo"));
|
||||
sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
|
||||
|
||||
f = fopen(path, "r+");
|
||||
while (f) {
|
||||
char buf[4096], *file, *p;
|
||||
|
||||
file = strchr(url, '/');
|
||||
if (!file)
|
||||
break;
|
||||
file += 2;
|
||||
file = strchr(file, '/');
|
||||
if (!file)
|
||||
break;
|
||||
file++;
|
||||
hlen = file - url;
|
||||
p = strrchr(file, '/');
|
||||
if (p)
|
||||
file = p;
|
||||
else
|
||||
file--;
|
||||
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
char *r1;
|
||||
|
||||
got = 0;
|
||||
|
||||
if (strncmp(buf, "url: ", 5))
|
||||
continue;
|
||||
if (strncmp(buf + 5, url, hlen))
|
||||
continue;
|
||||
r1 = strrchr(buf, '/');
|
||||
i = strlen(r1);
|
||||
r1[--i] = '\0';
|
||||
if (strncmp(r1, file, i))
|
||||
continue;
|
||||
pos = ftell(f);
|
||||
while (got < 4 && fgets(buf, sizeof(buf), f)) {
|
||||
if (!strncmp(buf, "size: ", 6)) {
|
||||
*size = strtol(buf + 6, NULL, 16);
|
||||
got++;
|
||||
} else if (!strncmp(buf, "hash: ", 6)) {
|
||||
unsigned char *ptr = hash, *in = (unsigned char *)buf + 6;
|
||||
int l = strlen((char *)in) - 1;
|
||||
for (i = 0; i < l; i += 2)
|
||||
*ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i + 1]);
|
||||
got++;
|
||||
} else if (!strncmp(buf, "date: ", 6)) {
|
||||
buf[strlen(buf) - 1] = '\0';
|
||||
strncpy(date, buf + 6, sizeof(date));
|
||||
got++;
|
||||
} else if (!strncmp(buf, "ctim: ", 6)) {
|
||||
buf[strlen(buf) - 1] = '\0';
|
||||
ctim = make_unix_time(buf + 6);
|
||||
got++;
|
||||
} else if (!strncmp(buf, "url: ", 5))
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cnow = time(NULL);
|
||||
/* If we got a cache time, see if it's young enough to use directly */
|
||||
if (age && ctim > 0) {
|
||||
ctim = cnow - ctim;
|
||||
ctim /= 3600 * 24; /* seconds to days */
|
||||
if (ctim < age) /* ok, it's new enough */
|
||||
goto out;
|
||||
}
|
||||
|
||||
in.first = 1;
|
||||
HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30);
|
||||
inflateInit(&zs);
|
||||
in.zs = &zs;
|
||||
|
||||
http.date = date;
|
||||
http.data = ∈
|
||||
|
||||
httpres = HTTP_get(&http, url, swfcrunch);
|
||||
|
||||
inflateEnd(&zs);
|
||||
|
||||
if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED) {
|
||||
ret = -1;
|
||||
if (httpres == HTTPRES_LOST_CONNECTION)
|
||||
RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s",
|
||||
__FUNCTION__, url);
|
||||
else if (httpres == HTTPRES_NOT_FOUND)
|
||||
RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
|
||||
else
|
||||
RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
|
||||
__FUNCTION__, url, http.status);
|
||||
} else {
|
||||
if (got && pos)
|
||||
fseek(f, pos, SEEK_SET);
|
||||
else {
|
||||
char *q;
|
||||
if (!f)
|
||||
f = fopen(path, "w");
|
||||
if (!f) {
|
||||
int err = errno;
|
||||
RTMP_Log(RTMP_LOGERROR,
|
||||
"%s: couldn't open %s for writing, errno %d (%s)",
|
||||
__FUNCTION__, path, err, strerror(err));
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
q = strchr(url, '?');
|
||||
if (q)
|
||||
i = q - url;
|
||||
else
|
||||
i = strlen(url);
|
||||
|
||||
fprintf(f, "url: %.*s\n", i, url);
|
||||
}
|
||||
strtime(&cnow, cctim);
|
||||
fprintf(f, "ctim: %s\n", cctim);
|
||||
|
||||
if (!in.first) {
|
||||
HMAC_finish(in.ctx, hash, hlen);
|
||||
*size = in.size;
|
||||
|
||||
fprintf(f, "date: %s\n", date);
|
||||
fprintf(f, "size: %08x\n", in.size);
|
||||
fprintf(f, "hash: ");
|
||||
for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
|
||||
fprintf(f, "%02x", hash[i]);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
HMAC_close(in.ctx);
|
||||
out:
|
||||
free(path);
|
||||
if (f)
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
|
||||
int age) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#ifndef __RTMP_HTTP_H__
|
||||
#define __RTMP_HTTP_H__
|
||||
/*
|
||||
* Copyright (C) 2010 Howard Chu
|
||||
* Copyright (C) 2010 Antti Ajanki
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
HTTPRES_OK, /* result OK */
|
||||
HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */
|
||||
HTTPRES_NOT_FOUND, /* not found */
|
||||
HTTPRES_BAD_REQUEST, /* client error */
|
||||
HTTPRES_SERVER_ERROR, /* server reported an error */
|
||||
HTTPRES_REDIRECTED, /* resource has been moved */
|
||||
HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */
|
||||
} HTTPResult;
|
||||
|
||||
struct HTTP_ctx {
|
||||
char *date;
|
||||
int size;
|
||||
int status;
|
||||
void *data;
|
||||
};
|
||||
|
||||
typedef size_t(HTTP_read_callback)(void *ptr, size_t size, size_t nmemb,
|
||||
void *stream);
|
||||
|
||||
HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url,
|
||||
HTTP_read_callback *cb);
|
||||
|
||||
#endif
|
||||
Executable
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "rtmp_sys.h"
|
||||
|
||||
#define MAX_PRINT_LEN 2048
|
||||
|
||||
RTMP_LogLevel RTMP_debuglevel = RTMP_LOGERROR;
|
||||
|
||||
static int neednl;
|
||||
|
||||
static FILE *fmsg;
|
||||
|
||||
static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;
|
||||
|
||||
static const char *levels[] = {
|
||||
"CRIT", "ERROR", "WARNING", "INFO",
|
||||
"DEBUG", "DEBUG2"};
|
||||
|
||||
static void rtmp_log_default(int level, const char *format, va_list vl) {
|
||||
char str[MAX_PRINT_LEN] = "";
|
||||
|
||||
vsnprintf(str, MAX_PRINT_LEN - 1, format, vl);
|
||||
|
||||
/* Filter out 'no-name' */
|
||||
if (RTMP_debuglevel < RTMP_LOGALL && strstr(str, "no-name") != NULL)
|
||||
return;
|
||||
|
||||
if (!fmsg) fmsg = stderr;
|
||||
|
||||
if (level <= RTMP_debuglevel) {
|
||||
if (neednl) {
|
||||
putc('\n', fmsg);
|
||||
neednl = 0;
|
||||
}
|
||||
fprintf(fmsg, "%s: %s\n", levels[level], str);
|
||||
#ifdef _DEBUG
|
||||
fflush(fmsg);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void RTMP_LogSetOutput(FILE *file) {
|
||||
fmsg = file;
|
||||
}
|
||||
|
||||
void RTMP_LogSetLevel(RTMP_LogLevel level) {
|
||||
RTMP_debuglevel = level;
|
||||
}
|
||||
|
||||
void RTMP_LogSetCallback(RTMP_LogCallback *cbp) {
|
||||
cb = cbp;
|
||||
}
|
||||
|
||||
RTMP_LogLevel RTMP_LogGetLevel() {
|
||||
return RTMP_debuglevel;
|
||||
}
|
||||
|
||||
void RTMP_Log(int level, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
cb(level, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
|
||||
void RTMP_LogHex(int level, const uint8_t *data, unsigned long len) {
|
||||
unsigned long i;
|
||||
char line[50], *ptr;
|
||||
|
||||
if (level > RTMP_debuglevel)
|
||||
return;
|
||||
|
||||
ptr = line;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
*ptr++ = hexdig[0x0f & (data[i] >> 4)];
|
||||
*ptr++ = hexdig[0x0f & data[i]];
|
||||
if ((i & 0x0f) == 0x0f) {
|
||||
*ptr = '\0';
|
||||
ptr = line;
|
||||
RTMP_Log(level, "%s", line);
|
||||
} else {
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
if (i & 0x0f) {
|
||||
*ptr = '\0';
|
||||
RTMP_Log(level, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len) {
|
||||
#define BP_OFFSET 9
|
||||
#define BP_GRAPH 60
|
||||
#define BP_LEN 80
|
||||
char line[BP_LEN];
|
||||
unsigned long i;
|
||||
|
||||
if (!data || level > RTMP_debuglevel)
|
||||
return;
|
||||
|
||||
/* in case len is zero */
|
||||
line[0] = '\0';
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
int n = i % 16;
|
||||
unsigned off;
|
||||
|
||||
if (!n) {
|
||||
if (i) RTMP_Log(level, "%s", line);
|
||||
memset(line, ' ', sizeof(line) - 2);
|
||||
line[sizeof(line) - 2] = '\0';
|
||||
|
||||
off = i % 0x0ffffU;
|
||||
|
||||
line[2] = hexdig[0x0f & (off >> 12)];
|
||||
line[3] = hexdig[0x0f & (off >> 8)];
|
||||
line[4] = hexdig[0x0f & (off >> 4)];
|
||||
line[5] = hexdig[0x0f & off];
|
||||
line[6] = ':';
|
||||
}
|
||||
|
||||
off = BP_OFFSET + n * 3 + ((n >= 8) ? 1 : 0);
|
||||
line[off] = hexdig[0x0f & (data[i] >> 4)];
|
||||
line[off + 1] = hexdig[0x0f & data[i]];
|
||||
|
||||
off = BP_GRAPH + n + ((n >= 8) ? 1 : 0);
|
||||
|
||||
if (isprint(data[i])) {
|
||||
line[BP_GRAPH + n] = data[i];
|
||||
} else {
|
||||
line[BP_GRAPH + n] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
RTMP_Log(level, "%s", line);
|
||||
}
|
||||
|
||||
/* These should only be used by apps, never by the library itself */
|
||||
void RTMP_LogPrintf(const char *format, ...) {
|
||||
char str[MAX_PRINT_LEN] = "";
|
||||
int len;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
len = vsnprintf(str, MAX_PRINT_LEN - 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
if (RTMP_debuglevel == RTMP_LOGCRIT)
|
||||
return;
|
||||
|
||||
if (!fmsg) fmsg = stderr;
|
||||
|
||||
if (neednl) {
|
||||
putc('\n', fmsg);
|
||||
neednl = 0;
|
||||
}
|
||||
|
||||
if (len > MAX_PRINT_LEN - 1)
|
||||
len = MAX_PRINT_LEN - 1;
|
||||
fprintf(fmsg, "%s", str);
|
||||
if (str[len - 1] == '\n')
|
||||
fflush(fmsg);
|
||||
}
|
||||
|
||||
void RTMP_LogStatus(const char *format, ...) {
|
||||
char str[MAX_PRINT_LEN] = "";
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(str, MAX_PRINT_LEN - 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
if (RTMP_debuglevel == RTMP_LOGCRIT)
|
||||
return;
|
||||
|
||||
if (!fmsg) fmsg = stderr;
|
||||
|
||||
fprintf(fmsg, "%s", str);
|
||||
fflush(fmsg);
|
||||
neednl = 1;
|
||||
}
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifndef __RTMP_LOG_H__
|
||||
#define __RTMP_LOG_H__
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* Enable this to get full debugging output */
|
||||
/* #define _DEBUG */
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef NODEBUG
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
RTMP_LOGCRIT = 0,
|
||||
RTMP_LOGERROR,
|
||||
RTMP_LOGWARNING,
|
||||
RTMP_LOGINFO,
|
||||
RTMP_LOGDEBUG,
|
||||
RTMP_LOGDEBUG2,
|
||||
RTMP_LOGALL
|
||||
} RTMP_LogLevel;
|
||||
|
||||
extern RTMP_LogLevel RTMP_debuglevel;
|
||||
|
||||
typedef void(RTMP_LogCallback)(int level, const char *fmt, va_list);
|
||||
void RTMP_LogSetCallback(RTMP_LogCallback *cb);
|
||||
void RTMP_LogSetOutput(FILE *file);
|
||||
void RTMP_LogPrintf(const char *format, ...);
|
||||
void RTMP_LogStatus(const char *format, ...);
|
||||
void RTMP_Log(int level, const char *format, ...);
|
||||
void RTMP_LogHex(int level, const uint8_t *data, unsigned long len);
|
||||
void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len);
|
||||
void RTMP_LogSetLevel(RTMP_LogLevel lvl);
|
||||
RTMP_LogLevel RTMP_LogGetLevel(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Executable
+312
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "rtmp_sys.h"
|
||||
|
||||
int PILI_RTMP_ParseURL2(const char *url, int *protocol, AVal *host, unsigned int *port,
|
||||
AVal *playpath, AVal *app, AVal *domainName) {
|
||||
char *p, *end, *col, *ques, *slash;
|
||||
|
||||
RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
|
||||
|
||||
*protocol = RTMP_PROTOCOL_RTMP;
|
||||
*port = 0;
|
||||
playpath->av_len = 0;
|
||||
playpath->av_val = NULL;
|
||||
app->av_len = 0;
|
||||
app->av_val = NULL;
|
||||
|
||||
/* Old School Parsing */
|
||||
|
||||
/* look for usual :// pattern */
|
||||
p = strstr(url, "://");
|
||||
if (!p) {
|
||||
RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
|
||||
return FALSE;
|
||||
}
|
||||
{
|
||||
int len = (int)(p - url);
|
||||
|
||||
if (len == 4 && strncasecmp(url, "rtmp", 4) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMP;
|
||||
else if (len == 5 && strncasecmp(url, "rtmpt", 5) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMPT;
|
||||
else if (len == 5 && strncasecmp(url, "rtmps", 5) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMPS;
|
||||
else if (len == 5 && strncasecmp(url, "rtmpe", 5) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMPE;
|
||||
else if (len == 5 && strncasecmp(url, "rtmfp", 5) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMFP;
|
||||
else if (len == 6 && strncasecmp(url, "rtmpte", 6) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMPTE;
|
||||
else if (len == 6 && strncasecmp(url, "rtmpts", 6) == 0)
|
||||
*protocol = RTMP_PROTOCOL_RTMPTS;
|
||||
else {
|
||||
RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
|
||||
goto parsehost;
|
||||
}
|
||||
}
|
||||
|
||||
RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
|
||||
|
||||
parsehost:
|
||||
/* let's get the hostname */
|
||||
p += 3;
|
||||
|
||||
/* check for sudden death */
|
||||
if (*p == 0) {
|
||||
RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
end = p + strlen(p);
|
||||
col = strchr(p, ':');
|
||||
ques = strchr(p, '?');
|
||||
slash = strchr(p, '/');
|
||||
|
||||
{
|
||||
int hostlen;
|
||||
if (slash)
|
||||
hostlen = slash - p;
|
||||
else
|
||||
hostlen = end - p;
|
||||
if (col && col - p < hostlen)
|
||||
hostlen = col - p;
|
||||
|
||||
if (hostlen < 256) {
|
||||
host->av_val = p;
|
||||
host->av_len = hostlen;
|
||||
RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
|
||||
} else {
|
||||
RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
|
||||
}
|
||||
|
||||
p += hostlen;
|
||||
}
|
||||
|
||||
/* get the port number if available */
|
||||
if (*p == ':') {
|
||||
unsigned int p2;
|
||||
p++;
|
||||
p2 = atoi(p);
|
||||
if (p2 > 65535) {
|
||||
RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
|
||||
} else {
|
||||
*port = p2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slash) {
|
||||
RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
|
||||
return TRUE;
|
||||
}
|
||||
p = slash + 1;
|
||||
|
||||
/** parse domain
|
||||
+ * rtmp://host:[port]/app/...?domain=a.com
|
||||
+ * use domain to replace host
|
||||
+ */
|
||||
|
||||
if (domainName != NULL && ques != NULL) {
|
||||
char *domain = strstr(ques, "domain=");
|
||||
if (domain) {
|
||||
end = domain - 1;
|
||||
domain += 7; //skip "domain="
|
||||
char *domain_end = strchr(domain, '&');
|
||||
int host_len = 0;
|
||||
if (domain_end) {
|
||||
host_len = domain_end - domain;
|
||||
} else {
|
||||
host_len = strlen(domain);
|
||||
}
|
||||
if (host_len < 256) {
|
||||
domainName->av_val = domain;
|
||||
domainName->av_len = host_len;
|
||||
RTMP_Log(RTMP_LOGDEBUG, "Parsed host and domain : %.*s", host_len, host->av_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* parse application
|
||||
*
|
||||
* rtmp://host[:port]/app[/appinstance][/...]
|
||||
* application = app[/appinstance]
|
||||
*/
|
||||
|
||||
char *slash2, *slash3 = NULL;
|
||||
int applen, appnamelen;
|
||||
|
||||
slash2 = strchr(p, '/');
|
||||
if (slash2)
|
||||
slash3 = strchr(slash2 + 1, '/');
|
||||
|
||||
applen = end - p; /* ondemand, pass all parameters as app */
|
||||
appnamelen = applen; /* ondemand length */
|
||||
|
||||
if (ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
|
||||
appnamelen = ques - p;
|
||||
} else if (strncmp(p, "ondemand/", 9) == 0) {
|
||||
/* app = ondemand/foobar, only pass app=ondemand */
|
||||
applen = 8;
|
||||
appnamelen = 8;
|
||||
} else { /* app!=ondemand, so app is app[/appinstance] */
|
||||
if (slash3)
|
||||
appnamelen = slash3 - p;
|
||||
else if (slash2)
|
||||
appnamelen = slash2 - p;
|
||||
|
||||
applen = appnamelen;
|
||||
}
|
||||
|
||||
app->av_val = p;
|
||||
app->av_len = applen;
|
||||
RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
|
||||
|
||||
p += appnamelen;
|
||||
}
|
||||
|
||||
if (*p == '/')
|
||||
p++;
|
||||
|
||||
if (end - p) {
|
||||
AVal av = {p, end - p};
|
||||
PILI_RTMP_ParsePlaypath(&av, playpath);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts playpath from RTMP URL. playpath is the file part of the
|
||||
* URL, i.e. the part that comes after rtmp://host:port/app/
|
||||
*
|
||||
* Returns the stream name in a format understood by FMS. The name is
|
||||
* the playpath part of the URL with formatting depending on the stream
|
||||
* type:
|
||||
*
|
||||
* mp4 streams: prepend "mp4:", remove extension
|
||||
* mp3 streams: prepend "mp3:", remove extension
|
||||
* flv streams: remove extension
|
||||
*/
|
||||
void PILI_RTMP_ParsePlaypath(AVal *in, AVal *out) {
|
||||
int addMP4 = 0;
|
||||
int addMP3 = 0;
|
||||
int subExt = 0;
|
||||
const char *playpath = in->av_val;
|
||||
const char *temp, *q, *ext = NULL;
|
||||
const char *ppstart = playpath;
|
||||
char *streamname, *destptr, *p;
|
||||
|
||||
int pplen = in->av_len;
|
||||
|
||||
out->av_val = NULL;
|
||||
out->av_len = 0;
|
||||
|
||||
if ((*ppstart == '?') &&
|
||||
(temp = strstr(ppstart, "slist=")) != 0) {
|
||||
ppstart = temp + 6;
|
||||
pplen = strlen(ppstart);
|
||||
|
||||
temp = strchr(ppstart, '&');
|
||||
if (temp) {
|
||||
pplen = temp - ppstart;
|
||||
}
|
||||
}
|
||||
|
||||
q = strchr(ppstart, '?');
|
||||
if (pplen >= 4) {
|
||||
if (q)
|
||||
ext = q - 4;
|
||||
else
|
||||
ext = &ppstart[pplen - 4];
|
||||
if ((strncmp(ext, ".f4v", 4) == 0) ||
|
||||
(strncmp(ext, ".mp4", 4) == 0)) {
|
||||
addMP4 = 1;
|
||||
subExt = 1;
|
||||
/* Only remove .flv from rtmp URL, not slist params */
|
||||
} else if ((ppstart == playpath) &&
|
||||
(strncmp(ext, ".flv", 4) == 0)) {
|
||||
subExt = 1;
|
||||
} else if (strncmp(ext, ".mp3", 4) == 0) {
|
||||
addMP3 = 1;
|
||||
subExt = 1;
|
||||
}
|
||||
}
|
||||
|
||||
streamname = (char *)malloc((pplen + 4 + 1) * sizeof(char));
|
||||
if (!streamname)
|
||||
return;
|
||||
|
||||
destptr = streamname;
|
||||
if (addMP4) {
|
||||
if (strncmp(ppstart, "mp4:", 4)) {
|
||||
strcpy(destptr, "mp4:");
|
||||
destptr += 4;
|
||||
} else {
|
||||
subExt = 0;
|
||||
}
|
||||
} else if (addMP3) {
|
||||
if (strncmp(ppstart, "mp3:", 4)) {
|
||||
strcpy(destptr, "mp3:");
|
||||
destptr += 4;
|
||||
} else {
|
||||
subExt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = (char *)ppstart; pplen > 0;) {
|
||||
/* skip extension */
|
||||
if (subExt && p == ext) {
|
||||
p += 4;
|
||||
pplen -= 4;
|
||||
continue;
|
||||
}
|
||||
if (*p == '%') {
|
||||
unsigned int c;
|
||||
sscanf(p + 1, "%02x", &c);
|
||||
*destptr++ = c;
|
||||
pplen -= 3;
|
||||
p += 3;
|
||||
} else {
|
||||
*destptr++ = *p++;
|
||||
pplen--;
|
||||
}
|
||||
}
|
||||
*destptr = '\0';
|
||||
|
||||
out->av_val = streamname;
|
||||
out->av_len = destptr - streamname;
|
||||
}
|
||||
|
||||
int PILI_RTMP_ParseURL(const char *url, int *protocol, AVal *host,
|
||||
unsigned int *port, AVal *playpath, AVal *app) {
|
||||
return PILI_RTMP_ParseURL2(url, protocol, host, port, playpath, app, NULL);
|
||||
}
|
||||
Executable
+4331
File diff suppressed because it is too large
Load Diff
Executable
+365
@@ -0,0 +1,365 @@
|
||||
#ifndef __RTMP_H__
|
||||
#define __RTMP_H__
|
||||
/*
|
||||
* Copyright (C) 2005-2008 Team XBMC
|
||||
* http://www.xbmc.org
|
||||
* Copyright (C) 2008-2009 Andrej Stepanchuk
|
||||
* Copyright (C) 2009-2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#define NO_CRYPTO
|
||||
|
||||
#if !defined(NO_CRYPTO) && !defined(CRYPTO)
|
||||
#define CRYPTO
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "amf.h"
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RTMP_LIB_VERSION 0x020300 /* 2.3 */
|
||||
|
||||
#define RTMP_FEATURE_HTTP 0x01
|
||||
#define RTMP_FEATURE_ENC 0x02
|
||||
#define RTMP_FEATURE_SSL 0x04
|
||||
#define RTMP_FEATURE_MFP 0x08 /* not yet supported */
|
||||
#define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
|
||||
#define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */
|
||||
#define RTMP_FEATURE_NONBLOCK 0x40 /* non block socket */
|
||||
|
||||
#define RTMP_PROTOCOL_UNDEFINED -1
|
||||
#define RTMP_PROTOCOL_RTMP 0
|
||||
#define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
|
||||
#define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
|
||||
#define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
|
||||
#define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC)
|
||||
#define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL)
|
||||
#define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP
|
||||
|
||||
#define RTMP_DEFAULT_CHUNKSIZE 128
|
||||
|
||||
/* needs to fit largest number of bytes recv() may return */
|
||||
#define RTMP_BUFFER_CACHE_SIZE (16 * 1024)
|
||||
|
||||
#define RTMP_CHANNELS 65600
|
||||
|
||||
extern const char PILI_RTMPProtocolStringsLower[][7];
|
||||
extern const AVal PILI_RTMP_DefaultFlashVer;
|
||||
extern int PILI_RTMP_ctrlC;
|
||||
|
||||
uint32_t PILI_RTMP_GetTime(void);
|
||||
|
||||
#define RTMP_PACKET_TYPE_AUDIO 0x08
|
||||
#define RTMP_PACKET_TYPE_VIDEO 0x09
|
||||
#define RTMP_PACKET_TYPE_INFO 0x12
|
||||
|
||||
#define RTMP_MAX_HEADER_SIZE 18
|
||||
|
||||
#define RTMP_PACKET_SIZE_LARGE 0
|
||||
#define RTMP_PACKET_SIZE_MEDIUM 1
|
||||
#define RTMP_PACKET_SIZE_SMALL 2
|
||||
#define RTMP_PACKET_SIZE_MINIMUM 3
|
||||
|
||||
typedef struct PILI_RTMPChunk {
|
||||
int c_headerSize;
|
||||
int c_chunkSize;
|
||||
char *c_chunk;
|
||||
char c_header[RTMP_MAX_HEADER_SIZE];
|
||||
} PILI_RTMPChunk;
|
||||
|
||||
typedef struct PILI_RTMPPacket {
|
||||
uint8_t m_headerType;
|
||||
uint8_t m_packetType;
|
||||
uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */
|
||||
int m_nChannel;
|
||||
uint32_t m_nTimeStamp; /* timestamp */
|
||||
int32_t m_nInfoField2; /* last 4 bytes in a long header */
|
||||
uint32_t m_nBodySize;
|
||||
uint32_t m_nBytesRead;
|
||||
PILI_RTMPChunk *m_chunk;
|
||||
char *m_body;
|
||||
} PILI_RTMPPacket;
|
||||
|
||||
typedef struct PILI_RTMPSockBuf {
|
||||
int sb_socket;
|
||||
int sb_size; /* number of unprocessed bytes in buffer */
|
||||
char *sb_start; /* pointer into sb_pBuffer of next byte to process */
|
||||
char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */
|
||||
int sb_timedout;
|
||||
void *sb_ssl;
|
||||
} PILI_RTMPSockBuf;
|
||||
|
||||
void PILI_RTMPPacket_Reset(PILI_RTMPPacket *p);
|
||||
void PILI_RTMPPacket_Dump(PILI_RTMPPacket *p);
|
||||
int PILI_RTMPPacket_Alloc(PILI_RTMPPacket *p, int nSize);
|
||||
void PILI_RTMPPacket_Free(PILI_RTMPPacket *p);
|
||||
|
||||
#define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize)
|
||||
|
||||
typedef struct PILI_RTMP_LNK {
|
||||
AVal hostname;
|
||||
AVal domain;
|
||||
AVal sockshost;
|
||||
|
||||
AVal playpath0; /* parsed from URL */
|
||||
AVal playpath; /* passed in explicitly */
|
||||
AVal tcUrl;
|
||||
AVal swfUrl;
|
||||
AVal pageUrl;
|
||||
AVal app;
|
||||
AVal auth;
|
||||
AVal flashVer;
|
||||
AVal subscribepath;
|
||||
AVal token;
|
||||
AMFObject extras;
|
||||
int edepth;
|
||||
|
||||
int seekTime;
|
||||
int stopTime;
|
||||
|
||||
#define RTMP_LF_AUTH 0x0001 /* using auth param */
|
||||
#define RTMP_LF_LIVE 0x0002 /* stream is live */
|
||||
#define RTMP_LF_SWFV 0x0004 /* do SWF verification */
|
||||
#define RTMP_LF_PLST 0x0008 /* send playlist before play */
|
||||
#define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
|
||||
#define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */
|
||||
int lFlags;
|
||||
|
||||
int swfAge;
|
||||
|
||||
int protocol;
|
||||
int timeout; /* connection timeout in seconds */
|
||||
int send_timeout; /* send data timeout */
|
||||
|
||||
unsigned short socksport;
|
||||
unsigned short port;
|
||||
|
||||
#ifdef CRYPTO
|
||||
#define RTMP_SWF_HASHLEN 32
|
||||
void *dh; /* for encryption */
|
||||
void *rc4keyIn;
|
||||
void *rc4keyOut;
|
||||
|
||||
uint32_t SWFSize;
|
||||
uint8_t SWFHash[RTMP_SWF_HASHLEN];
|
||||
char SWFVerificationResponse[RTMP_SWF_HASHLEN + 10];
|
||||
#endif
|
||||
} PILI_RTMP_LNK;
|
||||
|
||||
/* state for read() wrapper */
|
||||
typedef struct PILI_RTMP_READ {
|
||||
char *buf;
|
||||
char *bufpos;
|
||||
unsigned int buflen;
|
||||
uint32_t timestamp;
|
||||
uint8_t dataType;
|
||||
uint8_t flags;
|
||||
#define RTMP_READ_HEADER 0x01
|
||||
#define RTMP_READ_RESUME 0x02
|
||||
#define RTMP_READ_NO_IGNORE 0x04
|
||||
#define RTMP_READ_GOTKF 0x08
|
||||
#define RTMP_READ_GOTFLVK 0x10
|
||||
#define RTMP_READ_SEEKING 0x20
|
||||
int8_t status;
|
||||
#define RTMP_READ_COMPLETE -3
|
||||
#define RTMP_READ_ERROR -2
|
||||
#define RTMP_READ_EOF -1
|
||||
#define RTMP_READ_IGNORE 0
|
||||
|
||||
/* if bResume == TRUE */
|
||||
uint8_t initialFrameType;
|
||||
uint32_t nResumeTS;
|
||||
char *metaHeader;
|
||||
char *initialFrame;
|
||||
uint32_t nMetaHeaderSize;
|
||||
uint32_t nInitialFrameSize;
|
||||
uint32_t nIgnoredFrameCounter;
|
||||
uint32_t nIgnoredFlvFrameCounter;
|
||||
} PILI_RTMP_READ;
|
||||
|
||||
typedef struct PILI_RTMP_METHOD {
|
||||
AVal name;
|
||||
int num;
|
||||
} PILI_RTMP_METHOD;
|
||||
|
||||
typedef void (*PILI_RTMPErrorCallback)(RTMPError *error, void *userData);
|
||||
|
||||
typedef struct PILI_CONNECTION_TIME {
|
||||
uint32_t connect_time;
|
||||
uint32_t handshake_time;
|
||||
} PILI_CONNECTION_TIME;
|
||||
|
||||
typedef void (*PILI_RTMP_ConnectionTimeCallback)(
|
||||
PILI_CONNECTION_TIME *conn_time, void *userData);
|
||||
|
||||
typedef struct PILI_RTMP {
|
||||
int m_inChunkSize;
|
||||
int m_outChunkSize;
|
||||
int m_nBWCheckCounter;
|
||||
int m_nBytesIn;
|
||||
int m_nBytesInSent;
|
||||
int m_nBufferMS;
|
||||
int m_stream_id; /* returned in _result from createStream */
|
||||
int m_mediaChannel;
|
||||
uint32_t m_mediaStamp;
|
||||
uint32_t m_pauseStamp;
|
||||
int m_pausing;
|
||||
int m_nServerBW;
|
||||
int m_nClientBW;
|
||||
uint8_t m_nClientBW2;
|
||||
uint8_t m_bPlaying;
|
||||
uint8_t m_bSendEncoding;
|
||||
uint8_t m_bSendCounter;
|
||||
|
||||
int m_numInvokes;
|
||||
int m_numCalls;
|
||||
PILI_RTMP_METHOD *m_methodCalls; /* remote method calls queue */
|
||||
|
||||
PILI_RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];
|
||||
PILI_RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];
|
||||
int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */
|
||||
|
||||
double m_fAudioCodecs; /* audioCodecs for the connect packet */
|
||||
double m_fVideoCodecs; /* videoCodecs for the connect packet */
|
||||
double m_fEncoding; /* AMF0 or AMF3 */
|
||||
|
||||
double m_fDuration; /* duration of stream in seconds */
|
||||
|
||||
int m_msgCounter; /* RTMPT stuff */
|
||||
int m_polling;
|
||||
int m_resplen;
|
||||
int m_unackd;
|
||||
AVal m_clientID;
|
||||
|
||||
PILI_RTMP_READ m_read;
|
||||
PILI_RTMPPacket m_write;
|
||||
PILI_RTMPSockBuf m_sb;
|
||||
PILI_RTMP_LNK Link;
|
||||
|
||||
PILI_RTMPErrorCallback m_errorCallback;
|
||||
PILI_RTMP_ConnectionTimeCallback m_connCallback;
|
||||
RTMPError *m_error;
|
||||
void *m_userData;
|
||||
int m_is_closing;
|
||||
int m_tcp_nodelay;
|
||||
uint32_t ip;
|
||||
} PILI_RTMP;
|
||||
|
||||
int PILI_RTMP_ParseURL(const char *url, int *protocol, AVal *host,
|
||||
unsigned int *port, AVal *playpath, AVal *app);
|
||||
|
||||
int PILI_RTMP_ParseURL2(const char *url, int *protocol, AVal *host,
|
||||
unsigned int *port, AVal *playpath, AVal *app, AVal *domain);
|
||||
|
||||
void PILI_RTMP_ParsePlaypath(AVal *in, AVal *out);
|
||||
void PILI_RTMP_SetBufferMS(PILI_RTMP *r, int size);
|
||||
void PILI_RTMP_UpdateBufferMS(PILI_RTMP *r, RTMPError *error);
|
||||
|
||||
int PILI_RTMP_SetOpt(PILI_RTMP *r, const AVal *opt, AVal *arg,
|
||||
RTMPError *error);
|
||||
int PILI_RTMP_SetupURL(PILI_RTMP *r, const char *url, RTMPError *error);
|
||||
void PILI_RTMP_SetupStream(PILI_RTMP *r, int protocol, AVal *hostname,
|
||||
unsigned int port, AVal *sockshost, AVal *playpath,
|
||||
AVal *tcUrl, AVal *swfUrl, AVal *pageUrl, AVal *app,
|
||||
AVal *auth, AVal *swfSHA256Hash, uint32_t swfSize,
|
||||
AVal *flashVer, AVal *subscribepath, int dStart,
|
||||
int dStop, int bLiveStream, long int timeout);
|
||||
|
||||
int PILI_RTMP_Connect(PILI_RTMP *r, PILI_RTMPPacket *cp, RTMPError *error);
|
||||
struct sockaddr;
|
||||
int PILI_RTMP_Connect0(PILI_RTMP *r, struct addrinfo *ai, unsigned short port,
|
||||
RTMPError *error);
|
||||
int PILI_RTMP_Connect1(PILI_RTMP *r, PILI_RTMPPacket *cp, RTMPError *error);
|
||||
int PILI_RTMP_Serve(PILI_RTMP *r, RTMPError *error);
|
||||
|
||||
int PILI_RTMP_ReadPacket(PILI_RTMP *r, PILI_RTMPPacket *packet);
|
||||
int PILI_RTMP_SendPacket(PILI_RTMP *r, PILI_RTMPPacket *packet, int queue,
|
||||
RTMPError *error);
|
||||
int PILI_RTMP_SendChunk(PILI_RTMP *r, PILI_RTMPChunk *chunk, RTMPError *error);
|
||||
int PILI_RTMP_IsConnected(PILI_RTMP *r);
|
||||
int PILI_RTMP_Socket(PILI_RTMP *r);
|
||||
int PILI_RTMP_IsTimedout(PILI_RTMP *r);
|
||||
double PILI_RTMP_GetDuration(PILI_RTMP *r);
|
||||
int PILI_RTMP_ToggleStream(PILI_RTMP *r, RTMPError *error);
|
||||
|
||||
int PILI_RTMP_ConnectStream(PILI_RTMP *r, int seekTime, RTMPError *error);
|
||||
int PILI_RTMP_ReconnectStream(PILI_RTMP *r, int seekTime, RTMPError *error);
|
||||
void PILI_RTMP_DeleteStream(PILI_RTMP *r, RTMPError *error);
|
||||
int PILI_RTMP_GetNextMediaPacket(PILI_RTMP *r, PILI_RTMPPacket *packet);
|
||||
int PILI_RTMP_ClientPacket(PILI_RTMP *r, PILI_RTMPPacket *packet);
|
||||
|
||||
void PILI_RTMP_Init(PILI_RTMP *r);
|
||||
void PILI_RTMP_Close(PILI_RTMP *r, RTMPError *error);
|
||||
PILI_RTMP *PILI_RTMP_Alloc(void);
|
||||
void PILI_RTMP_Free(PILI_RTMP *r);
|
||||
void PILI_RTMP_EnableWrite(PILI_RTMP *r);
|
||||
|
||||
int PILI_RTMP_LibVersion(void);
|
||||
void PILI_RTMP_UserInterrupt(void); /* user typed Ctrl-C */
|
||||
|
||||
int PILI_RTMP_SendCtrl(PILI_RTMP *r, short nType, unsigned int nObject,
|
||||
unsigned int nTime, RTMPError *error);
|
||||
|
||||
/* caller probably doesn't know current timestamp, should
|
||||
* just use RTMP_Pause instead
|
||||
*/
|
||||
int PILI_RTMP_SendPause(PILI_RTMP *r, int DoPause, int dTime, RTMPError *error);
|
||||
int PILI_RTMP_Pause(PILI_RTMP *r, int DoPause, RTMPError *error);
|
||||
|
||||
int PILI_RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
|
||||
AMFObjectProperty *p);
|
||||
|
||||
int PILI_RTMPSockBuf_Fill(PILI_RTMPSockBuf *sb, int timeout);
|
||||
int PILI_RTMPSockBuf_Send(PILI_RTMPSockBuf *sb, const char *buf, int len);
|
||||
int PILI_RTMPSockBuf_Close(PILI_RTMPSockBuf *sb);
|
||||
|
||||
int PILI_RTMP_SendCreateStream(PILI_RTMP *r, RTMPError *error);
|
||||
int PILI_RTMP_SendSeek(PILI_RTMP *r, int dTime, RTMPError *error);
|
||||
int PILI_RTMP_SendServerBW(PILI_RTMP *r, RTMPError *error);
|
||||
int PILI_RTMP_SendClientBW(PILI_RTMP *r, RTMPError *error);
|
||||
void PILI_RTMP_DropRequest(PILI_RTMP *r, int i, int freeit);
|
||||
int PILI_RTMP_Read(PILI_RTMP *r, char *buf, int size);
|
||||
int PILI_RTMP_Write(PILI_RTMP *r, const char *buf, int size, RTMPError *error);
|
||||
|
||||
#define MAJOR 1
|
||||
#define MINOR 0
|
||||
#define PATCH 4
|
||||
|
||||
int PILI_RTMP_Version();
|
||||
|
||||
const char * PILI_RTMP_GetReqId();
|
||||
|
||||
/* hashswf.c */
|
||||
int PILI_RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
|
||||
int age);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#ifndef __RTMP_SYS_H__
|
||||
#define __RTMP_SYS_H__
|
||||
/*
|
||||
* Copyright (C) 2010 Howard Chu
|
||||
*
|
||||
* This file is part of librtmp.
|
||||
*
|
||||
* librtmp 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,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* librtmp 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with librtmp see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef _XBOX
|
||||
#include <winsockx.h>
|
||||
#include <xtl.h>
|
||||
#define snprintf _snprintf
|
||||
#define strcasecmp stricmp
|
||||
#define strncasecmp strnicmp
|
||||
#define vsnprintf _vsnprintf
|
||||
|
||||
#else /* !_XBOX */
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#define GetSockError() WSAGetLastError()
|
||||
#define SetSockError(e) WSASetLastError(e)
|
||||
#define setsockopt(a, b, c, d, e) (setsockopt)(a, b, c, (const char *)d, (int)e)
|
||||
#define EWOULDBLOCK \
|
||||
WSAETIMEDOUT /* we don't use nonblocking, but we do use timeouts */
|
||||
#define sleep(n) Sleep(n * 1000)
|
||||
#define msleep(n) Sleep(n)
|
||||
#define SET_RCVTIMEO(tv, s) int tv = s * 1000
|
||||
#else /* !_WIN32 */
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#define GetSockError() errno
|
||||
#define SetSockError(e) errno = e
|
||||
#undef closesocket
|
||||
#define closesocket(s) close(s)
|
||||
#define msleep(n) usleep(n * 1000)
|
||||
#define SET_RCVTIMEO(tv, s) struct timeval tv = {s, 0}
|
||||
#endif
|
||||
|
||||
#include "rtmp.h"
|
||||
|
||||
#ifdef USE_POLARSSL
|
||||
#include <polarssl/havege.h>
|
||||
#include <polarssl/net.h>
|
||||
#include <polarssl/ssl.h>
|
||||
typedef struct tls_ctx {
|
||||
havege_state hs;
|
||||
ssl_session ssn;
|
||||
} tls_ctx;
|
||||
#define TLS_CTX tls_ctx *
|
||||
#define TLS_client(ctx, s) \
|
||||
s = malloc(sizeof(ssl_context)); \
|
||||
ssl_init(s); \
|
||||
ssl_set_endpoint(s, SSL_IS_CLIENT); \
|
||||
ssl_set_authmode(s, SSL_VERIFY_NONE); \
|
||||
ssl_set_rng(s, havege_rand, &ctx->hs); \
|
||||
ssl_set_ciphers(s, ssl_default_ciphers); \
|
||||
ssl_set_session(s, 1, 600, &ctx->ssn)
|
||||
#define TLS_setfd(s, fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd)
|
||||
#define TLS_connect(s) ssl_handshake(s)
|
||||
#define TLS_read(s, b, l) ssl_read(s, (unsigned char *)b, l)
|
||||
#define TLS_write(s, b, l) ssl_write(s, (unsigned char *)b, l)
|
||||
#define TLS_shutdown(s) ssl_close_notify(s)
|
||||
#define TLS_close(s) \
|
||||
ssl_free(s); \
|
||||
free(s)
|
||||
|
||||
#elif defined(USE_GNUTLS)
|
||||
#include <gnutls/gnutls.h>
|
||||
typedef struct tls_ctx {
|
||||
gnutls_certificate_credentials_t cred;
|
||||
gnutls_priority_t prios;
|
||||
} tls_ctx;
|
||||
#define TLS_CTX tls_ctx *
|
||||
#define TLS_client(ctx, s) \
|
||||
gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); \
|
||||
gnutls_priority_set(s, ctx->prios); \
|
||||
gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred)
|
||||
#define TLS_setfd(s, fd) \
|
||||
gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd)
|
||||
#define TLS_connect(s) gnutls_handshake(s)
|
||||
#define TLS_read(s, b, l) gnutls_record_recv(s, b, l)
|
||||
#define TLS_write(s, b, l) gnutls_record_send(s, b, l)
|
||||
#define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR)
|
||||
#define TLS_close(s) gnutls_deinit(s)
|
||||
|
||||
#else /* USE_OPENSSL */
|
||||
#define TLS_CTX SSL_CTX *
|
||||
#define TLS_client(ctx, s) s = SSL_new(ctx)
|
||||
#define TLS_setfd(s, fd) SSL_set_fd(s, fd)
|
||||
#define TLS_connect(s) SSL_connect(s)
|
||||
#define TLS_read(s, b, l) SSL_read(s, b, l)
|
||||
#define TLS_write(s, b, l) SSL_write(s, b, l)
|
||||
#define TLS_shutdown(s) SSL_shutdown(s)
|
||||
#define TLS_close(s) SSL_free(s)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -179,12 +179,12 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B2D23E911D348F3D00B34CA8 /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */;
|
||||
buildPhases = (
|
||||
64B4341F5B9578C3E50D0608 /* 📦 Check Pods Manifest.lock */,
|
||||
6A9D2ED37E623D4A31A8D2C9 /* 📦 Check Pods Manifest.lock */,
|
||||
B2D23E761D348F3D00B34CA8 /* Sources */,
|
||||
B2D23E771D348F3D00B34CA8 /* Frameworks */,
|
||||
B2D23E781D348F3D00B34CA8 /* Resources */,
|
||||
90A9D0B9FC19DB224CE96A04 /* 📦 Embed Pods Frameworks */,
|
||||
5CD44155361B18A2D8700198 /* 📦 Copy Pods Resources */,
|
||||
34EEB2C8F5E0D371D13B66CA /* 📦 Copy Pods Resources */,
|
||||
7336E9C92EDCA6C7449F2624 /* 📦 Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -251,7 +251,7 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
5CD44155361B18A2D8700198 /* 📦 Copy Pods Resources */ = {
|
||||
34EEB2C8F5E0D371D13B66CA /* 📦 Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -266,7 +266,7 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKitDemo/Pods-LFLiveKitDemo-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
64B4341F5B9578C3E50D0608 /* 📦 Check Pods Manifest.lock */ = {
|
||||
6A9D2ED37E623D4A31A8D2C9 /* 📦 Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -281,7 +281,7 @@
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
90A9D0B9FC19DB224CE96A04 /* 📦 Embed Pods Frameworks */ = {
|
||||
7336E9C92EDCA6C7449F2624 /* 📦 Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -448,7 +448,7 @@
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = LFLiveKitDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
@@ -465,7 +465,7 @@
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = LFLiveKitDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
|
||||
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
@@ -156,7 +156,7 @@ inline static NSString *formatedSpeed(float bytes, float elapsed_milli) {
|
||||
|
||||
|
||||
/*** 默认分辨率368 * 640 音频:44.1 iphone6以上48 双声道 方向竖屏 ***/
|
||||
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium2 landscape:NO]];
|
||||
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium3 landscape:NO]];
|
||||
|
||||
/** 自己定制单声道 */
|
||||
/*
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios,'8.0'
|
||||
platform :ios,'7.0'
|
||||
|
||||
target 'LFLiveKitDemo' do
|
||||
|
||||
|
||||
|
||||
pod 'LFLiveKit', path: '../'
|
||||
end
|
||||
|
||||
pod 'LFLiveKit', path: '../'
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios,'8.0'
|
||||
platform :ios,'7.0'
|
||||
|
||||
pod 'LFLiveKit', path: '../'
|
||||
Reference in New Issue
Block a user