forked from justinmakaila/iOS-FrameEncoder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibRTMPClient.m
330 lines (247 loc) · 10.3 KB
/
libRTMPClient.m
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//
// libRTMPClient.m
// PresentRTMPStreamModule
//
// Created by Justin Makaila on 7/20/13.
// Copyright (c) 2013 Present, Inc. All rights reserved.
//
#import "libRTMPClient.h"
#import "FLVPacket.h"
#warning !FIX! Put your RTMP output URL here
#define outputURL ""
typedef enum {
kConnectHost = 0,
kConnectStream
}SOURCE;
@interface libRTMPClient () {
RTMP *rtmp; // RTMP Object
FLVContext *flv; // FLV context to manage contextual information (Offsets, start time, TS, last TS, etc)
FLVPacket *flvPkt; // Class to handle packetizing information
BOOL connected; // Is there a connection to the server?
BOOL streaming; // Is there a stream?
int framesRecieved; // The number of frames recieved
int framesWritten; // The number of frames written to stream
int bytesRecieved; // The number of bytes recieved
int bytesWritten; // The number of bytes written to stream
NSData *avcC; // Data reference to avcC information
int avcCSize; // Size of the avcC
NSData *magicCookie;
int magicCookieSize;
}
@end
// skip x bytes
void skip_bytes(uint8_t **data, uint8_t val) {
*data += val;
}
// puts 8 bits
void put_byte(uint8_t **data, uint8_t val) {
NSLog(@"***************** START of put_byte *****************");
NSLog(@"Value = %i", val);
assert(val >= -128 && val <= 255);
*data[0]++ = val;
NSLog(@"Value of data\n \t*data[0] = %p", (void*)*data[0]);
NSLog(@"Value of data pointers\n \tdata = %p\n \t*data = %p\n \t*data[0] = %p", data, *data, (void*)*data[0]);
NSLog(@"***************** END of put_byte *****************");
}
// puts 16 bits
void put_be16(uint8_t **data, unsigned int val) {
NSLog(@"***************** START of put_be16 *****************");
put_byte(data, (int)val >> 8);
put_byte(data, (uint8_t)val);
NSLog(@"***************** END of put_be16 *****************");
}
// puts 32 bits
void put_be32(uint8_t **data, unsigned int val) {
NSLog(@"***************** START of put_be32 *****************");
NSLog(@"Value = %i", val);
put_byte(data, val >> 24);
put_byte(data, (uint8_t)(val >> 16));
put_byte(data, (uint8_t)(val >> 8));
put_byte(data, (uint8_t) val);
NSLog(@"***************** END of put_be32 *****************");
}
// puts 24 bits
void put_be24(uint8_t **data, unsigned int val) {
NSLog(@"***************** START of put_be24 *****************");
NSLog(@"Value = %i", val);
put_be16(data, (int)val >> 8);
put_byte(data, (uint8_t)val);
NSLog(@"***************** END of put_be24 *****************");
}
// puts buffer
void put_buff(uint8_t **data, const uint8_t *src, int32_t srcsize) {
NSLog(@"***************** START of put_buff *****************");
memcpy(*data, src, srcsize);
*data += srcsize;
NSLog(@"***************** END of put_buff *****************");
}
// puts 8 bit representation of each char
void put_tag(uint8_t **data, const char *tag) {
NSLog(@"***************** START of put_tag *****************");
while (*tag) {
DLLog(@"\n\n \t *tag = %c", *tag);
put_byte(data, *tag++);
}
NSLog(@"***************** END of put_tag *****************");
}
@implementation libRTMPClient
-(id)init {
self = [super init];
if (self) {
flvPkt = [[FLVPacket alloc]init];
connected = NO;
streaming = NO;
}
return self;
}
-(id)initWithDelegate:(id)delegate {
self = [super init];
if (self) {
_delegate = delegate;
flvPkt = [[FLVPacket alloc]init];
connected = NO;
streaming = NO;
}
return self;
}
#pragma mark - Connection Lifecycle
-(void)connect {
rtmp = RTMP_Alloc();
RTMP_Init(rtmp);
RTMP_SetupURL(rtmp, outputURL);
RTMP_EnableWrite(rtmp);
RTMP_LogSetLevel(RTMP_LOGERROR);
if(!RTMP_Connect(rtmp, NULL)) {
[self fail:kConnectHost];
return;
}else
connected = YES;
[_delegate clientDidConnect];
if (!RTMP_ConnectStream(rtmp, 0)) {
[self fail:kConnectStream];
return;
}else
streaming = YES;
[_delegate clientDidConnectToStream];
}
-(void)disconnect {
if (connected) {
if ([self writeStreamTrailer] != 0) {
RTMP_Close(rtmp);
RTMP_Free(rtmp);
connected = NO;
[_delegate clientDidDisconnect];
}
}
}
-(void)fail:(int)source {
RTMP_Close(rtmp);
RTMP_Free(rtmp);
connected = NO;
streaming = NO;
NSString *localizedDesc;
switch (source) {
case 0:
localizedDesc = @"Client failed to connect to host!";
break;
case 1:
localizedDesc = @"Client failed to connect to stream!";
default:
break;
}
NSError *error = [NSError errorWithDomain:@"libRTMPClient"
code:100
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
@"localizedDescription", localizedDesc, nil]];
[_delegate clientDidFailWithError:error];
}
-(BOOL)isReadyForData {
return (connected && streaming);
}
-(BOOL)isConnected {
return connected;
}
-(BOOL)isStreaming {
return streaming;
}
#pragma mark - I/O
-(void)writeAvcCHeaderToStream:(NSData*)data {
avcC = data;
avcCSize = [data length];
int ret = 0;
char cBuffer[4096];
uint8_t *buffer = (uint8_t*)cBuffer;
NSLog(@"Checkpoint #%i: Will call writeStreamHeader:ofSize:withAvcC:ofSize:\n cBuffer = %p\n sizeof(cBuffer) = %ld\n\n buffer = %p\n *buffer = %p\n sizeof(buffer) = %ld", kNumCheckpoint++, cBuffer, sizeof(cBuffer), buffer, (void*)*buffer, sizeof(*buffer));
ret = [flvPkt writeStreamHeader:&buffer ofSize:sizeof(cBuffer) withAvcC:avcC ofSize:avcCSize];
NSLog(@"Checkpoint #%i: Did call writeStreamHeader:ofSize:withAvcC:ofSize:\n returned %i\n buffer = %p\n *buffer = %p\n cBuffer = %p", kNumCheckpoint++, ret, buffer, (void*)*buffer, cBuffer);
RTMP_Write(rtmp, (const char*)buffer, ret);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* NALUs are delivered to the server frame by frame. *
* Each packet must contain a full frame. *
* Format is as follows: *
* [4 byte length][4...n][4 byte length][n+4...n] *
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
-(void)writeNALUToStream:(NSArray*)data time:(double)pts {
int ret = 0; // Returned size of NALU
int size = 0; // Size of NALU
char cFrame[MAX_H264_FRAMESIZE]; // Allocate a buffer of MAX_H264_FRAMESIZE to hold the frame
char cBuffer[MAX_H264_FRAMESIZE]; // Allocate a buffer of MAX_H264_FRAMESIZE to hold the packet
uint8_t* frame = (uint8_t*)cFrame; // Buffer to hold NALU data
uint8_t* pFrameStart = (uint8_t*)frame; // Pointer to the start of the frame
uint8_t* buffer = (uint8_t*)cBuffer; // Buffer to hold encoded packet
uint8_t* fNALU = (uint8_t*)[data[0] bytes]; // Get the first NALU
int fSize = [data[0] length]; // Get the size of the first NALU
int naltype = fNALU[0] & 0x1f; // Get the type of the NALU(s)
put_be32(&frame, fSize); // Write the size of the first NALU to frame
put_buff(&frame, fNALU, fSize); // Write the first NALU to frame
if ([data count] > 1) {
uint8_t* sNALU = (uint8_t*)[data[1] bytes]; // Get the second NALU
int sSize = [data[1] length]; // Get the size of the second NALU
put_be32(&frame, sSize); // Write the size of the second NALU to frame
put_buff(&frame, sNALU, sSize); // Write the second NALU to frame
}
size = frame - pFrameStart; // Get the size of the data written to frame
frame = pFrameStart; // Reset the frame pointer to the beginning
NSLog(@"Checkpoint #%i: Will call writeNALU:ofSize:toPacket:time:keyframe:\n\n \tsize = %i", kNumCheckpoint++, size);
ret = [flvPkt writeNALU:frame // Write frame
ofSize:size // of size
toPacket:&buffer // to buffer
time:pts // With TS pts
keyframe:(naltype == 5)]; // If naltype == 5, frame is keyframe
if (ret < 0) {
// TODO: Implement error handling for failed writes to packet
return;
}
NSLog(@"\n \tReturned dataSize = %i", ret);
RTMP_Write(rtmp, (const char*)buffer, ret); // Write the buffer to RTMP
}
-(void)writeAACDataToStream:(NSData*)data time:(double)pts {
int ret = 0;
char cAudioFrame[MAX_H264_FRAMESIZE];
uint8_t* audioFrame = (uint8_t*)cAudioFrame;
uint8_t* audioData = (uint8_t*)[data bytes];
int audioSize = [data length];
if (audioSize == 0) return;
ret = [flvPkt writeAAC:audioData
ofSize:audioSize
toPacket:&audioFrame
time:pts];
if (ret < 0) {
// TODO: Implement error handling for failed writes to packet
return;
}
NSLog(@"Write aac returned %i", ret);
ret = RTMP_Write(rtmp, (const char*)audioFrame, ret);
NSLog(@"\n \tRTMP_Write returned %i", ret);
}
-(int)writeStreamTrailer {
int ret = 0;
char cBuf[256];
uint8_t* buffer = (uint8_t*)cBuf;
ret = [flvPkt writeStreamTrailer:&buffer time:0];
ret = RTMP_Write(rtmp, (const char*)buffer, ret);
NSLog(@"\n \tRTMP_Write returned %i", ret);
return ret;
}
@end