Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

To analyze the code, let's take StreamRecording example version with hash b21820c, which can be downloaded with build 2.5.2.4.

View class for the main view of the application: ViewController (header file ViewController.h; implementation file file ViewController.m).

1. Import of API. ViewController.m, line 12 code

Code Block
languagejscpp
themeRDark
#import <FPWCSApi2/FPWCSApi2.h>

2. Connection Session creation and connection to server.

ViewController method connect is called when Start button is tapped. ViewController.m, line 181

Code Block
languagejs
themeRDark
[self connect];

In the method,

- object with options for connection session is created (ViewController.m, line 34FPWCSApi2 createSession, FPWCSApi2Session connect code

The options include:

  • URL of WCS server
  • appKey of internal server-side application (defaultApp)
Code Block
languagejscpp
themeRDark
- (FPWCSApi2Session *)connect {
    FPWCSApi2SessionOptions *options = [[FPWCSApi2SessionOptions alloc] init];
.....
    url =[[NSURL alloc] initWithString:_connectUrl.text];
    options.urlServer = [NSString stringWithFormat:@"%@://%@:%@", url.scheme, url.host, url.port];
.....
options.appKey = @"defaultApp";

The options include URL of WCS server and appKey of internal server-side application.

- new session is created with method createSession (ViewController.m, line 40)

Code Block
languagejs
themeRDark
FPWCSApi2Session *session    streamName = [FPWCSApi2 createSession:options error:&error];

- callback functions for processing session statuses are added (ViewController.m, line 59)

Code Block
languagejs
themeRDark
[session on:kFPWCSSessionStatusEstablished callback:^(FPWCSApi2Session *rSession){url.path.stringByDeletingPathExtension stringByReplacingOccurrencesOfString: @"/" withString:@""];
    [self changeConnectionStatus:[rSession getStatus]]options.appKey = @"defaultApp";
    [self onConnected:rSession];
}];

[session on:kFPWCSSessionStatusDisconnected callback:^(FPWCSApi2Session *rSession){
    [self changeConnectionStatus:[rSession getStatus]]NSError *error;
    FPWCSApi2Session *session = [FPWCSApi2 createSession:options error:&error];
    [self onDisconnected];
}];

[session on:kFPWCSSessionStatusFailed callback:^(FPWCSApi2Session *rSession){...
    [self changeConnectionStatus:[rSession getStatus]]session connect];
    [selfreturn onDisconnected]session;
}];

Depending on the session status, corresponding ViewController method will be called to make appropriate changes in controls of the interface

  •  if connection is successfully established: onConnected
  • in case of disconnection, or connection failure: onDisconnected

- FPWCSApi2Session method connect is called to establish connection to server (ViewController.m, line 73)

Code Block
languagejs
themeRDark
[session connect];

3. Stream publishing.

...

3. Receiving the event confirming successful connection.

ViewController onConnected code

On this event, ViewController publishStream method is called to publish the stream. ViewController.m, line 140

Code Block
languagejscpp
themeRDark
- (void)onConnected:(FPWCSApi2Session *)session {
    [self publishStream];

In the method,

...


}

4. Stream publishing.

FPWCSApi2Session createStream, FPWCSApi2Stream publish code

Object with next stream options is passed to createStream method:

  • stream name
  • view to display video
  • 'true' for parameter 'record' to enable stream recording
  • video constraints for iPad


Code Block
languagejscpp
themeRDark
- (FPWCSApi2Stream *)publishStream {
    FPWCSApi2Session *session = [FPWCSApi2 getSessions][0];
    FPWCSApi2StreamOptions *options = [[FPWCSApi2StreamOptions alloc] init];
    options.name = streamName;
    options.display = _remoteDisplay;
    options.record = true;

The required options are stream name, view for displaying video and 'true' for parameter 'record' to enable stream recording.
Also, video constraints can be specified. E.g., in the example, constraints are added in case the iOS device is iPad:

Code Block
languagejs
themeRDark

    if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
        options.constraints = [[FPWCSApi2MediaConstraints alloc] initWithAudio:YES videoWidth:640 videoHeight:480 videoFps:15];
}

- new stream is created with FPWCSApi2Session method createStream (ViewController.m, line 87)

Code Block
languagejs
themeRDark
    }
    NSError *error;
    FPWCSApi2Stream *stream = [session createStream:options error:&error];

- callback functions for processing stream statuses are added (ViewController.m, line 105)

Code Block
languagejs
themeRDark
[stream on:kFPWCSStreamStatusPublishing callback:^(FPWCSApi2Stream *rStream){
    [self changeStreamStatus:rStream];
    [self onPublishing:rStream];
}];

[stream on:kFPWCSStreamStatusUnpublished callback:^(FPWCSApi2Stream *rStream){
    [self changeStreamStatus:rStream];
    [self onUnpublished];
}];

[stream on:kFPWCSStreamStatusFailed callback:^(FPWCSApi2Stream *rStream){
    [self changeStreamStatus:rStream];
    [self onUnpublished];
}];

Depending on the stream status, corresponding ViewController method will be called to make appropriate changes in controls of the interface

  • if stream is successfully published: onPublishing
  • in case of failure, or when stream is unpublished: onUnpublished

- FPWCSApi2Stream method publish is called to publish the stream (ViewController.m, line 119)

Code Block
languagejs
themeRDark
[stream publish:&error]

4. Filename of the recording. ViewController.m, line 157

When the stream is published, FPWCSApi2Stream method getRecordName is used to get the filename of the stream recording.

Code Block
languagejs
themeRDark
recordName = [stream getRecordName];

5. Download link. ViewController.m, line 148

Stream recordings are saved to directory WCS_HOME/client/records.
When the session is closed, download link for the recording is formed.

Code Block
languagejs
themeRDark

    ...
    if(![stream publish:&error]) {
        UIAlertController * alert = [UIAlertController
                                     alertControllerWithTitle:@"Failed to publish"
                                     message:error.localizedDescription
                                     preferredStyle:UIAlertControllerStyleAlert];
        
        UIAlertAction* okButton = [UIAlertAction
                                   actionWithTitle:@"Ok"
                                   style:UIAlertActionStyleDefault
                                   handler:^(UIAlertAction * action) {
                                       [session disconnect];
                                   }];
        
        [alert addAction:okButton];
        [self presentViewController:alert animated:YES completion:nil];
    }
    return stream;
}


5. Receiving the event confirming successful stream publishing.

ViewController onPublishing code

When the stream is published, FPWCSApi2Stream method getRecordName is used to get the filename of the stream recording

Code Block
languagecpp
themeRDark
- (void)onPublishing:(FPWCSApi2Stream *)stream {
    [_startButton setTitle:@"STOP" forState:UIControlStateNormal];
    [self changeViewState:_startButton enabled:YES];
    recordName = [stream getRecordName];
}

6. Disconnection.

FPWCSApi2Session disconnect code

Code Block
languagecpp
themeRDark
- (void)startButton:(UIButton *)button {
    [self changeViewState:button enabled:NO];
    if ([button.titleLabel.text isEqualToString:@"STOP"]) {
        if ([FPWCSApi2 getSessions].count) {
            FPWCSApi2Session *session = [FPWCSApi2 getSessions][0];
            NSLog(@"Disconnect session with server %@", [session getServerUrl]);
            [session disconnect];
        } else {
            NSLog(@"Nothing to disconnect");
            [self onDisconnected];
        }
    } else {
        [self changeViewState:_connectUrl enabled:NO];
        [self connect];
    }
}

7. Receiving the event confirming successful disconnection.

ViewController onDisconnected code

On this event, the record file download link is formed, and ViewController playVideo is called to play the record

Code Block
languagecpp
themeRDark
- (void)onDisconnected {
    [self changeViewState:_connectUrl enabled:YES];
    [self onUnpublished];
    if (url && recordName) {
//        NSString *urlString = @"http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4";
        NSString *urlString = [NSString stringWithFormat:@"http://%@:9091/client/records/%@", url.host, recordName];

Here

  • url.host - address of the WCS server
  • recordName - filename of the recording

6. Disconnection. ViewController.m, line 174

...


        _recordLink.text = urlString;
        [self playVideo: urlString];
    }
}

8. Record playback

AVPlayer play code

Code Block
languagejscpp
themeRDark
[session disconnect];- (void)playVideo:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    AVURLAsset *movieAsset = [AVURLAsset URLAssetWithURL:url options:nil];
    [movieAsset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
    
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];
    _player = [AVPlayer playerWithPlayerItem:playerItem];
    _playerViewController.player = _player;
    [_player play];
}