From 8a272b2fd08f85378f00cebbbe32f9f328a275d7 Mon Sep 17 00:00:00 2001 From: shogo4405 Date: Tue, 10 Oct 2023 21:54:03 +0900 Subject: [PATCH] Update NetStreamSwitcher for Example app. --- Examples/iOS/IngestViewController.swift | 2 +- Examples/iOS/NetStreamSwitcher.swift | 70 ++++++++++++++++-- Examples/iOS/PlaybackViewController.swift | 87 ++++------------------- Sources/Codec/VideoCodec.swift | 1 + 4 files changed, 78 insertions(+), 82 deletions(-) diff --git a/Examples/iOS/IngestViewController.swift b/Examples/iOS/IngestViewController.swift index be355b0af..22cec57e2 100644 --- a/Examples/iOS/IngestViewController.swift +++ b/Examples/iOS/IngestViewController.swift @@ -183,7 +183,7 @@ final class IngestViewController: UIViewController { publish.setTitle("●", for: []) } else { UIApplication.shared.isIdleTimerDisabled = true - netStreamSwitcher.open() + netStreamSwitcher.open(.ingest) publish.setTitle("■", for: []) } publish.isSelected.toggle() diff --git a/Examples/iOS/NetStreamSwitcher.swift b/Examples/iOS/NetStreamSwitcher.swift index 0e7c7fb11..399c66167 100644 --- a/Examples/iOS/NetStreamSwitcher.swift +++ b/Examples/iOS/NetStreamSwitcher.swift @@ -1,3 +1,4 @@ +import AVFoundation import Foundation import HaishinKit import SRTHaishinKit @@ -23,6 +24,11 @@ final class NetStreamSwitcher { } } + enum Method { + case ingest + case playback + } + var uri = "" { didSet { if uri.contains("srt://") { @@ -39,14 +45,17 @@ final class NetStreamSwitcher { } private var retryCount = 0 private var connection: Any? + private var method: Method = .ingest private(set) var stream: NetStream = .init() - func open() { + func open(_ method: Method) { + self.method = method switch mode { case .rtmp: - guard let connection = connection as? RTMPConnection else { + guard let connection = connection as? RTMPConnection, let stream = stream as? RTMPStream else { return } + stream.delegate = self connection.addEventListener(.rtmpStatus, selector: #selector(rtmpStatusHandler), observer: self) connection.addEventListener(.ioError, selector: #selector(rtmpErrorHandler), observer: self) connection.connect(uri) @@ -54,8 +63,14 @@ final class NetStreamSwitcher { guard let connection = connection as? SRTConnection, let stream = stream as? SRTStream else { return } + stream.delegate = self connection.open(URL(string: uri)) - stream.publish("") + switch method { + case .playback: + stream.play() + case .ingest: + stream.publish() + } } } @@ -69,8 +84,11 @@ final class NetStreamSwitcher { connection.removeEventListener(.rtmpStatus, selector: #selector(rtmpStatusHandler), observer: self) connection.removeEventListener(.ioError, selector: #selector(rtmpErrorHandler), observer: self) case .srt: - (stream as? SRTStream)?.close() - (connection as? SRTConnection)?.close() + guard let connection = connection as? SRTConnection, let stream = stream as? SRTStream else { + return + } + stream.close() + connection.close() } } @@ -84,7 +102,12 @@ final class NetStreamSwitcher { switch code { case RTMPConnection.Code.connectSuccess.rawValue: retryCount = 0 - (stream as? RTMPStream)?.publish(Preference.defaultInstance.streamName!) + switch method { + case .playback: + (stream as? RTMPStream)?.play(Preference.defaultInstance.streamName!) + case .ingest: + (stream as? RTMPStream)?.publish(Preference.defaultInstance.streamName!) + } case RTMPConnection.Code.connectFailed.rawValue, RTMPConnection.Code.connectClosed.rawValue: guard retryCount <= NetStreamSwitcher.maxRetryCount else { return @@ -103,3 +126,38 @@ final class NetStreamSwitcher { (connection as? RTMPConnection)?.connect(Preference.defaultInstance.uri!) } } + +extension NetStreamSwitcher: NetStreamDelegate { + // MARK: NetStreamDelegate + /// Tells the receiver to playback an audio packet incoming. + func stream(_ stream: NetStream, didOutput audio: AVAudioBuffer, when: AVAudioTime) { + } + + /// Tells the receiver to playback a video packet incoming. + func stream(_ stream: NetStream, didOutput video: CMSampleBuffer) { + } + + #if os(iOS) || os(tvOS) + /// Tells the receiver to session was interrupted. + @available(tvOS 17.0, *) + func stream(_ stream: NetStream, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?) { + } + + /// Tells the receiver to session interrupted ended. + @available(tvOS 17.0, *) + func stream(_ stream: NetStream, sessionInterruptionEnded session: AVCaptureSession) { + } + + #endif + /// Tells the receiver to video codec error occured. + func stream(_ stream: NetStream, videoCodecErrorOccurred error: VideoCodec.Error) { + } + + /// Tells the receiver to audio codec error occured. + func stream(_ stream: NetStream, audioCodecErrorOccurred error: HaishinKit.AudioCodec.Error) { + } + + /// Tells the receiver to the stream opened. + func streamDidOpen(_ stream: NetStream) { + } +} diff --git a/Examples/iOS/PlaybackViewController.swift b/Examples/iOS/PlaybackViewController.swift index 84fcf343d..eb3edff4c 100644 --- a/Examples/iOS/PlaybackViewController.swift +++ b/Examples/iOS/PlaybackViewController.swift @@ -5,24 +5,18 @@ import HaishinKit import UIKit final class PlaybackViewController: UIViewController { - private static let maxRetryCount: Int = 5 - @IBOutlet private weak var playbackButton: UIButton! - private var rtmpConnection = RTMPConnection() - private var rtmpStream: RTMPStream! - private var retryCount: Int = 0 - private var pictureInPictureController: AVPictureInPictureController? - - override func viewDidLoad() { - super.viewDidLoad() - rtmpStream = RTMPStream(connection: rtmpConnection) - rtmpStream.delegate = self + private let netStreamSwitcher: NetStreamSwitcher = .init() + private var stream: NetStream { + return netStreamSwitcher.stream } + private var pictureInPictureController: AVPictureInPictureController? override func viewWillAppear(_ animated: Bool) { logger.info("viewWillAppear") super.viewWillAppear(animated) - (view as? (any NetStreamDrawable))?.attachStream(rtmpStream) + netStreamSwitcher.uri = Preference.defaultInstance.uri ?? "" + (view as? (any NetStreamDrawable))?.attachStream(stream) if #available(iOS 15.0, *), let layer = view.layer as? AVSampleBufferDisplayLayer { pictureInPictureController = AVPictureInPictureController(contentSource: .init(sampleBufferDisplayLayer: layer, playbackDelegate: self)) } @@ -40,62 +34,29 @@ final class PlaybackViewController: UIViewController { @IBAction func didPlaybackButtonTap(_ button: UIButton) { if button.isSelected { UIApplication.shared.isIdleTimerDisabled = false - rtmpConnection.close() - rtmpConnection.removeEventListener(.rtmpStatus, selector: #selector(rtmpStatusHandler), observer: self) - rtmpConnection.removeEventListener(.ioError, selector: #selector(rtmpErrorHandler), observer: self) + netStreamSwitcher.close() button.setTitle("●", for: []) } else { UIApplication.shared.isIdleTimerDisabled = true - rtmpConnection.addEventListener(.rtmpStatus, selector: #selector(rtmpStatusHandler), observer: self) - rtmpConnection.addEventListener(.ioError, selector: #selector(rtmpErrorHandler), observer: self) - rtmpConnection.connect(Preference.defaultInstance.uri!) + netStreamSwitcher.open(.playback) button.setTitle("■", for: []) } button.isSelected.toggle() } @objc - private func rtmpStatusHandler(_ notification: Notification) { - let e = Event.from(notification) - guard let data = e.data as? ASObject, let code = data["code"] as? String else { - return - } - logger.info(code) - switch code { - case RTMPConnection.Code.connectSuccess.rawValue: - retryCount = 0 - rtmpStream.play(Preference.defaultInstance.streamName!) - case RTMPConnection.Code.connectFailed.rawValue, RTMPConnection.Code.connectClosed.rawValue: - guard retryCount <= PlaybackViewController.maxRetryCount else { - return - } - Thread.sleep(forTimeInterval: pow(2.0, Double(retryCount))) - rtmpConnection.connect(Preference.defaultInstance.uri!) - retryCount += 1 - default: - break - } - } - - @objc - private func rtmpErrorHandler(_ notification: Notification) { - logger.error(notification) - rtmpConnection.connect(Preference.defaultInstance.uri!) - } - - @objc - private func didEnterBackground(_ notification: Notification) { + private func didBecomeActive(_ notification: Notification) { logger.info(notification) if pictureInPictureController?.isPictureInPictureActive == false { - rtmpStream.receiveVideo = false + (stream as? RTMPStream)?.receiveVideo = true } } @objc - private func didBecomeActive(_ notification: Notification) { + private func didEnterBackground(_ notification: Notification) { logger.info(notification) if pictureInPictureController?.isPictureInPictureActive == false { - rtmpStream.receiveVideo = true + (stream as? RTMPStream)?.receiveVideo = false } } } @@ -120,27 +81,3 @@ extension PlaybackViewController: AVPictureInPictureSampleBufferPlaybackDelegate completionHandler() } } - -extension PlaybackViewController: NetStreamDelegate { - // MARK: NetStreamDelegate - func stream(_ stream: NetStream, didOutput audio: AVAudioBuffer, when: AVAudioTime) { - } - - func stream(_ stream: NetStream, didOutput video: CMSampleBuffer) { - } - - func stream(_ stream: NetStream, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?) { - } - - func stream(_ stream: NetStream, sessionInterruptionEnded session: AVCaptureSession) { - } - - func stream(_ stream: NetStream, videoCodecErrorOccurred error: VideoCodec.Error) { - } - - func stream(_ stream: NetStream, audioCodecErrorOccurred error: HaishinKit.AudioCodec.Error) { - } - - func streamDidOpen(_ stream: NetStream) { - } -} diff --git a/Sources/Codec/VideoCodec.swift b/Sources/Codec/VideoCodec.swift index 368c67e50..db7d2c740 100644 --- a/Sources/Codec/VideoCodec.swift +++ b/Sources/Codec/VideoCodec.swift @@ -119,6 +119,7 @@ public final class VideoCodec { } func appendSampleBuffer(_ sampleBuffer: CMSampleBuffer) { + inputFormat = sampleBuffer.formatDescription guard isRunning.value else { return }