Skip to content

Commit 39a1d57

Browse files
committed
added custom video source push
1 parent c71435c commit 39a1d57

9 files changed

Lines changed: 759 additions & 11 deletions

File tree

iOS/APIExample.xcodeproj/project.pbxproj

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
/* Begin PBXBuildFile section */
1010
0318857924CD667A00C699EB /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0318857824CD667A00C699EB /* SettingsViewController.swift */; };
1111
036C42A924D27AB000A59000 /* CustomVideoSourceMediaIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42A824D27AB000A59000 /* CustomVideoSourceMediaIO.swift */; };
12-
036C42AC24D292A700A59000 /* AgoraCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42AB24D292A700A59000 /* AgoraCamera.swift */; };
12+
036C42AC24D292A700A59000 /* AgoraCameraSourceMediaIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42AB24D292A700A59000 /* AgoraCameraSourceMediaIO.swift */; };
13+
036C42AE24D2950A00A59000 /* CustomVideoSourcePush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42AD24D2950A00A59000 /* CustomVideoSourcePush.swift */; };
14+
036C42B024D2955D00A59000 /* AgoraCameraSourcePush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42AF24D2955D00A59000 /* AgoraCameraSourcePush.swift */; };
15+
036C42B524D2A3C600A59000 /* AgoraMetalRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42B324D2A3C600A59000 /* AgoraMetalRender.swift */; };
16+
036C42B624D2A3C600A59000 /* AgoraMetalShader.metal in Sources */ = {isa = PBXBuildFile; fileRef = 036C42B424D2A3C600A59000 /* AgoraMetalShader.metal */; };
1317
03824D0D24CA822F00E9C047 /* VoiceChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03824D0C24CA822F00E9C047 /* VoiceChanger.swift */; };
1418
03824D0F24CAB61A00E9C047 /* PopMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03824D0E24CAB61A00E9C047 /* PopMenu.swift */; };
1519
03BCEC50244938C500ED7177 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BCEC4F244938C500ED7177 /* BaseViewController.swift */; };
@@ -45,7 +49,11 @@
4549
/* Begin PBXFileReference section */
4650
0318857824CD667A00C699EB /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
4751
036C42A824D27AB000A59000 /* CustomVideoSourceMediaIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomVideoSourceMediaIO.swift; sourceTree = "<group>"; };
48-
036C42AB24D292A700A59000 /* AgoraCamera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraCamera.swift; sourceTree = "<group>"; };
52+
036C42AB24D292A700A59000 /* AgoraCameraSourceMediaIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraCameraSourceMediaIO.swift; sourceTree = "<group>"; };
53+
036C42AD24D2950A00A59000 /* CustomVideoSourcePush.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomVideoSourcePush.swift; sourceTree = "<group>"; };
54+
036C42AF24D2955D00A59000 /* AgoraCameraSourcePush.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraCameraSourcePush.swift; sourceTree = "<group>"; };
55+
036C42B324D2A3C600A59000 /* AgoraMetalRender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraMetalRender.swift; sourceTree = "<group>"; };
56+
036C42B424D2A3C600A59000 /* AgoraMetalShader.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = AgoraMetalShader.metal; sourceTree = "<group>"; };
4957
03824D0C24CA822F00E9C047 /* VoiceChanger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceChanger.swift; sourceTree = "<group>"; };
5058
03824D0E24CAB61A00E9C047 /* PopMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenu.swift; sourceTree = "<group>"; };
5159
03BCEC4F244938C500ED7177 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
@@ -125,7 +133,10 @@
125133
036C42AA24D292A700A59000 /* ExternalVideo */ = {
126134
isa = PBXGroup;
127135
children = (
128-
036C42AB24D292A700A59000 /* AgoraCamera.swift */,
136+
036C42B424D2A3C600A59000 /* AgoraMetalShader.metal */,
137+
036C42B324D2A3C600A59000 /* AgoraMetalRender.swift */,
138+
036C42AF24D2955D00A59000 /* AgoraCameraSourcePush.swift */,
139+
036C42AB24D292A700A59000 /* AgoraCameraSourceMediaIO.swift */,
129140
);
130141
path = ExternalVideo;
131142
sourceTree = "<group>";
@@ -255,6 +266,7 @@
255266
03DF1D7724CFBF4800DF7151 /* CustomAudioSource.swift */,
256267
03DF1D9524D06AEF00DF7151 /* CustomAudioRender.swift */,
257268
036C42A824D27AB000A59000 /* CustomVideoSourceMediaIO.swift */,
269+
036C42AD24D2950A00A59000 /* CustomVideoSourcePush.swift */,
258270
);
259271
path = Advanced;
260272
sourceTree = "<group>";
@@ -404,9 +416,13 @@
404416
03DF1D9624D06AF000DF7151 /* CustomAudioRender.swift in Sources */,
405417
03824D0F24CAB61A00E9C047 /* PopMenu.swift in Sources */,
406418
8407E0942472320800AC5DE8 /* (null) in Sources */,
419+
036C42B524D2A3C600A59000 /* AgoraMetalRender.swift in Sources */,
420+
036C42AE24D2950A00A59000 /* CustomVideoSourcePush.swift in Sources */,
407421
03DF1D9324CFC29700DF7151 /* ExternalAudio.mm in Sources */,
408422
03DF1D7824CFBF4800DF7151 /* CustomAudioSource.swift in Sources */,
409423
A75A56DB24A0603100D0089E /* JoinChannelVideo.swift in Sources */,
424+
036C42B624D2A3C600A59000 /* AgoraMetalShader.metal in Sources */,
425+
036C42B024D2955D00A59000 /* AgoraCameraSourcePush.swift in Sources */,
410426
0318857924CD667A00C699EB /* SettingsViewController.swift in Sources */,
411427
03824D0D24CA822F00E9C047 /* VoiceChanger.swift in Sources */,
412428
A7BD7660247CC6920062A6B3 /* UITypeAlias.swift in Sources */,
@@ -417,7 +433,7 @@
417433
03DF1D9424CFC29700DF7151 /* AudioController.m in Sources */,
418434
036C42A924D27AB000A59000 /* CustomVideoSourceMediaIO.swift in Sources */,
419435
A75A56DC24A0603100D0089E /* JoinChannelAudio.swift in Sources */,
420-
036C42AC24D292A700A59000 /* AgoraCamera.swift in Sources */,
436+
036C42AC24D292A700A59000 /* AgoraCameraSourceMediaIO.swift in Sources */,
421437
A75A56DD24A0603100D0089E /* RTMPStreaming.swift in Sources */,
422438
A7CA48C624553D3500507435 /* VideoView.swift in Sources */,
423439
03DF1D9024CFC29700DF7151 /* AudioWriteToFile.m in Sources */,

iOS/APIExample/Base.lproj/Main.storyboard

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,5 +631,38 @@
631631
</objects>
632632
<point key="canvasLocation" x="118.84057971014494" y="930.80357142857133"/>
633633
</scene>
634+
<!--CustomVideoSourcePush-->
635+
<scene sceneID="aWj-r9-MLv">
636+
<objects>
637+
<viewController storyboardIdentifier="CustomVideoSourcePush" title="Join Channel Video" id="pjq-Wh-4Ys" userLabel="CustomVideoSourcePush" customClass="CustomVideoSourcePush" customModule="APIExample" customModuleProvider="target" sceneMemberID="viewController">
638+
<view key="view" contentMode="scaleToFill" id="9hG-MH-18b">
639+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
640+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
641+
<subviews>
642+
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dhe-dP-S21" customClass="AGEVideoContainer" customModule="AGEVideoLayout">
643+
<rect key="frame" x="0.0" y="44" width="414" height="233"/>
644+
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
645+
<constraints>
646+
<constraint firstAttribute="width" secondItem="Dhe-dP-S21" secondAttribute="height" multiplier="16:9" id="zvH-0k-xq5"/>
647+
</constraints>
648+
</view>
649+
</subviews>
650+
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
651+
<constraints>
652+
<constraint firstItem="sNP-3V-smz" firstAttribute="trailing" secondItem="Dhe-dP-S21" secondAttribute="trailing" id="JFK-11-DEk"/>
653+
<constraint firstItem="Dhe-dP-S21" firstAttribute="top" secondItem="sNP-3V-smz" secondAttribute="top" id="cXZ-dI-sqt"/>
654+
<constraint firstItem="Dhe-dP-S21" firstAttribute="leading" secondItem="sNP-3V-smz" secondAttribute="leading" id="mnA-US-hxx"/>
655+
</constraints>
656+
<viewLayoutGuide key="safeArea" id="sNP-3V-smz"/>
657+
</view>
658+
<navigationItem key="navigationItem" title="Join Channel" id="a4k-1t-KLv"/>
659+
<connections>
660+
<outlet property="container" destination="Dhe-dP-S21" id="vvZ-34-MwV"/>
661+
</connections>
662+
</viewController>
663+
<placeholder placeholderIdentifier="IBFirstResponder" id="Mzb-sw-W1R" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
664+
</objects>
665+
<point key="canvasLocation" x="118.84057971014494" y="930.80357142857133"/>
666+
</scene>
634667
</scenes>
635668
</document>

iOS/APIExample/Common/ExternalVideo/AgoraCamera.swift renamed to iOS/APIExample/Common/ExternalVideo/AgoraCameraSourceMediaIO.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension AVCaptureDevice.Position {
2424
}
2525
}
2626

27-
class AgoraCamera: NSObject {
27+
class AgoraCameraSourceMediaIO: NSObject {
2828
var consumer: AgoraVideoFrameConsumer?
2929

3030
var isFront: Bool {
@@ -76,7 +76,7 @@ class AgoraCamera: NSObject {
7676
}
7777
}
7878

79-
private extension AgoraCamera {
79+
private extension AgoraCameraSourceMediaIO {
8080
func initialize() -> Bool {
8181
let captureSession = AVCaptureSession()
8282
captureSession.usesApplicationAudioSession = false
@@ -125,7 +125,7 @@ private extension AgoraCamera {
125125
}
126126
}
127127

128-
private extension AgoraCamera {
128+
private extension AgoraCameraSourceMediaIO {
129129
func changeCaptureDevice(toPosition position: AVCaptureDevice.Position, ofSession captureSession: AVCaptureSession) {
130130
guard let captureDevice = captureDevice(atPosition: position) else {
131131
return
@@ -158,7 +158,7 @@ private extension AgoraCamera {
158158
}
159159
}
160160

161-
extension AgoraCamera: AgoraVideoSourceProtocol {
161+
extension AgoraCameraSourceMediaIO: AgoraVideoSourceProtocol {
162162
func shouldInitialize() -> Bool {
163163
return initialize()
164164
}
@@ -180,7 +180,7 @@ extension AgoraCamera: AgoraVideoSourceProtocol {
180180
}
181181
}
182182

183-
extension AgoraCamera: AVCaptureVideoDataOutputSampleBufferDelegate {
183+
extension AgoraCameraSourceMediaIO: AVCaptureVideoDataOutputSampleBufferDelegate {
184184
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
185185
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) == kCVReturnSuccess else {
186186
return
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
//
2+
// MyVideoCapture.swift
3+
// Agora-Video-Source
4+
//
5+
// Created by GongYuhua on 2017/4/11.
6+
// Copyright © 2017年 Agora. All rights reserved.
7+
//
8+
9+
import UIKit
10+
import AVFoundation
11+
12+
protocol AgoraCameraSourcePushDelegate {
13+
func myVideoCapture(_ capture: AgoraCameraSourcePush, didOutputSampleBuffer pixelBuffer: CVPixelBuffer, rotation: Int, timeStamp: CMTime)
14+
}
15+
16+
enum Camera: Int {
17+
case front = 1
18+
case back = 0
19+
20+
static func defaultCamera() -> Camera {
21+
return .front
22+
}
23+
24+
func next() -> Camera {
25+
switch self {
26+
case .back: return .front
27+
case .front: return .back
28+
}
29+
}
30+
}
31+
32+
class AgoraCameraSourcePush: NSObject {
33+
34+
fileprivate var delegate: AgoraCameraSourcePushDelegate?
35+
private var videoView: CustomVideoSourcePreview
36+
37+
private var currentCamera = Camera.defaultCamera()
38+
private let captureSession: AVCaptureSession
39+
private let captureQueue: DispatchQueue
40+
private var currentOutput: AVCaptureVideoDataOutput? {
41+
if let outputs = self.captureSession.outputs as? [AVCaptureVideoDataOutput] {
42+
return outputs.first
43+
} else {
44+
return nil
45+
}
46+
}
47+
48+
init(delegate: AgoraCameraSourcePushDelegate?, videoView: CustomVideoSourcePreview) {
49+
self.delegate = delegate
50+
self.videoView = videoView
51+
52+
captureSession = AVCaptureSession()
53+
captureSession.usesApplicationAudioSession = false
54+
55+
let captureOutput = AVCaptureVideoDataOutput()
56+
captureOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]
57+
if captureSession.canAddOutput(captureOutput) {
58+
captureSession.addOutput(captureOutput)
59+
}
60+
61+
captureQueue = DispatchQueue(label: "MyCaptureQueue")
62+
63+
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
64+
videoView.insertCaptureVideoPreviewLayer(previewLayer: previewLayer)
65+
}
66+
67+
deinit {
68+
captureSession.stopRunning()
69+
}
70+
71+
func startCapture(ofCamera camera: Camera) {
72+
guard let currentOutput = currentOutput else {
73+
return
74+
}
75+
76+
currentCamera = camera
77+
currentOutput.setSampleBufferDelegate(self, queue: captureQueue)
78+
79+
captureQueue.async { [weak self] in
80+
guard let strongSelf = self else {
81+
return
82+
}
83+
strongSelf.changeCaptureDevice(toIndex: camera.rawValue, ofSession: strongSelf.captureSession)
84+
strongSelf.captureSession.beginConfiguration()
85+
if strongSelf.captureSession.canSetSessionPreset(AVCaptureSession.Preset.vga640x480) {
86+
strongSelf.captureSession.sessionPreset = AVCaptureSession.Preset.vga640x480
87+
}
88+
strongSelf.captureSession.commitConfiguration()
89+
strongSelf.captureSession.startRunning()
90+
}
91+
}
92+
93+
func stopCapture() {
94+
currentOutput?.setSampleBufferDelegate(nil, queue: nil)
95+
captureQueue.async { [weak self] in
96+
self?.captureSession.stopRunning()
97+
}
98+
}
99+
100+
func switchCamera() {
101+
stopCapture()
102+
currentCamera = currentCamera.next()
103+
startCapture(ofCamera: currentCamera)
104+
}
105+
}
106+
107+
private extension AgoraCameraSourcePush {
108+
func changeCaptureDevice(toIndex index: Int, ofSession captureSession: AVCaptureSession) {
109+
guard let captureDevice = captureDevice(atIndex: index) else {
110+
return
111+
}
112+
113+
let currentInputs = captureSession.inputs as? [AVCaptureDeviceInput]
114+
let currentInput = currentInputs?.first
115+
116+
if let currentInputName = currentInput?.device.localizedName,
117+
currentInputName == captureDevice.uniqueID {
118+
return
119+
}
120+
121+
guard let newInput = try? AVCaptureDeviceInput(device: captureDevice) else {
122+
return
123+
}
124+
125+
captureSession.beginConfiguration()
126+
if let currentInput = currentInput {
127+
captureSession.removeInput(currentInput)
128+
}
129+
if captureSession.canAddInput(newInput) {
130+
captureSession.addInput(newInput)
131+
}
132+
captureSession.commitConfiguration()
133+
}
134+
135+
func captureDevice(atIndex index: Int) -> AVCaptureDevice? {
136+
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back)
137+
let devices = deviceDiscoverySession.devices
138+
139+
let count = devices.count
140+
guard count > 0, index >= 0 else {
141+
return nil
142+
}
143+
144+
let device: AVCaptureDevice
145+
if index >= count {
146+
device = devices.last!
147+
} else {
148+
device = devices[index]
149+
}
150+
151+
return device
152+
}
153+
}
154+
155+
extension AgoraCameraSourcePush: AVCaptureVideoDataOutputSampleBufferDelegate {
156+
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
157+
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
158+
return
159+
}
160+
let time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
161+
DispatchQueue.main.async {[weak self] in
162+
guard let weakSelf = self else {
163+
return
164+
}
165+
166+
weakSelf.delegate?.myVideoCapture(weakSelf, didOutputSampleBuffer: pixelBuffer, rotation: 90, timeStamp: time)
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)