Skip to content

Commit 1e1b3ac

Browse files
author
xianing
committed
finish video filter implementation
1 parent d38c599 commit 1e1b3ac

27 files changed

Lines changed: 818 additions & 143 deletions

Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/SimpleAudioExtension.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,6 @@ public class SimpleAudioExtension extends BaseFragment implements View.OnClickLi
5050
private RtcEngine engine;
5151
private int myUid;
5252
private boolean joined = false;
53-
private static final Integer SAMPLE_RATE = 44100;
54-
private static final Integer SAMPLE_NUM_OF_CHANNEL = 2;
55-
private static final Integer BIT_PER_SAMPLE = 8;
56-
private static final Integer SAMPLES = 441 * 10;
57-
private static final String AUDIO_FILE = "output.raw";
5853
private SeekBar record;
5954

6055

iOS/APIExample.xcodeproj/project.pbxproj

Lines changed: 90 additions & 58 deletions
Large diffs are not rendered by default.

iOS/APIExample/APIExample.entitlements

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
<plist version="1.0">
44
<dict>
55
<key>com.apple.security.application-groups</key>
6-
<array>
7-
<string>group.sharescreen.io</string>
8-
</array>
6+
<array/>
97
</dict>
108
</plist>

iOS/APIExample/Base.lproj/Main.storyboard

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="HWc-eV-zrm">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="HWc-eV-zrm">
33
<device id="retina6_1" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
77
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
88
<capability name="System colors in document resources" minToolsVersion="11.0"/>
99
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -153,7 +153,7 @@
153153
<color key="backgroundColor" red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
154154
<prototypes>
155155
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="SliderCell" id="G0g-5t-oRK" customClass="SettingsSliderCell" customModule="APIExample" customModuleProvider="target">
156-
<rect key="frame" x="0.0" y="55.5" width="414" height="54.5"/>
156+
<rect key="frame" x="0.0" y="49.5" width="414" height="54.5"/>
157157
<autoresizingMask key="autoresizingMask"/>
158158
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="G0g-5t-oRK" id="8Yv-dJ-Q4d">
159159
<rect key="frame" x="0.0" y="0.0" width="414" height="54.5"/>
@@ -176,7 +176,7 @@
176176
</slider>
177177
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GRE-S2-EUw">
178178
<rect key="frame" x="318" y="0.0" width="56" height="44.5"/>
179-
<fontDescription key="fontDescription" type="system" pointSize="15"/>
179+
<fontDescription key="fontDescription" type="system" pointSize="12"/>
180180
<nil key="textColor"/>
181181
<nil key="highlightedColor"/>
182182
</label>
@@ -203,7 +203,7 @@
203203
</connections>
204204
</tableViewCell>
205205
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="LabelCell" id="YOS-Tx-FSj" userLabel="LabelCell" customClass="SettingsLabelCell" customModule="APIExample" customModuleProvider="target">
206-
<rect key="frame" x="0.0" y="110" width="414" height="54.5"/>
206+
<rect key="frame" x="0.0" y="104" width="414" height="54.5"/>
207207
<autoresizingMask key="autoresizingMask"/>
208208
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="YOS-Tx-FSj" id="Ezh-hI-UFS">
209209
<rect key="frame" x="0.0" y="0.0" width="414" height="54.5"/>
@@ -245,7 +245,7 @@
245245
</connections>
246246
</tableViewCell>
247247
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="SelectCell" id="wPb-Hp-Lzk" customClass="SettingsSelectCell" customModule="APIExample" customModuleProvider="target">
248-
<rect key="frame" x="0.0" y="164.5" width="414" height="54.5"/>
248+
<rect key="frame" x="0.0" y="158.5" width="414" height="54.5"/>
249249
<autoresizingMask key="autoresizingMask"/>
250250
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="wPb-Hp-Lzk" id="xjH-Qx-gt4">
251251
<rect key="frame" x="0.0" y="0.0" width="414" height="54.5"/>

iOS/APIExample/Examples/Advanced/SimpleAudioFilter/Base.lproj/SimpleAudioFilter.storyboard renamed to iOS/APIExample/Examples/Advanced/SimpleFilter/Base.lproj/SimpleFilter.storyboard

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
1010
</dependencies>
1111
<scenes>
12-
<!--Simple Audio Filter Entry-->
12+
<!--Simple Filter Entry-->
1313
<scene sceneID="x72-3b-GCg">
1414
<objects>
15-
<viewController storyboardIdentifier="EntryViewController" id="O0d-ef-mTa" customClass="SimpleAudioFilterEntry" customModule="APIExample" customModuleProvider="target" sceneMemberID="viewController">
15+
<viewController storyboardIdentifier="EntryViewController" id="O0d-ef-mTa" customClass="SimpleFilterEntry" customModule="APIExample" customModuleProvider="target" sceneMemberID="viewController">
1616
<view key="view" contentMode="scaleToFill" id="iRI-aA-06F">
1717
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
1818
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -79,10 +79,10 @@
7979
</objects>
8080
<point key="canvasLocation" x="2246" y="931"/>
8181
</scene>
82-
<!--Simple Audio Filter-->
82+
<!--Simple Filter-->
8383
<scene sceneID="Hul-Ue-Iim">
8484
<objects>
85-
<viewController storyboardIdentifier="SimpleAudioFilter" title="Simple Audio Filter" id="jxp-ZN-2yG" customClass="SimpleAudioFilterMain" customModule="APIExample" sceneMemberID="viewController">
85+
<viewController storyboardIdentifier="SimpleFilter" title="Simple Filter" id="jxp-ZN-2yG" userLabel="Simple Filter" customClass="SimpleFilterMain" customModule="APIExample" sceneMemberID="viewController">
8686
<view key="view" contentMode="scaleToFill" id="ZdT-KJ-IC4">
8787
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
8888
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>

iOS/APIExample/Examples/Advanced/SimpleAudioFilter/SimpleAudioFilter.swift renamed to iOS/APIExample/Examples/Advanced/SimpleFilter/SimpleFilter.swift

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
import UIKit
1010
import AgoraRtcKit
1111
import AGEVideoLayout
12-
import SimpleAudioFilter
12+
import SimpleFilter
1313

14-
class SimpleAudioFilterEntry : UIViewController
14+
class SimpleFilterEntry : UIViewController
1515
{
1616
@IBOutlet weak var joinButton: AGButton!
1717
@IBOutlet weak var channelTextField: AGTextField!
18-
let identifier = "SimpleAudioFilter"
18+
let identifier = "SimpleFilter"
1919

2020
override func viewDidLoad() {
2121
super.viewDidLoad()
@@ -36,12 +36,14 @@ class SimpleAudioFilterEntry : UIViewController
3636

3737
}
3838

39-
class SimpleAudioFilterMain: BaseViewController {
39+
class SimpleFilterMain: BaseViewController {
4040

4141
var agoraKit: AgoraRtcEngineKit!
4242
@IBOutlet weak var container: AGEVideoContainer!
43-
var audioViews: [UInt:VideoView] = [:]
44-
let FILTER_NAME = "VolumeChange"
43+
var localVideo = Bundle.loadVideoView(type: .local, audioOnly: false)
44+
var remoteVideo = Bundle.loadVideoView(type: .remote, audioOnly: false)
45+
let AUDIO_FILTER_NAME = "VolumeChange"
46+
let VIDEO_FILTER_NAME = "Watermark"
4547

4648
// indicate if current instance has joined channel
4749
var isJoined: Bool = false
@@ -51,6 +53,10 @@ class SimpleAudioFilterMain: BaseViewController {
5153

5254
guard let channelName = configs["channelName"] as? String
5355
else { return }
56+
// layout render view
57+
localVideo.setPlaceholder(text: "Local Host".localized)
58+
remoteVideo.setPlaceholder(text: "Remote Host".localized)
59+
container.layoutStream(views: [localVideo, remoteVideo])
5460

5561
// set up agora instance when view loaded
5662
let config = AgoraRtcEngineConfig()
@@ -61,38 +67,46 @@ class SimpleAudioFilterMain: BaseViewController {
6167
config.audioScenario = .default
6268

6369
// set audio filter extension
64-
config.mediaFilterExtensions = [SimpleAudioFilterManager()]
70+
config.mediaFilterExtensions = [SimpleFilterManager()]
6571
config.eventDelegate = self
6672

6773
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
6874

69-
agoraKit.enableExtension(withVendor: SimpleAudioFilterManager.vendorName(), extension: FILTER_NAME, enabled: true)
75+
agoraKit.enableExtension(withVendor: SimpleFilterManager.vendorName(), extension: VIDEO_FILTER_NAME, enabled: true)
76+
agoraKit.enableExtension(withVendor: SimpleFilterManager.vendorName(), extension: AUDIO_FILTER_NAME, enabled: true)
7077

7178
agoraKit.setLogFile(LogUtils.sdkLogPath())
7279

7380
// make myself a broadcaster
7481
agoraKit.setClientRole(.broadcaster)
7582

7683
// disable video module
77-
agoraKit.disableVideo()
84+
agoraKit.enableVideo()
85+
86+
// set up local video to render your local camera preview
87+
let videoCanvas = AgoraRtcVideoCanvas()
88+
videoCanvas.uid = 0
89+
// the view to be binded
90+
videoCanvas.view = localVideo.videoView
91+
videoCanvas.renderMode = .hidden
92+
agoraKit.setupLocalVideo(videoCanvas)
93+
// you have to call startPreview to see local video
94+
agoraKit.startPreview()
7895

7996
// set audio profile
8097
agoraKit.setAudioProfile(.default)
8198

8299
// Set audio route to speaker
83100
agoraKit.setDefaultAudioRouteToSpeakerphone(true)
84101

85-
// enable volume indicator
86-
// agoraKit.enableAudioVolumeIndication(200, smooth: 3)
87-
88102
// start joining channel
89103
// 1. Users can only see each other after they join the
90104
// same channel successfully using the same app id.
91105
// 2. If app certificate is turned on at dashboard, token is needed
92106
// when joining channel. The channel name and uid used to calculate
93107
// the token has to match the ones used for channel join
94108
let option = AgoraRtcChannelMediaOptions()
95-
option.publishCameraTrack = .of(false)
109+
option.publishCameraTrack = .of(true)
96110
option.clientRoleType = .of((Int32)(AgoraClientRole.broadcaster.rawValue))
97111

98112
let result = agoraKit.joinChannel(byToken: KeyCenter.Token, channelId: channelName, uid: 0, mediaOptions: option)
@@ -116,19 +130,15 @@ class SimpleAudioFilterMain: BaseViewController {
116130
}
117131
}
118132

119-
func sortedViews() -> [VideoView] {
120-
return Array(audioViews.values).sorted(by: { $0.uid < $1.uid })
121-
}
122-
123133
@IBAction func onChangeRecordingVolume(_ sender:UISlider){
124134
let value:Int = Int(sender.value)
125135
print("adjustRecordingSignalVolume \(value)")
126-
agoraKit.setExtensionPropertyWithVendor(SimpleAudioFilterManager.vendorName(), extension: FILTER_NAME, key: "volume", value: String(value))
136+
agoraKit.setExtensionPropertyWithVendor(SimpleFilterManager.vendorName(), extension: AUDIO_FILTER_NAME, key: "volume", value: String(value))
127137
}
128138
}
129139

130140
/// agora rtc engine delegate events
131-
extension SimpleAudioFilterMain: AgoraRtcEngineDelegate {
141+
extension SimpleFilterMain: AgoraRtcEngineDelegate {
132142
/// callback when warning occured for agora sdk, warning can usually be ignored, still it's nice to check out
133143
/// what is happening
134144
/// Warning code description can be found at:
@@ -153,27 +163,23 @@ extension SimpleAudioFilterMain: AgoraRtcEngineDelegate {
153163
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
154164
self.isJoined = true
155165
LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info)
156-
157-
//set up local audio view, this view will not show video but just a placeholder
158-
let view = Bundle.loadVideoView(type: .local, audioOnly: true)
159-
self.audioViews[0] = view
160-
view.setPlaceholder(text: self.getAudioLabel(uid: uid, isLocal: true))
161-
self.container.layoutStream3x2(views: self.sortedViews())
162166
}
163167

164168
/// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event
165169
/// @param uid uid of remote joined user
166170
/// @param elapsed time elapse since current sdk instance join the channel in ms
167171
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
168172
LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info)
169-
170-
//set up remote audio view, this view will not show video but just a placeholder
171-
let view = Bundle.loadVideoView(type: .remote, audioOnly: true)
172-
view.uid = uid
173-
self.audioViews[uid] = view
174-
view.setPlaceholder(text: self.getAudioLabel(uid: uid, isLocal: false))
175-
self.container.layoutStream3x2(views: sortedViews())
176-
self.container.reload(level: 0, animated: true)
173+
174+
// Only one remote video view is available for this
175+
// tutorial. Here we check if there exists a surface
176+
// view tagged as this uid.
177+
let videoCanvas = AgoraRtcVideoCanvas()
178+
videoCanvas.uid = uid
179+
// the view to be binded
180+
videoCanvas.view = remoteVideo.videoView
181+
videoCanvas.renderMode = .hidden
182+
agoraKit.setupRemoteVideo(videoCanvas)
177183
}
178184

179185
/// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event
@@ -183,10 +189,15 @@ extension SimpleAudioFilterMain: AgoraRtcEngineDelegate {
183189
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
184190
LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info)
185191

186-
//remove remote audio view
187-
self.audioViews.removeValue(forKey: uid)
188-
self.container.layoutStream3x2(views: sortedViews())
189-
self.container.reload(level: 0, animated: true)
192+
// to unlink your view from sdk, so that your view reference will be released
193+
// note the video will stay at its last frame, to completely remove it
194+
// you will need to remove the EAGL sublayer from your binded view
195+
let videoCanvas = AgoraRtcVideoCanvas()
196+
videoCanvas.uid = uid
197+
// the view to be binded
198+
videoCanvas.view = nil
199+
videoCanvas.renderMode = .hidden
200+
agoraKit.setupRemoteVideo(videoCanvas)
190201
}
191202

192203
/// Reports which users are speaking, the speakers' volumes, and whether the local user is speaking.
@@ -203,23 +214,35 @@ extension SimpleAudioFilterMain: AgoraRtcEngineDelegate {
203214
/// Reports the statistics of the current call. The SDK triggers this callback once every two seconds after the user joins the channel.
204215
/// @param stats stats struct
205216
func rtcEngine(_ engine: AgoraRtcEngineKit, reportRtcStats stats: AgoraChannelStats) {
206-
audioViews[0]?.statsInfo?.updateChannelStats(stats)
217+
localVideo.statsInfo?.updateChannelStats(stats)
218+
}
219+
220+
/// Reports the statistics of the uploading local video streams once every two seconds.
221+
/// @param stats stats struct
222+
func rtcEngine(_ engine: AgoraRtcEngineKit, localVideoStats stats: AgoraRtcLocalVideoStats) {
223+
localVideo.statsInfo?.updateLocalVideoStats(stats)
207224
}
208225

209226
/// Reports the statistics of the uploading local audio streams once every two seconds.
210227
/// @param stats stats struct
211228
func rtcEngine(_ engine: AgoraRtcEngineKit, localAudioStats stats: AgoraRtcLocalAudioStats) {
212-
audioViews[0]?.statsInfo?.updateLocalAudioStats(stats)
229+
localVideo.statsInfo?.updateLocalAudioStats(stats)
230+
}
231+
232+
/// Reports the statistics of the video stream from each remote user/host.
233+
/// @param stats stats struct
234+
func rtcEngine(_ engine: AgoraRtcEngineKit, remoteVideoStats stats: AgoraRtcRemoteVideoStats) {
235+
remoteVideo.statsInfo?.updateVideoStats(stats)
213236
}
214237

215238
/// Reports the statistics of the audio stream from each remote user/host.
216239
/// @param stats stats struct for current call statistics
217240
func rtcEngine(_ engine: AgoraRtcEngineKit, remoteAudioStats stats: AgoraRtcRemoteAudioStats) {
218-
audioViews[stats.uid]?.statsInfo?.updateAudioStats(stats)
241+
remoteVideo.statsInfo?.updateAudioStats(stats)
219242
}
220243
}
221244

222-
extension SimpleAudioFilterMain: AgoraMediaFilterEventDelegate{
245+
extension SimpleFilterMain: AgoraMediaFilterEventDelegate{
223246
func onEvent(_ vendor: String?, extension: String?, key: String?, json_value: String?) {
224247
LogUtils.log(message: "onEvent: \(String(describing: key)) \(String(describing: json_value))", level: .info)
225248
}

iOS/APIExample/Examples/Advanced/SimpleAudioFilter/en.lproj/SimpleAudioFilter.strings renamed to iOS/APIExample/Examples/Advanced/SimpleFilter/en.lproj/SimpleFilter.strings

File renamed without changes.

iOS/APIExample/Examples/Advanced/SimpleAudioFilter/zh-Hans.lproj/SimpleAudioFilter.strings renamed to iOS/APIExample/Examples/Advanced/SimpleFilter/zh-Hans.lproj/SimpleFilter.strings

File renamed without changes.

iOS/APIExample/Examples/Basic/JoinChannelAudio/Base.lproj/JoinChannelAudio.storyboard

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33
<device id="retina5_9" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
77
<capability name="Named colors" minToolsVersion="9.0"/>
88
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
99
<capability name="System colors in document resources" minToolsVersion="11.0"/>

0 commit comments

Comments
 (0)