The LinkVisual SDK for apps provides features such as audio and video playback and voice intercom.
Dependent SDK |
Overview |
API channel |
Provides API channel capability |
Initialization
For more information about initialization, see SDK initialization.
Pod integration
Add the following configuration to your Podfile and run the pod update command.
// 1. Add the source reference in Podfile
source 'https://github.com/aliyun/aliyun-specs.git'
// 2. Add library dependency
pod 'LinkVisualMedia', '2.7.5-ilop'
For more information about version releases, see LinkVisual SDK update history.
Project configuration
Change file extension from .m to .mm for C++ and Objective-C mixed compilation
Because part of the SDK is written in C++, you must perform mixed compilation with Objective-C. To ensure that the Clang compiler correctly identifies these files, change the file extension of any source file that references the SDK from .m to .mm. Otherwise, you may encounter an error similar to the following:

Add -ObjC to your project's linker flags
Because the SDK static library uses categories, you must add this linker flag to prevent the "selector not recognized" compilation error. For more information, see https://developer.apple.com/library/archive/qa/qa1490/_index.html.
Swift support
In Swift projects, you can use a bridging header to call the LinkVisualMedia library. To do this, create a BridgingHeader file and add the following content.
//
// TestBridgingHeader.h
// swift-test
//
#define USE_SWIFT
#import "LinkVisualMedia/LVMedia.h"
#import "LinkVisualMedia/LVGlkView.h"
#import "LinkVisualMedia/LVLivePlayer.h"
#import "LinkVisualMedia/LVVodPlayer.h"
#import "LinkVisualMedia/LVLiveIntercom.h"
#import "LinkVisualMedia/LVAudioFilePlayer.h"
#import "LinkVisualMedia/LVAudioFileRecorder.h"
#import "LinkVisualMedia/LVOSSUploader.h"
API documentation
Players
Video players are divided into three types based on their functionality.
Live player
Supports live playback from LinkVisual devices.
Features low playback latency.
Provides peer-to-peer (P2P) communication with devices.
Video-on-demand (VOD) player for device recordings
Plays back local device recordings and lets you adjust the playback progress.
Provides P2P communication with devices.
VOD player for HLS cloud recordings
Plays back cloud recordings that are based on HTTP Live Streaming (HLS). This player supports MPEG-TS and fMP4 containers with AES-128 encryption.
Player feature comparison
Feature |
Live player |
VOD player (device recordings) |
VOD player (HLS cloud recordings) |
Video playback |
✓ |
✓ |
✓ |
Audio playback |
✓ |
✓ |
✓ |
Pause/resume |
- |
✓ |
✓ |
Playback reconnection |
✓ |
- |
- |
Seek to specific position |
- |
✓ |
✓ |
Total duration |
- |
✓ |
✓ |
Current playback progress |
- |
✓ |
✓ |
Player state change notification |
✓ |
✓ |
✓ |
Mute playback |
✓ |
✓ |
✓ |
Variable-speed playback |
- |
✓ |
✓ |
Frame-by-frame stepping |
- |
✓ |
✓ |
Set image scaling mode |
✓ |
✓ |
✓ |
Set display mode when playback stops |
✓ |
✓ |
✓ |
NVR multi-view playback |
✓ |
✓ |
✓ |
Digital zoom |
✓ |
✓ |
✓ |
Screenshot of the playback window |
✓ |
✓ |
✓ |
Save screenshot to file |
✓ |
✓ |
✓ |
Record while playing |
✓ |
✓ |
✓ |
Hardware decoding |
✓ |
✓ |
✓ |
Get stream information |
✓ |
✓ |
✓ |
Provide YUV data |
✓ |
✓ |
✓ |
Provide raw stream data |
✓ |
✓ |
✓ |
Provide SEI data |
✓ |
✓ |
✓ |
Player usage examples:
Live player
// Create player instance self.player = [[LVLivePlayer alloc] init]; // Set required delegates self.player.livePlayerDelegate = self; ... #pragma mark LVLivePlayerDelegate - (void) onLivePlayerError:(LVLivePlayer *_Nonnull)player error:(NSError *_Nonnull)error{ [self appendInfoText:[NSString stringWithFormat:@"Playback error: %@\n", error]]; } - (void) onLivePlayerStateChange:(LVLivePlayer *_Nonnull)player playerState:(LVPlayerState)state{ [self appendInfoText:[NSString stringWithFormat:@"State changed: %d\n", state]]; } - (void) onLivePlayerRenderedFirstFrame:(LVLivePlayer *_Nonnull)player elapsedTimeInMs:(NSInteger)elapsedTimeInMs{ [self appendInfoText:[NSString stringWithFormat:@"First frame rendered in: %ldms\n", (long)elapsedTimeInMs]]; [self appendInfoText:[self.player getStatisticsInfo]]; } - (void) onLivePlayerVideoSizeChanged:(LVLivePlayer *_Nonnull)player width:(NSInteger)width height:(NSInteger)height{ [self appendInfoText:[NSString stringWithFormat:@"Video size changed: w=%ld h=%ld\n", (long)width, (long)height]]; } // Deprecated, will be removed - (void) onLivePlayerSeiInfoUpdate:(LVLivePlayer *_Nonnull)player sei:(NSData*_Nonnull)data timeStamp:(NSInteger)timeStamp{ [self appendInfoText:[NSString stringWithFormat:@"Private SEI: %lu %ld %@", (unsigned long)data.length, timeStamp,[data description]]]; } - (void) onLivePlayerStandardSeiInfoUpdate:(LVLivePlayer *_Nonnull)player sei:(NSData*_Nonnull)data timeStamp:(NSInteger)timeStamp{ [self appendInfoText:[NSString stringWithFormat:@"Standard SEI: %lu %ld %@", (unsigned long)data.length, timeStamp,[data description]]]; } - (void) onLivePlayerVideoJitterBufferEmpty:(LVLivePlayer *_Nonnull)player{ } ... // Create LVGlkView with zoom capability inside scrollview self.lvGlkView = [[LVGlkView alloc] initWithFrame:CGRectMake(0,0,self.scrollView.frame.size.width, self.scrollView.frame.size.height)];; [self.scrollView addSubview:self.lvGlkView]; [self.lvGlkView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(@(0)); make.centerY.equalTo(@(0)); make.height.equalTo(self.scrollView.mas_height); make.width.equalTo(self.scrollView.mas_width); }]; // Set rendering view for player, no rotation applied [self.player setWindow:self.lvGlkView videoRotationMode:LV_MEDIA_VIDEO_ROTATE_90_CLOCKWISE]; ... // Set data source using device IotId [self.player setLiveDataSource:iotId streamType:LV_STREAM_TYPE_MAJOR]; // Set automatic reconnection attempts [self.player setReconnectCount:3]; // Set decoder strategy to prefer hardware decoding [self.player setDecoderStrategy:LV_DECODER_STRATEGY_HARDWARE_FIRST]; // Set video scaling mode to maintain aspect ratio [self.player setVideoScalingMode:LV_MEDIA_VIDEO_SCALING_MODE_FIT]; // Keep last frame displayed when playback stops [self.player setPlayerStoppedDrawingMode:LV_PLAYER_STOPPED_DRAWING_MODE_ALWAYS_KEEP_LAST_FRAME]; ... // Start playback [self.player start]; ... // Stop playback [self.player stop];VOD player for device recordings
// Create player instance self.player = [[LVVodPlayer alloc] init]; // Set required delegates self.player.vodPlayerDelegate = self; ... #pragma mark LVVodPlayerDelegate - (void) onVodPlayerError:(LVVodPlayer *_Nonnull)player error:(NSError *_Nonnull)error{ [self appendInfoText:[NSString stringWithFormat:@"Playback error: %@\n", error]]; } - (void) onVodPlayerStateChange:(LVVodPlayer *_Nonnull)player playerState:(LVPlayerState)state{ [self appendInfoText:[NSString stringWithFormat:@"State changed: %d\n", state]]; } - (void) onVodPlayerRenderedFirstFrame:(LVVodPlayer *_Nonnull)player elapsedTimeInMs:(NSInteger)elapsedTimeInMs{ [self appendInfoText:[NSString stringWithFormat:@"First frame rendered in: %ldms\n", (long)elapsedTimeInMs]]; [self appendInfoText:[self.player getStatisticsInfo]]; } - (void) onVodPlayerVideoSizeChanged:(LVVodPlayer *_Nonnull)player width:(NSInteger)width height:(NSInteger)height{ [self appendInfoText:[NSString stringWithFormat:@"Video size changed: w=%ld h=%ld\n", (long)width, (long)height]]; } // Deprecated, will be removed - (void) onVodPlayerSeiInfoUpdate:(LVVodPlayer *_Nonnull)player sei:(NSData*_Nonnull)data timeStamp:(NSInteger)timeStamp{ [self appendInfoText:[NSString stringWithFormat:@"Private SEI: %lu %ld %@", (unsigned long)data.length, timeStamp,[data description]]]; } - (void) onVodPlayerStandardSeiInfoUpdate:(LVVodPlayer *_Nonnull)player sei:(NSData*_Nonnull)data timeStamp:(NSInteger)timeStamp{ [self appendInfoText:[NSString stringWithFormat:@"Standard SEI: %lu %ld %@", (unsigned long)data.length, timeStamp,[data description]]]; } - (void) onVodPlayerVideoJitterBufferEmpty:(LVLivePlayer *_Nonnull)player{ } - (void)onVodPlayerCompletion:(LVVodPlayer * _Nonnull)player { [self appendInfoText:@"Playback completed"]; } ... // Create LVGlkView with zoom capability inside scrollview self.lvGlkView = [[LVGlkView alloc] initWithFrame:CGRectMake(0,0,self.scrollView.frame.size.width, self.scrollView.frame.size.height)];; [self.scrollView addSubview:self.lvGlkView]; [self.glkView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(@(0)); make.centerY.equalTo(@(0)); make.height.equalTo(self.scrollView.mas_height); make.width.equalTo(self.scrollView.mas_width); }]; // Set rendering view for player [self.player setWindow:self.lvGlkView]; ... // Set data source: both methods work // 1. By device filename, seek to 20 seconds [self.player setDataSourceByLocalRecordFileName:iotId fileName:@"fileNamexxxxxxxx" seekToPositionInMs:20000]; // 2. By device time range (2022-08-29 17:54:14 to 2022-08-30 02:47:34), seek to 20 seconds, all recording types [self.player setDataSourceByLocalRecordTime:iotId beginTimeInS:1661766854 endTimeInS:1661798854 seekToPositionInMs:20000 recordType:99]; // Set decoder strategy to prefer hardware decoding [self.player setDecoderStrategy:LV_DECODER_STRATEGY_HARDWARE_FIRST]; // Set video scaling mode to maintain aspect ratio [self.player setVideoScalingMode:LV_MEDIA_VIDEO_SCALING_MODE_FIT]; ... // Start playback [self.player start]; ... // Pause [self.player pause]; ... // Resume [self.player resume]; ... // Stop playback [self.player stop];VOD player for HLS cloud storage
// Create player instance self.player = [[LVVodPlayer alloc] init]; // Set required delegates self.player.vodPlayerDelegate = self; ... #pragma mark LVVodPlayerDelegate - (void) onVodPlayerError:(LVVodPlayer *_Nonnull)player error:(NSError *_Nonnull)error{ [self appendInfoText:[NSString stringWithFormat:@"Playback error: %@\n", error]]; } - (void) onVodPlayerStateChange:(LVVodPlayer *_Nonnull)player playerState:(LVPlayerState)state{ [self appendInfoText:[NSString stringWithFormat:@"State changed: %d\n", state]]; } - (void) onVodPlayerRenderedFirstFrame:(LVVodPlayer *_Nonnull)player elapsedTimeInMs:(NSInteger)elapsedTimeInMs{ [self appendInfoText:[NSString stringWithFormat:@"First frame rendered in: %ldms\n", (long)elapsedTimeInMs]]; [self appendInfoText:[self.player getStatisticsInfo]]; } - (void) onVodPlayerVideoSizeChanged:(LVVodPlayer *_Nonnull)player width:(NSInteger)width height:(NSInteger)height{ [self appendInfoText:[NSString stringWithFormat:@"Video size changed: w=%ld h=%ld\n", (long)width, (long)height]]; } // Deprecated, will be removed - (void) onVodPlayerSeiInfoUpdate:(LVVodPlayer *_Nonnull)player sei:(NSData*_Nonnull)data timeStamp:(NSInteger)timeStamp{ [self appendInfoText:[NSString stringWithFormat:@"Private SEI: %lu %ld %@", (unsigned long)data.length, timeStamp,[data description]]]; } - (void) onVodPlayerStandardSeiInfoUpdate:(LVVodPlayer *_Nonnull)player sei:(NSData*_Nonnull)data timeStamp:(NSInteger)timeStamp{ [self appendInfoText:[NSString stringWithFormat:@"Standard SEI: %lu %ld %@", (unsigned long)data.length, timeStamp,[data description]]]; } - (void) onVodPlayerVideoJitterBufferEmpty:(LVLivePlayer *_Nonnull)player{ } - (void)onVodPlayerCompletion:(LVVodPlayer * _Nonnull)player { [self appendInfoText:@"Playback completed"]; } ... // Create LVGlkView with zoom capability inside scrollview self.lvGlkView = [[LVGlkView alloc] initWithFrame:CGRectMake(0,0,self.scrollView.frame.size.width, self.scrollView.frame.size.height)];; [self.scrollView addSubview:self.lvGlkView]; [self.glkView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(@(0)); make.centerY.equalTo(@(0)); make.height.equalTo(self.scrollView.mas_height); make.width.equalTo(self.scrollView.mas_width); }]; // Set rendering view for player [self.player setWindow:self.lvGlkView]; ... // Set data source: cloud storage filename, seek to 20 seconds [self.player setDataSourceByCloudRecordFileName:iotId fileName:@"fileNamexxxxxxxx" seekToPositionInMs:20000]; // Set decoder strategy to prefer hardware decoding [self.player setDecoderStrategy:LV_DECODER_STRATEGY_HARDWARE_FIRST]; // Set video scaling mode to maintain aspect ratio [self.player setVideoScalingMode:LV_MEDIA_VIDEO_SCALING_MODE_FIT]; ... // Start playback [self.player start]; ... // Pause [self.player pause]; ... // Resume [self.player resume]; ... // Stop playback [self.player stop];
Player states
You can set a player state listener to receive state change events and update UI elements.
State change events are triggered by playback errors or when you manually stop playback.

IDLE: The player has no content to play.
BUFFERING: The player is buffering and cannot play from the current position. This state occurs when playback starts, after a seekTo() operation, or because of poor network conditions during VOD playback.
READY: The player is actively playing content. This state is triggered after the first frame is rendered or after a seekTo() operation completes buffering. For VOD players, when the end of the file is reached, the
onVodPlayerCompletionmethod of theLVVodPlayerDelegateis triggered, and the player automatically switches to the ENDED state.ENDED: Playback has finished. This state is triggered by playback errors, after you call the stop() method, or when the end of the playback is reached.
Player error codes
Primary Error Code |
Error subcode |
Description |
Solution |
LV_PLAYER_ERROR_CODE_SOURCE |
LV_PLAYER_ERROR_SUB_CODE_SOURCE_STREAM_CONNECT |
Failed to connect to data source |
This usually occurs due to network issues. Ensure your network connection is stable and retry. |
LV_PLAYER_ERROR_SUB_CODE_SOURCE_INVALID_PARAMETER |
Invalid data source parameters |
Verify your configuration parameters are valid and retry. |
|
LV_PLAYER_ERROR_SUB_CODE_SOURCE_QUERY_URL_FAILED |
Failed to request playback URL |
Check the msg field in the error message to identify and fix the issue. |
|
LV_PLAYER_ERROR_CODE_RENDER |
LV_PLAYER_ERROR_SUB_CODE_RENDER_DECODE |
Decoding error |
If decoding fails consistently, save the stream data for analysis. When log level is set to Debug, raw stream data is automatically saved to the app's tmp directory. |
LV_PLAYER_ERROR_CODE_UNEXPECTED |
LV_PLAYER_ERROR_SUB_CODE_UNEXPECTED_PULL_STREAM_TIMEOUT |
No stream received for over 8 seconds or connection unexpectedly dropped |
Check for the following issues and then retry: 1. The device is not ingesting a stream. 2. The network for the application or device is abnormal. |
LV_PLAYER_ERROR_SUB_CODE_UNEXPECTED_PLAY_DURATION_EXCEED |
Continuous playback duration exceeded limit |
To prevent unnecessary bandwidth consumption from unattended playback, consider upgrading your traffic package if you need extended viewing sessions. |
Voice intercom
This feature provides end-to-end simplex or duplex real-time intercom between your app and Internet Protocol Camera (IPC) devices.
Simplex: The app captures and sends audio to the device for playback. The app remains silent during audio capture.
Full-duplex: The app and the device simultaneously capture and play audio. The device must support Acoustic Echo Cancellation (AEC). Do not use this mode if the device does not support AEC.
Half-duplex: The app and the device handle audio capture and playback, but not simultaneously. This mode works like a walkie-talkie, where the app controls the start and stop of recording multiple times during a session. AEC support is not required on the device.
Supported audio formats:
Type |
Sample rate |
Encoding |
Decoding |
G711A |
8Khz/16Khz |
✓ |
✓ |
G711U |
8Khz/16Khz |
✓ |
✓ |
AAC_LC |
8Khz/16Khz |
✓ |
✓ |
Voice intercom usage:
Create a voice intercom instance and set the audio parameters.
// Create voice intercom instance LVMediaAudioHeader header; header.audioBitPerSample = 16; header.audioChannel = 1; header.audioSamplesPerSec = 8000; header.audioEncType = LV_MEDIA_AUDIO_ENC_G711A; self.liveIntercom = [LVLiveIntercom create:header];Register delegates and handle voice intercom callbacks.
You must handle events such as intercom ready, recording start and end, and volume changes for UI display.
For information about possible errors during intercom setup and operation, see the voice intercom error list.
self.liveIntercom.liveIntercomDelegate = self; self.liveIntercom.liveIntercomVoiceChangeDelegate = self; #pragma mark - LiveIntercomDelgate - (void) onLiveIntercomTalkReady:(LVLiveIntercom *_Nonnull)liveIntercom{ [self appendInfoText:@"Ready to speak\n"]; } - (void) onLiveIntercomRecorderStart:(LVLiveIntercom *_Nonnull)liveIntercom{ [self appendInfoText:@"Recorder started\n"]; } - (void) onLiveIntercomRecorderEnd:(LVLiveIntercom *_Nonnull)liveIntercom{ [self appendInfoText:@"Recorder stopped\n"]; } - (void) onLiveIntercomRecorderVolume:(LVLiveIntercom *_Nonnull)liveIntercom volume:(NSInteger)volume{ [self appendInfoText:[NSString stringWithFormat:@"Volume changed: %ld\n", volume]]; } - (void) onLiveIntercomError:(LVLiveIntercom *_Nonnull)liveIntercom error:(NSError *_Nonnull)error{ [self appendInfoText:[NSString stringWithFormat:@"Intercom error: %@:\n", error]]; }Set the gain level.
You can set the microphone gain level on the app side. You can choose from six levels: -1 (None), 0 (Mild), 1 (Medium), 2 (High), 3 (Aggressive), 4 (Very Aggressive), and 5 (Max). The default value is None. You can adjust the gain level based on the device performance.
[self.liveIntercom setGainLevel:-1];Set the intercom mode.
You can choose from five modes:
Simplex: The app captures and sends audio to the device for playback. The app remains silent during audio capture.
Full-duplex (standalone): The app and the device simultaneously capture and play audio. The device must support AEC. The audio plays through the device's intercom channel and works independently without requiring a live stream.
Full-duplex (with live stream): The app and the device simultaneously capture and play audio. The device must support AEC. The audio plays through the device's live stream channel, which requires an active live stream. Before you start the intercom, call LVLivePlayer.audioFocus() to select the corresponding live stream.
Half-duplex (standalone): The app and the device handle audio capture and playback, but not simultaneously, similar to a walkie-talkie. The app controls the start and stop of recording multiple times during a session. The audio plays through the device's intercom channel and works independently without requiring a live stream. This mode requires device SDK v2.3.3 or later. Older versions fall back to the full-duplex mode.
Half-duplex (with live stream): The app and the device handle audio capture and playback, but not simultaneously, similar to a walkie-talkie. The app controls the start and stop of recording multiple times during a session. The audio plays through the device's live stream channel, which requires an active live stream. Before you start the intercom, call LVLivePlayer.audioFocus() to select the corresponding live stream. This mode requires device SDK v2.3.3 or later. Older versions fall back to the full-duplex mode.
// Set standalone duplex mode [self.liveIntercom setLiveIntercomMode:LV_LIVE_INTERCOM_MODE_DOUBLE_TALK];Start the intercom.
When the intercom starts, it requests audio focus and switches to call mode. In call mode, audio is routed to a Bluetooth or wired headset if one is connected.
// Start intercom [self.liveIntercom start:iotId];Switch the talk/listen state (half-duplex only).
After you start a half-duplex intercom, you must manually switch states to control audio capture and playback.
// App stops playback, captures audio to send to device; device stops capturing and plays app's audio [self.liveIntercom listen:YES]; ... // App resumes playback; device stops playback and resumes audio capture [self.liveIntercom listen:NO];Stop the intercom.
After the intercom is stopped, audio focus is released and the normal mode is restored. Acoustic echo cancellation is enabled by default during the intercom.
// Stop intercom [self.liveIntercom stop];
Voice intercom error codes
Fault enumeration |
Description |
Solution |
LV_LIVE_INTERCOM_ERROR_START_REQUEST_FAILED |
Failed to initiate voice intercom request. See subCode for details. |
Check and fix the following issues, then retry:
|
LV_LIVE_INTERCOM_ERROR_CONNECT_STREAM_FAILED |
Failed to establish voice intercom stream channel |
Check and fix the following issues, then retry:
|
LV_LIVE_INTERCOM_ERROR_SEND_STREAM_DATA_FAILED |
Failed to send audio data |
Network environment is unstable. Switch to a better network and retry. |
LV_LIVE_INTERCOM_ERROR_RECEIVE_STREAM_DATA_FAILED |
Failed to receive audio data |
Network environment is unstable. Switch to a better network and retry. |
LV_LIVE_INTERCOM_ERROR_INIT_AUDIO_RECORDER_FAILED |
Audio recorder initialization failed |
Check and fix the following issues, then retry:
|
LV_LIVE_INTERCOM_ERROR_START_AUDIO_RECORDER_FAILED |
Audio recorder startup failed |
Check if another app is using the microphone. Terminate other apps and retry. |
LV_LIVE_INTERCOM_ERROR_READ_AUDIO_RECORDER_FAILED |
Audio recorder data read error |
Audio recorder malfunction. Restart the device (e.g., reboot phone). |
LV_LIVE_INTERCOM_ERROR_INIT_AUDIO_TRACK_FAILED |
Audio player creation failed |
Audio player malfunction. Restart the device (e.g., reboot phone). |
Audio file recording, playback, and upload
This feature provides the capabilities to record audio files, play local or network audio files, and upload audio files.
The following audio formats are supported for recording and playback:
File type |
Sample rate |
Codec |
AMR |
8Khz (recording and playback)/16Khz (playback only) |
amr |
WAV |
8Khz/16Khz |
PCM |
G711 |
Audio file recording, playback, and upload usage:
Audio file recording
// Create audio file recorder self.audioFileRecorder = [[LVAudioFileRecorder alloc] init]; // Set recording delegate self.audioFileRecorder.audioFileRecorderDelegate = self; #pragma mark - LVAudioFileRecorderDelegate - (void) onAudioFileRecorderError:(LVAudioFileRecorder *_Nonnull)recorder error:(NSError *_Nonnull)error{ [self ims_showHUDWithMessage:[NSString stringWithFormat:@"AMR recording failed: %@", error]]; } - (void) onAudioFileRecorderRecordStart:(LVAudioFileRecorder *_Nonnull)recorder{ [self ims_showHUDWithMessage:@"AMR recording started"]; } - (void) onAudioFileRecorderRecordEnd:(LVAudioFileRecorder *_Nonnull)recorder{ [self ims_showHUDWithMessage:@"AMR recording finished"]; } ... // Set recording parameters and start recording LVMediaAudioHeader header; header.audioBitPerSample = 16; header.audioChannel = 1; header.audioSamplesPerSec = 8000; header.audioEncType = LV_MEDIA_AUDIO_ENC_AMRNB; NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test.amr"]; [self.audioFileRecorder startRecorder:&header filePath:filePath]; ... // Stop recording [self.audioFileRecorder stopRecorder];Audio file playback
// Create audio file player self.audioFilePlayer = [[LVAudioFilePlayer alloc] init]; // Set playback delegate self.audioFilePlayer.audioFileplayerDelegate = self; #pragma mark - LVAudioFilePlayerDelegate - (void) onAudioFilePlayerCompletion:(LVAudioFilePlayer *_Nonnull)player{ [self ims_showHUDWithMessage:@"AMR file playback completed"]; } ... // Play local file NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test.amr"]; [self.audioFilePlayer startPlay:LV_AUDIO_FILE_TYPE_AMR filePath:filePath]; // Play network file NSString *tmpPath = @"https://lv-demo.oss-cn-hangzhou.aliyuncs.com/test.amr"; [self.audioFilePlayer startPlay:LV_AUDIO_FILE_TYPE_AMR filePath:tmpPath]; // Stop playback [self.audioFilePlayer stopPlay];File upload
// Create file uploader self.ossUploader = [[LVOSSUploader alloc] init]; // Set upload delegate self.ossUploader.OSSUploaderDelegate = self; #pragma mark - LVOSSUploaderDelegate - (void) onOSSUploaderError:(LVOSSUploader *_Nonnull)ossUploader sessionId:(NSString *_Nonnull)sessionId fileName:(NSString *_Nonnull)fileName error:(NSError*_Nonnull)error{ [self ims_showHUDWithMessage:[NSString stringWithFormat:@"AMR file upload failed: %@", error]]; } -(void) onOSSUploaderProgress:(LVOSSUploader *_Nonnull)ossUploader uploadBytes:(NSInteger)uploadBytes totalBytes:(NSInteger)totalBytes{ } -(void) onOSSUploaderCompletion:(LVOSSUploader *_Nonnull)ossUploader sessionId:(NSString *_Nonnull)sessionId fileName:(NSString *_Nonnull)fileName{ [self ims_showHUDWithMessage:@"AMR upload completed"]; } - (void) onAudioFileRecorderRecordVolume:(LVAudioFileRecorder *_Nonnull)recorder volume:(NSInteger)volume{ } ... // Start upload NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test.amr"]; [self.ossUploader upload:tmpPath fileName:@"test.amr" sessionId:@"123456" uploadUrl:@"https://vision-customer-daily.oss-cn-hangzhou.aliyuncs.com/test.amr"];
Audio file error codes
Enumerated faults |
Description |
Solution |
LV_AUDIO_FILE_ERROR_CREATE_FILE_FAILED |
File creation failed |
Verify the file can be created and written to. |
LV_AUDIO_FILE_ERROR_INIT_AUDIO_RECORDER_FAILED |
Audio recorder initialization failed |
Check and fix the following issues, then retry:
|
LV_AUDIO_FILE_ERROR_START_AUDIO_RECORDER_FAILED |
Audio recorder startup failed |
Check if another app is using the microphone. Terminate other apps and retry. |
LV_AUDIO_FILE_ERROR_READ_AUDIO_RECORDER_FAILED |
Audio recorder data read error |
Audio recorder malfunction. Restart the device (e.g., reboot phone). This is rare. |
LV_AUDIO_FILE_ERROR_READ_FILE_FAILED |
File read failed |
Verify the file exists and can be read. |
LV_AUDIO_FILE_ERROR_INIT_AUDIO_TRACK_FAILED |
Audio player creation failed |
Audio player malfunction. Restart the app or device. This is rare. |
Migration guide from 1.x SDK to 2.x
The iOS Media SDK v2.0.0 introduced significant changes to interfaces, methods, and classes. If you are using a version earlier than 2.0.0-ilop, you can use the methods that are provided in this guide to upgrade to v2.0.0-ilop.
For more information about the v1.x documentation, see LinkVisual Video Media SDK.
Key changes
Key change |
1.x |
2.x |
Separate players and voice intercom |
All player and intercom features were in IMSLinkVisualPlayerViewController |
Split by functionality:
|
Decouple players from ViewController |
All player and intercom features were encapsulated in IMSLinkVisualPlayerViewController |
No ViewController provided. Use LVGlkView interface instead. |
Extract common gateway API requests |
Encapsulated within SDK |
Removed from SDK and provided as source code. |
API changes (use text search to locate)
Global components
Change detail |
1.x |
2.x |
Recommended Changes |
New global LVMedia component |
- |
LVMedia |
Update package and class names. |
New API: Pre-connect for live streams |
- |
(LVPlayerCode) preConnectByIotId:(NSString *_Nonnull)iotId streamType:(LVStreamType)streamType; |
Starting from 2.0.0, iOS supports live stream pre-connection. |
New API: Set log level |
- |
(void) setLogLevel:(IMSLinkVisualMediaLogLevel)level; |
Global log level setting API. |
Live player
Change detail |
1.x |
2.x |
Recommended changes |
Package and class name changes |
IMSLinkVisualPlayerViewController |
LVLivePlayer |
Update package and class names. |
Player no longer provides ViewController |
IMSLinkVisualPlayerViewController encapsulated GLKView for rendering |
Player decoupled from rendering View |
Create LVGlkView in your VC and call (LVPlayerCode) setWindow:(LVGlkView *_Nullable)view in viewDidAppear to assign it to the player. |
New API: Set encrypted RTMP live stream URL |
- |
(LVPlayerCode) setDataSource:(NSString *_Nonnull)url |
- |
For encrypted live stream data source, decryptIv and decryptKey changed from Base64-decoded binary data to pre-decoder strings |
(BOOL)setDataSource_Live:(NSString *_Nullable)rtmpPathneedEncrypt:(BOOL)needEncryptiv:(NSData *_Nullable)ivkey:(NSData *_Nullable)key; |
(LVPlayerCode) setDataSource:(NSString *_Nonnull)url isEncrypted:(BOOL)isEncrypted decryptIvBase64:(NSString *_Nullable)decryptIvBase64 decryptKeyBase64:(NSString *_Nullable)decryptKeyBase64; |
Remove Base64 decoding. The SDK handles Base64 string decoding internally. |
API rename: Set IPC device live stream data source connected via IoT Platform |
(BOOL)setDataSource:(NSString*_Nullable)iotIdstreamType:(IMSLinkVisualPlayerLiveStreamType)streamTypecacheDurationInMs:(int)cacheDurationInMs; |
|
Updated API name. |
API removed: IPC device live stream data source connected via IoT Platform |
(BOOL)setDataSource:(NSString*_Nullable)iotIdstreamType:(IMSLinkVisualPlayerLiveStreamType)streamTypeneedForceIFrame:(BOOL)needForceIFrame __attribute__ ((deprecated)); |
- |
Starting from 2.0.0, encryption and forced I-frame are mandatory. |
Command APIs like |
- |
Return value enum: |
Check return values to determine if API operations succeeded. |
New API: Get current stream width and height |
- |
|
- |
Class and method name changes: Set decoder strategy and get decoder type |
|
|
Update class and method names. |
API removed: P2P global initialization and release |
|
- |
No action needed. |
API removed: Device P2P pre-connection |
(void)preCreateP2P:(NSString*_Nonnull)iotId; |
- |
Starting from 2.0.0, use LVMedia's global preConnect API for pre-connection. |
API removed: Stop all players |
(void)stopAllPlayer; |
- |
Starting from 2.0.0, stop individual players directly. This API has alternatives and is no longer needed. |
Property removed: Activate audio manager |
activeAudioManager; |
- |
Starting from 2.0.0, since intercom and players are separated, recording permission is only triggered when using specific features. This property is obsolete. |
API change: Audio focus |
(void)activePlayer; |
|
Starting from 2.0.0, the SDK supports at most one audio stream at a time. In multi-view scenarios (e.g., NVR multi-screen), call audioFocus() on the desired player to switch audio playback. |
API rename: Set player buffer |
|
|
API renamed. |
API rename: Set maximum jitter buffer duration |
|
|
API renamed. |
API removed: Disable voice intercom in VC |
(instancetype_Nonnull)initDisableIntercom; |
- |
Starting from 2.0.0, players and intercom are functionally separated and no longer coupled. This API is obsolete. |
API rename: Screenshot |
(UIImage * __nullable)videoSnapshot; |
(UIImage *_Nullable) snapShot; |
API renamed. |
New API: Save screenshot to file |
- |
|
- |
API rename: Record while playing |
|
|
API renamed. |
Property Removed: Disable automatic resume when switching to the background. |
cancelForeground |
- |
Starting from 2.0.0, since no ViewController is provided, background/foreground handling logic is removed. Handle this in your own ViewController as needed. |
Property change: Frame rate/bitrate |
|
(LVPlayInfo) getCurrentPlayInfo; |
You can change the property to an interface. |
Property change: Connection type |
connectType |
(LVStreamConnectType) getStreamConnectType; |
Convert the property to an interface. |
API change: Log level setting |
(void)setLogLevel:(IMSLinkVisualMediaLogLevel)logLevel decodeLog:(bool)bShow; |
[LVMedia getInstance] (void) setLogLevel:(IMSLinkVisualMediaLogLevel)level; |
Starting from 2.0.0, use LVMedia's global setLogLevel API to set log level. |
Property change: Mute |
mute |
|
This has been changed from a property to an interface. |
Property change: Set video scaling mode parameter adjustment |
contentMode |
(LVPlayerCode) setVideoScalingMode:(LVMediaVideoScalingMode)videoScalingMode; |
Changed a property to an interface and modified the parameter and class names. |
Property change: Player state |
playerState |
(LVPlayerState) getPlayerState; |
As of version 2.0.0, the property is an interface and the iOS state machine has been updated. For more information, see the state diagram. |
Delegate change: IMSLinkVisualDelegate |
IMSLinkVisualDelegate |
|
|
Property change: Set internal/external rendering mode |
lvDisplayMode |
(LVPlayerCode) setUseExternalRender;(BOOL)useExternalVideoRender useExternalAudioRender: (BOOL)useExternalAudioRender; |
This property is now an interface that supports external video rendering and external audio playback. |
New API: Get YUV frame data |
- |
|
Custom rendering no longer returns data in callbacks. Retrieve data in the rendering thread instead. |
Video-on-demand player
Change detail |
1.x |
2.x |
Suggested Modifications |
Package and class name changes |
IMSLinkVisualPlayerViewController |
LVVodPlayer |
Update package and class names. |
Player no longer provides ViewController |
IMSLinkVisualPlayerViewController encapsulated GLKView for rendering |
Player decoupled from rendering View |
Create LVGlkView in your VC and call (LVPlayerCode) setWindow:(LVGlkView *_Nullable)view in viewDidAppear to assign it to the player. |
New API: Set RTMP VOD and HLS VOD URLs as data source |
- |
(LVPlayerCode) setDataSource:(NSString *_Nonnull)url |
- |
For an encrypted live data source, the values of decryptIv and decryptKey have been changed from Base64-decoded binary data to Base64-encoded strings. |
(BOOL)setDataSource_Vod:(NSString *_Nullable)rtmpPathneedEncrypt:(BOOL)needEncryptiv:(NSData *_Nullable)ivkey:(NSData *_Nullable)key; |
(LVPlayerCode) setDataSource:(NSString *_Nonnull)url isEncrypted:(BOOL)isEncrypted decryptIvBase64:(NSString *_Nullable)decryptIvBase64 decryptKeyBase64:(NSString *_Nullable)decryptKeyBase64; |
Remove Base64 decoding. The SDK handles Base64 string decoding internally. |
API rename: Set IPC device recording VOD data source connected via IoT Platform |
|
|
Updated API names. |
API removed: IPC device recording VOD data source connected via IoT Platform |
|
- |
Starting from 2.0.0, encryption and forced I-frame are mandatory. |
API rename: Set IPC device cloud storage VOD data source connected via IoT Platform |
|
|
Updated API names. |
Command APIs like |
- |
Return value enum: |
Check return values to determine if API operations succeeded. |
API rename: Resume playback |
restore |
resume |
Updated API name. |
API rename: Frame-by-frame playback |
nextFrame |
playFrameByFrame |
Updated API name. |
API rename: Seek to specific time |
seekToDuration |
seekTo |
Updated API name. |
New API: Get current stream width and height |
- |
|
- |
Class and method name changes: Set decoder strategy and get decoder type |
|
|
Update class and method names. |
API removed: P2P global initialization and release |
|
- |
No action needed. |
API removed: Device P2P pre-connection |
(void)preCreateP2P:(NSString*_Nonnull)iotId; |
- |
Starting from 2.0.0, use LVMedia's global preConnect API for pre-connection. |
API removed: Stop all players |
(void)stopAllPlayer; |
- |
Starting from 2.0.0, stop individual players directly. This API has alternatives and is no longer needed. |
Property removed: Activate audio manager |
activeAudioManager; |
- |
Starting from 2.0.0, since intercom and players are separated, recording permission is only triggered when using specific features. This property is obsolete. |
API change: Audio focus |
(void)activePlayer; |
|
Starting from 2.0.0, the SDK supports at most one audio stream at a time. In multi-view scenarios (e.g., NVR multi-screen), call audioFocus() on the desired player to switch audio playback. |
API removed: Set player buffer |
|
- |
This API is only supported in live players. Removed from VOD players. |
API removed: Set maximum jitter buffer duration |
|
- |
This API is only supported in live players. Removed from VOD players. |
API removed: Disable voice intercom in VC |
(instancetype_Nonnull)initDisableIntercom; |
- |
Starting from 2.0.0, players and intercom are functionally separated and no longer coupled. This API is obsolete. |
API rename: Screenshot |
(UIImage * __nullable)videoSnapshot; |
(UIImage *_Nullable) snapShot; |
API renamed. |
New API: Save screenshot to file |
- |
|
- |
API rename: Record while playing |
|
|
API renamed. |
Attribute removed: Automatic resume on background switch |
cancelForeground |
- |
Starting from 2.0.0, since no ViewController is provided, background/foreground handling logic is removed. Handle this in your own ViewController as needed. |
Property change: Frame rate/bitrate |
|
(LVPlayInfo) getCurrentPlayInfo; |
You can change the property to an interface. |
Property change: Connection type |
connectType |
(LVStreamConnectType) getStreamConnectType; |
You can change the property to an interface. |
API change: Log level setting |
(void)setLogLevel:(IMSLinkVisualMediaLogLevel)logLevel decodeLog:(bool)bShow; |
[LVMedia getInstance] (void) setLogLevel:(IMSLinkVisualMediaLogLevel)level; |
Starting from 2.0.0, use LVMedia's global setLogLevel API to set log level. |
Property change: Mute |
mute |
|
This has been changed from a property to an interface. |
Property change: Playback speed |
playSpeed |
(LVPlayerCode) setPlaybackSpeed:(float)speed; |
The property was converted to an interface. |
Property change: Total duration and current position |
|
|
The property was changed to an interface. |
Property change: Set video scaling mode parameter adjustment |
contentMode |
(LVPlayerCode) setVideoScalingMode:(LVMediaVideoScalingMode)videoScalingMode; |
Changed a property to an interface and renamed parameters and classes. |
Property change: Player state |
playerState |
(LVPlayerState) getPlayerState; |
Starting with version 2.0.0, the property has been changed to an interface and the state machine for iOS has been adjusted. For more information, see the state diagram. |
Delegate change: IMSLinkVisualDelegate |
IMSLinkVisualDelegate |
|
|
Property change: Set internal/external rendering mode |
lvDisplayMode |
(LVPlayerCode) setUseExternalRender;(BOOL)useExternalVideoRender useExternalAudioRender: (BOOL)useExternalAudioRender; |
The property is now an interface and supports external audio playback in addition to external video rendering. |
New API: Get YUV frame data |
- |
|
Custom rendering no longer returns data in callbacks. Retrieve data in the rendering thread instead. |
Voice intercom
Change detail |
1.x |
2.x |
Suggested Changes |
Package and class name changes |
IMSLinkVisualPlayerViewController |
LVLiveIntercom |
Update package and class names. |
API change: Initialization |
intercomEncodeParams |
(id_Nonnull)create:(LVMediaAudioHeader)lvMediaAudioHeader; |
Starting from SDK 2.0.0, audio parameters must be provided when creating the intercom instance. |
API change:
|
(void)startIntercom:(IMSLinkVisualIntercomAudioMode)mode; |
|
Starting from SDK 2.0.0, intercom mode is set using a separate API. |
New API: Set intercom mode |
- |
(LVLiveIntercomCode) setLiveIntercomMode:(LVLiveIntercomMode)liveIntercomMode; |
Starting from SDK 2.0.0, DoubleTalkWithLive mode is added: Both app and device simultaneously capture and play audio. The device must support AEC. Audio plays through the device's live stream channel, requiring an active live stream. Use LVLivePlayer.audioFocus() before starting intercom to select the corresponding live stream. |
Command APIs like start()/stop() now return values |
- |
Return value enum: |
Check return values to determine if API operations succeeded. |
New API: Mute |
- |
|
- |
Property change: Gain and voice change |
|
|
Properties changed to methods. |
API removed: Send recorded audio data to device |
(void)sendAudioData:(NSData *_Nullable)data; |
- |
Starting from 2.0.0, the SDK handles data transmission internally. This API is obsolete. |
API change: Set intercom listener |
intercomDelegate |
LVLiveIntercomDelegate |
|
API change: External voice change implementation |
Process audio before calling sendAudioData |
|
Enable external voice change and implement onLiveIntercomAudioData in the external intercom voice change delegate. |