├── .gitignore ├── README.md ├── VideoCodecKit.xcworkspace └── contents.xcworkspacedata ├── VideoCodecKit ├── VideoCodecKit.xcodeproj │ └── project.pbxproj └── VideoCodecKit │ ├── BaseModel │ └── VCBaseCodecProtocol.h │ ├── Codec │ ├── Decoder │ │ ├── VCH264HardwareDecoder.h │ │ ├── VCH264HardwareDecoder.m │ │ ├── VCH264SoftwareDecoder.h │ │ ├── VCH264SoftwareDecoder.m │ │ ├── VCH265HardwareDecoder.h │ │ ├── VCH265HardwareDecoder.m │ │ └── VCVideoDecoder.h │ ├── Encoder │ │ ├── VCH264HardwareEncoder.h │ │ ├── VCH264HardwareEncoder.m │ │ ├── VCH264SoftwareEncoder.h │ │ ├── VCH264SoftwareEncoder.m │ │ └── VCVideoEncoder.h │ ├── VCAudioConverter.h │ └── VCAudioConverter.m │ ├── Encoder │ └── Hardware │ │ └── H264 │ │ ├── VCH264BaseEncoderConfig.h │ │ └── VCH264BaseEncoderConfig.m │ ├── ISO │ ├── AAC │ │ ├── VCAudioSpecificConfig.h │ │ └── VCAudioSpecificConfig.m │ ├── AMF │ │ ├── VCAMF0Serialization.h │ │ ├── VCAMF0Serialization.m │ │ ├── VCAMF3Serialization.h │ │ ├── VCAMF3Serialization.m │ │ ├── VCActionScriptTypes.h │ │ └── VCActionScriptTypes.m │ ├── AVC │ │ ├── VCAVCConfigurationRecord.h │ │ ├── VCAVCConfigurationRecord.m │ │ ├── VCAVCFormatStream.h │ │ └── VCAVCFormatStream.m │ ├── AnnexB │ │ ├── VCAnnexBFormatParser.h │ │ ├── VCAnnexBFormatParser.m │ │ ├── VCAnnexBFormatStream.h │ │ └── VCAnnexBFormatStream.m │ ├── FileFormat │ │ ├── FLV │ │ │ ├── VCFLVFile.h │ │ │ ├── VCFLVFile.m │ │ │ ├── VCFLVReader.h │ │ │ ├── VCFLVReader.m │ │ │ ├── VCFLVTag.h │ │ │ ├── VCFLVTag.m │ │ │ ├── VCFLVWriter.h │ │ │ └── VCFLVWriter.m │ │ ├── H264 │ │ │ ├── VCRawH264Reader.h │ │ │ └── VCRawH264Reader.m │ │ ├── H265 │ │ │ ├── VCRawH265Reader.h │ │ │ └── VCRawH265Reader.m │ │ ├── MP4 │ │ │ ├── VCMP4Reader.h │ │ │ └── VCMP4Reader.m │ │ ├── VCAssetReader.h │ │ └── VCAssetReader.m │ └── NALU │ │ ├── VCH264NALU.h │ │ ├── VCH264NALU.m │ │ ├── VCH265NALU.h │ │ └── VCH265NALU.m │ ├── Info.plist │ ├── Media │ ├── VCMicRecorder.h │ └── VCMicRecorder.m │ ├── Model │ ├── VCSafeBuffer.h │ ├── VCSafeBuffer.m │ ├── VCSafeObjectQueue.h │ ├── VCSafeObjectQueue.m │ ├── VCSampleBuffer.h │ └── VCSampleBuffer.m │ ├── Network │ ├── Publisher │ │ ├── VCRTMPPublisher.h │ │ └── VCRTMPPublisher.m │ ├── RTMP │ │ ├── VCRTMPChunk.h │ │ ├── VCRTMPChunk.m │ │ ├── VCRTMPChunkChannel.h │ │ ├── VCRTMPChunkChannel.m │ │ ├── VCRTMPCommandMessageCommand.h │ │ ├── VCRTMPCommandMessageCommand.m │ │ ├── VCRTMPHandshake.h │ │ ├── VCRTMPHandshake.m │ │ ├── VCRTMPMessage.h │ │ ├── VCRTMPMessage.m │ │ ├── VCRTMPNetConnection.h │ │ ├── VCRTMPNetConnection.m │ │ ├── VCRTMPNetConnection_Private.h │ │ ├── VCRTMPNetStream.h │ │ ├── VCRTMPNetStream.m │ │ ├── VCRTMPNetStream_Private.h │ │ ├── VCRTMPSession+CommandMessageHandler.h │ │ ├── VCRTMPSession+CommandMessageHandler.m │ │ ├── VCRTMPSession+ProtocolControlMessageHandler.h │ │ ├── VCRTMPSession+ProtocolControlMessageHandler.m │ │ ├── VCRTMPSession.h │ │ ├── VCRTMPSession.m │ │ └── VCRTMPSession_Private.h │ ├── Socket │ │ ├── VCTCPSocket.h │ │ └── VCTCPSocket.m │ └── Subscriber │ │ ├── VCRTMPSubscriber.h │ │ └── VCRTMPSubscriber.m │ ├── Render │ └── Audio │ │ ├── VCAudioPCMRender.h │ │ └── VCAudioPCMRender.m │ ├── Utils │ ├── AVAudioFormat+Utils.h │ ├── AVAudioFormat+Utils.m │ ├── VCByteArray.h │ └── VCByteArray.m │ ├── VCMarco.h │ ├── VideoCodecKit.h │ ├── VideoCodecKit.pch │ ├── VideoCodecKitTools.h │ └── VideoCodecKitTools.m └── VideoCodecKitDemo ├── VideoCodecKitDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── VideoCodecKitDemo ├── Classses ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── Main.storyboard ├── BaseViewController │ ├── VCDemoBackableViewController.h │ ├── VCDemoBackableViewController.m │ ├── VCDemoNavigationController.h │ ├── VCDemoNavigationController.m │ ├── VCDemoViewController.h │ └── VCDemoViewController.m ├── CameraPublish │ ├── VCDemoCameraPublishViewController.h │ └── VCDemoCameraPublishViewController.m ├── ISO │ ├── VCDemoISOTestViewController.h │ └── VCDemoISOTestViewController.m ├── VCDemoListViewController.h └── VCDemoListViewController.m ├── Info.plist ├── Resources ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon_60pt@2x.png │ │ └── icon_60pt@3x.png │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard └── test.flv └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | VideoCodecKitDemo/VideoCodecKitDemo/VideoCodecKit/Vendor/binary/ffmpeg 2 | VideoCodecKitDemo/VideoCodecKitDemo.xcodeproj/project.xcworkspace/xcshareddata 3 | VideoCodecKitDemo/VideoCodecKitDemo.xcodeproj/project.xcworkspace/xcuserdata 4 | VideoCodecKitDemo/VideoCodecKitDemo.xcodeproj/xcuserdata 5 | VideoCodecKit/VideoCodecKit/Vendor/binary/ffmpeg 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VideoDecoderKit 2 | 3 | `Version: 0.9.5` 4 | 5 | ------------- 6 | 7 | `0.9.5`: 修复推流音频问题。重构了一堆接口。封装了推流器VCRTMPPublish,可以方便推流RTMP了。优化了H264编码器,加入参数修改接口,便于推流时修改参数。简化删除了不同的Demo,去掉了Masonry依赖。同时提供静态Framework和动态Framework两种连接方式(考虑到苹果在iOS 13.3.1 中关闭了免费正式对动态库的签名,Demo使用静态连接)。 8 | 9 | `0.9.4`: 实现RTMP,可以推流FLVTag了 10 | 11 | `0.9.3`: 添加H265硬解码支持,支持播放H264 H265裸流 12 | 13 | `0.9.2.1`: 添加macOS支持,删除多余的ffmpeg编译参数,添加播放FLV音频测试Demo。 14 | 15 | ## Video 16 | - [x] VideoToolBox H264 硬解码 17 | - [x] VideoToolBox H264 硬编码 18 | - [x] VideoToolBox H265 硬解码 19 | - [ ] VideoToolBox H265 硬编码 20 | - [ ] 重构视频渲染接口 21 | - [ ] 重构Metal渲染 22 | - [ ] OpenGL渲染 23 | ## Audio 24 | - [x] AudioConverter 解码AAC 25 | - [x] AudioConverter 编码PCM 26 | - [x] 多声道AAC支持 27 | - [x] AVAudioEngine 播放PCM数据 28 | ## Media 29 | - [x] H264 裸流解析 30 | - [x] H265 裸流解析 31 | - [x] FLV 文件解析 32 | - [ ] MP4 文件解析 33 | - [ ] TS 文件解析 34 | - [ ] FLV 文件写入 35 | - [x] 麦克风接口封装,数据获取 36 | ## Publish 37 | - [x] RTMP协议 38 | - [x] RTMP推流器 39 | ## Player 40 | - [x] 音视频同步 41 | - [ ] 缓存队列 42 | ## Build 43 | - [x] macOS 支持 44 | - [x] 动态库 45 | - [x] 静态库 46 | -------------------------------------------------------------------------------- /VideoCodecKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/BaseModel/VCBaseCodecProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCBaseCodecProtocol.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/10/21. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol VCBaseCodecProtocol 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCH264HardwareDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264HardwareDecoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCSampleBuffer.h" 11 | #import "VCVideoDecoder.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface VCH264HardwareDecoder : NSObject 16 | 17 | @property (nonatomic, weak) id delegate; 18 | @property (nonatomic, strong) NSDictionary *attributes; // defaultAttributes 19 | 20 | + (NSDictionary *)defaultAttributes; 21 | - (void)setFormatDescription:(CMFormatDescriptionRef)formatDescription; 22 | 23 | - (OSStatus)decodeSampleBuffer:(VCSampleBuffer *)sampleBuffer; 24 | - (void)clear; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCH264HardwareDecoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264HardwareDecoder.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCH264HardwareDecoder.h" 11 | 12 | #define kVCH264HardwareDecoderMinGOPCount (3) 13 | 14 | @interface VCH264HardwareDecoder () 15 | @property (nonatomic, strong) NSMutableArray *decodeBuffer; 16 | 17 | @property (nonatomic) CMFormatDescriptionRef formatDescription; 18 | @property (nonatomic) VTDecompressionSessionRef session; 19 | 20 | @property (nonatomic, readonly) BOOL isBaseline; 21 | @property (nonatomic, assign) BOOL shouldClearDecodeBuffer; 22 | @end 23 | 24 | @implementation VCH264HardwareDecoder 25 | @synthesize session = _session; 26 | 27 | #pragma mark - Callback 28 | static void decompressionOutputCallback(void *decompressionOutputRefCon, 29 | void *sourceFrameRefCon, 30 | OSStatus status, 31 | VTDecodeInfoFlags infoFlags, 32 | CVImageBufferRef imageBuffer, 33 | CMTime presentationTimeStamp, 34 | CMTime presentationDuration) { 35 | VCH264HardwareDecoder *decoder = (__bridge VCH264HardwareDecoder *)decompressionOutputRefCon; 36 | if (decoder == nil) return; 37 | [decoder decodeSessionDidOuputWithStatus:status infoFlags:infoFlags imageBuffer:imageBuffer presentationTimeStamp:presentationTimeStamp duration:presentationDuration]; 38 | } 39 | 40 | #pragma mark - Init 41 | - (instancetype)init { 42 | self = [super init]; 43 | if (self) { 44 | _decodeBuffer = [NSMutableArray arrayWithCapacity:kVCH264HardwareDecoderMinGOPCount]; 45 | _attributes = [VCH264HardwareDecoder defaultAttributes]; 46 | _isBaseline = NO; 47 | _shouldClearDecodeBuffer = NO; 48 | _formatDescription = NULL; 49 | } 50 | return self; 51 | } 52 | 53 | - (void)dealloc { 54 | 55 | if (_session != NULL) { 56 | VTDecompressionSessionFinishDelayedFrames(_session); 57 | VTDecompressionSessionWaitForAsynchronousFrames(_session); 58 | VTDecompressionSessionInvalidate(_session); 59 | CFRelease(_session); 60 | // _session = NULL; 61 | } 62 | 63 | if (_formatDescription != NULL) { 64 | CFRelease(_formatDescription); 65 | _formatDescription = NULL; 66 | } 67 | } 68 | #pragma mark - Getter Setter 69 | - (void)setFormatDescription:(CMFormatDescriptionRef)formatDescription { 70 | if (!CMFormatDescriptionEqual(_formatDescription, formatDescription)) { 71 | // [TODO] 编写解析器解析额外的avcC数据 72 | // [TODO] 如果没有avcC数据, 则流为Annex-B,掉对应解析器解析。 73 | // 判断是否为baseline 74 | self.session = nil; 75 | _formatDescription = CFRetain(formatDescription); 76 | } 77 | } 78 | 79 | + (NSDictionary *)defaultAttributes { 80 | return @{ 81 | #if (TARGET_OS_IOS) 82 | (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), 83 | (NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}, 84 | (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @(YES), 85 | #else 86 | (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), 87 | (NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}, 88 | (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @(YES), 89 | #endif 90 | }; 91 | } 92 | 93 | - (VTDecompressionSessionRef)session { 94 | if (_session != nil) { 95 | return _session; 96 | } 97 | 98 | if (_formatDescription == nil) { 99 | return nil; 100 | } 101 | 102 | VTDecompressionOutputCallbackRecord callbackRecord; 103 | callbackRecord.decompressionOutputRefCon = (__bridge void * _Nullable)(self); 104 | callbackRecord.decompressionOutputCallback = decompressionOutputCallback; 105 | 106 | CFDictionaryRef attributes = (__bridge CFDictionaryRef)self.attributes; 107 | OSStatus ret = VTDecompressionSessionCreate(kCFAllocatorDefault, 108 | _formatDescription, 109 | nil, 110 | attributes, 111 | &callbackRecord, 112 | &_session); 113 | if (ret != noErr) { 114 | return nil; 115 | } 116 | return _session; 117 | } 118 | 119 | - (void)setSession:(VTDecompressionSessionRef)session { 120 | VTDecompressionSessionFinishDelayedFrames(_session); 121 | VTDecompressionSessionInvalidate(_session); 122 | _session = session; 123 | } 124 | 125 | #pragma mark - Decode Method 126 | - (void)decodeSessionDidOuputWithStatus:(OSStatus)status 127 | infoFlags:(VTDecodeInfoFlags)infoFlags 128 | imageBuffer:(nullable CVImageBufferRef)imageBuffer 129 | presentationTimeStamp:(CMTime)presentationTimeStamp 130 | duration:(CMTime)duration { 131 | if (status != noErr || 132 | imageBuffer == nil) { 133 | return; 134 | } 135 | 136 | OSStatus ret; 137 | 138 | CMSampleTimingInfo timingInfo; 139 | timingInfo.duration = duration; 140 | timingInfo.presentationTimeStamp = presentationTimeStamp; 141 | timingInfo.decodeTimeStamp = kCMTimeInvalid; 142 | 143 | CMVideoFormatDescriptionRef videoFormatDescription = NULL; 144 | ret = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, 145 | imageBuffer, 146 | &videoFormatDescription); 147 | if (ret != noErr) { 148 | return; 149 | } 150 | 151 | CMSampleBufferRef sampleBuffer; 152 | ret = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, 153 | imageBuffer, 154 | true, 155 | nil, 156 | nil, 157 | videoFormatDescription, 158 | &timingInfo, 159 | &sampleBuffer); 160 | CFRelease(videoFormatDescription); 161 | if (ret != noErr) { 162 | return; 163 | } 164 | 165 | if (sampleBuffer == nil) { 166 | return; 167 | } 168 | 169 | VCSampleBuffer *outputSampleBuffer = [[VCSampleBuffer alloc] initWithSampleBuffer:sampleBuffer]; 170 | 171 | if (self.isBaseline) { 172 | if (self.delegate && [self.delegate respondsToSelector:@selector(videoDecoder:didOutputSampleBuffer:)]) { 173 | [self.delegate videoDecoder:self didOutputSampleBuffer:outputSampleBuffer]; 174 | } 175 | } else { 176 | [self.decodeBuffer addObject:outputSampleBuffer]; 177 | [self.decodeBuffer sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 178 | return CMTimeCompare([obj1 presentationTimeStamp], [obj2 presentationTimeStamp]) <= 0; 179 | }]; 180 | if (self.decodeBuffer.count >= kVCH264HardwareDecoderMinGOPCount) { 181 | if (self.delegate && [self.delegate respondsToSelector:@selector(videoDecoder:didOutputSampleBuffer:)]) { 182 | [self.delegate videoDecoder:self didOutputSampleBuffer:self.decodeBuffer[0]]; 183 | } 184 | [self.decodeBuffer removeObjectAtIndex:0]; 185 | } 186 | } 187 | 188 | if (_shouldClearDecodeBuffer) { 189 | [self.decodeBuffer removeAllObjects]; 190 | _shouldClearDecodeBuffer = NO; 191 | } 192 | } 193 | 194 | - (OSStatus)decodeSampleBuffer:(VCSampleBuffer *)sampleBuffer { 195 | if (self.session == nil) { 196 | return kVTInvalidSessionErr; 197 | } 198 | // [TODO] kVTDecodeFrame_EnableTemporalProcessing 判断是否需要加 199 | VTDecodeFrameFlags flags = 0; 200 | return VTDecompressionSessionDecodeFrame(self.session, sampleBuffer.sampleBuffer, flags, nil, nil); 201 | } 202 | 203 | - (void)clear { 204 | _shouldClearDecodeBuffer = YES; 205 | } 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCH264SoftwareDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264SoftwareDecoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCH264SoftwareDecoder : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCH264SoftwareDecoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264SoftwareDecoder.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCH264SoftwareDecoder.h" 10 | 11 | @implementation VCH264SoftwareDecoder 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCH265HardwareDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH265HardwareDecoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/23. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCSampleBuffer.h" 11 | #import "VCVideoDecoder.h" 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface VCH265HardwareDecoder : NSObject 15 | 16 | @property (nonatomic, weak) id delegate; 17 | @property (nonatomic, strong) NSDictionary *attributes; 18 | 19 | + (NSDictionary *)defaultAttributes; 20 | - (void)setFormatDescription:(CMFormatDescriptionRef)formatDescription; 21 | 22 | - (OSStatus)decodeSampleBuffer:(VCSampleBuffer *)sampleBuffer; 23 | - (void)clear; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCH265HardwareDecoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH265HardwareDecoder.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/23. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCH265HardwareDecoder.h" 11 | 12 | #define kVCH265HardwareDecoderMinGOPCount (3) 13 | 14 | @interface VCH265HardwareDecoder () 15 | @property (nonatomic, strong) NSMutableArray *decodeBuffer; 16 | @property (nonatomic) CMFormatDescriptionRef formatDescription; 17 | @property (nonatomic) VTDecompressionSessionRef session; 18 | 19 | @property (nonatomic, assign) BOOL isBaseline; 20 | @property (nonatomic, assign) BOOL shouldClearDecodeBuffer; 21 | @end 22 | 23 | @implementation VCH265HardwareDecoder 24 | @synthesize session = _session; 25 | 26 | #pragma mark - Callback 27 | static void decompressionOutputCallback(void *decompressionOutputRefCon, 28 | void *sourceFrameRefCon, 29 | OSStatus status, 30 | VTDecodeInfoFlags infoFlags, 31 | CVImageBufferRef imageBuffer, 32 | CMTime presentationTimeStamp, 33 | CMTime presentationDuration) { 34 | VCH265HardwareDecoder *decoder = (__bridge VCH265HardwareDecoder *)decompressionOutputRefCon; 35 | if (decoder == nil) return; 36 | [decoder decodeSessionDidOuputWithStatus:status infoFlags:infoFlags imageBuffer:imageBuffer presentationTimeStamp:presentationTimeStamp duration:presentationDuration]; 37 | } 38 | 39 | #pragma mark - Init 40 | - (instancetype)init { 41 | self = [super init]; 42 | if (self) { 43 | _decodeBuffer = [NSMutableArray arrayWithCapacity:kVCH265HardwareDecoderMinGOPCount]; 44 | _attributes = [VCH265HardwareDecoder defaultAttributes]; 45 | 46 | _formatDescription = NULL; 47 | } 48 | return self; 49 | } 50 | 51 | - (void)dealloc { 52 | 53 | if (_session != NULL) { 54 | VTDecompressionSessionFinishDelayedFrames(_session); 55 | VTDecompressionSessionWaitForAsynchronousFrames(_session); 56 | VTDecompressionSessionInvalidate(_session); 57 | CFRelease(_session); 58 | // _session = NULL; 59 | } 60 | 61 | if (_formatDescription != NULL) { 62 | CFRelease(_formatDescription); 63 | _formatDescription = NULL; 64 | } 65 | } 66 | #pragma mark - Getter Setter 67 | - (void)setFormatDescription:(CMFormatDescriptionRef)formatDescription { 68 | if (!CMFormatDescriptionEqual(_formatDescription, formatDescription)) { 69 | // [TODO] 编写解析器解析额外的avcC数据 70 | // [TODO] 如果没有avcC数据, 则流为Annex-B,掉对应解析器解析。 71 | // 判断是否为baseline 72 | self.session = nil; 73 | _formatDescription = CFRetain(formatDescription); 74 | } 75 | } 76 | 77 | + (NSDictionary *)defaultAttributes { 78 | return @{ 79 | #if (TARGET_OS_IOS) 80 | (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), 81 | (NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}, 82 | (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @(YES), 83 | (NSString *)kCVPixelBufferMetalCompatibilityKey: @(YES) 84 | #else 85 | (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), 86 | (NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}, 87 | (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @(YES), 88 | #endif 89 | }; 90 | } 91 | 92 | - (VTDecompressionSessionRef)session { 93 | if (_session != nil) { 94 | return _session; 95 | } 96 | 97 | if (_formatDescription == nil) { 98 | return nil; 99 | } 100 | 101 | VTDecompressionOutputCallbackRecord callbackRecord; 102 | callbackRecord.decompressionOutputRefCon = (__bridge void * _Nullable)(self); 103 | callbackRecord.decompressionOutputCallback = decompressionOutputCallback; 104 | 105 | CFDictionaryRef attributes = (__bridge CFDictionaryRef)self.attributes; 106 | OSStatus ret = VTDecompressionSessionCreate(kCFAllocatorDefault, 107 | _formatDescription, 108 | nil, 109 | attributes, 110 | &callbackRecord, 111 | &_session); 112 | if (ret != noErr) { 113 | return nil; 114 | } 115 | return _session; 116 | } 117 | 118 | - (void)setSession:(VTDecompressionSessionRef)session { 119 | VTDecompressionSessionFinishDelayedFrames(_session); 120 | VTDecompressionSessionInvalidate(_session); 121 | _session = session; 122 | } 123 | 124 | #pragma mark - Decode Method 125 | - (void)decodeSessionDidOuputWithStatus:(OSStatus)status 126 | infoFlags:(VTDecodeInfoFlags)infoFlags 127 | imageBuffer:(nullable CVImageBufferRef)imageBuffer 128 | presentationTimeStamp:(CMTime)presentationTimeStamp 129 | duration:(CMTime)duration { 130 | if (status != noErr || 131 | imageBuffer == nil) { 132 | return; 133 | } 134 | 135 | OSStatus ret; 136 | 137 | CMSampleTimingInfo timingInfo; 138 | timingInfo.duration = duration; 139 | timingInfo.presentationTimeStamp = presentationTimeStamp; 140 | timingInfo.decodeTimeStamp = kCMTimeInvalid; 141 | 142 | CMVideoFormatDescriptionRef videoFormatDescription = NULL; 143 | ret = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, 144 | imageBuffer, 145 | &videoFormatDescription); 146 | if (ret != noErr) { 147 | return; 148 | } 149 | 150 | CMSampleBufferRef sampleBuffer; 151 | ret = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, 152 | imageBuffer, 153 | true, 154 | nil, 155 | nil, 156 | videoFormatDescription, 157 | &timingInfo, 158 | &sampleBuffer); 159 | CFRelease(videoFormatDescription); 160 | if (ret != noErr) { 161 | return; 162 | } 163 | 164 | if (sampleBuffer == nil) { 165 | return; 166 | } 167 | 168 | VCSampleBuffer *outputSampleBuffer = [[VCSampleBuffer alloc] initWithSampleBuffer:sampleBuffer]; 169 | 170 | if (self.isBaseline) { 171 | if (self.delegate && [self.delegate respondsToSelector:@selector(videoDecoder:didOutputSampleBuffer:)]) { 172 | [self.delegate videoDecoder:self didOutputSampleBuffer:outputSampleBuffer]; 173 | } 174 | } else { 175 | [self.decodeBuffer addObject:outputSampleBuffer]; 176 | [self.decodeBuffer sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 177 | return CMTimeCompare([obj1 presentationTimeStamp], [obj2 presentationTimeStamp]) <= 0; 178 | }]; 179 | if (self.decodeBuffer.count >= kVCH265HardwareDecoderMinGOPCount) { 180 | if (self.delegate && [self.delegate respondsToSelector:@selector(videoDecoder:didOutputSampleBuffer:)]) { 181 | [self.delegate videoDecoder:self didOutputSampleBuffer:self.decodeBuffer[0]]; 182 | } 183 | [self.decodeBuffer removeObjectAtIndex:0]; 184 | } 185 | } 186 | 187 | if (_shouldClearDecodeBuffer) { 188 | [self.decodeBuffer removeAllObjects]; 189 | _shouldClearDecodeBuffer = NO; 190 | } 191 | } 192 | 193 | - (OSStatus)decodeSampleBuffer:(VCSampleBuffer *)sampleBuffer { 194 | if (self.session == nil) { 195 | return kVTInvalidSessionErr; 196 | } 197 | // [TODO] kVTDecodeFrame_EnableTemporalProcessing 判断是否需要加 198 | VTDecodeFrameFlags flags = kVTDecodeFrame_EnableAsynchronousDecompression; 199 | return VTDecompressionSessionDecodeFrame(self.session, sampleBuffer.sampleBuffer, flags, nil, nil); 200 | } 201 | 202 | - (void)clear { 203 | _shouldClearDecodeBuffer = YES; 204 | } 205 | 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Decoder/VCVideoDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCVideoDecoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | @class VCSampleBuffer; 13 | @protocol VCVideoDecoder 14 | @required 15 | - (OSStatus)decodeSampleBuffer:(VCSampleBuffer *)sampleBuffer; 16 | @end 17 | 18 | @protocol VCVideoDecoderDelegate 19 | - (void)videoDecoder:(id)decoder didOutputSampleBuffer:(VCSampleBuffer *)sampleBuffer; 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Encoder/VCH264HardwareEncoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264HardwareEncoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCVideoEncoder.h" 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCH264HardwareEncoderParameter : NSObject 14 | @property (nonatomic, copy) NSNumber *width; 15 | @property (nonatomic, copy) NSNumber *height; 16 | @property (nonatomic, copy) NSNumber *bitrate; 17 | @property (nonatomic, copy) NSNumber *frameRate; 18 | @property (nonatomic, copy) NSString *profileLevel; 19 | @property (nonatomic, copy) NSNumber *maxKeyFrameInterval; 20 | @property (nonatomic, copy) NSNumber *maxKeyFrameIntervalDuration; 21 | @property (nonatomic, copy) NSNumber *allowFrameReordering; 22 | @property (nonatomic, copy) NSNumber *realTime; 23 | @end 24 | 25 | @interface VCH264HardwareEncoder : NSObject 26 | 27 | @property (nonatomic, weak) id delegate; 28 | @property (nonatomic, strong) NSDictionary *imageBufferAttributes; //defaultAttributes 29 | // Encoder Configuration 30 | @property (nonatomic, readonly) VCH264HardwareEncoderParameter *parameter; 31 | 32 | - (VCH264HardwareEncoderParameter *)beginConfiguration; 33 | - (void)commitConfiguration; 34 | 35 | - (OSStatus)encodeSampleBuffer:(VCSampleBuffer *)sampleBuffer; 36 | 37 | + (NSArray *)supportProperties; 38 | + (NSDictionary *)defaultImageBufferAttributes; 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Encoder/VCH264SoftwareEncoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264SoftwareEncoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCH264SoftwareEncoder : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Encoder/VCH264SoftwareEncoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264SoftwareEncoder.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCH264SoftwareEncoder.h" 10 | 11 | @implementation VCH264SoftwareEncoder 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/Encoder/VCVideoEncoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCVideoEncoder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | @class VCSampleBuffer; 13 | @protocol VCVideoEncoder 14 | @required 15 | - (OSStatus)encodeSampleBuffer:(VCSampleBuffer *)sampleBuffer; 16 | @end 17 | 18 | @protocol VCVideoEncoderDelegate 19 | - (void)videoEncoder:(id)encoder didOutputSampleBuffer:(VCSampleBuffer *)sampleBuffer; 20 | - (void)videoEncoder:(id)encoder didOutputFormatDescription:(CMFormatDescriptionRef)description; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Codec/VCAudioConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAudioConverter.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/6. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "VCSampleBuffer.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @class VCAudioConverter; 17 | @protocol VCAudioConverterDelegate 18 | - (void)converter:(VCAudioConverter *)converter didOutputSampleBuffer:(VCSampleBuffer *)sampleBuffer; 19 | - (void)converter:(VCAudioConverter *)converter didOutputFormatDescriptor:(CMFormatDescriptionRef)formatDescription; 20 | @end 21 | 22 | @class VCAudioSpecificConfig; 23 | @interface VCAudioConverter : NSObject 24 | @property (nonatomic, weak) id delegate; 25 | 26 | @property (nonatomic, strong) AVAudioFormat *outputFormat; 27 | @property (nonatomic, strong) AVAudioFormat *sourceFormat; 28 | 29 | @property (nonatomic) UInt32 bitrate; 30 | @property (nonatomic) UInt32 audioConverterQuality; 31 | 32 | - (instancetype)init NS_UNAVAILABLE; 33 | - (instancetype)initWithOutputFormat:(AVAudioFormat *)outputFormat 34 | sourceFormat:(AVAudioFormat *)sourceFormat 35 | delegate:(id)delegate 36 | delegateQueue:(dispatch_queue_t)queue; 37 | 38 | - (OSStatus)convertSampleBuffer:(VCSampleBuffer *)sampleBuffer; 39 | - (OSStatus)convertAudioBufferList:(const AudioBufferList *)audioBufferList 40 | presentationTimeStamp:(CMTime)pts; 41 | - (void)reset; 42 | 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Encoder/Hardware/H264/VCH264BaseEncoderConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264BaseEncoderConfig.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCH264BaseEncoderConfig.h" 10 | 11 | @interface VCH264BaseEncoderConfig : VCBaseEncoderConfig 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Encoder/Hardware/H264/VCH264BaseEncoderConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264BaseEncoderConfig.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCH264BaseEncoderConfig.h" 10 | 11 | @implementation VCH264BaseEncoderConfig 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AAC/VCAudioSpecificConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAudioSpecificConfig.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/31. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | // seealso: https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio 15 | typedef NS_ENUM(uint8_t, VCAudioSpecificConfigObjectTypeSampleRateIndex) { 16 | VCAudioSpecificConfigObjectTypeSampleRateIndex96000 = 0, 17 | VCAudioSpecificConfigObjectTypeSampleRateIndex88200 = 1, 18 | VCAudioSpecificConfigObjectTypeSampleRateIndex64000 = 2, 19 | VCAudioSpecificConfigObjectTypeSampleRateIndex48000 = 3, 20 | VCAudioSpecificConfigObjectTypeSampleRateIndex44100 = 4, 21 | VCAudioSpecificConfigObjectTypeSampleRateIndex32000 = 5, 22 | VCAudioSpecificConfigObjectTypeSampleRateIndex24000 = 6, 23 | VCAudioSpecificConfigObjectTypeSampleRateIndex22050 = 7, 24 | VCAudioSpecificConfigObjectTypeSampleRateIndex16000 = 8, 25 | }; 26 | 27 | @class VCAudioSpecificConfig; 28 | @interface AVAudioFormat (AudioSpecificConfig) 29 | - (VCAudioSpecificConfig *)audioSpecificConfig; 30 | @end 31 | 32 | @interface VCAudioSpecificConfig : NSObject 33 | 34 | @property (nonatomic, assign) AudioFormatFlags objectType; 35 | @property (nonatomic, assign) NSInteger sampleRate; 36 | @property (nonatomic, assign) uint8_t channels; 37 | @property (nonatomic, assign) BOOL frameLengthFlag; // 0 -> 960, 1 -> 1024 38 | @property (nonatomic, assign) BOOL isDependOnCoreCoder; 39 | @property (nonatomic, assign) BOOL isExtension; 40 | 41 | - (instancetype)initWithData:(NSData *)data; 42 | - (OSStatus)createAudioFormatDescription:(CMFormatDescriptionRef _Nullable * _Nullable)outputDescription; 43 | 44 | - (NSData *)serialize; 45 | - (void)deserialize; 46 | 47 | - (NSData *)adtsDataForPacketLength:(NSUInteger)packetLength; 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AAC/VCAudioSpecificConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAudioSpecificConfig.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/31. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAudioSpecificConfig.h" 10 | #import "VCByteArray.h" 11 | 12 | @implementation AVAudioFormat (AudioSpecificConfig) 13 | - (VCAudioSpecificConfig *)audioSpecificConfig { 14 | const AudioStreamBasicDescription *outputDesc = self.streamDescription; 15 | 16 | VCAudioSpecificConfig *config = [[VCAudioSpecificConfig alloc] init]; 17 | config.channels = outputDesc->mChannelsPerFrame; 18 | config.frameLengthFlag = NO; 19 | config.objectType = outputDesc->mFormatFlags; 20 | config.sampleRate = outputDesc->mSampleRate; 21 | config.isDependOnCoreCoder = NO; 22 | config.isExtension = NO; 23 | return config; 24 | } 25 | @end 26 | 27 | @interface VCAudioSpecificConfig () 28 | @property (nonatomic, strong) NSData *data; 29 | @property (nonatomic, assign) VCAudioSpecificConfigObjectTypeSampleRateIndex sampleRateIndex; 30 | @end 31 | 32 | @implementation VCAudioSpecificConfig 33 | 34 | - (instancetype)initWithData:(NSData *)data { 35 | self = [super init]; 36 | if (self) { 37 | _data = data; 38 | } 39 | return self; 40 | } 41 | 42 | - (OSStatus)createAudioFormatDescription:(CMFormatDescriptionRef *)outputDescription { 43 | if (outputDescription == NULL) { 44 | return -1; 45 | } 46 | AudioStreamBasicDescription basicDescription; 47 | basicDescription.mFormatID = kAudioFormatMPEG4AAC; 48 | basicDescription.mSampleRate = self.sampleRate; 49 | basicDescription.mFormatFlags = self.objectType; 50 | basicDescription.mBytesPerFrame = 0; 51 | basicDescription.mFramesPerPacket = self.frameLengthFlag ? 960 : 1024; 52 | basicDescription.mBytesPerPacket = 0; 53 | basicDescription.mChannelsPerFrame = self.channels; 54 | basicDescription.mBitsPerChannel = 0; 55 | basicDescription.mReserved = 0; 56 | 57 | return CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &basicDescription, 0, NULL, 0, NULL, NULL, outputDescription); 58 | } 59 | 60 | - (NSInteger)sampleRate { 61 | switch (self.sampleRateIndex) { 62 | case VCAudioSpecificConfigObjectTypeSampleRateIndex16000: 63 | return 16000; 64 | case VCAudioSpecificConfigObjectTypeSampleRateIndex22050: 65 | return 22050; 66 | case VCAudioSpecificConfigObjectTypeSampleRateIndex24000: 67 | return 24000; 68 | case VCAudioSpecificConfigObjectTypeSampleRateIndex32000: 69 | return 32000; 70 | case VCAudioSpecificConfigObjectTypeSampleRateIndex44100: 71 | return 44100; 72 | case VCAudioSpecificConfigObjectTypeSampleRateIndex64000: 73 | return 64000; 74 | case VCAudioSpecificConfigObjectTypeSampleRateIndex48000: 75 | return 48000; 76 | case VCAudioSpecificConfigObjectTypeSampleRateIndex88200: 77 | return 88200; 78 | case VCAudioSpecificConfigObjectTypeSampleRateIndex96000: 79 | return 96000; 80 | } 81 | return 44100; 82 | } 83 | 84 | - (void)setSampleRate:(NSInteger)sampleRate { 85 | switch (sampleRate) { 86 | case 16000: 87 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex16000; 88 | break; 89 | case 22050: 90 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex22050; 91 | break; 92 | case 24000: 93 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex24000; 94 | break; 95 | case 32000: 96 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex32000; 97 | break; 98 | case 44100: 99 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex44100; 100 | break; 101 | case 64000: 102 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex64000; 103 | break; 104 | case 48000: 105 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex48000; 106 | break; 107 | case 88200: 108 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex88200; 109 | break; 110 | case 96000: 111 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex96000; 112 | break; 113 | default: 114 | self.sampleRateIndex = VCAudioSpecificConfigObjectTypeSampleRateIndex44100; 115 | break; 116 | } 117 | } 118 | 119 | - (AudioFormatFlags)formatFlags { 120 | // seealso: https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio 121 | return (AudioFormatFlags)self.objectType; 122 | } 123 | 124 | - (NSData *)serialize { 125 | VCByteArray *array = [[VCByteArray alloc] init]; 126 | uint16_t v = 0; 127 | v |= (self.objectType & 0x1F) << 11; // 5 bit 128 | v |= (self.sampleRateIndex & 0x0F) << 7; // 4 bit 129 | v |= (self.channels & 0x0F) << 3; // 4 bit 130 | v |= (self.frameLengthFlag & 0x01) << 2; // 1bit 131 | v |= (self.isDependOnCoreCoder & 0x01) << 1; // 1bit 132 | v |= self.isExtension & 0x01; // 1 bit 133 | [array writeUInt16:v]; 134 | return array.data; 135 | } 136 | 137 | - (void)deserialize { 138 | VCByteArray *array = [[VCByteArray alloc] initWithData:self.data]; 139 | uint16_t v = [array readUInt16]; 140 | 141 | self.objectType = (AudioFormatFlags)((v >> 11) & 0x1F); 142 | self.sampleRateIndex = (VCAudioSpecificConfigObjectTypeSampleRateIndex)((v >> 7) & 0x0F); 143 | self.channels = ((v >> 3) & 0x0F); 144 | self.frameLengthFlag = ((v >> 2) & 0x01); 145 | self.isDependOnCoreCoder = ((v >> 1) & 0x01); 146 | self.isExtension = ((v >> 0) & 0x01); 147 | } 148 | 149 | - (NSData *)adtsDataForPacketLength:(NSUInteger)packetLength { 150 | int adtsLength = 7; 151 | uint8_t *packet = (uint8_t *)malloc(sizeof(uint8_t) * adtsLength); 152 | // Variables Recycled by addADTStoPacket 153 | int profile = self.objectType; 154 | //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD; 155 | int freqIdx = self.sampleRateIndex; 156 | int chanCfg = self.channels; //MPEG-4 Audio Channel Configuration. 1 Channel front-center 157 | NSUInteger fullLength = adtsLength + packetLength; 158 | // fill in ADTS data 159 | packet[0] = 0xFF; // 11111111 = syncword 160 | packet[1] = 0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC 161 | packet[2] = ((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2); 162 | packet[3] = ((chanCfg & 3) << 6) + (fullLength >> 11); 163 | packet[4] = (fullLength & 0x7FF) >> 3; 164 | packet[5] = ((fullLength & 7) << 5) + 0x1F; 165 | packet[6] = 0xFC; 166 | NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES]; 167 | return data; 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AMF/VCAMF0Serialization.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAMF0Serialization.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCActionScriptTypes.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | // reference: Action Message Format -- AMF 0 15 | 16 | // see: 2.1 Types Overview 17 | typedef NS_ENUM(uint8_t, VCAMF0TypeMarker) { 18 | VCAMF0TypeMarkerNumber = 0x00, 19 | VCAMF0TypeMarkerBoolean = 0x01, 20 | VCAMF0TypeMarkerString = 0x02, 21 | VCAMF0TypeMarkerObject = 0x03, 22 | VCAMF0TypeMarkerMovieClip = 0x04, // reserved, not support 23 | VCAMF0TypeMarkerNull = 0x05, 24 | VCAMF0TypeMarkerUndefined = 0x06, 25 | VCAMF0TypeMarkerReference = 0x07, 26 | VCAMF0TypeMarkerEcmaArray = 0x08, 27 | VCAMF0TypeMarkerObjectEnd = 0x09, 28 | VCAMF0TypeMarkerStrictArray = 0x0A, 29 | VCAMF0TypeMarkerDate = 0x0B, 30 | VCAMF0TypeMarkerLongString = 0x0C, 31 | VCAMF0TypeMarkerUnsupported = 0x0D, 32 | VCAMF0TypeMarkerRecordset = 0x0E, // reserved, not support 33 | VCAMF0TypeMarkerXmlDocument = 0x0F, 34 | VCAMF0TypeMarkerTypedObject = 0x10, 35 | VCAMF0TypeMarkerAvmplusObject = 0x11 36 | }; 37 | 38 | @class VCActionScriptType; 39 | @interface VCAMF0Serialization : NSObject 40 | 41 | @property (nonatomic, assign) NSInteger position; 42 | @property (nonatomic, readonly) NSData *serializedData; 43 | 44 | - (instancetype)initWithData:(NSData *)data; 45 | 46 | #pragma mark - Serialize Method 47 | - (VCAMF0Serialization *)serialize:(VCActionScriptType *)type; 48 | #pragma mark - Deserialize Method 49 | - (VCActionScriptType *)deserialize; 50 | 51 | @end 52 | 53 | NS_ASSUME_NONNULL_END 54 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AMF/VCAMF0Serialization.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAMF0Serialization.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAMF0Serialization.h" 10 | #import "VCActionScriptTypes.h" 11 | #import "VCByteArray.h" 12 | 13 | @interface VCAMF0Serialization () 14 | @property (nonatomic, strong) VCByteArray *array; 15 | @end 16 | 17 | @implementation VCAMF0Serialization 18 | 19 | - (instancetype)init { 20 | return [self initWithData:[NSData data]]; 21 | } 22 | 23 | - (instancetype)initWithData:(NSData *)data { 24 | self = [super init]; 25 | if (self) { 26 | _array = [[VCByteArray alloc] initWithData:data]; 27 | } 28 | return self; 29 | } 30 | 31 | - (VCAMF0Serialization *)serialize:(VCActionScriptType *)type { 32 | [type serializeTypeMarkToArrayByte:self.array]; 33 | [type serializeToArrayByte:self.array]; 34 | return self; 35 | } 36 | 37 | - (VCActionScriptType *)deserialize { 38 | return [VCActionScriptType deserializeFromByteArray:self.array]; 39 | } 40 | 41 | - (NSInteger)position { 42 | return self.array.postion; 43 | } 44 | 45 | - (void)setPosition:(NSInteger)position { 46 | self.array.postion = position; 47 | } 48 | 49 | - (NSData *)serializedData { 50 | return self.array.data; 51 | } 52 | @end 53 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AMF/VCAMF3Serialization.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAMF3Serialization.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCAMF3Serialization : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AMF/VCAMF3Serialization.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAMF3Serialization.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAMF3Serialization.h" 10 | 11 | @implementation VCAMF3Serialization 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AMF/VCActionScriptTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCActionScriptTypes.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | @class VCByteArray; 13 | @interface VCActionScriptType: NSObject 14 | @property (nonatomic, readonly) uint8_t type; 15 | + (instancetype)deserializeFromByteArray:(VCByteArray *)byteArray; 16 | - (void)serializeTypeMarkToArrayByte:(VCByteArray *)byteArray; 17 | - (void)serializeToArrayByte:(VCByteArray *)byteArray; 18 | - (id)value; 19 | @end 20 | 21 | // seealso: 2.2 Number Type 22 | @interface VCActionScriptNumber: VCActionScriptType 23 | @property (nonatomic, strong) NSNumber *value; 24 | + (instancetype)asTypeWithNumber:(NSNumber *)aNumber; 25 | @end 26 | 27 | @interface NSNumber (VCActionScriptNumber) 28 | - (VCActionScriptNumber *)asNumber; 29 | @end 30 | 31 | // seealso: 2.3 Boolean Type 32 | @interface VCActionScriptBool: VCActionScriptType 33 | @property (nonatomic, assign) BOOL value; 34 | + (instancetype)asTypeWithBool:(BOOL)aBool; 35 | @end 36 | 37 | @interface NSNumber (VCActionScriptBool) 38 | - (VCActionScriptBool *)asBool; 39 | @end 40 | 41 | // seealso: 2.4 String Type 42 | @interface VCActionScriptString: VCActionScriptType 43 | @property (nonatomic, copy) NSString *value; 44 | @property (nonatomic, getter=isLongString, readonly) BOOL longString; 45 | - (BOOL)isLongString; 46 | + (instancetype)emptyString; 47 | + (instancetype)asTypeWithString:(NSString *)aString; 48 | @end 49 | 50 | @interface NSString (VCActionScriptString) 51 | - (VCActionScriptString *)asString; 52 | @end 53 | 54 | // seealso: 2.5 Object Type 55 | @interface VCActionScriptObject: VCActionScriptType 56 | @property (nonatomic, strong) NSMutableDictionary *value; 57 | + (instancetype)asTypeWithDictionary:(NSDictionary *)aDict; 58 | @end 59 | 60 | // seealso: 2.7 Null Type 61 | @interface VCActionScriptNull: VCActionScriptType 62 | + (instancetype)null; 63 | @end 64 | 65 | @interface NSNull (VCActionScriptNull) 66 | + (VCActionScriptNull *)asNull; 67 | @end 68 | 69 | // seealso: 2.8 Undefined Type 70 | @interface VCActionScriptUndefined: VCActionScriptType 71 | + (instancetype)undefined; 72 | @end 73 | 74 | // seealso: 2.10 ECMA Array Type 75 | @interface VCActionScriptECMAArray: VCActionScriptType 76 | @property (nonatomic, strong) NSDictionary *value; 77 | + (instancetype)asTypeWithDictionary:(NSDictionary *)aDict; 78 | @end 79 | 80 | // seealso: 2.11 Object End Type 81 | @interface VCActionScriptObjectEnd: VCActionScriptType 82 | + (instancetype)objectEnd; 83 | @end 84 | 85 | // sesalso: 2.12 Strict Array Type 86 | @interface VCActionScriptStrictArray: VCActionScriptType 87 | @property (nonatomic, strong) NSMutableArray *value; 88 | + (instancetype)asTypeWithArray:(NSArray *)aArray; 89 | @end 90 | 91 | // seealso: 2.13 Date Type 92 | @interface VCActionScriptDate: VCActionScriptType 93 | @property (nonatomic, strong) NSDate *value; 94 | @property (nonatomic, assign) int16_t timeZone; 95 | + (instancetype)asTypeWithDate:(NSDate *)aDate 96 | timeZone:(int16_t)timeZone; 97 | @end 98 | 99 | // seealso: 2.17 XML Document Type 100 | @interface VCActionScriptXMLDocument: VCActionScriptType 101 | @property (nonatomic, copy) NSString *value; 102 | + (instancetype)asTypeWithString:(NSString *)aString; 103 | @end 104 | 105 | 106 | NS_ASSUME_NONNULL_END 107 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AVC/VCAVCConfigurationRecord.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAVCConfigurationRecord.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | // reference: ISO/IEC 14496-15 2010 14 | @interface VCAVCConfigurationRecord : NSObject 15 | @property (nonatomic, readonly) uint8_t AVCProfileIndication; 16 | @property (nonatomic, readonly) uint8_t profileCompatibility; 17 | @property (nonatomic, readonly) uint8_t AVCLevelIndication; 18 | @property (nonatomic, readonly) uint8_t lengthSizeMinusOne; 19 | @property (nonatomic, readonly) uint8_t numOfSequenceParameterSets; 20 | @property (nonatomic, readonly) uint8_t numOfPictureParameterSets; 21 | 22 | @property (nonatomic, readonly) uint8_t chromaFormat; 23 | @property (nonatomic, readonly) uint8_t bitDepthLumaMinus8; 24 | @property (nonatomic, readonly) uint8_t bitDepthChromaMinus8; 25 | @property (nonatomic, readonly) uint8_t numOfSequenceParameterSetExt; 26 | 27 | @property (nonatomic, readonly) NSData *data; 28 | 29 | - (nullable instancetype)initWithFormatDescription:(CMFormatDescriptionRef)formatDescription; 30 | - (nullable instancetype)initWithData:(NSData *)data; 31 | 32 | - (NSInteger)naluLength; 33 | - (OSStatus)createFormatDescription:(CMVideoFormatDescriptionRef _Nullable * _Nonnull)outFormatDescription; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AVC/VCAVCConfigurationRecord.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAVCConfigurationRecord.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAVCConfigurationRecord.h" 10 | #import "VCByteArray.h" 11 | @interface VCAVCConfigurationRecord () 12 | @property (nonatomic, strong) NSData *data; 13 | @property (nonatomic, strong) NSMutableArray *sequenceParameterSets; 14 | @property (nonatomic, strong) NSMutableArray *pictureParameterSets; 15 | @property (nonatomic, strong) NSMutableArray *sequenceParameterSetExt; 16 | @end 17 | 18 | @implementation VCAVCConfigurationRecord 19 | 20 | - (instancetype)initWithFormatDescription:(CMFormatDescriptionRef)formatDescription { 21 | NSDictionary *extension = (__bridge NSDictionary *)CMFormatDescriptionGetExtensions(formatDescription); 22 | if (extension == nil) { 23 | return nil; 24 | } 25 | NSDictionary *atoms = extension[(__bridge NSString *)kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms]; 26 | if (atoms == nil) { 27 | return nil; 28 | } 29 | NSData *avc = atoms[@"avcC"]; 30 | if (avc == nil) { 31 | return nil; 32 | } 33 | 34 | return [self initWithData:avc]; 35 | } 36 | 37 | - (instancetype)initWithData:(NSData *)data { 38 | self = [super init]; 39 | if (self) { 40 | _data = data; 41 | _sequenceParameterSets = [NSMutableArray array]; 42 | _pictureParameterSets = [NSMutableArray array]; 43 | _sequenceParameterSetExt = [NSMutableArray array]; 44 | @try { 45 | VCByteArray *array = [[VCByteArray alloc] initWithData:data]; 46 | if ([array readUInt8] != 1) return nil; 47 | 48 | _AVCProfileIndication = [array readUInt8]; 49 | _profileCompatibility = [array readUInt8]; 50 | _AVCLevelIndication = [array readUInt8]; 51 | _lengthSizeMinusOne = [array readUInt8] & 0x03; 52 | _numOfSequenceParameterSets = [array readUInt8] & 0x1F; 53 | 54 | for (int i = 0; i < _numOfSequenceParameterSets; ++i) { 55 | uint16_t sequenceParameterSetLength = [array readUInt16]; 56 | NSData *sequenceParameterSetNALUnit = [array readBytes:sequenceParameterSetLength]; 57 | [_sequenceParameterSets addObject:sequenceParameterSetNALUnit]; 58 | } 59 | 60 | _numOfPictureParameterSets = [array readUInt8]; 61 | for (int i = 0; i < _numOfPictureParameterSets; ++i) { 62 | uint16_t pictureParameterSetLength = [array readUInt16]; 63 | NSData *pictureParameterSetNALUnit = [array readBytes:pictureParameterSetLength]; 64 | [_pictureParameterSets addObject:pictureParameterSetNALUnit]; 65 | } 66 | 67 | // 有些文件并不符合规范.... 68 | if (_AVCProfileIndication == 100 || 69 | _AVCProfileIndication == 110 || 70 | _AVCProfileIndication == 122 || 71 | _AVCProfileIndication == 144) { 72 | _chromaFormat = [array readUInt8] & 0x03; 73 | _bitDepthLumaMinus8 = [array readUInt8] & 0x1F; 74 | _bitDepthChromaMinus8 = [array readUInt8] & 0x1F; 75 | _numOfSequenceParameterSetExt = [array readUInt8]; 76 | 77 | for (int i = 0; i < _numOfSequenceParameterSetExt; i++) { 78 | uint16_t sequenceParameterSetExtLength = [array readUInt16]; 79 | NSData *sequenceParameterSetExtNALUnit = [array readBytes:sequenceParameterSetExtLength]; 80 | [_sequenceParameterSetExt addObject:sequenceParameterSetExtNALUnit]; 81 | } 82 | } 83 | 84 | } @catch (NSException *exception) { 85 | if (_sequenceParameterSets.count > 0 || 86 | _pictureParameterSets.count > 0) { 87 | return self; 88 | } 89 | return nil; 90 | } 91 | } 92 | return self; 93 | } 94 | 95 | - (NSInteger)naluLength { 96 | return _lengthSizeMinusOne + 1; 97 | } 98 | 99 | - (OSStatus)createFormatDescription:(CMVideoFormatDescriptionRef *)outFormatDescription { 100 | // Only Support First SPS PPS 101 | if (!(_sequenceParameterSets.count > 0 && 102 | _pictureParameterSets.count > 0)) { 103 | return -1; 104 | } 105 | NSData *firstSPSData = _sequenceParameterSets[0]; 106 | NSData *firstPPSData = _pictureParameterSets[0]; 107 | 108 | const uint8_t *parameterSets[] = { 109 | (const uint8_t *)[firstSPSData bytes], 110 | (const uint8_t *)[firstPPSData bytes], 111 | }; 112 | 113 | size_t parameterSetSizes[] = { 114 | firstSPSData.length, 115 | firstPPSData.length 116 | }; 117 | 118 | return CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault, 119 | 2, parameterSets, parameterSetSizes, (int)[self naluLength], outFormatDescription); 120 | } 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AVC/VCAVCFormatStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAVCFormatStream.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | // conver avc format to annex-b format 13 | // AVC Format: 14 | // xx xx xx xx [4 bytes length] | xx xx xx xx .... [data] 15 | // Annex-B Format: 16 | // 00 00 00 01 xx xx xx xx 17 | // 00 00 01 xx xx xx 18 | // ------------------------------------------------------ 19 | @class VCAnnexBFormatStream; 20 | @interface VCAVCFormatStream : NSObject 21 | 22 | @property (nonatomic, readonly) NSData *data; 23 | // should be 4 or 3 24 | @property (nonatomic, readonly) NSUInteger startCodeLength; 25 | 26 | @property (nonatomic, readonly) NSArray *nalus; 27 | @property (nonatomic, assign) Class naluClass; 28 | 29 | - (instancetype)initWithData:(NSData *)aData 30 | startCodeLength:(NSUInteger)startCodeLength; 31 | 32 | - (VCAnnexBFormatStream *)toAnnexBFormatData; 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AVC/VCAVCFormatStream.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAVCFormatStream.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAVCFormatStream.h" 10 | #import "VCAnnexBFormatStream.h" 11 | #import "VCByteArray.h" 12 | #import "VCH264NALU.h" 13 | 14 | @interface VCAVCFormatStream () 15 | @property (nonatomic, copy) NSArray *nalus; 16 | @end 17 | 18 | @implementation VCAVCFormatStream 19 | - (instancetype)initWithData:(NSData *)aData 20 | startCodeLength:(NSUInteger)startCodeLength { 21 | self = [super self]; 22 | if (self) { 23 | _data = aData; 24 | _startCodeLength = startCodeLength; 25 | } 26 | return self; 27 | } 28 | 29 | - (VCAnnexBFormatStream *)toAnnexBFormatData { 30 | NSMutableData *outputData = [[NSMutableData alloc] init]; 31 | static uint8_t startCode[4] = {0x00, 0x00, 0x00, 0x01}; 32 | 33 | VCByteArray *array = [[VCByteArray alloc] initWithData:_data]; 34 | 35 | @try { 36 | do { 37 | uint32_t len = 0; 38 | if (self.startCodeLength == 4) { 39 | len = [array readUInt32]; 40 | } else if (self.startCodeLength == 3) { 41 | len = [array readUInt24]; 42 | } 43 | [outputData appendBytes:startCode length:4]; 44 | [outputData appendData:[array readBytes:len]]; 45 | } while (array.bytesAvailable > 0); 46 | VCAnnexBFormatStream *data = [[VCAnnexBFormatStream alloc] initWithData:outputData]; 47 | return data; 48 | } @catch (VCByteArrayException *exception) { 49 | return nil; 50 | } 51 | } 52 | 53 | - (NSArray *)nalus { 54 | if (_nalus) { 55 | return _nalus; 56 | } 57 | NSMutableArray *arr = [[NSMutableArray alloc] init]; 58 | VCByteArray *array = [[VCByteArray alloc] initWithData:_data]; 59 | @try { 60 | do { 61 | uint32_t len = 0; 62 | if (self.startCodeLength == 4) { 63 | len = [array readUInt32]; 64 | } else if (self.startCodeLength == 3) { 65 | len = [array readUInt24]; 66 | } 67 | NSData *naluData = [array readBytes:len]; 68 | id naluObj = [[self.naluClass alloc] initWithData:naluData]; 69 | if (naluObj) { 70 | [arr addObject:naluObj]; 71 | } 72 | 73 | } while (array.bytesAvailable > 0); 74 | } @catch (NSException *exception) { 75 | _nalus = arr; 76 | return _nalus; 77 | } 78 | _nalus = arr; 79 | return _nalus; 80 | } 81 | @end 82 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AnnexB/VCAnnexBFormatParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAnnexBFormatParser.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/28. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | @class VCAnnexBFormatStream; 13 | @interface VCAnnexBFormatParser : NSObject 14 | 15 | - (void)appendData:(NSData *)data; 16 | - (nullable VCAnnexBFormatStream *)next; 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AnnexB/VCAnnexBFormatParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAnnexBFormatParser.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/28. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAnnexBFormatParser.h" 10 | #import "VCAnnexBFormatStream.h" 11 | 12 | #define kVCAnnexBFormatParserDefaultBufferCapacity (40960) 13 | 14 | // +-----+ 15 | // | | 16 | // v | 17 | // 7--(0)-->2--(0)-->1--(0)-->0-(0)-+ 18 | // ^ | | | 19 | // | (1) (1) (1) 20 | // | | | | 21 | // +--------+ v v 22 | // 4 5 23 | 24 | 25 | typedef NS_ENUM(NSUInteger, VCAnnexBFormatParserState) { 26 | VCAnnexBFormatParserStateInit = 7, 27 | VCAnnexBFormatParserStateFind1Zero = 2, 28 | VCAnnexBFormatParserStateFind2Zero = 1, 29 | VCAnnexBFormatParserStateFindMoreThan2Zero = 0, 30 | VCAnnexBFormatParserStateFind2ZeroAnd1One = 4, 31 | VCAnnexBFormatParserStateFindMoreThan2ZeroAnd1One = 5, 32 | }; 33 | 34 | @interface VCAnnexBFormatParser () 35 | // parse 缓冲区 36 | @property (nonatomic, strong) NSMutableData *parsingBuffer; 37 | // 最后一个 分隔符 之后的数据加到 appendingBuffer中, 38 | // 和parsingBuffer在appendData方法中合并 39 | @property (nonatomic, strong) NSMutableData *appendingBuffer; 40 | 41 | // 解析位置 42 | @property (nonatomic, assign) NSInteger position; 43 | @property (nonatomic, assign) NSInteger nextFramePosition; 44 | @property (nonatomic, assign) BOOL isFirstStartCode; 45 | @end 46 | 47 | @implementation VCAnnexBFormatParser 48 | 49 | - (instancetype)init { 50 | self = [super init]; 51 | if (self) { 52 | _parsingBuffer = [[NSMutableData alloc] initWithCapacity:kVCAnnexBFormatParserDefaultBufferCapacity]; 53 | _appendingBuffer = [[NSMutableData alloc] initWithCapacity:kVCAnnexBFormatParserDefaultBufferCapacity]; 54 | _isFirstStartCode = YES; 55 | } 56 | return self; 57 | } 58 | 59 | - (VCAnnexBFormatStream *)next { 60 | NSMutableData *outputData = [[NSMutableData alloc] initWithCapacity:self.parsingBuffer.length]; 61 | NSData *payloadData = nil; 62 | BOOL findStartCode = NO; 63 | 64 | VCAnnexBFormatParserState state = VCAnnexBFormatParserStateInit; 65 | static uint8_t reserveStartCode[4] = {0x00, 0x00, 0x00, 0x01}; 66 | 67 | uint8_t *ptr = (uint8_t *)[self.parsingBuffer bytes]; 68 | 69 | for (; _position < self.parsingBuffer.length; ++_position) { 70 | uint8_t byte = *(ptr + _position); 71 | // alsosee: ffmpeg h264_find_frame_end() 72 | 73 | if (state == VCAnnexBFormatParserStateInit) { 74 | if (byte == 0) { 75 | state = VCAnnexBFormatParserStateFind1Zero; 76 | } 77 | } else if (state <= VCAnnexBFormatParserStateFind1Zero) { 78 | // 找到0的时候 79 | if (byte == 1) { 80 | // 发现一个1 81 | state ^= VCAnnexBFormatParserStateFindMoreThan2ZeroAnd1One; 82 | } else if (byte) { 83 | state = VCAnnexBFormatParserStateInit; 84 | } else { 85 | state >>= 1; // 发现一个0 86 | } 87 | } else if (state <= VCAnnexBFormatParserStateFindMoreThan2ZeroAnd1One) { 88 | // 如果startCode之前无数据,跳过 89 | if (_isFirstStartCode) { 90 | _isFirstStartCode = NO; 91 | state = VCAnnexBFormatParserStateInit; 92 | _nextFramePosition = _position; 93 | continue; 94 | } 95 | findStartCode = YES; 96 | break; 97 | } else { 98 | // TODO 99 | } 100 | } 101 | 102 | if (findStartCode) { 103 | if (state == VCAnnexBFormatParserStateFindMoreThan2ZeroAnd1One) { 104 | // 00 00 00 01 105 | if (_position - _nextFramePosition > 4) { 106 | payloadData = [self.parsingBuffer subdataWithRange:NSMakeRange(_nextFramePosition, _position - _nextFramePosition)]; 107 | [outputData appendBytes:reserveStartCode length:4]; 108 | [outputData appendData:[payloadData subdataWithRange:NSMakeRange(0, payloadData.length - 4)]]; 109 | payloadData = [[NSMutableData alloc] initWithCapacity:self.parsingBuffer.length]; 110 | 111 | VCAnnexBFormatStream *data = [[VCAnnexBFormatStream alloc] initWithData:outputData]; 112 | findStartCode = NO; 113 | state = VCAnnexBFormatParserStateInit; 114 | _nextFramePosition = _position; 115 | return data; 116 | } 117 | } else if (state == VCAnnexBFormatParserStateFind2ZeroAnd1One) { 118 | // 00 00 01 119 | if (_position - _nextFramePosition > 3) { 120 | payloadData = [self.parsingBuffer subdataWithRange:NSMakeRange(_nextFramePosition, _position - _nextFramePosition)]; 121 | [outputData appendBytes:reserveStartCode length:4]; 122 | [outputData appendData:[payloadData subdataWithRange:NSMakeRange(0, payloadData.length - 3)]]; 123 | payloadData = [[NSMutableData alloc] initWithCapacity:self.parsingBuffer.length]; 124 | 125 | VCAnnexBFormatStream *data = [[VCAnnexBFormatStream alloc] initWithData:outputData]; 126 | findStartCode = NO; 127 | state = VCAnnexBFormatParserStateInit; 128 | _nextFramePosition = _position; 129 | return data; 130 | } 131 | } 132 | } else { 133 | // 末尾数据 134 | payloadData = [self.parsingBuffer subdataWithRange:NSMakeRange(_nextFramePosition, _position - _nextFramePosition)]; 135 | [outputData appendBytes:reserveStartCode length:4]; 136 | [outputData appendData:payloadData]; 137 | _appendingBuffer = outputData; 138 | } 139 | 140 | return nil; 141 | } 142 | 143 | - (void)appendData:(NSData *)data { 144 | _parsingBuffer = [[NSMutableData alloc] initWithCapacity:_appendingBuffer.length + data.length]; 145 | if ([self.appendingBuffer length] > 0) { 146 | [_parsingBuffer appendData:_appendingBuffer]; 147 | _appendingBuffer = [[NSMutableData alloc] initWithCapacity:kVCAnnexBFormatParserDefaultBufferCapacity]; 148 | } 149 | [_parsingBuffer appendData:data]; 150 | _position = 0; 151 | _nextFramePosition = _parsingBuffer.length; 152 | _isFirstStartCode = YES; 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AnnexB/VCAnnexBFormatStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAnnexBFormatStream.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | // Convert annex-b format to avc format 13 | // Annex-B Format: 14 | // 00 00 00 01 xx xx xx .... 15 | // 00 00 01 xx xx xx xx .... 16 | // AVC Format: 17 | // xx xx xx xx [4 bytes length] | xx xx xx xx .... [data] 18 | // ----------------------------- 19 | @class VCAVCFormatStream; 20 | @interface VCAnnexBFormatStream : NSObject 21 | 22 | @property (nonatomic, readonly) NSData *data; 23 | // 使用AnnexB格式数据初始化 24 | - (instancetype)initWithData:(NSData *)aData; 25 | 26 | - (VCAVCFormatStream *)toAVCFormatStream; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/AnnexB/VCAnnexBFormatStream.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAnnexBFormatStream.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAnnexBFormatStream.h" 10 | #import "VCAVCFormatStream.h" 11 | #import "VCByteArray.h" 12 | 13 | 14 | // +-----+ 15 | // | | 16 | // v | 17 | // 7--(0)-->2--(0)-->1--(0)-->0-(0)-+ 18 | // ^ | | | 19 | // | (1) (1) (1) 20 | // | | | | 21 | // +--------+ v v 22 | // 4 5 23 | 24 | 25 | typedef NS_ENUM(NSUInteger, VCAnnexBFormatStreamParseState) { 26 | VCAnnexBFormatStreamParseStateInit = 7, 27 | VCAnnexBFormatStreamParseStateFind1Zero = 2, 28 | VCAnnexBFormatStreamParseStateFind2Zero = 1, 29 | VCAnnexBFormatStreamParseStateFindMoreThan2Zero = 0, 30 | VCAnnexBFormatStreamParseStateFind2ZeroAnd1One = 4, 31 | VCAnnexBFormatStreamParseStateFindMoreThan2ZeroAnd1One = 5, 32 | }; 33 | 34 | @implementation VCAnnexBFormatStream 35 | 36 | - (instancetype)initWithData:(NSData *)aData { 37 | self = [super init]; 38 | if (self) { 39 | _data = aData; 40 | } 41 | return self; 42 | } 43 | 44 | - (VCAVCFormatStream *)toAVCFormatStream { 45 | NSMutableData *outputData = [[NSMutableData alloc] initWithCapacity:_data.length]; 46 | NSMutableData *payloadData = [[NSMutableData alloc] initWithCapacity:_data.length]; 47 | VCAnnexBFormatStreamParseState state = VCAnnexBFormatStreamParseStateInit; 48 | 49 | NSUInteger nextFramePos = _data.length; 50 | uint8_t *ptr = (uint8_t *)[_data bytes]; 51 | 52 | for (NSUInteger i = 0; i < _data.length; ++i) { 53 | uint8_t byte = *(ptr + i); 54 | // alsosee: ffmpeg h264_find_frame_end() 55 | 56 | if (state == VCAnnexBFormatStreamParseStateInit) { 57 | if (byte == 0) { 58 | state = VCAnnexBFormatStreamParseStateFind1Zero; 59 | } 60 | } else if (state <= VCAnnexBFormatStreamParseStateFind1Zero) { 61 | // 找到0的时候 62 | if (byte == 1) { 63 | // 发现一个1 64 | state ^= VCAnnexBFormatStreamParseStateFindMoreThan2ZeroAnd1One; 65 | } else if (byte) { 66 | state = VCAnnexBFormatStreamParseStateInit; 67 | } else { 68 | state >>= 1; // 发现一个0 69 | } 70 | } else if (state <= VCAnnexBFormatStreamParseStateFindMoreThan2ZeroAnd1One) { 71 | if (state == VCAnnexBFormatStreamParseStateFind2ZeroAnd1One) { 72 | // 找到 00 00 01 73 | if (payloadData.length > 3) { 74 | uint32_t len = CFSwapInt32HostToBig((uint32_t)payloadData.length - 3); 75 | [outputData appendBytes:&len length:4]; 76 | [outputData appendData:[payloadData subdataWithRange:NSMakeRange(0, payloadData.length - 3)]]; 77 | payloadData = [[NSMutableData alloc] initWithCapacity:_data.length]; 78 | } 79 | } else if (state == VCAnnexBFormatStreamParseStateFindMoreThan2ZeroAnd1One) { 80 | // 找到 00 00 00 01 81 | if (payloadData.length > 4) { 82 | uint32_t len = CFSwapInt32HostToBig((uint32_t)payloadData.length - 4); 83 | [outputData appendBytes:&len length:4]; 84 | [outputData appendData:[payloadData subdataWithRange:NSMakeRange(0, payloadData.length - 4)]]; 85 | payloadData = [[NSMutableData alloc] initWithCapacity:_data.length]; 86 | } 87 | } 88 | nextFramePos = i; 89 | state = VCAnnexBFormatStreamParseStateInit; 90 | } else { 91 | // TODO 92 | 93 | } 94 | 95 | if (i >= nextFramePos) { 96 | // read data 97 | [payloadData appendBytes:&byte length:1]; 98 | } 99 | } 100 | 101 | if (payloadData.length > 0) { 102 | // 末尾数据 103 | uint32_t len = CFSwapInt32HostToBig((uint32_t)payloadData.length); 104 | [outputData appendBytes:&len length:4]; 105 | [outputData appendData:payloadData]; 106 | payloadData = [[NSMutableData alloc] init]; 107 | } 108 | 109 | VCAVCFormatStream *avcStream = [[VCAVCFormatStream alloc] initWithData:outputData startCodeLength:4]; 110 | return avcStream; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/FLV/VCFLVFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCFLVFile.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/30. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class VCFLVTag; 14 | @interface VCFLVFile : NSObject 15 | 16 | @property (nonatomic, readonly) uint8_t version; 17 | @property (nonatomic, readonly) uint32_t dataOffset; 18 | @property (nonatomic, strong) NSData *headerData; 19 | 20 | @property (nonatomic, readonly) NSUInteger fileSize; 21 | @property (nonatomic, readonly) NSUInteger currentTagOffsetInFile; 22 | @property (nonatomic, assign) NSUInteger currentFileOffset; 23 | 24 | - (nullable instancetype)initWithURL:(NSURL *)fileURL; 25 | - (nullable VCFLVTag *)nextTag; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/FLV/VCFLVFile.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCFLVFile.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/30. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCFLVFile.h" 10 | #import "VCByteArray.h" 11 | #import "VCFLVTag.h" 12 | 13 | #define kVCFLVFileHeaderSize (9) 14 | #define kVCFLVFileFirstTagOffset (13) 15 | 16 | @interface VCFLVFile () 17 | @property (nonatomic, strong) NSFileHandle *fileHandle; 18 | @end 19 | 20 | @implementation VCFLVFile 21 | - (instancetype)initWithURL:(NSURL *)fileURL { 22 | self = [super init]; 23 | if (self) { 24 | _currentFileOffset = 0; 25 | _currentTagOffsetInFile = 0; 26 | NSError *error; 27 | _fileHandle = [NSFileHandle fileHandleForReadingFromURL:fileURL error:&error]; 28 | if (error && ![self isFLVFile]) { 29 | return nil; 30 | } 31 | _fileSize = (NSUInteger)[_fileHandle seekToEndOfFile]; 32 | [_fileHandle seekToFileOffset:0]; 33 | 34 | _headerData = [_fileHandle readDataOfLength:kVCFLVFileHeaderSize]; 35 | _currentFileOffset = kVCFLVFileHeaderSize; 36 | [_fileHandle seekToFileOffset:kVCFLVFileFirstTagOffset]; 37 | _currentFileOffset = kVCFLVFileFirstTagOffset; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)setCurrentFileOffset:(NSUInteger)currentFileOffset { 43 | _currentFileOffset = currentFileOffset; 44 | [_fileHandle seekToFileOffset:currentFileOffset]; 45 | } 46 | 47 | - (BOOL)isFLVFile { 48 | VCByteArray *array = [[VCByteArray alloc] initWithData:self.headerData]; 49 | if ('F' == [array readInt8] && 50 | 'L' == [array readInt8] && 51 | 'V' == [array readInt8]) { 52 | return YES; 53 | } 54 | return NO; 55 | } 56 | 57 | - (uint8_t)version { 58 | VCByteArray *array = [[VCByteArray alloc] initWithData:self.headerData]; 59 | array.postion = 3; 60 | return [array readUInt8]; 61 | } 62 | 63 | - (uint32_t)dataOffset { 64 | VCByteArray *array = [[VCByteArray alloc] initWithData:self.headerData]; 65 | array.postion = 5; 66 | return [array readUInt32]; 67 | } 68 | 69 | - (VCFLVTag *)nextTag { 70 | _currentTagOffsetInFile = _currentFileOffset; 71 | if (_currentFileOffset == _fileSize) { 72 | return nil; 73 | } 74 | 75 | [_fileHandle seekToFileOffset:_currentFileOffset]; 76 | 77 | VCFLVTag *nextTag = nil; 78 | 79 | NSMutableData *tagData = [[NSMutableData alloc] initWithData:[_fileHandle readDataOfLength:kVCFLVTagHeaderSize]]; 80 | if (tagData == nil) { 81 | return nil; 82 | } 83 | 84 | VCByteArray *array = [[VCByteArray alloc] initWithData:tagData]; 85 | VCFLVTagType type = [array readUInt8]; 86 | uint32_t tagSize = [array readUInt24]; 87 | [tagData appendData:[_fileHandle readDataOfLength:tagSize]]; 88 | 89 | 90 | if (type == VCFLVTagTypeVideo) { 91 | nextTag = [[VCFLVVideoTag alloc] initWithData:tagData]; 92 | [nextTag deserialize]; 93 | } else if (type == VCFLVTagTypeAudio) { 94 | nextTag = [[VCFLVAudioTag alloc] initWithData:tagData]; 95 | [nextTag deserialize]; 96 | } else if (type == VCFLVTagTypeMeta) { 97 | nextTag = [[VCFLVMetaTag alloc] initWithData:tagData]; 98 | [nextTag deserialize]; 99 | } else { 100 | 101 | } 102 | _currentFileOffset += tagData.length; 103 | _currentFileOffset += 4; 104 | return nextTag; 105 | } 106 | 107 | - (void)dealloc { 108 | [_fileHandle closeFile]; 109 | } 110 | @end 111 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/FLV/VCFLVReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCFLVReader.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/30. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "VCAssetReader.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | @class VCFLVReader; 15 | 16 | @interface VCFLVVideoKeyFrameIndex : NSObject 17 | @property (nonatomic, assign) NSUInteger position; 18 | @property (nonatomic, assign) CMTime presentationTime; 19 | @end 20 | 21 | @interface VCFLVReader : VCAssetReader 22 | 23 | @property (nonatomic, readonly) BOOL isReading; 24 | 25 | @property (nonatomic, readonly) NSArray *keyFrameIndex; 26 | // 必须调用createSeekTable之后才能获取到时长 27 | // [TODO]: 使用meta tag 获取时长 28 | @property (nonatomic, readonly) CMTime duration; 29 | 30 | - (nullable instancetype)initWithURL:(NSURL *)url; 31 | 32 | - (void)createSeekTable; 33 | 34 | - (void)starAsyncReading; 35 | - (void)startReading; 36 | - (void)stopReading; 37 | 38 | - (void)seekToTime:(CMTime)time; 39 | @end 40 | 41 | @interface NSArray (VCFLVReaderSeek) 42 | - (nullable VCFLVVideoKeyFrameIndex *)indexOfTime:(CMTime)time; 43 | @end 44 | NS_ASSUME_NONNULL_END 45 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/FLV/VCFLVTag.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCFLVTag.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/30. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define kVCFLVTagHeaderSize (11) 12 | #define kVCFLVVideoTagExternHeaderSize (5) 13 | #define kVCFLVAudioTagExternHeaderSize (2) 14 | #define kVCFLVMetaTagExternHeaderSize (0) 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | typedef NS_ENUM(uint8_t, VCFLVTagType) { 19 | VCFLVTagTypeAudio = 8, 20 | VCFLVTagTypeVideo = 9, 21 | VCFLVTagTypeMeta = 18, 22 | VCFLVTagReserve = 0, 23 | }; 24 | 25 | @interface VCFLVTag : NSObject 26 | @property (nonatomic, assign) VCFLVTagType tagType; 27 | @property (nonatomic, assign) uint32_t dataSize; 28 | @property (nonatomic, assign) uint32_t timestamp; 29 | @property (nonatomic, assign) uint8_t timestampExtended; 30 | @property (nonatomic, assign) uint32_t streamID; 31 | @property (nonatomic, strong) NSData *payloadData; 32 | + (instancetype)tag; 33 | - (nullable instancetype)initWithData:(NSData *)data; 34 | - (NSData *)payloadDataWithoutExternTimestamp; 35 | - (uint32_t)extendedTimestamp; 36 | - (void)setExtendedTimestamp:(uint32_t)extendedTimestamp; 37 | 38 | - (NSData *)serialize; 39 | - (void)deserialize; 40 | @end 41 | 42 | typedef NS_ENUM(uint8_t, VCFLVVideoTagFrameType) { 43 | VCFLVVideoTagFrameTypeKeyFrame = 1, 44 | VCFLVVideoTagFrameTypeInterFrame = 2, 45 | VCFLVVideoTagFrameTypeDisposableInterFrame = 3, 46 | VCFLVVideoTagFrameTypeGeneratedKeyFrame = 4, 47 | VCFLVVideoTagFrameTypeVideoInfoFrame = 5, 48 | }; 49 | 50 | typedef NS_ENUM(uint8_t, VCFLVVideoTagEncodeID) { 51 | VCFLVVideoTagEncodeIDJPEG = 1, 52 | VCFLVVideoTagEncodeIDH263 = 2, 53 | VCFLVVideoTagEncodeIDScreenVideo = 3, 54 | VCFLVVideoTagEncodeIDOn2VP6 = 4, 55 | VCFLVVideoTagEncodeIDOn2VP6WithAlpha = 5, 56 | VCFLVVideoTagEncodeIDScreenVideoVersion2 = 6, 57 | VCFLVVideoTagEncodeIDAVC = 7, 58 | }; 59 | 60 | typedef NS_ENUM(uint8_t, VCFLVVideoTagAVCPacketType) { 61 | VCFLVVideoTagAVCPacketTypeSequenceHeader = 0, 62 | VCFLVVideoTagAVCPacketTypeNALU = 1, 63 | VCFLVVideoTagAVCPacketTypeEndOfSequence = 2, 64 | }; 65 | 66 | @interface VCFLVVideoTag : VCFLVTag 67 | @property (nonatomic, assign) VCFLVVideoTagFrameType frameType; 68 | @property (nonatomic, assign) VCFLVVideoTagEncodeID encodeID; 69 | @property (nonatomic, assign) VCFLVVideoTagAVCPacketType AVCPacketType; 70 | @property (nonatomic, assign) uint32_t compositionTime; 71 | 72 | + (instancetype)sequenceHeaderTagForAVC; 73 | + (instancetype)tagForAVC; 74 | 75 | - (BOOL)isSupportCurrentFrameType; 76 | - (uint32_t)presentationTimeStamp; 77 | @end 78 | 79 | typedef NS_ENUM(uint8_t, VCFLVAudioTagFormatType) { 80 | VCFLVAudioTagFormatTypeLinearPCMPlatformEndian = 0, 81 | VCFLVAudioTagFormatTypeADPCM = 1, 82 | VCFLVAudioTagFormatTypeMP3 = 2, 83 | VCFLVAudioTagFormatTypeLinearPCMLittleEndian = 3, 84 | VCFLVAudioTagFormatTypeNellymoser16kHzMono = 4, 85 | VCFLVAudioTagFormatTypeNellymoser8kHzMono = 5, 86 | VCFLVAudioTagFormatTypeNellymoser = 6, 87 | VCFLVAudioTagFormatTypeG711ALaw = 7, 88 | VCFLVAudioTagFormatTypeG711MuLaw = 8, 89 | VCFLVAudioTagFormatTypeReserved = 9, 90 | VCFLVAudioTagFormatTypeAAC = 10, 91 | VCFLVAudioTagFormatTypeSpeex = 11, 92 | VCFLVAudioTagFormatTypeMP38kHz = 14, 93 | VCFLVAudioTagFormatTypeDeviceSpecificSound = 15, 94 | }; 95 | 96 | typedef NS_ENUM(uint8_t, VCFLVAudioTagSampleRate) { 97 | VCFLVAudioTagSampleRate5k5Hz = 0, 98 | VCFLVAudioTagSampleRate11kHz = 1, 99 | VCFLVAudioTagSampleRate22kHz = 2, 100 | VCFLVAudioTagSampleRate44kHz = 3, 101 | }; 102 | 103 | typedef NS_ENUM(uint8_t, VCFLVAudioTagSampleLength) { 104 | VCFLVAudioTagSampleLength8Bit = 0, 105 | VCFLVAudioTagSampleLength16Bit = 1, 106 | }; 107 | 108 | typedef NS_ENUM(uint8_t, VCFLVAudioTagAudioType) { 109 | VCFLVAudioTagAudioTypeMono = 0, 110 | VCFLVAudioTagAudioTypeStereo = 1, 111 | }; 112 | 113 | typedef NS_ENUM(uint8_t, VCFLVAudioTagAACPacketType) { 114 | VCFLVAudioTagAACPacketTypeSequenceHeader = 0, 115 | VCFLVAudioTagAACPacketTypeRaw = 1, 116 | }; 117 | 118 | @interface VCFLVAudioTag : VCFLVTag 119 | @property (nonatomic, assign) VCFLVAudioTagFormatType formatType; 120 | @property (nonatomic, assign) VCFLVAudioTagSampleRate sampleRate; 121 | @property (nonatomic, assign) VCFLVAudioTagSampleLength sampleLength; 122 | @property (nonatomic, assign) VCFLVAudioTagAudioType audioType; 123 | @property (nonatomic, assign) VCFLVAudioTagAACPacketType AACPacketType; 124 | 125 | + (instancetype)sequenceHeaderTagForAAC; 126 | + (instancetype)tagForAAC; 127 | @end 128 | 129 | @interface VCFLVMetaTag : VCFLVTag 130 | 131 | @end 132 | NS_ASSUME_NONNULL_END 133 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/FLV/VCFLVWriter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCFLVWriter.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/30. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCFLVWriter : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/FLV/VCFLVWriter.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCFLVWriter.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/30. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCFLVWriter.h" 10 | 11 | @implementation VCFLVWriter 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/H264/VCRawH264Reader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRawH264Reader.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAssetReader.h" 10 | #import "VCSampleBuffer.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface VCRawH264Reader : VCAssetReader 15 | 16 | - (instancetype)initWithURL:(NSURL *)fileURL; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/H265/VCRawH265Reader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRawH265Reader.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCRawH265Reader : VCAssetReader 14 | 15 | - (instancetype)initWithURL:(NSURL *)fileURL; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/MP4/VCMP4Reader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCMP4Reader.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAssetReader.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCMP4Reader : VCAssetReader 14 | 15 | @property (nonatomic, strong) NSError *lastError; 16 | 17 | - (instancetype)init NS_UNAVAILABLE; 18 | - (nullable instancetype)initWithURL:(NSURL *)fileURL error:(NSError * _Nullable * _Nullable)error; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/MP4/VCMP4Reader.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCMP4Reader.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCMP4Reader.h" 11 | 12 | @interface VCMP4Reader () 13 | @property (nonatomic, strong) AVAssetReader *assetReader; 14 | @property (nonatomic, strong) AVAssetReaderTrackOutput *audioOutput; 15 | @property (nonatomic, strong) AVAssetReaderTrackOutput *videoOutput; 16 | @end 17 | 18 | @implementation VCMP4Reader 19 | 20 | - (instancetype)initWithURL:(NSURL *)fileURL error:(NSError * _Nullable __autoreleasing *)error { 21 | self = [super init]; 22 | if (self) { 23 | NSError *err = nil; 24 | do { 25 | AVAsset *asset = [AVAsset assetWithURL:fileURL]; 26 | AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject]; 27 | AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject]; 28 | 29 | _assetReader = [[AVAssetReader alloc] initWithAsset:asset error:&err]; 30 | if (error != nil|| 31 | _assetReader == nil) { 32 | break; 33 | } 34 | 35 | if (videoTrack) { 36 | _videoOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:@{ 37 | 38 | }]; 39 | } 40 | if (audioTrack) { 41 | _audioOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:audioTrack outputSettings:@{ 42 | 43 | }]; 44 | } 45 | 46 | } while (0); 47 | if (*error != nil) { 48 | *error = err; 49 | } 50 | if (err == nil) { 51 | return self; 52 | } 53 | } 54 | return nil; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/VCAssetReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAssetReader.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "VCSampleBuffer.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @class VCAssetReader; 16 | 17 | @protocol VCAssetReaderDelegate 18 | - (void)reader:(VCAssetReader *)reader didGetVideoFormatDescription:(CMFormatDescriptionRef)formatDescription; 19 | - (void)reader:(VCAssetReader *)reader didGetVideoSampleBuffer:(VCSampleBuffer *)sampleBuffer; 20 | - (void)reader:(VCAssetReader *)reader didGetAudioFormatDescription:(CMFormatDescriptionRef)formatDescription; 21 | - (void)reader:(VCAssetReader *)reader didGetAudioSampleBuffer:(VCSampleBuffer *)sampleBuffer; 22 | - (void)readerDidReachEOF:(VCAssetReader *)reader; 23 | @end 24 | 25 | @interface VCAssetReader : NSObject { 26 | @protected 27 | CMFormatDescriptionRef _videoFormatDescription; 28 | CMFormatDescriptionRef _audioFormatDescription; 29 | } 30 | 31 | @property (nonatomic, weak) id delegate; 32 | 33 | @property (nonatomic, readonly) CMFormatDescriptionRef videoFormatDescription; 34 | @property (nonatomic, readonly) CMFormatDescriptionRef audioFormatDescription; 35 | 36 | - (void)startReading; 37 | - (void)stopReading; 38 | 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/FileFormat/VCAssetReader.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAssetReader.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCAssetReader.h" 10 | 11 | @implementation VCAssetReader 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | if (self) { 16 | _audioFormatDescription = NULL; 17 | _videoFormatDescription = NULL; 18 | } 19 | return self; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/NALU/VCH264NALU.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264NALU.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(NSUInteger, VCH264NALUType) { 14 | VCH264NALUTypeNoSpecific = 0, 15 | VCH264NALUTypeSliceData = 1, // 一个非IDR图像的编码条带 16 | VCH264NALUTypeSliceDataPartitionALayer = 2, // 编码条带数据分割块A 17 | VCH264NALUTypeSliceDataPartitionBLayer = 3, // 编码条带数据分割块B 18 | VCH264NALUTypeSliceDataPartitionCLayer = 4, // 编码条带数据分割块C 19 | VCH264NALUTypeSliceIDR = 5, // IDR图像的编码条带 20 | VCH264NALUTypeSEI = 6, // 辅助增强信息 SEI 21 | VCH264NALUTypeSeqParameterSet = 7, // 序列参数集 SPS 22 | VCH264NALUTypePicParameterSet = 8, // 图像参数集 PPS 23 | VCH264NALUTypeAccessUnitDelimiter = 9, // 访问单元分隔符 AUD 24 | VCH264NALUTypeEndOfSeq = 10, // 序列结尾 EOSEQ 25 | VCH264NALUTypeEndOfStream = 11, // 流结尾 EOSTREAM 26 | VCH264NALUTypeFillData = 12, // 填充数据 FILL 27 | VCH264NALUTypeSeqParameterSetExtension = 13, // 序列参数集扩展 28 | VCH264NALUTypeSliceLayerWithouPartitioning = 19, // 未分割的辅助编码图像的编码条带 29 | }; 30 | 31 | @interface VCH264NALU : NSObject 32 | 33 | @property (nonatomic, readonly) NSData *data; 34 | @property (nonatomic, readonly) VCH264NALUType type; 35 | - (instancetype)initWithData:(NSData *)data; 36 | 37 | - (NSData *)warpAVCStartCode; 38 | - (NSData *)warpAnnexBStartCode; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/NALU/VCH264NALU.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH264NALU.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/27. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCByteArray.h" 10 | #import "VCH264NALU.h" 11 | 12 | @interface VCH264NALU () 13 | @property (nonatomic, strong) NSData *data; 14 | @end 15 | 16 | @implementation VCH264NALU 17 | 18 | - (instancetype)initWithData:(NSData *)data { 19 | self = [super init]; 20 | if (self) { 21 | _data = data; 22 | } 23 | return self; 24 | } 25 | 26 | - (VCH264NALUType)type { 27 | VCByteArray *byteArray = [[VCByteArray alloc] initWithData:_data]; 28 | uint8_t header = [byteArray readUInt8]; 29 | uint8_t naluType = (header & 0x1F); 30 | return naluType; 31 | } 32 | 33 | - (NSData *)warpAVCStartCode { 34 | uint32_t len = CFSwapInt32HostToBig((uint32_t)self.data.length); 35 | NSMutableData *data = [[NSMutableData alloc] initWithCapacity:4 + self.data.length]; 36 | [data appendBytes:&len length:4]; 37 | [data appendData:self.data]; 38 | return data; 39 | } 40 | 41 | - (NSData *)warpAnnexBStartCode { 42 | static uint8_t startCode[4] = {0, 0, 0, 1}; 43 | NSMutableData *data = [[NSMutableData alloc] initWithCapacity:4 + self.data.length]; 44 | [data appendBytes:&startCode length:4]; 45 | [data appendData:self.data]; 46 | return data; 47 | } 48 | 49 | - (NSString *)description 50 | { 51 | return [NSString stringWithFormat:@"NALU Type: %@", [VCH264NALU NALUTypeDescription][@(self.type)]]; 52 | } 53 | 54 | + (NSDictionary *)NALUTypeDescription { 55 | static NSDictionary *description = nil; 56 | if (description == nil) { 57 | description = @{ 58 | @(VCH264NALUTypeNoSpecific): @"未指定", 59 | @(VCH264NALUTypeSliceData): @"一个非IDR图像的编码条带", 60 | @(VCH264NALUTypeSliceDataPartitionALayer): @"编码条带数据分割块A", 61 | @(VCH264NALUTypeSliceDataPartitionBLayer): @"编码条带数据分割块B", 62 | @(VCH264NALUTypeSliceDataPartitionCLayer): @"编码条带数据分割块C", 63 | @(VCH264NALUTypeSliceIDR): @"IDR图像的编码条带", 64 | @(VCH264NALUTypeSEI): @"辅助增强信息 SEI", 65 | @(VCH264NALUTypeSeqParameterSet): @"序列参数集 SPS", 66 | @(VCH264NALUTypePicParameterSet): @"图像参数集 PPS", 67 | @(VCH264NALUTypeAccessUnitDelimiter): @"访问单元分隔符 AUD", 68 | @(VCH264NALUTypeEndOfSeq): @"序列结尾 EOSEQ", 69 | @(VCH264NALUTypeEndOfStream): @"流结尾 EOSTREAM", 70 | @(VCH264NALUTypeFillData): @"填充数据 FILL", 71 | @(VCH264NALUTypeSeqParameterSetExtension): @"序列参数集扩展", 72 | @(VCH264NALUTypeSliceLayerWithouPartitioning): @"未分割的辅助编码图像的编码条带", 73 | }; 74 | } 75 | return description; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/NALU/VCH265NALU.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCH265NALU.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/28. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(NSUInteger, VCH265NALUType) { 14 | VCH265NALUTypeVPS = 32, 15 | VCH265NALUTypeSPS = 33, 16 | VCH265NALUTypePPS = 34, 17 | VCH265NALUTypeSEI = 39, 18 | VCH265NALUTypeIDR = 19, 19 | VCH265NALUTypeSliceN = 0, 20 | VCH265NALUTypeSliceR = 1, 21 | VCH265NALUTypeCRA = 21, 22 | }; 23 | 24 | @interface VCH265NALU : NSObject 25 | 26 | @property (nonatomic, readonly) NSData *data; 27 | @property (nonatomic, readonly) VCH265NALUType type; 28 | 29 | @property (nonatomic, readonly) BOOL keyFrame; 30 | 31 | - (instancetype)initWithData:(NSData *)data; 32 | 33 | - (NSData *)warpAVCStartCode; 34 | - (NSData *)warpAnnexBStartCode; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/ISO/NALU/VCH265NALU.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCH265NALU.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/1/28. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCH265NALU.h" 10 | #import "VCByteArray.h" 11 | 12 | @interface VCH265NALU () 13 | @property (nonatomic, strong) NSData *data; 14 | @end 15 | 16 | @implementation VCH265NALU 17 | 18 | - (instancetype)initWithData:(NSData *)data { 19 | self = [super init]; 20 | if (self) { 21 | _data = data; 22 | } 23 | return self; 24 | } 25 | 26 | - (VCH265NALUType)type { 27 | VCByteArray *byteArray = [[VCByteArray alloc] initWithData:_data]; 28 | uint8_t header = [byteArray readUInt8]; 29 | uint8_t type = (header & 0x7E) >> 1; 30 | return type; 31 | } 32 | 33 | - (NSData *)warpAVCStartCode { 34 | uint32_t len = CFSwapInt32HostToBig((uint32_t)self.data.length); 35 | NSMutableData *data = [[NSMutableData alloc] initWithCapacity:4 + self.data.length]; 36 | [data appendBytes:&len length:4]; 37 | [data appendData:self.data]; 38 | return data; 39 | } 40 | 41 | - (NSData *)warpAnnexBStartCode { 42 | static uint8_t startCode[4] = {0, 0, 0, 1}; 43 | NSMutableData *data = [[NSMutableData alloc] initWithCapacity:4 + self.data.length]; 44 | [data appendBytes:&startCode length:4]; 45 | [data appendData:self.data]; 46 | return data; 47 | } 48 | 49 | - (NSString *)description { 50 | NSString *s = [VCH265NALU NALUTypeDescription][@(self.type)]; 51 | if (s == nil) { 52 | s = [NSString stringWithFormat:@"%@", @(self.type)]; 53 | } 54 | return [NSString stringWithFormat:@"NALU Type: %@", s]; 55 | } 56 | 57 | + (NSDictionary *)NALUTypeDescription { 58 | static NSDictionary *description = nil; 59 | if (description == nil) { 60 | description = @{ 61 | @(VCH265NALUTypeVPS): @"VPS", 62 | @(VCH265NALUTypeSPS): @"SPS", 63 | @(VCH265NALUTypePPS): @"PPS", 64 | @(VCH265NALUTypeSEI): @"SEI", 65 | @(VCH265NALUTypeIDR): @"IDR", 66 | @(VCH265NALUTypeSliceN): @"SliceN", 67 | @(VCH265NALUTypeSliceR): @"SliceR", 68 | @(VCH265NALUTypeCRA): @"CRA" 69 | }; 70 | } 71 | return description; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Media/VCMicRecorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCMicRecorder.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/7. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface VCMicRecorder : NSObject 15 | 16 | @property (nonatomic, readonly) BOOL recording; 17 | 18 | @property (nonatomic, assign) uint32_t sampleRate; 19 | @property (nonatomic, assign) uint32_t channelCount; 20 | 21 | @property (nonatomic, readonly) AVAudioFormat *outputFormat; 22 | 23 | @property (nonatomic, readonly) AVAudioFormat *nodeFormat; 24 | 25 | - (BOOL)startRecoderWithFormat:(AVAudioFormat *)format 26 | block:(AVAudioNodeTapBlock)block; 27 | - (void)stop; 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Media/VCMicRecorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCMicRecorder.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/7. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "VCMicRecorder.h" 12 | 13 | @interface VCMicRecorder () 14 | @property (nonatomic, strong) AVAudioEngine *engine; 15 | @end 16 | 17 | @implementation VCMicRecorder 18 | 19 | - (instancetype)init { 20 | self = [super init]; 21 | if (self) { 22 | _engine = [[AVAudioEngine alloc] init]; 23 | _recording = NO; 24 | _sampleRate = 48000; 25 | _channelCount = 2; 26 | } 27 | return self; 28 | } 29 | 30 | - (AVAudioFormat *)outputFormat { 31 | AVAudioFormat *pcmFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatInt16 sampleRate:self.sampleRate channels:self.channelCount interleaved:NO]; 32 | return pcmFormat; 33 | } 34 | 35 | - (AVAudioFormat *)nodeFormat { 36 | return [self.engine.inputNode outputFormatForBus:0]; 37 | } 38 | 39 | - (BOOL)startRecoderWithFormat:(AVAudioFormat *)format 40 | block:(AVAudioNodeTapBlock)block { 41 | if (_recording) return NO; 42 | 43 | AVAudioInputNode *input = [self.engine inputNode]; 44 | [input installTapOnBus:0 bufferSize:4096 format:format block:block]; 45 | NSError *err = nil; 46 | [self.engine startAndReturnError:&err]; 47 | if (err == nil) { 48 | _recording = YES; 49 | return YES; 50 | } 51 | _recording = NO; 52 | return NO; 53 | } 54 | 55 | - (void)stop { 56 | [self.engine.inputNode removeTapOnBus:0]; 57 | _recording = NO; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Model/VCSafeBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCSafeBuffer.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/11/9. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface VCSafeBufferNode: NSObject 11 | @property (nonatomic, readonly) NSData *data; 12 | @property (nonatomic, assign) NSInteger readOffset; 13 | - (instancetype)initWithData:(NSData *)data; 14 | - (NSData *)pull:(NSInteger *)length; 15 | - (NSInteger)length; 16 | - (NSInteger)readableLength; 17 | @end 18 | 19 | 20 | @interface VCSafeBuffer : NSObject 21 | 22 | /** 23 | 是否可以写入数据,默认YES 24 | */ 25 | @property (nonatomic, assign) BOOL canWrite; 26 | /** 27 | 是否开启线程安全 28 | */ 29 | @property (nonatomic, assign) BOOL isThreadSafe; 30 | /** 31 | 拉取失败是否等待并重试,默认YES 32 | */ 33 | @property (nonatomic, assign) BOOL shouldWaitWhenPullFailed; 34 | /** 35 | 创建一个缓冲区 36 | 37 | @param isThreadSafe 是否线程安全 38 | @return 队列实例 39 | */ 40 | - (instancetype)initWithThreadSafe:(BOOL)isThreadSafe; 41 | 42 | /** 43 | 移除队列所有对象 44 | */ 45 | - (void)clear; 46 | 47 | /** 48 | * Gets the number of objects in queue. 49 | * 50 | * @return the number of objects in queue 51 | */ 52 | /** 53 | 当前队列对象个数 54 | 55 | @return 当前队列对象个数 56 | */ 57 | - (int)count; 58 | 59 | 60 | /** 61 | 缓冲区加入数据 62 | 63 | @param data 数据 64 | @return 是否添加成功 65 | */ 66 | - (BOOL)push:(VCSafeBufferNode *)data; 67 | 68 | 69 | /** 70 | 拉取数据对象 71 | 72 | @param length 73 | IN: 希望多少数据 74 | OUT: 实际拿了多少 75 | 76 | @return 拉取的数据 77 | */ 78 | - (NSData *)pull:(NSInteger *)length; 79 | 80 | 81 | /** 82 | 获取数据,不从缓冲区删除 83 | 84 | @param length 85 | IN: 希望多少数据 86 | OUT: 实际拿了多少 87 | 88 | @return 获取的数据 89 | */ 90 | - (NSData *)fetch:(NSInteger *)length; 91 | 92 | /** 93 | 唤起所有线程 94 | */ 95 | - (void)wakeupReader; 96 | @end 97 | 98 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Model/VCSafeBuffer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCSafeBuffer.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/11/9. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCSafeObjectQueue.h" 11 | #import "VCSafeBuffer.h" 12 | 13 | #define kVCPerformIfNeedThreadSafe(__code__) if (_isThreadSafe) { __code__;} 14 | 15 | #define kVCSafeBufferMaxDataQueueSize (50) 16 | 17 | @interface VCSafeBuffer () { 18 | pthread_mutex_t _lock; 19 | } 20 | @property (nonatomic, strong) VCSafeObjectQueue *dataQueue; 21 | @property (nonatomic, assign) int dataCount; 22 | @end 23 | 24 | @implementation VCSafeBufferNode 25 | - (instancetype)initWithData:(NSData *)data { 26 | self = [super init]; 27 | if (self) { 28 | _data = data; 29 | _readOffset = 0; 30 | } 31 | return self; 32 | } 33 | 34 | - (NSData *)pull:(NSInteger *)length { 35 | NSInteger pullLength = MIN(self.data.length - _readOffset, *length); 36 | if (pullLength == 0) { 37 | return nil; 38 | } 39 | NSData *pullData = [self.data subdataWithRange:NSMakeRange(_readOffset, pullLength)]; 40 | *length = pullData.length; 41 | _readOffset += pullData.length; 42 | return pullData; 43 | } 44 | 45 | - (NSInteger)length { 46 | return self.data.length; 47 | } 48 | 49 | - (NSInteger)readableLength { 50 | return self.data.length - self.readOffset; 51 | } 52 | 53 | @end 54 | 55 | @implementation VCSafeBuffer 56 | 57 | - (instancetype)init { 58 | return [self initWithThreadSafe:YES]; 59 | } 60 | 61 | - (instancetype)initWithThreadSafe:(BOOL)isThreadSafe { 62 | self = [super init]; 63 | if (self) { 64 | _isThreadSafe = isThreadSafe; 65 | _canWrite = YES; 66 | _shouldWaitWhenPullFailed = YES; 67 | kVCPerformIfNeedThreadSafe(pthread_mutex_init(&_lock, NULL)); 68 | _dataQueue = [[VCSafeObjectQueue alloc] initWithSize:kVCSafeBufferMaxDataQueueSize threadSafe:isThreadSafe]; 69 | _dataCount = 0; 70 | } 71 | return self; 72 | } 73 | 74 | - (void)clear { 75 | kVCPerformIfNeedThreadSafe(pthread_mutex_lock(&_lock)); 76 | [_dataQueue clear]; 77 | _dataCount = 0; 78 | kVCPerformIfNeedThreadSafe(pthread_mutex_unlock(&_lock)); 79 | } 80 | 81 | - (int)count { 82 | return self.dataCount; 83 | } 84 | 85 | - (BOOL)push:(VCSafeBufferNode *)data { 86 | if (data == nil || data.readableLength == 0) return NO; 87 | kVCPerformIfNeedThreadSafe(pthread_mutex_lock(&_lock)); 88 | BOOL ret = [_dataQueue push:data]; 89 | if (ret) { 90 | _dataCount += data.readableLength; 91 | } 92 | kVCPerformIfNeedThreadSafe(pthread_mutex_unlock(&_lock)); 93 | return ret; 94 | } 95 | 96 | 97 | - (NSData *)pull:(NSInteger *)length { 98 | if (*length == 0) return nil; 99 | kVCPerformIfNeedThreadSafe(pthread_mutex_lock(&_lock)); 100 | NSInteger desiredLength = *length; 101 | NSMutableData *totalData = [[NSMutableData alloc] initWithCapacity:desiredLength]; 102 | NSInteger pullLength = 0; 103 | while (pullLength < desiredLength) { 104 | NSInteger lastLength = desiredLength - pullLength; 105 | VCSafeBufferNode *data = (VCSafeBufferNode *)[self.dataQueue fetch]; 106 | if (data == nil || ![data isKindOfClass:[VCSafeBufferNode class]]) { 107 | break; 108 | } else if (data.readableLength == 0) { 109 | // 丢空Node 110 | [self.dataQueue pull]; 111 | } else { 112 | if (data.readableLength <= lastLength) { 113 | // 读完, 丢这个node 114 | NSInteger readFromNode = data.readableLength; 115 | pullLength += data.readableLength; 116 | [totalData appendData:[data pull:&readFromNode]]; 117 | [self.dataQueue pull]; 118 | } else { 119 | // 不用读完 120 | NSInteger readFromNode = lastLength; 121 | pullLength += lastLength; 122 | [totalData appendData:[data pull:&readFromNode]]; 123 | } 124 | } 125 | } 126 | *length = pullLength; 127 | _dataCount -= totalData.length; 128 | kVCPerformIfNeedThreadSafe(pthread_mutex_unlock(&_lock)); 129 | if (pullLength == 0) { 130 | return nil; 131 | } 132 | return totalData; 133 | } 134 | 135 | - (NSData *)fetch:(NSInteger *)length { 136 | kVCPerformIfNeedThreadSafe(pthread_mutex_lock(&_lock)); 137 | VCSafeBufferNode *data = (VCSafeBufferNode *)[self.dataQueue fetch]; 138 | NSInteger readLen = MIN(data.readableLength, *length); 139 | *length = readLen; 140 | kVCPerformIfNeedThreadSafe(pthread_mutex_unlock(&_lock)); 141 | return [data.data subdataWithRange:NSMakeRange(data.readOffset, readLen)]; 142 | } 143 | 144 | - (void)wakeupReader { 145 | if (_isThreadSafe) { 146 | pthread_mutex_lock(&_lock); 147 | [self.dataQueue wakeupReader]; 148 | pthread_mutex_unlock(&_lock); 149 | } 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Model/VCSafeObjectQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCSafeObjectQueue.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/21. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface VCSafeObjectQueue : NSObject 13 | 14 | /** 15 | 是否开启线程安全 16 | */ 17 | @property (nonatomic, assign) BOOL isThreadSafe; 18 | /** 19 | 拉取失败是否等待并重试,默认YES 20 | */ 21 | @property (nonatomic, assign) BOOL shouldWaitWhenPullFailed; 22 | /** 23 | 创建一个队列 24 | 25 | @param size 队列大小 26 | @param isThreadSafe 是否线程安全 27 | @return 队列实例 28 | */ 29 | - (instancetype)initWithSize:(int)size 30 | threadSafe:(BOOL)isThreadSafe NS_DESIGNATED_INITIALIZER; 31 | /** 32 | 创建一个线程安全队列 33 | 34 | @param size 队列大小 35 | @return VCSafeObjectQueue实例 36 | */ 37 | - (VCSafeObjectQueue *)initWithSize:(int)size; 38 | 39 | /** 40 | 移除队列所有对象 41 | */ 42 | - (void)clear; 43 | 44 | /** 45 | * Gets the number of objects in queue. 46 | * 47 | * @return the number of objects in queue 48 | */ 49 | /** 50 | 当前队列对象个数 51 | 52 | @return 当前队列对象个数 53 | */ 54 | - (int)count; 55 | 56 | 57 | /** 58 | 当前队列大小 59 | 60 | @return 当前队列大小 61 | */ 62 | - (int)size; 63 | 64 | /** 65 | 把对象压入队列中 66 | 67 | @param object 对象 68 | @return 操作是否成功 69 | */ 70 | - (BOOL)push:(NSObject *)object; 71 | 72 | 73 | /** 74 | 从队列中拉取对象 75 | 76 | @return 对象 77 | */ 78 | - (NSObject *)pull; 79 | 80 | /** 81 | 只取队头对象,不出队列 82 | 83 | @return 对象 84 | */ 85 | - (NSObject *)fetch; 86 | /** 87 | 队列是否满 88 | 89 | @return 是否满 90 | */ 91 | - (BOOL)isFull; 92 | 93 | 94 | /** 95 | 唤起所有线程 96 | */ 97 | - (void)wakeupReader; 98 | 99 | 100 | /** 101 | 阻塞等待,有空间时返回 102 | */ 103 | - (void)waitForCapacity; 104 | 105 | @end 106 | 107 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Model/VCSafeObjectQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCSafeObjectQueue.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/21. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCSafeObjectQueue.h" 10 | #import 11 | 12 | #define kVCPerformIfNeedThreadSafe(__code__) if (_isThreadSafe) { __code__;} 13 | 14 | @interface VCSafeObjectQueue () { 15 | // node 16 | NSMutableArray *_node; 17 | // total size of the queue 18 | int _size; 19 | // number of nodes 20 | int _count; 21 | int _head; 22 | int _tail; 23 | } 24 | @property (nonatomic, strong) NSCondition *condition; 25 | @end 26 | 27 | @implementation VCSafeObjectQueue 28 | 29 | - (instancetype)initWithSize:(int)size threadSafe:(BOOL)isThreadSafe { 30 | self = [super init]; 31 | if (self) { 32 | _isThreadSafe = isThreadSafe; 33 | _shouldWaitWhenPullFailed = YES; 34 | 35 | kVCPerformIfNeedThreadSafe(_condition = [[NSCondition alloc] init]); 36 | 37 | _head = 0; 38 | _tail = 0; 39 | _count = 0; 40 | _size = 0; 41 | _node = NULL; 42 | if (size <= 0) return self; 43 | _size = size; 44 | _node = [[NSMutableArray alloc] initWithCapacity:_size]; 45 | } 46 | return self; 47 | } 48 | 49 | - (instancetype)initWithSize:(int)size{ 50 | return [self initWithSize:size threadSafe:YES]; 51 | } 52 | 53 | - (instancetype)init { 54 | return [self initWithSize:10]; 55 | } 56 | 57 | - (void)clear{ 58 | kVCPerformIfNeedThreadSafe([_condition lock]); 59 | _head = 0; 60 | _tail = 0; 61 | _count = 0; 62 | [_node removeAllObjects]; 63 | kVCPerformIfNeedThreadSafe([_condition unlock]); 64 | } 65 | 66 | - (BOOL)push:(NSObject *)object { 67 | kVCPerformIfNeedThreadSafe([_condition lock]); 68 | if(object == nil || [self isFull]){ 69 | kVCPerformIfNeedThreadSafe([_condition unlock]); 70 | return NO; 71 | } 72 | _node[_tail] = object; 73 | _tail++; 74 | if(_tail >= _size) _tail = 0; 75 | _count++; 76 | kVCPerformIfNeedThreadSafe([_condition broadcast]); 77 | kVCPerformIfNeedThreadSafe([_condition unlock]); 78 | return YES; 79 | } 80 | 81 | - (NSObject *)pull{ 82 | kVCPerformIfNeedThreadSafe([_condition lock]); 83 | if(_count == 0) 84 | { 85 | if (!_shouldWaitWhenPullFailed) { 86 | kVCPerformIfNeedThreadSafe([_condition unlock]); 87 | return NULL; 88 | } 89 | 90 | kVCPerformIfNeedThreadSafe([_condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]); 91 | 92 | if(_count == 0) { 93 | kVCPerformIfNeedThreadSafe([_condition unlock]); 94 | return NULL; 95 | } 96 | } 97 | 98 | NSObject *tmp = [_node objectAtIndex:_head]; 99 | _head++; 100 | if(_head>=_size)_head = 0; 101 | _count--; 102 | kVCPerformIfNeedThreadSafe([_condition unlock]); 103 | return tmp; 104 | } 105 | 106 | - (NSObject *)fetch { 107 | kVCPerformIfNeedThreadSafe([_condition lock]); 108 | if(_count == 0) 109 | { 110 | if (!_shouldWaitWhenPullFailed) { 111 | kVCPerformIfNeedThreadSafe([_condition unlock]); 112 | return NULL; 113 | } 114 | 115 | kVCPerformIfNeedThreadSafe([_condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]); 116 | if(_count == 0) { 117 | kVCPerformIfNeedThreadSafe([_condition unlock]); 118 | return NULL; 119 | } 120 | } 121 | 122 | NSObject *tmp = [_node objectAtIndex:_head]; 123 | kVCPerformIfNeedThreadSafe([_condition unlock]); 124 | return tmp; 125 | } 126 | 127 | - (void)wakeupReader{ 128 | kVCPerformIfNeedThreadSafe([_condition broadcast]); 129 | } 130 | 131 | - (int)count{ 132 | return _count; 133 | } 134 | 135 | - (int)size{ 136 | return _size; 137 | } 138 | 139 | - (BOOL)isFull{ 140 | if(_count == _size){ 141 | return YES; 142 | } 143 | else{ 144 | return NO; 145 | } 146 | } 147 | 148 | - (void)waitForCapacity { 149 | kVCPerformIfNeedThreadSafe([_condition wait]); 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Model/VCSampleBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCSampleBuffer.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface VCSampleBuffer : NSObject 16 | @property (nonatomic, assign) CMSampleBufferRef sampleBuffer; 17 | @property (nonatomic, readonly) CMMediaType mediaType; 18 | @property (nonatomic) CMBlockBufferRef dataBuffer; 19 | @property (nonatomic, readonly) CVImageBufferRef imageBuffer; 20 | @property (nonatomic, readonly) CMItemCount numberOfSamples; 21 | @property (nonatomic, readonly) CMTime duration; 22 | @property (nonatomic, readonly) CMFormatDescriptionRef formatDescription; 23 | @property (nonatomic, readonly) CMTime decodeTimeStamp; 24 | @property (nonatomic, readonly) CMTime presentationTimeStamp; 25 | @property (nonatomic, readonly) BOOL keyFrame; 26 | @property (nonatomic, readonly) AudioStreamBasicDescription audioStreamBasicDescription; 27 | 28 | - (instancetype)init NS_UNAVAILABLE; 29 | - (instancetype)initWithSampleBuffer:(CMSampleBufferRef)aSampleBuffer; 30 | - (instancetype)initWithSampleBuffer:(CMSampleBufferRef)aSampleBuffer freeWhenDone:(BOOL)shouldFreeWhenDone NS_DESIGNATED_INITIALIZER; 31 | 32 | - (nullable NSData *)h264ParameterSetData; 33 | - (nullable NSData *)dataBufferData; 34 | 35 | - (AVAudioBuffer *)audioBuffer; 36 | - (AVAudioFormat *)audioFormat; 37 | @end 38 | 39 | NS_ASSUME_NONNULL_END 40 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Model/VCSampleBuffer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCSampleBuffer.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCSampleBuffer.h" 10 | 11 | @interface VCSampleBuffer () 12 | @property (nonatomic, assign) BOOL shouldFreeWhenDone; 13 | @end 14 | 15 | @implementation VCSampleBuffer 16 | 17 | - (instancetype)initWithSampleBuffer:(CMSampleBufferRef)aSampleBuffer { 18 | return [self initWithSampleBuffer:aSampleBuffer freeWhenDone:YES]; 19 | } 20 | 21 | - (instancetype)initWithSampleBuffer:(CMSampleBufferRef)aSampleBuffer freeWhenDone:(BOOL)shouldFreeWhenDone { 22 | self = [super init]; 23 | if (self) { 24 | _sampleBuffer = aSampleBuffer; 25 | _shouldFreeWhenDone = shouldFreeWhenDone; 26 | } 27 | return self; 28 | } 29 | 30 | - (CMMediaType)mediaType { 31 | return CMFormatDescriptionGetMediaType(self.formatDescription); 32 | } 33 | 34 | - (CMBlockBufferRef)dataBuffer { 35 | return CMSampleBufferGetDataBuffer(_sampleBuffer); 36 | } 37 | 38 | - (void)setDataBuffer:(CMBlockBufferRef)dataBuffer { 39 | CMSampleBufferSetDataBuffer(_sampleBuffer, dataBuffer); 40 | } 41 | 42 | - (CVImageBufferRef)imageBuffer { 43 | return CMSampleBufferGetImageBuffer(_sampleBuffer); 44 | } 45 | 46 | - (CMItemCount)numberOfSamples { 47 | return CMSampleBufferGetNumSamples(_sampleBuffer); 48 | } 49 | 50 | - (CMTime)duration { 51 | return CMSampleBufferGetDuration(_sampleBuffer); 52 | } 53 | 54 | - (CMFormatDescriptionRef)formatDescription { 55 | return CMSampleBufferGetFormatDescription(_sampleBuffer); 56 | } 57 | 58 | - (CMTime)decodeTimeStamp { 59 | return CMSampleBufferGetDecodeTimeStamp(_sampleBuffer); 60 | } 61 | 62 | - (CMTime)presentationTimeStamp { 63 | return CMSampleBufferGetPresentationTimeStamp(_sampleBuffer); 64 | } 65 | 66 | - (BOOL)keyFrame { 67 | CFArrayRef attach = CMSampleBufferGetSampleAttachmentsArray(_sampleBuffer, false); 68 | if (attach == NULL) { 69 | return YES; 70 | } 71 | CFDictionaryRef dict = CFArrayGetValueAtIndex(attach, 0); 72 | if (dict == NULL) { 73 | return YES; 74 | } 75 | BOOL keyFrame = !CFDictionaryContainsKey(dict, kCMSampleAttachmentKey_NotSync); 76 | return keyFrame; 77 | } 78 | 79 | - (AudioStreamBasicDescription)audioStreamBasicDescription { 80 | CMAudioFormatDescriptionRef audioFormat = CMSampleBufferGetFormatDescription(_sampleBuffer); 81 | return *CMAudioFormatDescriptionGetStreamBasicDescription(audioFormat); 82 | } 83 | 84 | - (NSData *)h264ParameterSetData { 85 | const uint8_t *outPtr = nil; 86 | size_t outSize = 0; 87 | uint8_t header[] = {0x00, 0x00, 0x00, 0x01}; 88 | NSMutableData *data = [[NSMutableData alloc] init]; 89 | OSStatus ret = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(self.formatDescription, 0, &outPtr, &outSize, NULL, NULL); 90 | if (ret != noErr) return nil; 91 | [data appendBytes:header length:4]; 92 | [data appendBytes:outPtr length:outSize]; 93 | 94 | ret = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(self.formatDescription, 1, &outPtr, &outSize, NULL, NULL); 95 | if (ret != noErr) return nil; 96 | [data appendBytes:header length:4]; 97 | [data appendBytes:outPtr length:outSize]; 98 | return data; 99 | } 100 | 101 | - (NSData *)dataBufferData { 102 | char *outPtr = nil; 103 | size_t outSize = 0; 104 | OSStatus ret = CMBlockBufferGetDataPointer(self.dataBuffer, 0, NULL, &outSize, &outPtr); 105 | if (ret != noErr) { 106 | return nil; 107 | } 108 | NSData *data = [[NSData alloc] initWithBytes:(void *)outPtr length:outSize]; 109 | return data; 110 | } 111 | 112 | - (AVAudioFormat *)audioFormat { 113 | return [[AVAudioFormat alloc] initWithCMAudioFormatDescription:self.formatDescription]; 114 | } 115 | 116 | - (AVAudioBuffer *)audioBuffer { 117 | size_t size = sizeof(AudioBufferList) + (self.audioFormat.channelCount - 1) * sizeof(AudioBuffer); 118 | if (self.audioFormat.streamDescription->mFormatID == kAudioFormatMPEG4AAC) { 119 | size = sizeof(AudioBufferList); 120 | } 121 | AudioBufferList *bufferList = malloc(size); 122 | memset(bufferList, 0, size); 123 | CMBlockBufferRef blockBuffer = NULL; 124 | OSStatus ret = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(_sampleBuffer, 125 | NULL, 126 | bufferList, 127 | size, 128 | kCFAllocatorDefault, 129 | kCFAllocatorDefault, 130 | 0, 131 | &blockBuffer); 132 | 133 | if (ret != noErr) { 134 | free(bufferList); 135 | return NULL; 136 | } 137 | 138 | AVAudioBuffer *outputBuffer = nil; 139 | if (self.audioFormat.streamDescription->mFormatID == kAudioFormatLinearPCM) { 140 | AVAudioPCMBuffer *pcmBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioFormat frameCapacity:(AVAudioFrameCount)1024 * self.audioFormat.streamDescription->mBytesPerFrame]; 141 | for (int i = 0; i < bufferList->mNumberBuffers; ++i) { 142 | memcpy(pcmBuffer.audioBufferList->mBuffers[i].mData, bufferList->mBuffers[i].mData, bufferList->mBuffers[i].mDataByteSize); 143 | } 144 | // frameLength 为有效PCM数据 145 | pcmBuffer.frameLength = (AVAudioFrameCount)CMSampleBufferGetNumSamples(_sampleBuffer); 146 | outputBuffer = pcmBuffer; 147 | } else if (self.audioFormat.streamDescription->mFormatID == kAudioFormatMPEG4AAC) { 148 | NSUInteger audioBufferListSize = 0; 149 | for (int i = 0; i < bufferList->mNumberBuffers; ++i) { 150 | audioBufferListSize += bufferList->mBuffers[i].mDataByteSize; 151 | } 152 | AVAudioCompressedBuffer *compressedBuffer = [[AVAudioCompressedBuffer alloc] initWithFormat:self.audioFormat packetCapacity:bufferList->mNumberBuffers maximumPacketSize:audioBufferListSize]; 153 | for (int i = 0; i < compressedBuffer.audioBufferList->mNumberBuffers; ++i) { 154 | memcpy(compressedBuffer.audioBufferList->mBuffers[i].mData, bufferList->mBuffers[i].mData, bufferList->mBuffers[i].mDataByteSize); 155 | } 156 | compressedBuffer.packetCount = (AVAudioPacketCount)compressedBuffer.audioBufferList->mNumberBuffers; 157 | compressedBuffer.byteLength = (UInt32)audioBufferListSize; 158 | outputBuffer = compressedBuffer; 159 | } 160 | 161 | if (blockBuffer != NULL) { 162 | CFRelease(blockBuffer); 163 | blockBuffer = NULL; 164 | } 165 | 166 | if (bufferList != NULL) { 167 | free(bufferList); 168 | bufferList = NULL; 169 | } 170 | 171 | return outputBuffer; 172 | } 173 | 174 | - (void)dealloc { 175 | if (_sampleBuffer && 176 | _shouldFreeWhenDone) { 177 | CFRelease(_sampleBuffer); 178 | _sampleBuffer = NULL; 179 | } 180 | } 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/Publisher/VCRTMPPublisher.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPPublisher.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/15. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCSampleBuffer.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | extern NSErrorDomain const VCRTMPPublisherErrorDomain; 15 | typedef NS_ENUM(NSUInteger, VCRTMPPublisherErrorCode) { 16 | VCRTMPPublisherErrorCodeHandshakeFailed = -10, 17 | VCRTMPPublisherErrorCodeCreateNetConnectionFailed = -11, 18 | VCRTMPPublisherErrorCodeCreateNetStreamFailed = -12, 19 | VCRTMPPublisherErrorCodePublishStreamFailed = -13, 20 | VCRTMPPublisherErrorCodeConnectionFailed = -14, 21 | VCRTMPPublisherErrorCodeProtocolUnsupport = -15, 22 | VCRTMPPublisherErrorCodeBadURL = -15, 23 | }; 24 | 25 | typedef NS_ENUM(NSUInteger, VCRTMPPublisherState) { 26 | VCRTMPPublisherStateInit, 27 | VCRTMPPublisherStateReadyToPublish, 28 | VCRTMPPublisherStateError, 29 | VCRTMPPublisherStateEnd, 30 | }; 31 | 32 | @class VCRTMPPublisher; 33 | @protocol VCRTMPPublisherDelegate 34 | - (void)publisher:(VCRTMPPublisher *)publisher didChangeState:(VCRTMPPublisherState)state error:(NSError * _Nullable )error; 35 | @end 36 | 37 | @class VCActionScriptType; 38 | @class VCFLVTag; 39 | @interface VCRTMPPublisher : NSObject 40 | 41 | @property (nonatomic, readonly) VCRTMPPublisherState state; 42 | @property (nonatomic, weak) id delegate; 43 | 44 | @property (nonatomic, strong) NSDictionary *connectionParameter; 45 | @property (nonatomic, strong) NSDictionary *streamMetaData; 46 | 47 | - (instancetype)initWithURL:(NSURL *)url 48 | publishKey:(NSString *)publishKey; 49 | 50 | - (void)start; 51 | - (void)stop; 52 | 53 | - (void)publishSampleBuffer:(VCSampleBuffer *)sampleBuffer; 54 | - (void)publishFormatDescription:(CMFormatDescriptionRef)formatDescription; 55 | 56 | @end 57 | 58 | NS_ASSUME_NONNULL_END 59 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPChunk.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPChunk.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | // reference: Adobe’s Real Time Messaging Protocol 14 | // rtmp_specification_1.0.pdf 15 | 16 | /* 17 | 18 | Chunk Format: 19 | 20 | +--------------+----------------+--------------------+--------------+ 21 | | Basic Header | Message Header | Extended Timestamp | Chunk Data | 22 | +--------------+----------------+--------------------+--------------+ 23 | | | | 24 | |<------------------- Chunk Header ----------------->| | 25 | |<------------------------------ Chunk ---------------------------->| 26 | 27 | */ 28 | 29 | // seealso: 5.3.1.1. Chunk Basic Header 30 | typedef NS_ENUM(uint32_t, VCRTMPChunkStreamID) { 31 | VCRTMPChunkStreamIDControl = 0x02, 32 | VCRTMPChunkStreamIDCommand = 0x03, 33 | VCRTMPChunkStreamIDAudio = 0x04, 34 | VCRTMPChunkStreamIDVideo = 0x05, 35 | }; 36 | 37 | 38 | typedef NS_ENUM(uint8_t, VCRTMPChunkMessageHeaderType) { 39 | VCRTMPChunkMessageHeaderType0 = 0, 40 | VCRTMPChunkMessageHeaderType1 = 1, 41 | VCRTMPChunkMessageHeaderType2 = 2, 42 | VCRTMPChunkMessageHeaderType3 = 3, 43 | }; 44 | 45 | @class VCRTMPMessage; 46 | @interface VCRTMPChunk : NSObject 47 | 48 | /** 49 | Use VCRTMPChunkStreamID when publish 50 | */ 51 | @property (nonatomic, assign) VCRTMPChunkStreamID chunkStreamID; 52 | @property (nonatomic, assign) VCRTMPChunkMessageHeaderType messageHeaderType; 53 | @property (nonatomic, strong, nullable) VCRTMPMessage *message; 54 | @property (nonatomic, strong, nullable) NSData *chunkData; 55 | 56 | - (instancetype)initWithType:(VCRTMPChunkMessageHeaderType)type 57 | chunkStreamID:(VCRTMPChunkStreamID)chunkStreamID 58 | message:(VCRTMPMessage *)message; 59 | 60 | - (NSInteger)basicHeaderSize; 61 | - (NSInteger)messageHeaderSize; 62 | - (NSInteger)extendedTimestampSize; 63 | 64 | - (NSData *)makeBasicHeader; 65 | - (NSData *)makeMessageHeaderWithExtendedTimestamp; 66 | - (NSData *)makeChunkHeader; 67 | - (NSData *)makeChunk; 68 | 69 | @end 70 | 71 | typedef NS_ENUM(uint8_t, VCRTMPChunkSetPeerBandwidthLimitType) { 72 | VCRTMPChunkSetPeerBandwidthLimitTypeHard, 73 | VCRTMPChunkSetPeerBandwidthLimitTypeSoft, 74 | VCRTMPChunkSetPeerBandwidthLimitTypeDynamic, 75 | }; 76 | 77 | @interface VCRTMPChunk (ProtocolControlMessage) 78 | + (instancetype)makeSetChunkSize:(uint32_t)size; 79 | - (uint32_t)setChunkSizeValue; 80 | 81 | + (instancetype)makeAbortMessage:(uint32_t)chunkStreamID; 82 | - (uint32_t)abortMessageValue; 83 | 84 | + (instancetype)makeAcknowledgement:(uint32_t)seq; 85 | - (uint32_t)acknowledgementValue; 86 | 87 | + (instancetype)makeWindowAcknowledgementSize:(uint32_t)windowSize; 88 | - (uint32_t)windowAcknowledgementSizeValue; 89 | 90 | + (instancetype)makeSetPeerBandwidth:(uint32_t)ackWindowSize 91 | limitType:(VCRTMPChunkSetPeerBandwidthLimitType)limitType; 92 | - (uint32_t)setPeerBandwidthValue; 93 | - (uint8_t)limitTypeValue; 94 | @end 95 | 96 | NS_ASSUME_NONNULL_END 97 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPChunkChannel.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPChunkChannel.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/6. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCRTMPChunk.h" 11 | #import "VCTCPSocket.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @class VCRTMPChunkChannel; 16 | @protocol VCRTMPChunkChannelDelegate 17 | - (void)channel:(VCRTMPChunkChannel *)channel didReceiveFrame:(VCRTMPChunk *)chunk; 18 | - (void)channelNeedAck:(VCRTMPChunkChannel *)channel; 19 | - (void)channelConnectionDidEnd; 20 | - (void)channel:(VCRTMPChunkChannel *)channel connectionHasError:(NSError *)error; 21 | @end 22 | 23 | @interface VCRTMPChunkChannel : NSObject 24 | 25 | @property (nonatomic, readonly) NSUInteger totalRecvByte; 26 | @property (nonatomic, readonly) NSUInteger totalSendByte; 27 | 28 | @property (nonatomic, assign) NSUInteger localChunkSize; 29 | @property (nonatomic, assign) NSUInteger remoteChunkSize; 30 | @property (nonatomic, assign) NSUInteger acknowlegmentWindowSize; 31 | @property (nonatomic, assign) NSUInteger bandwidth; 32 | 33 | @property (nonatomic, weak) id delegate; 34 | 35 | + (instancetype)channelForSocket:(VCTCPSocket *)socket; 36 | 37 | - (void)writeFrame:(VCRTMPChunk *)chunk; 38 | 39 | - (void)resetRecvByteCount; 40 | - (void)resetSendByteCount; 41 | - (void)useCurrntAcknowlegmentWindowSizeAsBandwidth; 42 | 43 | - (void)end; 44 | @end 45 | 46 | NS_ASSUME_NONNULL_END 47 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPCommandMessageCommand.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPCommandMessageCommand.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/6. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCRTMPChunk.h" 11 | #import "VCByteArray.h" 12 | #import "VCActionScriptTypes.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface VCRTMPCommandMessageCommand : NSObject { 17 | @protected 18 | NSData *_data; 19 | } 20 | + (instancetype)command; 21 | - (instancetype)initWithData:(NSData *)data; 22 | - (NSData *)chunkData; 23 | - (void)serializeToByteArray:(VCByteArray *)byteArray; 24 | - (NSData *)serialize; 25 | - (void)deserialize; 26 | @end 27 | 28 | @class VCRTMPCommandMessageResponse; 29 | typedef void(^VCRTMPCommandMessageResponseBlock)(VCRTMPCommandMessageResponse * _Nullable response, BOOL isSuccess); 30 | 31 | @class VCRTMPCommandMessageCommand; 32 | @interface VCRTMPChunk (CommandMessageComand) 33 | + (instancetype)makeNetConnectionCommand:(VCRTMPCommandMessageCommand *)command; 34 | + (instancetype)makeNetStreamCommand:(VCRTMPCommandMessageCommand *)command; 35 | - (NSString *)commandTypeValue; 36 | - (NSNumber *)transactionIDValue; 37 | @end 38 | @interface VCRTMPChunk (CommandMessageVideoAudio) 39 | + (instancetype)makeVideoChunk; 40 | + (instancetype)makeAudioChunk; 41 | @end 42 | 43 | extern NSString * const VCRTMPCommandMessageResponseSuccess; 44 | extern NSString * const VCRTMPCommandMessageResponseError; 45 | extern NSString * const VCRTMPCommandMessageResponseLevelStatus; 46 | @interface VCRTMPCommandMessageResponse : VCRTMPCommandMessageCommand 47 | @property (nonatomic, copy, nullable) NSString *response; 48 | @property (nonatomic, strong, nullable) NSNumber *transactionID; 49 | @end 50 | 51 | #pragma mark - Net Connection Command 52 | 53 | @interface VCRTMPNetConnectionCommandConnect : VCRTMPCommandMessageCommand 54 | @property (nonatomic, copy, nullable) NSString *commandName; 55 | @property (nonatomic, strong, nullable) NSNumber *transactionID; 56 | @property (nonatomic, strong, nullable) NSDictionary *commandObject; 57 | @property (nonatomic, strong, nullable) NSDictionary *optionalUserArguments; 58 | @end 59 | 60 | @interface VCRTMPNetConnectionCommandConnectResult : VCRTMPCommandMessageResponse 61 | @property (nonatomic, strong, nullable) NSDictionary *properties; 62 | @property (nonatomic, strong, nullable) NSDictionary *information; 63 | @end 64 | 65 | @interface VCRTMPNetConnectionCommandFCPublish : VCRTMPCommandMessageCommand 66 | @property (nonatomic, copy, nullable) NSString *commandName; 67 | @property (nonatomic, strong, nullable) NSNumber *transactionID; 68 | @property (nonatomic, strong, nullable) NSDictionary *commandObject; 69 | @property (nonatomic, strong, nullable) NSString *streamName; 70 | @end 71 | 72 | @interface VCRTMPNetConnectionCommandCreateStream : VCRTMPCommandMessageCommand 73 | @property (nonatomic, copy, nullable) NSString *commandName; 74 | @property (nonatomic, strong, nullable) NSNumber *transactionID; 75 | @property (nonatomic, strong, nullable) NSDictionary *commandObject; 76 | @end 77 | 78 | @interface VCRTMPNetConnectionCommandCreateStreamResult : VCRTMPCommandMessageResponse 79 | @property (nonatomic, strong, nullable) NSDictionary *commandObject; 80 | @property (nonatomic, strong, nullable) NSNumber *streamID; 81 | @end 82 | 83 | @interface VCRTMPNetConnectionCommandReleaseStream : VCRTMPCommandMessageCommand 84 | @property (nonatomic, copy, nullable) NSString *commandName; 85 | @property (nonatomic, strong, nullable) NSNumber *transactionID; 86 | @property (nonatomic, strong, nullable) NSDictionary *commandObject; 87 | @property (nonatomic, copy, nullable) NSString *streamName; 88 | @end 89 | 90 | #pragma mark - Net Stream Command 91 | extern NSString * const VCRTMPNetStreamCommandPublishTypeLive; 92 | extern NSString * const VCRTMPNetStreamCommandPublishTypeRecord; 93 | extern NSString * const VCRTMPNetStreamCommandPublishTypeAppend; 94 | @interface VCRTMPNetStreamCommandPublish : VCRTMPCommandMessageCommand 95 | @property (nonatomic, copy, nullable) NSString *commandName; 96 | @property (nonatomic, strong, nullable) NSNumber *transactionID; 97 | @property (nonatomic, strong, nullable) NSDictionary *commandObject; 98 | @property (nonatomic, copy, nullable) NSString *publishingName; 99 | @property (nonatomic, copy, nullable) NSString *publishingType; 100 | @end 101 | 102 | @interface VCRTMPNetStreamCommandOnStatus : VCRTMPCommandMessageResponse 103 | @property (nonatomic, strong, nullable) NSDictionary *properties; 104 | @property (nonatomic, strong, nullable) NSDictionary *information; 105 | @end 106 | 107 | @interface VCRTMPNetStreamCommandSetDataFrame : VCRTMPCommandMessageCommand 108 | @property (nonatomic, copy, nullable) NSString *commandName; 109 | @property (nonatomic, copy, nullable) NSString *subCommandName; 110 | @property (nonatomic, strong, nullable) NSDictionary *param; 111 | @end 112 | NS_ASSUME_NONNULL_END 113 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPHandshake.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPHandshake.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCTCPSocket.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | extern NSErrorDomain const VCRTMPHandshakeErrorDomain; 15 | typedef NS_ENUM(NSInteger, VCRTMPHandshakeErrorCode) { 16 | VCRTMPHandshakeErrorCodeConnectTimeout = -1000, 17 | VCRTMPHandshakeErrorCodeUnknow = -1001, 18 | VCRTMPHandshakeErrorCodeConnectReset = -1002, 19 | VCRTMPHandshakeErrorCodeConnectError = -1003, 20 | VCRTMPHandshakeErrorCodeVerifyS0S1 = -1004, 21 | VCRTMPHandshakeErrorCodeVerifyS2 = -1005, 22 | }; 23 | 24 | typedef NS_ENUM(NSUInteger, VCRTMPHandshakeState) { 25 | VCRTMPHandshakeStateUninitialized, 26 | VCRTMPHandshakeStateVersionSent, 27 | VCRTMPHandshakeStateAckSent, 28 | VCRTMPHandshakeStateHandshakeDone, 29 | VCRTMPHandshakeStateError 30 | }; 31 | 32 | @class VCRTMPHandshake; 33 | @class VCRTMPSession; 34 | typedef void(^VCRTMPHandshakeBlock)(VCRTMPHandshake *handshake, VCRTMPSession * _Nullable session, BOOL isSuccess, NSError * _Nullable error); 35 | 36 | @class VCRTMPNetConnection; 37 | @interface VCRTMPHandshake : NSObject 38 | @property (nonatomic, readonly) VCRTMPHandshakeState state; 39 | 40 | #pragma mark - RTMP Handshake Property 41 | @property (nonatomic, assign) uint8_t version; 42 | 43 | /** 44 | 默认初始化方法 45 | 46 | @param socket 需要握手的套接字 47 | @return handshake实例 48 | */ 49 | + (instancetype)handshakeForSocket:(VCTCPSocket *)socket; 50 | - (void)startHandshakeWithBlock:(VCRTMPHandshakeBlock)block; 51 | 52 | @end 53 | 54 | NS_ASSUME_NONNULL_END 55 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPMessage.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPMessage.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(uint8_t, VCRTMPMessageType) { 14 | // 5.4. Protocol Control Messages 15 | VCRTMPMessageTypeSetChunkSize = 1, 16 | VCRTMPMessageTypeAbortMessage = 2, 17 | VCRTMPMessageTypeAcknowledgement = 3, 18 | VCRTMPMessageTypeWindowAcknowledgement = 5, 19 | VCRTMPMessageTypeUserControl = 4, 20 | VCRTMPMessageTypeSetPeerBandwidth = 6, 21 | 22 | // 7.1. Types of Messages 23 | VCRTMPMessageTypeAMF0Command = 20, 24 | VCRTMPMessageTypeAMF3Command = 17, 25 | VCRTMPMessageTypeAMF0Data = 18, 26 | VCRTMPMessageTypeAMF3Data = 15, 27 | VCRTMPMessageTypeAMF0SharedObject = 19, 28 | VCRTMPMessageTypeAMF3SharedObject = 16, 29 | VCRTMPMessageTypeAudio = 8, 30 | VCRTMPMessageTypeVideo = 9, 31 | VCRTMPMessageTypeAggregate = 22, 32 | 33 | VCRTMPMessageTypeUnknow = 0xFF, 34 | }; 35 | @interface VCRTMPMessage : NSObject 36 | @property (nonatomic, assign) uint32_t timestamp; 37 | @property (nonatomic, assign) uint32_t messageLength; 38 | @property (nonatomic, assign) VCRTMPMessageType messageTypeID; 39 | @property (nonatomic, assign) uint32_t messageStreamID; 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPMessage.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPMessage.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPMessage.h" 10 | 11 | #define kVCRTMPMessageDefaultStreamID (0) 12 | 13 | @implementation VCRTMPMessage 14 | - (instancetype)init { 15 | self = [super init]; 16 | if (self) { 17 | _timestamp = 0; 18 | _messageLength = 0; 19 | _messageTypeID = 0; 20 | _messageStreamID = kVCRTMPMessageDefaultStreamID; 21 | } 22 | return self; 23 | } 24 | 25 | - (instancetype)copyWithZone:(nullable NSZone *)zone { 26 | VCRTMPMessage *message = [[VCRTMPMessage alloc] init]; 27 | message.timestamp = self.timestamp; 28 | message.messageLength = self.messageLength; 29 | message.messageTypeID = self.messageTypeID; 30 | message.messageStreamID = self.messageStreamID; 31 | return message; 32 | } 33 | - (NSString *)description { 34 | return [NSString stringWithFormat:@"{messageTypeID: %d, messageStreamID: %d}", self.messageTypeID, self.messageStreamID]; 35 | } 36 | @end 37 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPNetConnection.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPNetConnection.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/5. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCRTMPCommandMessageCommand.h" 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class VCRTMPSession; 14 | extern NSErrorDomain const VCRTMPNetConnectionErrorDomain; 15 | typedef NS_ENUM(NSUInteger, VCRTMPNetConnectionErrorCode) { 16 | VCRTMPNetConnectionErrorCodeUnknow = -2000, 17 | VCRTMPNetConnectionErrorCodeConnectReset = -2001, 18 | VCRTMPNetConnectionErrorCodeConnectError = -2002, 19 | }; 20 | 21 | @class VCRTMPNetStream; 22 | @interface VCRTMPNetConnection : NSObject 23 | 24 | + (instancetype)netConnectionForSession:(VCRTMPSession *)session; 25 | 26 | - (void)connecWithParam:(NSDictionary *)param completion:(VCRTMPCommandMessageResponseBlock)block; 27 | - (void)releaseStream:(NSString *)streamName; 28 | - (void)createStream:(NSString *)streamName completion:(VCRTMPCommandMessageResponseBlock)block; 29 | 30 | - (VCRTMPNetStream *)makeNetStreamWithStreamName:(NSString *)streamName 31 | streamID:(uint32_t)streamID; 32 | 33 | - (void)end; 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPNetConnection.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPNetConnection.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/5. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPNetConnection.h" 10 | #import "VCRTMPNetConnection_Private.h" 11 | #import "VCRTMPSession.h" 12 | #import "VCRTMPSession_Private.h" 13 | #import "VCRTMPSession+CommandMessageHandler.h" 14 | #import "VCRTMPNetStream.h" 15 | #import "VCRTMPChunkChannel.h" 16 | #import "VCRTMPCommandMessageCommand.h" 17 | 18 | NSErrorDomain const VCRTMPNetConnectionErrorDomain = @"VCRTMPNetConnectionErrorDomain"; 19 | 20 | @implementation VCRTMPNetConnection 21 | 22 | - (void)dealloc { 23 | [self end]; 24 | } 25 | 26 | - (instancetype)init { 27 | self = [super init]; 28 | if (self) { 29 | _netStreams = [[NSMutableDictionary alloc] init]; 30 | } 31 | return self; 32 | } 33 | 34 | + (instancetype)netConnectionForSession:(VCRTMPSession *)session { 35 | VCRTMPNetConnection *connection = [[VCRTMPNetConnection alloc] init]; 36 | connection.session = session; 37 | return connection; 38 | } 39 | 40 | - (void)connecWithParam:(NSDictionary *)param completion:(VCRTMPCommandMessageResponseBlock)block { 41 | self.responseBlock = block; 42 | 43 | VCRTMPNetConnectionCommandConnect *command = [VCRTMPNetConnectionCommandConnect command]; 44 | command.transactionID = @([self.session nextTransactionID]); 45 | command.commandObject = param; 46 | VCRTMPChunk *chunk = [VCRTMPChunk makeNetConnectionCommand:command]; 47 | [self.session registerTransactionID:command.transactionID.unsignedIntegerValue 48 | observer:self 49 | handler:@selector(handleConnectionResult:)]; 50 | [self.session.channel writeFrame:chunk]; 51 | } 52 | 53 | - (void)releaseStream:(NSString *)streamName { 54 | VCRTMPNetConnectionCommandReleaseStream *command = [VCRTMPNetConnectionCommandReleaseStream command]; 55 | command.transactionID = @([self.session nextTransactionID]); 56 | command.streamName = streamName; 57 | VCRTMPChunk *chunk = [VCRTMPChunk makeNetConnectionCommand:command]; 58 | [self.session.channel writeFrame:chunk]; 59 | } 60 | 61 | - (void)createStream:(NSString *)streamName completion:(VCRTMPCommandMessageResponseBlock)block { 62 | self.responseBlock = block; 63 | 64 | VCRTMPNetConnectionCommandFCPublish *publish = [VCRTMPNetConnectionCommandFCPublish command]; 65 | publish.transactionID = @([self.session nextTransactionID]); 66 | publish.streamName = streamName; 67 | VCRTMPChunk *publishChunk = [VCRTMPChunk makeNetConnectionCommand:publish]; 68 | [self.session.channel writeFrame:publishChunk]; 69 | 70 | VCRTMPNetConnectionCommandCreateStream *createStream = [VCRTMPNetConnectionCommandCreateStream command]; 71 | createStream.transactionID = @([self.session nextTransactionID]); 72 | VCRTMPChunk *createStreamChunk = [VCRTMPChunk makeNetConnectionCommand:createStream]; 73 | [self.session registerTransactionID:createStream.transactionID.unsignedIntegerValue 74 | observer:self 75 | handler:@selector(handleCreateStreamResult:)]; 76 | [self.session.channel writeFrame:createStreamChunk]; 77 | } 78 | 79 | - (void)end { 80 | for (VCRTMPNetStream *stream in self.netStreams.allValues) { 81 | [stream end]; 82 | [self releaseStream:stream.streamName]; 83 | } 84 | } 85 | 86 | #pragma mark - Net Stream 87 | - (VCRTMPNetStream *)makeNetStreamWithStreamName:(NSString *)streamName 88 | streamID:(uint32_t)streamID { 89 | VCRTMPNetStream *stream = [VCRTMPNetStream netStreamWithName:streamName 90 | streamID:streamID 91 | forNetConnection:self]; 92 | [self.netStreams setObject:stream forKey:streamName]; 93 | return stream; 94 | } 95 | 96 | - (void)removeNetStreamWithStreamName:(NSString *)streamName { 97 | [self.netStreams removeObjectForKey:streamName]; 98 | } 99 | 100 | #pragma mark - Handle Message 101 | - (void)handleConnectionResult:(VCRTMPCommandMessageResponse *)result { 102 | BOOL success = NO; 103 | if ([result.response isEqualToString:VCRTMPCommandMessageResponseSuccess]) { 104 | success = YES; 105 | result = [[VCRTMPNetConnectionCommandConnectResult alloc] initWithData:result.chunkData]; 106 | [result deserialize]; 107 | } 108 | if (self.responseBlock) { 109 | self.responseBlock(result, success); 110 | } 111 | } 112 | 113 | - (void)handleCreateStreamResult:(VCRTMPCommandMessageResponse *)result { 114 | BOOL success = NO; 115 | if ([result.response isEqualToString:VCRTMPCommandMessageResponseSuccess]) { 116 | success = YES; 117 | result = [[VCRTMPNetConnectionCommandCreateStreamResult alloc] initWithData:result.chunkData]; 118 | [result deserialize]; 119 | } 120 | if (self.responseBlock) { 121 | self.responseBlock(result, success); 122 | } 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPNetConnection_Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPNetConnection_Private.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/7. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPNetConnection.h" 10 | #import "VCRTMPCommandMessageCommand.h" 11 | 12 | @interface VCRTMPNetConnection () 13 | @property (nonatomic, copy) VCRTMPCommandMessageResponseBlock responseBlock; 14 | @property (nonatomic, weak) VCRTMPSession *session; 15 | @property (nonatomic, strong) NSMutableDictionary *netStreams; 16 | - (void)handleConnectionResult:(VCRTMPCommandMessageResponse *)result; 17 | 18 | - (void)removeNetStreamWithStreamName:(NSString *)streamName; 19 | @end 20 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPNetStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPNetStream.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/8. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCRTMPCommandMessageCommand.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @class VCRTMPNetConnection; 15 | @interface VCRTMPNetStream : NSObject 16 | 17 | @property (nonatomic, readonly) NSString *streamName; 18 | @property (nonatomic, readonly) uint32_t streamID; 19 | 20 | + (instancetype)netStreamWithName:(NSString *)streamName 21 | streamID:(uint32_t)streamID 22 | forNetConnection:(VCRTMPNetConnection *)netConnection; 23 | 24 | - (void)publishWithCompletion:(VCRTMPCommandMessageResponseBlock)block; 25 | - (void)setMetaData:(NSDictionary *)param; 26 | 27 | - (void)writeChunk:(VCRTMPChunk *)chunk; 28 | 29 | - (void)end; 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPNetStream.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPNetStream.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/8. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPNetStream.h" 10 | #import "VCRTMPNetStream_Private.h" 11 | #import "VCRTMPNetConnection_Private.h" 12 | #import "VCRTMPSession_Private.h" 13 | #import "VCRTMPSession+CommandMessageHandler.h" 14 | #import "VCRTMPChunk.h" 15 | #import "VCRTMPMessage.h" 16 | 17 | @implementation VCRTMPNetStream 18 | 19 | - (void)dealloc { 20 | [self end]; 21 | [self.netConnection.session removeMessageStreamIDTask:self.streamID]; 22 | } 23 | 24 | + (instancetype)netStreamWithName:(NSString *)streamName 25 | streamID:(uint32_t)streamID 26 | forNetConnection:(VCRTMPNetConnection *)netConnection { 27 | VCRTMPNetStream *netStream = [[VCRTMPNetStream alloc] init]; 28 | netStream.streamName = streamName; 29 | netStream.streamID = streamID; 30 | netStream.netConnection = netConnection; 31 | [netConnection.session registerMessageStreamID:streamID 32 | observer:netStream 33 | handler:@selector(handleNetStreamMessage:)]; 34 | return netStream; 35 | } 36 | 37 | - (VCRTMPChunk *)makeNetStreamChunkWithCommand:(VCRTMPCommandMessageCommand *)command { 38 | VCRTMPChunk *chunk = [VCRTMPChunk makeNetStreamCommand:command]; 39 | chunk.message.messageStreamID = self.streamID; 40 | return chunk; 41 | } 42 | 43 | #pragma mark - Publish 44 | - (void)publishWithCompletion:(VCRTMPCommandMessageResponseBlock)block { 45 | self.responseBlock = block; 46 | 47 | VCRTMPNetStreamCommandPublish *command = [VCRTMPNetStreamCommandPublish command]; 48 | command.transactionID = @([self.netConnection.session nextTransactionID]); 49 | command.publishingName = self.streamName; 50 | command.publishingType = VCRTMPNetStreamCommandPublishTypeLive; 51 | 52 | VCRTMPChunk *chunk = [self makeNetStreamChunkWithCommand:command]; 53 | [self.netConnection.session.channel writeFrame:chunk]; 54 | } 55 | 56 | - (void)end { 57 | 58 | } 59 | 60 | #pragma mark - Set Meta Data 61 | - (void)setMetaData:(NSDictionary *)param { 62 | VCRTMPNetStreamCommandSetDataFrame *command = [VCRTMPNetStreamCommandSetDataFrame command]; 63 | command.subCommandName = @"onMetaData"; 64 | command.param = param; 65 | 66 | VCRTMPChunk *chunk = [self makeNetStreamChunkWithCommand:command]; 67 | [self.netConnection.session.channel writeFrame:chunk]; 68 | } 69 | 70 | #pragma mark - Net Stream Message Handle 71 | + (NSDictionary *)commandMessageHandlerMap { 72 | static NSDictionary *map = nil; 73 | if (map != nil) { 74 | return map; 75 | } 76 | map = @{ 77 | @"onStatus": NSStringFromSelector(@selector(handleOnStatusMessage:)), 78 | }; 79 | return map; 80 | } 81 | 82 | - (void)handleNetStreamMessage:(VCRTMPChunk *)chunk { 83 | VCRTMPCommandMessageResponse *response = [[VCRTMPCommandMessageResponse alloc] initWithData:chunk.chunkData]; 84 | [response deserialize]; 85 | NSString *selString = [[[self class] commandMessageHandlerMap] objectForKey:response.response]; 86 | SEL selector = NSSelectorFromString(selString); 87 | if (selector) { 88 | #pragma clang diagnostic push 89 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 90 | [self performSelector:selector withObject:response]; 91 | #pragma clang diagnostic pop 92 | } 93 | } 94 | 95 | - (void)handleOnStatusMessage:(VCRTMPCommandMessageResponse *)response { 96 | VCRTMPNetStreamCommandOnStatus *onStatus = [[VCRTMPNetStreamCommandOnStatus alloc] initWithData:response.chunkData]; 97 | [onStatus deserialize]; 98 | BOOL isSuccess = NO; 99 | if (onStatus.information) { 100 | NSString *levelStr = [onStatus.information objectForKey:@"level"].value; 101 | if (levelStr && 102 | [levelStr isEqualToString:VCRTMPCommandMessageResponseLevelStatus]) { 103 | isSuccess = YES; 104 | } 105 | } 106 | if (self.responseBlock) { 107 | self.responseBlock(onStatus, isSuccess); 108 | self.responseBlock = nil; 109 | } 110 | } 111 | 112 | - (void)writeChunk:(VCRTMPChunk *)chunk { 113 | [self.netConnection.session.channel writeFrame:chunk]; 114 | } 115 | @end 116 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPNetStream_Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPNetStream_Private.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/8. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPNetStream.h" 10 | #import "VCRTMPCommandMessageCommand.h" 11 | 12 | @class VCRTMPNetConnection; 13 | @interface VCRTMPNetStream () 14 | @property (nonatomic, copy) NSString *streamName; 15 | @property (nonatomic, assign) uint32_t streamID; 16 | @property (nonatomic, copy) VCRTMPCommandMessageResponseBlock responseBlock; 17 | 18 | @property (nonatomic, weak) VCRTMPNetConnection *netConnection; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession+CommandMessageHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession+CommandMessageHandler.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/7. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSession.h" 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class VCRTMPCommandMessageCommand; 13 | @interface VCRTMPSession (CommandMessageHandler) 14 | 15 | - (void)registerTransactionID:(NSUInteger)transactionID 16 | observer:(NSObject *)observer 17 | handler:(SEL)handler; 18 | 19 | - (void)removeTransactionIDTask:(NSUInteger)transactionID; 20 | 21 | - (void)registerMessageStreamID:(NSUInteger)messageStreamID 22 | observer:(NSObject *)observer 23 | handler:(SEL)handler; 24 | 25 | - (void)removeMessageStreamIDTask:(NSUInteger)messageStreamID; 26 | 27 | - (void)handleAMF0Command:(VCRTMPChunk *)chunk; 28 | 29 | - (void)handleCommandMessageResponse:(VCRTMPChunk *)chunk; 30 | 31 | - (void)handleNetStreamMessage:(VCRTMPChunk *)chunk; 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession+CommandMessageHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession+CommandMessageHandler.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/7. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSession+CommandMessageHandler.h" 10 | #import "VCRTMPSession_Private.h" 11 | #import "VCRTMPNetConnection_Private.h" 12 | #import "VCRTMPCommandMessageCommand.h" 13 | #import "VCRTMPChunk.h" 14 | #import "VCRTMPMessage.h" 15 | 16 | @implementation VCRTMPSession (CommandMessageHandler) 17 | 18 | - (void)registerTransactionID:(NSUInteger)transactionID 19 | observer:(NSObject *)observer 20 | handler:(SEL)handler { 21 | VCRTMPCommandMessageTask *task = [[VCRTMPCommandMessageTask alloc] init]; 22 | task.transactionID = transactionID; 23 | task.observer = observer; 24 | task.handler = handler; 25 | [self.commandMessageTasks setObject:task forKey:@(transactionID)]; 26 | } 27 | 28 | - (void)removeTransactionIDTask:(NSUInteger)transactionID { 29 | [self.commandMessageTasks removeObjectForKey:@(transactionID)]; 30 | } 31 | 32 | - (void)registerMessageStreamID:(NSUInteger)messageStreamID 33 | observer:(NSObject *)observer 34 | handler:(SEL)handler { 35 | VCRTMPNetStreamMessageTask *task = [[VCRTMPNetStreamMessageTask alloc] init]; 36 | task.messageStreamID = messageStreamID; 37 | task.observer = observer; 38 | task.handler = handler; 39 | [self.netStreamMessageTasks setObject:task forKey:@(messageStreamID)]; 40 | } 41 | 42 | - (void)removeMessageStreamIDTask:(NSUInteger)messageStreamID { 43 | [self.netStreamMessageTasks removeObjectForKey:@(messageStreamID)]; 44 | } 45 | 46 | - (void)handleAMF0Command:(VCRTMPChunk *)chunk { 47 | NSString *commandType = chunk.commandTypeValue; 48 | NSLog(@"[RTMP][CHANNEL] Command: %@", commandType); 49 | NSString *handler = [[self class] commandMessageHandlerMap][commandType]; 50 | if (handler) { 51 | SEL selector = NSSelectorFromString(handler); 52 | if (selector && 53 | [self respondsToSelector:selector]) { 54 | #pragma clang diagnostic push 55 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 56 | [self performSelector:selector withObject:chunk]; 57 | #pragma clang diagnostic pop 58 | } 59 | } 60 | } 61 | 62 | - (void)handleCommandMessageResponse:(VCRTMPChunk *)chunk { 63 | NSUInteger transactionID = chunk.transactionIDValue.unsignedIntegerValue; 64 | VCRTMPCommandMessageTask *task = [self.commandMessageTasks objectForKey:@(transactionID)]; 65 | if (task && 66 | task.observer && 67 | task.handler) { 68 | VCRTMPCommandMessageResponse *response = [[VCRTMPCommandMessageResponse alloc] initWithData:chunk.chunkData]; 69 | [response deserialize]; 70 | if ([task.observer respondsToSelector:task.handler]) { 71 | #pragma clang diagnostic push 72 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 73 | [task.observer performSelector:task.handler withObject:response]; 74 | #pragma clang diagnostic pop 75 | } 76 | } 77 | [self removeTransactionIDTask:transactionID]; 78 | } 79 | 80 | - (void)handleNetStreamMessage:(VCRTMPChunk *)chunk { 81 | VCRTMPNetStreamMessageTask *task = [self.netStreamMessageTasks objectForKey:@(chunk.message.messageStreamID)]; 82 | if (task && 83 | task.observer && 84 | task.handler) { 85 | if ([task.observer respondsToSelector:task.handler]) { 86 | #pragma clang diagnostic push 87 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 88 | [task.observer performSelector:task.handler withObject:chunk]; 89 | #pragma clang diagnostic pop 90 | } 91 | } else { 92 | if (task.observer == nil) { 93 | [self.netStreamMessageTasks removeObjectForKey:@(chunk.message.messageStreamID)]; 94 | } 95 | } 96 | } 97 | @end 98 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession+ProtocolControlMessageHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession+ProtocolControlMessageHandler.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/6. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSession.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCRTMPSession (ProtocolControlMessageHandler) 14 | - (void)handleWindowAcknowledgementSize:(VCRTMPChunk *)chunk; 15 | - (void)handleSetPeerBandwidthValue:(VCRTMPChunk *)chunk; 16 | - (void)handleSetChunkSize:(VCRTMPChunk *)chunk; 17 | - (void)handleAcknowledgement:(VCRTMPChunk *)chunk; 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession+ProtocolControlMessageHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession+ProtocolControlMessageHandler.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/6. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSession+ProtocolControlMessageHandler.h" 10 | #import "VCRTMPSession_Private.h" 11 | #import "VCRTMPCommandMessageCommand.h" 12 | #import "VCRTMPChunk.h" 13 | #import "VCRTMPMessage.h" 14 | 15 | @implementation VCRTMPSession (ProtocolControlMessageHandler) 16 | - (void)handleWindowAcknowledgementSize:(VCRTMPChunk *)chunk { 17 | NSInteger s = [chunk windowAcknowledgementSizeValue]; 18 | NSLog(@"[RTMP][CHANNEL] Window Acknowledgement Size: %ld", (long)s); 19 | self.channel.acknowlegmentWindowSize = s; 20 | } 21 | 22 | - (void)handleSetPeerBandwidthValue:(VCRTMPChunk *)chunk { 23 | uint32_t s = (uint32_t)[chunk setPeerBandwidthValue]; 24 | VCRTMPChunkSetPeerBandwidthLimitType limitType = [chunk limitTypeValue]; 25 | NSLog(@"[RTMP][CHANNEL] Set Peer Bandwidth: %ld", (long)s); 26 | 27 | __weak typeof(self) weakSelf = self; 28 | void (^setBandwidthBlock)(uint32_t size) = ^(uint32_t size) { 29 | if (weakSelf.channel.acknowlegmentWindowSize != size) { 30 | [weakSelf respondWindowAcknowledgmentWithSize:size]; 31 | } 32 | weakSelf.channel.acknowlegmentWindowSize = size; 33 | [weakSelf.channel useCurrntAcknowlegmentWindowSizeAsBandwidth]; 34 | }; 35 | 36 | if (limitType == VCRTMPChunkSetPeerBandwidthLimitTypeHard) { 37 | setBandwidthBlock(s); 38 | } else if (limitType == VCRTMPChunkSetPeerBandwidthLimitTypeSoft || 39 | limitType == VCRTMPChunkSetPeerBandwidthLimitTypeDynamic) { 40 | if (self.channel.bandwidth == 0) { 41 | setBandwidthBlock(s); 42 | } else { 43 | uint32_t minBandwidth = MIN((uint32_t)self.channel.bandwidth, s); 44 | setBandwidthBlock(minBandwidth); 45 | } 46 | } 47 | } 48 | 49 | - (void)handleSetChunkSize:(VCRTMPChunk *)chunk { 50 | NSInteger s = [chunk setChunkSizeValue]; 51 | NSLog(@"[RTMP][CHANNEL] Set Chunk Size: %ld", (long)s); 52 | self.channel.remoteChunkSize = s; 53 | } 54 | 55 | - (void)handleAcknowledgement:(VCRTMPChunk *)chunk { 56 | NSInteger s = [chunk acknowledgementValue]; 57 | NSLog(@"[RTMP][CHANNEL] Ack: %ld", (long)s); 58 | [self.channel resetSendByteCount]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/3. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCRTMPChunk.h" 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class VCTCPSocket; 14 | @class VCRTMPNetConnection; 15 | extern NSErrorDomain const VCRTMPSessionErrorDomain; 16 | typedef NS_ENUM(NSUInteger, VCRTMPSessionErrorCode) { 17 | VCRTMPSessionErrorCodeChannelEnd = -3000, 18 | VCRTMPSessionErrorCodeChannelError = -3001, 19 | }; 20 | 21 | typedef void(^VCRTMPSessionChannelCloseHandle)(NSError *error); 22 | @interface VCRTMPSession : NSObject 23 | 24 | + (instancetype)sessionForSocket:(VCTCPSocket *)socket; 25 | 26 | - (void)registerChannelCloseHandle:(VCRTMPSessionChannelCloseHandle)handle; 27 | 28 | - (void)setChunkSize:(uint32_t)size; 29 | - (void)setPeerBandwidth:(uint32_t)bandwidth limitType:(VCRTMPChunkSetPeerBandwidthLimitType)limitType; 30 | - (void)abortMessage:(uint32_t)chunkStreamID; 31 | 32 | - (VCRTMPNetConnection *)makeNetConnection; 33 | 34 | - (void)end; 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/3. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSession.h" 10 | #import "VCRTMPSession_Private.h" 11 | #import "VCRTMPSession+ProtocolControlMessageHandler.h" 12 | #import "VCRTMPSession+CommandMessageHandler.h" 13 | #import "VCRTMPChunkChannel.h" 14 | #import "VCTCPSocket.h" 15 | #import "VCRTMPMessage.h" 16 | #import "VCRTMPCommandMessageCommand.h" 17 | #import "VCRTMPNetConnection.h" 18 | 19 | NSErrorDomain const VCRTMPSessionErrorDomain = @"VCRTMPSessionErrorDomain"; 20 | 21 | @implementation VCRTMPSession 22 | 23 | - (void)dealloc { 24 | [self end]; 25 | } 26 | 27 | - (instancetype)init { 28 | self = [super init]; 29 | if (self) { 30 | _transactionIDCounter = 1; 31 | _commandMessageTasks = [[NSMutableDictionary alloc] init]; 32 | _netStreamMessageTasks = [[NSMutableDictionary alloc] init]; 33 | } 34 | return self; 35 | } 36 | 37 | + (instancetype)sessionForSocket:(VCTCPSocket *)socket { 38 | VCRTMPSession *session = [[VCRTMPSession alloc] init]; 39 | VCRTMPChunkChannel *channel = [VCRTMPChunkChannel channelForSocket:socket]; 40 | session.channel = channel; 41 | session.channel.delegate = session; 42 | return session; 43 | } 44 | 45 | - (NSUInteger)nextTransactionID { 46 | return ++self.transactionIDCounter; 47 | } 48 | 49 | - (void)registerChannelCloseHandle:(VCRTMPSessionChannelCloseHandle)handle { 50 | self.channelCloseHandle = handle; 51 | } 52 | 53 | #pragma mark - Net Connection 54 | - (VCRTMPNetConnection *)makeNetConnection { 55 | VCRTMPNetConnection *netConnection = [VCRTMPNetConnection netConnectionForSession:self]; 56 | self.netConnection = netConnection; 57 | return netConnection; 58 | } 59 | 60 | #pragma mark - Protocol Control Message 61 | - (void)setChunkSize:(uint32_t)size { 62 | VCRTMPChunk *chunk = [VCRTMPChunk makeSetChunkSize:size]; 63 | [self.channel writeFrame:chunk]; 64 | self.channel.localChunkSize = size; 65 | } 66 | 67 | - (void)abortMessage:(uint32_t)chunkStreamID { 68 | VCRTMPChunk *chunk = [VCRTMPChunk makeAbortMessage:chunkStreamID]; 69 | [self.channel writeFrame:chunk]; 70 | } 71 | 72 | - (void)setPeerBandwidth:(uint32_t)bandwidth limitType:(VCRTMPChunkSetPeerBandwidthLimitType)limitType { 73 | VCRTMPChunk *chunk = [VCRTMPChunk makeSetPeerBandwidth:bandwidth limitType:limitType]; 74 | [self.channel writeFrame:chunk]; 75 | } 76 | 77 | - (void)respondWindowAcknowledgmentWithSize:(uint32_t)size { 78 | VCRTMPChunk *chunk = [VCRTMPChunk makeWindowAcknowledgementSize:size]; 79 | [self.channel writeFrame:chunk]; 80 | } 81 | 82 | - (void)end { 83 | [self.netConnection end]; 84 | [self.channel end]; 85 | } 86 | #pragma mark - Handle Protocol Control Message 87 | + (NSDictionary *)protocolControlMessageHandlerMap { 88 | static NSDictionary *map = nil; 89 | if (map != nil) { 90 | return map; 91 | } 92 | map = @{ 93 | @(VCRTMPMessageTypeWindowAcknowledgement): NSStringFromSelector(@selector(handleWindowAcknowledgementSize:)), 94 | @(VCRTMPMessageTypeSetPeerBandwidth): NSStringFromSelector(@selector(handleSetPeerBandwidthValue:)), 95 | @(VCRTMPMessageTypeSetChunkSize): NSStringFromSelector(@selector(handleSetChunkSize:)), 96 | @(VCRTMPMessageTypeAMF0Command): NSStringFromSelector(@selector(handleAMF0Command:)), 97 | @(VCRTMPMessageTypeAcknowledgement): NSStringFromSelector(@selector(handleAcknowledgement:)), 98 | }; 99 | return map; 100 | } 101 | 102 | #pragma mark - Handle Command Message 103 | + (NSDictionary *)commandMessageHandlerMap { 104 | static NSDictionary *map = nil; 105 | if (map != nil) { 106 | return map; 107 | } 108 | map = @{ 109 | @"_result": NSStringFromSelector(@selector(handleCommandMessageResponse:)), 110 | @"_error": NSStringFromSelector(@selector(handleCommandMessageResponse:)), 111 | @"onStatus": NSStringFromSelector(@selector(handleNetStreamMessage:)), 112 | }; 113 | return map; 114 | } 115 | 116 | #pragma mark - Chunk Packet 117 | - (void)channel:(VCRTMPChunkChannel *)channel didReceiveFrame:(VCRTMPChunk *)chunk { 118 | NSLog(@"[RTMP][CHANNEL] 收到%@", chunk); 119 | NSString *handler = [[self class] protocolControlMessageHandlerMap][@(chunk.message.messageTypeID)]; 120 | if (handler) { 121 | SEL selector = NSSelectorFromString(handler); 122 | if (selector && 123 | [self respondsToSelector:selector]) { 124 | #pragma clang diagnostic push 125 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 126 | [self performSelector:selector withObject:chunk]; 127 | #pragma clang diagnostic pop 128 | } 129 | } 130 | } 131 | 132 | - (void)channelNeedAck:(VCRTMPChunkChannel *)channel { 133 | NSLog(@"[RTMP][CHANNLE] Need ACK"); 134 | [self.channel writeFrame:[VCRTMPChunk makeAcknowledgement:(uint32_t)self.channel.totalRecvByte]]; 135 | } 136 | 137 | - (void)channelConnectionDidEnd { 138 | NSLog(@"[RTMP][CHANNEL] End"); 139 | if (self.channelCloseHandle) { 140 | self.channelCloseHandle([NSError errorWithDomain:VCRTMPSessionErrorDomain code:VCRTMPSessionErrorCodeChannelEnd userInfo:nil]); 141 | } 142 | } 143 | 144 | - (void)channel:(VCRTMPChunkChannel *)channel connectionHasError:(NSError *)error { 145 | NSLog(@"[RTMP][CHANNEL] Error %@", error); 146 | if (self.channelCloseHandle) { 147 | self.channelCloseHandle([NSError errorWithDomain:VCRTMPSessionErrorDomain code:VCRTMPSessionErrorCodeChannelError userInfo:@{ 148 | @"error": error 149 | }]); 150 | } 151 | } 152 | 153 | @end 154 | 155 | @implementation VCRTMPCommandMessageTask 156 | @end 157 | @implementation VCRTMPNetStreamMessageTask 158 | @end 159 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/RTMP/VCRTMPSession_Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSession_Private.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/6. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSession.h" 10 | #import "VCRTMPChunkChannel.h" 11 | 12 | 13 | @interface VCRTMPCommandMessageTask : NSObject 14 | @property (nonatomic, weak) id observer; 15 | @property (nonatomic, assign) SEL handler; 16 | @property (nonatomic, assign) NSUInteger transactionID; 17 | @end 18 | 19 | @interface VCRTMPNetStreamMessageTask : NSObject 20 | @property (nonatomic, weak) id observer; 21 | @property (nonatomic, assign) SEL handler; 22 | @property (nonatomic, assign) NSUInteger messageStreamID; 23 | @end 24 | 25 | @interface VCRTMPSession () 26 | 27 | @property (nonatomic, copy) VCRTMPSessionChannelCloseHandle channelCloseHandle; 28 | 29 | @property (nonatomic, assign) NSUInteger transactionIDCounter; 30 | @property (nonatomic, strong) NSMutableDictionary *commandMessageTasks; 31 | @property (nonatomic, strong) NSMutableDictionary *netStreamMessageTasks; 32 | 33 | @property (nonatomic, strong) VCRTMPChunkChannel *channel; 34 | @property (nonatomic, strong) VCRTMPNetConnection *netConnection; 35 | 36 | - (NSUInteger)nextTransactionID; 37 | 38 | - (void)respondWindowAcknowledgmentWithSize:(uint32_t)size; 39 | 40 | + (NSDictionary *)protocolControlMessageHandlerMap; 41 | + (NSDictionary *)commandMessageHandlerMap; 42 | @end 43 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/Socket/VCTCPSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCTCPSocket.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/12. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class VCTCPSocket; 14 | 15 | /** 16 | 所有使用VCTCPSocket的子类应继承此接口 17 | 对此socket的读写请使用 使用此socket的对象 的读写方法 18 | */ 19 | @protocol VCTCPComm 20 | - (VCTCPSocket *)socket; 21 | @end 22 | 23 | @protocol VCTCPSocketDelegate 24 | - (void)tcpSocketConnectTimeout:(VCTCPSocket *)socket; 25 | - (void)tcpSocketDidConnected:(VCTCPSocket *)socket; 26 | - (void)tcpSocketHasByteAvailable:(VCTCPSocket *)socket; 27 | - (void)tcpSocketErrorOccurred:(VCTCPSocket *)socket stream:(NSStream *)stream; 28 | - (void)tcpSocketEndcountered:(VCTCPSocket *)socket; 29 | @end 30 | 31 | @interface VCTCPSocket : NSObject 32 | 33 | @property (nonatomic, readonly) NSString *host; 34 | @property (nonatomic, readonly) NSInteger port; 35 | 36 | @property (nonatomic, weak) id delegate; 37 | 38 | @property (nonatomic, assign) BOOL connected; 39 | 40 | @property (nonatomic, assign) NSTimeInterval timeout; 41 | @property (nonatomic, assign) NSInteger inputBufferWindowSize; 42 | 43 | @property (nonatomic, strong, nullable) NSInputStream *inputStream; 44 | @property (nonatomic, strong, nullable) NSOutputStream *outputStream; 45 | 46 | @property (nonatomic, readonly) NSInteger byteAvaliable; 47 | 48 | - (instancetype)init NS_UNAVAILABLE; 49 | - (instancetype)initWithHost:(NSString *)host 50 | port:(NSInteger)port NS_DESIGNATED_INITIALIZER; 51 | 52 | - (void)connect; 53 | 54 | - (void)close; 55 | 56 | - (void)writeData:(NSData *)data; 57 | - (nullable NSData *)readData; 58 | @end 59 | 60 | NS_ASSUME_NONNULL_END 61 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/Subscriber/VCRTMPSubscriber.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSubscriber.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/15. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCRTMPSubscriber : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Network/Subscriber/VCRTMPSubscriber.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCRTMPSubscriber.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/2/15. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCRTMPSubscriber.h" 10 | 11 | @implementation VCRTMPSubscriber 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Render/Audio/VCAudioPCMRender.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCAudioPCMRender.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/2. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "VCSampleBuffer.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface VCAudioPCMRender : NSObject 16 | - (instancetype)initWithPCMFormat:(AVAudioFormat *)format; 17 | - (void)renderPCMBuffer:(AVAudioPCMBuffer *)pcmBuffer withPresentationTimeStamp:(CMTime)presentationTimeStamp completionHandler:(AVAudioNodeCompletionHandler __nullable)handler; 18 | - (void)renderSampleBuffer:(VCSampleBuffer *)sampleBuffer completionHandler:(AVAudioNodeCompletionHandler _Nullable)handler; 19 | 20 | - (void)play; 21 | - (void)pause; 22 | - (void)stop; 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Render/Audio/VCAudioPCMRender.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCAudioPCMRender.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/2/2. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCAudioPCMRender.h" 11 | 12 | @interface VCAudioPCMRender () 13 | @property (nonatomic, strong) AVAudioEngine *audioEngine; 14 | @property (nonatomic, strong) AVAudioPlayerNode *playerNode; 15 | 16 | @property (nonatomic, strong) AVAudioFormat *pcmFormat; 17 | @end 18 | 19 | @implementation VCAudioPCMRender 20 | - (instancetype)initWithPCMFormat:(AVAudioFormat *)format { 21 | self = [super init]; 22 | if (self) { 23 | _audioEngine = [[AVAudioEngine alloc] init]; 24 | _playerNode = [[AVAudioPlayerNode alloc] init]; 25 | _pcmFormat = format; 26 | 27 | [_audioEngine attachNode:_playerNode]; 28 | [_audioEngine connect:_playerNode to:_audioEngine.mainMixerNode format:format]; 29 | [_audioEngine prepare]; 30 | 31 | NSError *error = nil; 32 | [_audioEngine startAndReturnError:&error]; 33 | if (error != nil) { 34 | return nil; 35 | } 36 | 37 | [self bindData]; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)bindData { 43 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAudioRouteChange:) name:AVAudioSessionRouteChangeNotification object:nil]; 44 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAudioRouteChange:) name:AVAudioSessionInterruptionNotification object:nil]; 45 | } 46 | 47 | - (void)handleAudioRouteChange:(NSNotification *)aNotification { 48 | NSLog(@"Route Change %@", aNotification); 49 | [self.audioEngine stop]; 50 | [self.playerNode stop]; 51 | } 52 | 53 | - (void)play { 54 | if (!self.audioEngine.isRunning) { 55 | [self.audioEngine startAndReturnError:nil]; 56 | } 57 | 58 | if (!self.playerNode.isPlaying) { 59 | [_playerNode play]; 60 | } 61 | } 62 | 63 | - (void)stop { 64 | [_playerNode stop]; 65 | } 66 | 67 | - (void)pause { 68 | [_playerNode pause]; 69 | } 70 | 71 | - (void)renderPCMBuffer:(AVAudioPCMBuffer *)pcmBuffer withPresentationTimeStamp:(CMTime)presentationTimeStamp completionHandler:(AVAudioNodeCompletionHandler)handler { 72 | if (pcmBuffer != NULL) { 73 | [_playerNode scheduleBuffer:pcmBuffer completionHandler:handler]; 74 | } 75 | } 76 | 77 | - (void)renderSampleBuffer:(VCSampleBuffer *)sampleBuffer completionHandler:(AVAudioNodeCompletionHandler)handler { 78 | [self renderPCMBuffer:(AVAudioPCMBuffer *)sampleBuffer.audioBuffer withPresentationTimeStamp:sampleBuffer.presentationTimeStamp completionHandler:handler]; 79 | } 80 | - (void)dealloc { 81 | [self.audioEngine stop]; 82 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 83 | } 84 | @end 85 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Utils/AVAudioFormat+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVAudioFormat+Utils.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/3/7. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface AVAudioFormat (Utils) 14 | 15 | + (AVAudioFormat *)AACFormatWithSampleRate:(Float64)sampleRate channels:(UInt32)channels; 16 | + (AVAudioFormat *)AACFormatWithSampleRate:(Float64)sampleRate 17 | formatFlags:(AudioFormatFlags)flags 18 | channels:(UInt32)channels; 19 | + (AVAudioFormat *)PCMFormatWithSampleRate:(Float64)sampleRate 20 | channels:(UInt32)channels; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Utils/AVAudioFormat+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVAudioFormat+Utils.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2020/3/7. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "AVAudioFormat+Utils.h" 10 | 11 | @implementation AVAudioFormat (Utils) 12 | 13 | + (AVAudioFormat *)AACFormatWithSampleRate:(Float64)sampleRate channels:(UInt32)channels { 14 | return [AVAudioFormat AACFormatWithSampleRate:sampleRate 15 | formatFlags:kMPEG4Object_AAC_LC 16 | channels:channels]; 17 | } 18 | 19 | + (AVAudioFormat *)AACFormatWithSampleRate:(Float64)sampleRate 20 | formatFlags:(AudioFormatFlags)flags 21 | channels:(UInt32)channels { 22 | AudioStreamBasicDescription basicDescription; 23 | basicDescription.mFormatID = kAudioFormatMPEG4AAC; 24 | basicDescription.mSampleRate = sampleRate; 25 | basicDescription.mFormatFlags = flags; 26 | basicDescription.mBytesPerFrame = 0; 27 | basicDescription.mFramesPerPacket = 1024; 28 | basicDescription.mBytesPerPacket = 0; 29 | basicDescription.mChannelsPerFrame = channels; 30 | basicDescription.mBitsPerChannel = 0; 31 | basicDescription.mReserved = 0; 32 | 33 | CMAudioFormatDescriptionRef outputDescription = nil; 34 | OSStatus ret = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &basicDescription, 0, NULL, 0, NULL, NULL, &outputDescription); 35 | if (ret != noErr) { 36 | return nil; 37 | } 38 | 39 | AVAudioFormat *format = [[AVAudioFormat alloc] initWithCMAudioFormatDescription:outputDescription]; 40 | CFRelease(outputDescription); 41 | 42 | return format; 43 | } 44 | 45 | + (AVAudioFormat *)formatWithCMAudioFormatDescription:(CMAudioFormatDescriptionRef)audioFormatDescription { 46 | AVAudioFormat *format = [[AVAudioFormat alloc] initWithCMAudioFormatDescription:audioFormatDescription]; 47 | return format; 48 | } 49 | 50 | + (AVAudioFormat *)PCMFormatWithSampleRate:(Float64)sampleRate 51 | channels:(UInt32)channels { 52 | AudioChannelLayoutTag channelLayoutTag = kAudioChannelLayoutTag_Stereo; 53 | if (channels == 1) { 54 | channelLayoutTag = kAudioChannelLayoutTag_Mono; 55 | } else if (channels == 2) { 56 | channelLayoutTag = kAudioChannelLayoutTag_Stereo; 57 | } else if (channels == 3) { 58 | channelLayoutTag = kAudioChannelLayoutTag_AAC_3_0; 59 | } else if (channels == 4) { 60 | channelLayoutTag = kAudioChannelLayoutTag_AAC_4_0; 61 | } else if (channels == 5) { 62 | channelLayoutTag = kAudioChannelLayoutTag_AAC_5_0; 63 | } else if (channels == 6) { 64 | channelLayoutTag = kAudioChannelLayoutTag_AAC_5_1; 65 | } 66 | AVAudioChannelLayout *layout = [[AVAudioChannelLayout alloc] initWithLayoutTag:channelLayoutTag]; 67 | AVAudioFormat *format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:sampleRate interleaved:NO channelLayout:layout]; 68 | return format; 69 | } 70 | 71 | + (AVAudioFormat *)defaultPCMFormat { 72 | return [AVAudioFormat PCMFormatWithSampleRate:44100 channels:2]; 73 | } 74 | 75 | + (AVAudioFormat *)defaultAACFormat { 76 | return [AVAudioFormat AACFormatWithSampleRate:44100 channels:2]; 77 | } 78 | @end 79 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/Utils/VCByteArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCByteArray.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2019/1/19. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define kVCByteArraySizeOfInt8 1 12 | #define kVCByteArraySizeOfInt16 2 13 | #define kVCByteArraySizeOfInt24 3 14 | #define kVCByteArraySizeOfInt32 4 15 | #define kVCByteArraySizeOfFloat 4 16 | #define kVCByteArraySizeOfDouble 8 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | typedef NS_ENUM(NSUInteger, VCByteArrayError) { 21 | VCByteArrayErrorEOF, 22 | VCByteArrayErrorParse, 23 | }; 24 | 25 | @interface VCByteArrayException : NSException 26 | @property (nonatomic, readonly) VCByteArrayError errorType; 27 | 28 | + (instancetype)eofException; 29 | + (instancetype)parseException; 30 | @end 31 | 32 | @class VCByteArray; 33 | @interface VCByteArrayWriter: NSObject 34 | @property (nonatomic, strong) VCByteArray *target; 35 | - (VCByteArrayWriter * (^)(uint8_t value))writeUInt8; 36 | - (VCByteArrayWriter * (^)(int8_t value))writeInt8; 37 | - (VCByteArrayWriter * (^)(uint16_t value))writeUInt16; 38 | - (VCByteArrayWriter * (^)(int16_t value))writeInt16; 39 | - (VCByteArrayWriter * (^)(uint32_t value))writeUInt24; 40 | - (VCByteArrayWriter * (^)(int32_t value))writeInt24; 41 | - (VCByteArrayWriter * (^)(uint32_t value))writeUInt32; 42 | - (VCByteArrayWriter * (^)(uint32_t value))writeUInt32Little; 43 | - (VCByteArrayWriter * (^)(int32_t value))writeInt32; 44 | - (VCByteArrayWriter * (^)(double value))writeDouble; 45 | - (VCByteArrayWriter * (^)(float value))writeFloat; 46 | - (VCByteArrayWriter * (^)(NSData *value))writeBytes; 47 | @end 48 | 49 | typedef void(^VCByteArrayWriterBlock)(VCByteArrayWriter *writer); 50 | 51 | @interface VCByteArray : NSObject 52 | @property (nonatomic, readonly) NSData *data; 53 | @property (nonatomic, assign) NSInteger postion; 54 | @property (nonatomic, assign) NSInteger length; 55 | @property (nonatomic, readonly) NSInteger bytesAvailable; 56 | 57 | - (instancetype)initWithData:(NSData *)data NS_DESIGNATED_INITIALIZER; 58 | 59 | - (uint8_t)readUInt8; 60 | - (int8_t)readInt8; 61 | - (uint16_t)readUInt16; 62 | - (int16_t)readInt16; 63 | - (uint32_t)readUInt24; 64 | - (int32_t)readInt24; 65 | - (uint32_t)readUInt32; 66 | - (uint32_t)readUInt32Little; 67 | - (int32_t)readInt32; 68 | - (double)readDouble; 69 | - (float)readFloat; 70 | - (NSString *)readUTF8; 71 | - (NSString *)readUTF8Bytes:(NSInteger)length; 72 | - (NSData *)readBytes:(NSInteger)length; 73 | 74 | - (void)writeUInt8:(uint8_t)value; 75 | - (void)writeInt8:(int8_t)value; 76 | - (void)writeUInt16:(uint16_t)value; 77 | - (void)writeInt16:(int16_t)value; 78 | - (void)writeUInt24:(uint32_t)value; 79 | - (void)writeInt24:(int32_t)value; 80 | - (void)writeUInt32:(uint32_t)value; 81 | - (void)writeUInt32Little:(uint32_t)value; 82 | - (void)writeInt32:(int32_t)value; 83 | - (void)writeDouble:(double)value; 84 | - (void)writeFloat:(float)value; 85 | - (void)writeBytes:(NSData *)value; 86 | 87 | - (void)writing:(VCByteArrayWriterBlock)block; 88 | - (void)clear; 89 | @end 90 | 91 | NS_ASSUME_NONNULL_END 92 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/VCMarco.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCMarco.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/9. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #ifndef VCMarco_h 10 | #define VCMarco_h 11 | 12 | #pragma mark - String Tools 13 | #define DECLARE_CONST_STRING(str) extern NSString const * str 14 | #define CONST_STRING(str) NSString * str = @#str 15 | 16 | #pragma mark - Value Define 17 | #define kVCRTMPPort (1935) 18 | 19 | #define kVC720P (1280 * 720) 20 | #define kVC1080P (1920 * 1080) 21 | #define kVC480P (720 * 480) 22 | 23 | #define kVCPriorityIDR (0) 24 | #define kVCDefaultFPS (30) 25 | 26 | #endif /* VCMarco_h */ 27 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/VideoCodecKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // VideoCodecKit.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/10/1. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | #if TARGET_OS_IOS 9 | #import 10 | 11 | //! Project version number for VideoCodecKit. 12 | FOUNDATION_EXPORT double VideoCodecKitVersionNumber; 13 | 14 | //! Project version string for VideoCodecKit. 15 | FOUNDATION_EXPORT const unsigned char VideoCodecKitVersionString[]; 16 | #endif 17 | 18 | #pragma mark - Utils 19 | #import 20 | #import 21 | #import 22 | #import 23 | 24 | #pragma mark - Format 25 | #pragma mark NALU 26 | #import 27 | #import 28 | #pragma mark Asset 29 | #import 30 | #import 31 | #import 32 | #pragma mark AnnexB 33 | #import 34 | #import 35 | #pragma mark AVC 36 | #import 37 | #import 38 | #pragma mark AAC 39 | #import 40 | #pragma mark FLV 41 | #import 42 | #import 43 | #import 44 | #import 45 | #pragma mark AMF 46 | #import 47 | #import 48 | #import 49 | 50 | #pragma mark - Network 51 | #pragma mark TCP Socket 52 | #import 53 | #pragma mark RTMP 54 | #import 55 | #import 56 | #import 57 | #import 58 | #import 59 | #import 60 | #import 61 | #import 62 | 63 | #pragma mark - Codec 64 | #import 65 | #import 66 | #import 67 | #import 68 | #import 69 | #import 70 | 71 | #pragma mark - Render 72 | #import 73 | 74 | #pragma mark - Media 75 | #import 76 | 77 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/VideoCodecKit.pch: -------------------------------------------------------------------------------- 1 | // 2 | // VideoCodecKit.pch 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/9. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #ifndef VideoCodecKit_pch 10 | #define VideoCodecKit_pch 11 | 12 | #ifdef __OBJC__ 13 | #import "VCMarco.h" 14 | #import "VideoCodecKitTools.h" 15 | #endif 16 | 17 | #endif /* VideoCodecKit_pch */ 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/VideoCodecKitTools.h: -------------------------------------------------------------------------------- 1 | // 2 | // VideoCodecKitTools.h 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/10/31. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSBundle (VideoCodecKitBundle) 12 | + (NSBundle *)videoCodecKitBundle; 13 | @end 14 | 15 | @interface VideoCodecKitTools : NSObject 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /VideoCodecKit/VideoCodecKit/VideoCodecKitTools.m: -------------------------------------------------------------------------------- 1 | // 2 | // VideoCodecKitTools.m 3 | // VideoCodecKit 4 | // 5 | // Created by CmST0us on 2018/10/31. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VideoCodecKitTools.h" 10 | 11 | @implementation NSBundle (VideoCodecKitBundle) 12 | 13 | + (NSBundle *)videoCodecKitBundle { 14 | NSBundle *frameworkBundle = [NSBundle bundleWithPath:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Frameworks/VideoCodecKit.framework"]]; 15 | return frameworkBundle; 16 | } 17 | 18 | @end 19 | 20 | @implementation VideoCodecKitTools 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/8. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/8. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; 22 | [[AVAudioSession sharedInstance] setActive:YES error:nil]; 23 | [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://www.baidu.com"]] resume]; 24 | return YES; 25 | } 26 | 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | } 32 | 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *)application { 35 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 36 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 37 | } 38 | 39 | 40 | - (void)applicationWillEnterForeground:(UIApplication *)application { 41 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 42 | } 43 | 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application { 46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 47 | } 48 | 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/BaseViewController/VCDemoBackableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoBackableViewController.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoViewController.h" 10 | @interface VCDemoBackableViewController : VCDemoViewController 11 | @property (nonatomic, strong) UIButton *backButton; 12 | 13 | - (void)onBack:(UIButton *)button; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/BaseViewController/VCDemoBackableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoBackableViewController.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoBackableViewController.h" 10 | 11 | @interface VCDemoBackableViewController () 12 | 13 | @end 14 | 15 | @implementation VCDemoBackableViewController 16 | 17 | #pragma mmark - Override 18 | - (void)customInit { 19 | [super customInit]; 20 | } 21 | 22 | #pragma mark - Private Method 23 | - (void)createBackButton { 24 | self.backButton = [[UIButton alloc] init]; 25 | [self.view addSubview:self.backButton]; 26 | } 27 | 28 | - (void)setupBackButton { 29 | [self.backButton setBackgroundColor:[UIColor clearColor]]; 30 | [self.backButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; 31 | [self.backButton.titleLabel setFont:[UIFont systemFontOfSize:15 weight:UIFontWeightBold]]; 32 | [self.backButton setTitle:NSLocalizedString(@"返回", nil) forState:UIControlStateNormal]; 33 | [self.backButton.titleLabel setTextAlignment:NSTextAlignmentLeft]; 34 | [self.backButton addTarget:self action:@selector(onBack:) forControlEvents:UIControlEventTouchUpInside]; 35 | [self.backButton sizeToFit]; 36 | } 37 | 38 | - (void)createBackButtonConstraints { 39 | self.backButton.translatesAutoresizingMaskIntoConstraints = NO; 40 | [self.backButton.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:4].active = YES; 41 | [self.backButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:8].active = YES; 42 | [self.backButton.heightAnchor constraintEqualToConstant:20].active = YES; 43 | } 44 | 45 | #pragma mark - Life Cycle 46 | - (void)viewDidLoad { 47 | [super viewDidLoad]; 48 | 49 | [self createBackButton]; 50 | [self setupBackButton]; 51 | [self createBackButtonConstraints]; 52 | } 53 | 54 | #pragma mark - Action 55 | - (void)onBack:(UIButton *)button { 56 | UINavigationController *nav = self.navigationController; 57 | if (nav) { 58 | [self.navigationController popViewControllerAnimated:YES]; 59 | } 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/BaseViewController/VCDemoNavigationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoNavigationController.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VCDemoNavigationController : UINavigationController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/BaseViewController/VCDemoNavigationController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoNavigationController.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoNavigationController.h" 10 | 11 | @interface VCDemoNavigationController () 12 | 13 | @end 14 | 15 | @implementation VCDemoNavigationController 16 | 17 | - (instancetype)init { 18 | self = [super init]; 19 | if (self) { 20 | [self commonInit]; 21 | } 22 | return self; 23 | } 24 | 25 | - (instancetype)initWithCoder:(NSCoder *)coder { 26 | self = [super initWithCoder:coder]; 27 | if (self) { 28 | [self commonInit]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)commonInit { 34 | [self setNavigationBarHidden:YES animated:NO]; 35 | } 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | } 40 | 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/BaseViewController/VCDemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoViewController.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VCDemoViewController : UIViewController 12 | /** 13 | 自定义的初始化动作,子类重写 14 | */ 15 | - (void)customInit NS_REQUIRES_SUPER; 16 | @end 17 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/BaseViewController/VCDemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoViewController.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/30. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoViewController.h" 10 | 11 | @interface VCDemoViewController () 12 | 13 | @end 14 | 15 | @implementation VCDemoViewController 16 | 17 | - (instancetype)init { 18 | self = [super init]; 19 | if (self) { 20 | [self customInit]; 21 | self.view.backgroundColor = [UIColor whiteColor]; 22 | } 23 | return self; 24 | } 25 | 26 | - (instancetype)initWithCoder:(NSCoder *)coder { 27 | self = [super initWithCoder:coder]; 28 | if (self) { 29 | [self customInit]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | } 37 | 38 | - (void)customInit { 39 | 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/CameraPublish/VCDemoCameraPublishViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoCameraPublishViewController.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2020/2/16. 6 | // Copyright © 2020 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoBackableViewController.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCDemoCameraPublishViewController : VCDemoBackableViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/ISO/VCDemoISOTestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoISOTestViewController.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2019/1/27. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoBackableViewController.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCDemoISOTestViewController : VCDemoBackableViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/ISO/VCDemoISOTestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoISOTestViewController.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2019/1/27. 6 | // Copyright © 2019 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VCDemoISOTestViewController.h" 11 | 12 | @interface VCDemoISOTestViewController () { 13 | } 14 | @property (nonatomic, assign) BOOL playing; 15 | @property (nonatomic, assign) BOOL seeking; 16 | 17 | @property (nonatomic, strong) NSCondition *readerConsumeCondition; 18 | 19 | @property (nonatomic, strong) VCH264HardwareDecoder *decoder; 20 | @property (nonatomic, strong) AVSampleBufferDisplayLayer *displayLayer; 21 | @property (nonatomic, strong) VCAudioConverter *converter; 22 | @property (nonatomic, strong) VCAudioPCMRender *render; 23 | @property (nonatomic, strong) VCFLVReader *reader; 24 | 25 | @property (nonatomic, strong) UISlider *timeSeekSlider; 26 | 27 | @property (nonatomic, assign) CMTime audioTime; 28 | @end 29 | 30 | @implementation VCDemoISOTestViewController 31 | - (void)timeSeekSliderDidStartSeek { 32 | self.seeking = YES; 33 | self.playing = NO; 34 | _audioTime = CMTimeMake(0, 0); 35 | [self.displayLayer flush]; 36 | [self.render stop]; 37 | } 38 | 39 | - (void)timeSeekSliderDidStopSeek { 40 | [self.render play]; 41 | self.playing = YES; 42 | self.seeking = NO; 43 | [self.readerConsumeCondition broadcast]; 44 | } 45 | 46 | - (void)timeSeekSliderValueDidChange { 47 | [_reader seekToTime:CMTimeMake(self.timeSeekSlider.value, 1000)]; 48 | } 49 | 50 | - (void)viewDidLoad { 51 | [super viewDidLoad]; 52 | // UI 53 | self.timeSeekSlider = [[UISlider alloc] init]; 54 | [self.view addSubview:self.timeSeekSlider]; 55 | self.timeSeekSlider.translatesAutoresizingMaskIntoConstraints = NO; 56 | [self.timeSeekSlider.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:0].active = YES; 57 | [self.timeSeekSlider.rightAnchor constraintEqualToAnchor:self.view.rightAnchor constant:0].active = YES; 58 | [self.timeSeekSlider.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-44].active = YES; 59 | 60 | [self.timeSeekSlider addTarget:self action:@selector(timeSeekSliderValueDidChange) forControlEvents:UIControlEventValueChanged]; 61 | [self.timeSeekSlider addTarget:self action:@selector(timeSeekSliderDidStartSeek) forControlEvents:UIControlEventTouchDown]; 62 | [self.timeSeekSlider addTarget:self action:@selector(timeSeekSliderDidStopSeek) forControlEvents:UIControlEventTouchUpInside]; 63 | 64 | // Comopnent 65 | _playing = NO; 66 | _readerConsumeCondition = [[NSCondition alloc] init]; 67 | 68 | self.decoder = [[VCH264HardwareDecoder alloc] init]; 69 | self.decoder.delegate = self; 70 | self.displayLayer = [[AVSampleBufferDisplayLayer alloc] init]; 71 | 72 | CMTimebaseRef timeBase = nil; 73 | CMTimebaseCreateWithMasterClock(kCFAllocatorDefault, CMClockGetHostTimeClock(), &timeBase); 74 | 75 | [self.displayLayer setControlTimebase:timeBase]; 76 | CFRelease(timeBase); 77 | 78 | self.displayLayer.frame = self.view.bounds; 79 | [self.view.layer addSublayer:self.displayLayer]; 80 | 81 | _reader = [[VCFLVReader alloc] initWithURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"flv"]]; 82 | _reader.delegate = self; 83 | [_reader createSeekTable]; 84 | [_reader starAsyncReading]; 85 | self.timeSeekSlider.minimumValue = 0; 86 | self.timeSeekSlider.maximumValue = _reader.duration.value; 87 | } 88 | 89 | - (void)onBack:(UIButton *)button { 90 | [super onBack:button]; 91 | [self.render stop]; 92 | } 93 | 94 | #pragma mark - Reader Delegate (Reader Threader) 95 | - (void)reader:(VCFLVReader *)reader didGetVideoSampleBuffer:(VCSampleBuffer *)sampleBuffer { 96 | CMTime sampleBufferPts = sampleBuffer.presentationTimeStamp; 97 | [self.decoder decodeSampleBuffer:sampleBuffer]; 98 | 99 | // [TODO] 通知自旋锁模型? 100 | while (YES) { 101 | if (!self.playing) { 102 | [self.readerConsumeCondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.5]]; 103 | if ([[NSThread currentThread] isCancelled]) break; 104 | continue; 105 | } 106 | if (self.audioTime.flags == kCMTimeFlags_Valid && 107 | sampleBufferPts.value > self.audioTime.value + 3 * self.audioTime.timescale) { 108 | [self.readerConsumeCondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.5]]; 109 | if ([[NSThread currentThread] isCancelled]) break; 110 | continue; 111 | } 112 | break; 113 | } 114 | } 115 | 116 | - (void)reader:(VCFLVReader *)reader didGetAudioSampleBuffer:(VCSampleBuffer *)sampleBuffer { 117 | [self.converter convertSampleBuffer:sampleBuffer]; 118 | CMTime sampleBufferPts = sampleBuffer.presentationTimeStamp; 119 | while (YES) { 120 | if (!_playing) { 121 | [self.readerConsumeCondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.5]]; 122 | if ([[NSThread currentThread] isCancelled]) break; 123 | continue; 124 | } 125 | if (_audioTime.flags == kCMTimeFlags_Valid && 126 | sampleBufferPts.value > _audioTime.value + 3 * _audioTime.timescale) { 127 | [self.readerConsumeCondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.5]]; 128 | if ([[NSThread currentThread] isCancelled]) break; 129 | continue; 130 | } 131 | break; 132 | } 133 | } 134 | 135 | - (void)reader:(VCFLVReader *)reader didGetVideoFormatDescription:(CMFormatDescriptionRef)formatDescription { 136 | [self.decoder setFormatDescription:formatDescription]; 137 | } 138 | 139 | - (void)reader:(VCFLVReader *)reader didGetAudioFormatDescription:(CMFormatDescriptionRef)formatDescription { 140 | AVAudioFormat *sourceFormat = [[AVAudioFormat alloc] initWithCMAudioFormatDescription:formatDescription]; 141 | AVAudioFormat *outputFormat = [AVAudioFormat PCMFormatWithSampleRate:sourceFormat.sampleRate channels:sourceFormat.channelCount]; 142 | self.converter = [[VCAudioConverter alloc] initWithOutputFormat:outputFormat 143 | sourceFormat:sourceFormat 144 | delegate:self 145 | delegateQueue:dispatch_get_main_queue()]; 146 | self.render = [[VCAudioPCMRender alloc] initWithPCMFormat:outputFormat]; 147 | } 148 | 149 | - (void)readerDidReachEOF:(VCFLVReader *)reader { 150 | 151 | } 152 | 153 | #pragma mark - Converter Delegate (Caller Thread) (Reader Thread) 154 | - (void)converter:(VCAudioConverter *)converter didOutputSampleBuffer:(VCSampleBuffer *)sampleBuffer { 155 | __weak typeof(self) weakSelf = self; 156 | [self.render renderSampleBuffer:sampleBuffer completionHandler:^{ 157 | if (!weakSelf.seeking) { 158 | dispatch_async(dispatch_get_main_queue(), ^{ 159 | weakSelf.timeSeekSlider.value = sampleBuffer.presentationTimeStamp.value; 160 | }); 161 | } 162 | if (weakSelf.playing) { 163 | weakSelf.audioTime = sampleBuffer.presentationTimeStamp; 164 | } else { 165 | weakSelf.audioTime = CMTimeMake(0, 0); 166 | } 167 | CMTimebaseSetTime(weakSelf.displayLayer.controlTimebase, sampleBuffer.presentationTimeStamp); 168 | }]; 169 | } 170 | 171 | #pragma mark - Video Decoder Delegate (Decoder Delegate) 172 | - (void)videoDecoder:(id)decoder didOutputSampleBuffer:(VCSampleBuffer *)sampleBuffer { 173 | [self.displayLayer enqueueSampleBuffer:sampleBuffer.sampleBuffer]; 174 | } 175 | 176 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 177 | if (self.playing) { 178 | self.playing = NO; 179 | [self.render pause]; 180 | CMTimebaseSetRate(self.displayLayer.controlTimebase, 0); 181 | } else { 182 | self.playing = YES; 183 | [self.render play]; 184 | CMTimebaseSetRate(self.displayLayer.controlTimebase, 1.0); 185 | } 186 | [self.readerConsumeCondition broadcast]; 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/VCDemoListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoListViewController.h 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/31. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoViewController.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VCDemoListViewController : UITableViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Classses/VCDemoListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCDemoListViewController.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/10/31. 6 | // Copyright © 2018 eric3u. All rights reserved. 7 | // 8 | 9 | #import "VCDemoListViewController.h" 10 | #import "VCDemoISOTestViewController.h" 11 | #import "VCDemoCameraPublishViewController.h" 12 | 13 | @interface VCDemoListViewController () 14 | 15 | @end 16 | 17 | @implementation VCDemoListViewController 18 | 19 | - (NSArray *)testCases { 20 | static NSArray *cases = nil; 21 | if (cases != nil) return cases; 22 | cases = @[ 23 | @{ 24 | @"title": @"FLV播放", 25 | @"class": NSStringFromClass([VCDemoISOTestViewController class]), 26 | }, 27 | @{ 28 | @"title": @"RTMP 相机推流", 29 | @"class": NSStringFromClass([VCDemoCameraPublishViewController class]), 30 | } 31 | ]; 32 | return cases; 33 | } 34 | 35 | - (void)viewDidLoad { 36 | [super viewDidLoad]; 37 | } 38 | 39 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 40 | return 1; 41 | } 42 | 43 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 44 | return [self testCases].count; 45 | } 46 | 47 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 48 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 49 | NSDictionary *dict = [[self testCases] objectAtIndex:indexPath.row]; 50 | cell.textLabel.text = dict[@"title"]; 51 | return cell; 52 | } 53 | 54 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 55 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 56 | NSDictionary *dict = [[self testCases] objectAtIndex:indexPath.row]; 57 | Class targetClass = NSClassFromString(dict[@"class"]); 58 | UIViewController *vc = [(UIViewController *)[targetClass alloc] init]; 59 | [self.navigationController pushViewController:vc animated:YES]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | NSCameraUsageDescription 29 | 点击同意使用相机 30 | NSMicrophoneUsageDescription 31 | 允许以开启录音 32 | UIBackgroundModes 33 | 34 | audio 35 | 36 | UIFileSharingEnabled 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIMainStoryboardFile 41 | Main 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | UIInterfaceOrientationPortrait 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon_60pt@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon_60pt@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "idiom" : "ipad", 77 | "size" : "76x76", 78 | "scale" : "1x" 79 | }, 80 | { 81 | "idiom" : "ipad", 82 | "size" : "76x76", 83 | "scale" : "2x" 84 | }, 85 | { 86 | "idiom" : "ipad", 87 | "size" : "83.5x83.5", 88 | "scale" : "2x" 89 | }, 90 | { 91 | "idiom" : "ios-marketing", 92 | "size" : "1024x1024", 93 | "scale" : "1x" 94 | } 95 | ], 96 | "info" : { 97 | "version" : 1, 98 | "author" : "xcode" 99 | } 100 | } -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Resources/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CmST0us/VideoCodecKit/728368e73c01163ec0b11aae2c3a9b12725a68c1/VideoCodecKitDemo/VideoCodecKitDemo/Resources/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Resources/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CmST0us/VideoCodecKit/728368e73c01163ec0b11aae2c3a9b12725a68c1/VideoCodecKitDemo/VideoCodecKitDemo/Resources/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/Resources/test.flv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CmST0us/VideoCodecKit/728368e73c01163ec0b11aae2c3a9b12725a68c1/VideoCodecKitDemo/VideoCodecKitDemo/Resources/test.flv -------------------------------------------------------------------------------- /VideoCodecKitDemo/VideoCodecKitDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // VideoCodecKitDemo 4 | // 5 | // Created by CmST0us on 2018/9/8. 6 | // Copyright © 2018年 eric3u. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------