├── .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 |
--------------------------------------------------------------------------------