- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在制作一个使用 AVPlayer(单个播放/暂停按钮)流式传输实时音频的应用程序。应用程序工作正常,但是由于这是实时音频,如果出现轻微的网络问题,音频会停止并且播放器不会继续播放,即使按下播放/暂停按钮。恢复应用程序的唯一方法是终止它并每次重启。
有人可以建议 AVPlayer 的任何东西或替代品,或者我如何缓冲音频以便在连接丢失时播放器会同时缓冲它?我是IOS编程的新手。赞赏
最佳答案
我遇到了同样的问题。答案是创建一个错误委托(delegate),每次播放器停止时都会启动一个选择器(当网络连接中断或流未正确加载时,错误会发生变化):
这是我的代表,就在我的 RadioPlayer 类之外和上方:
protocol errorMessageDelegate {
func errorMessageChanged(newVal: String)
}
protocol sharedInstanceDelegate {
func sharedInstanceChanged(newVal: Bool)
}
现在我的课:
import Foundation
import AVFoundation
import UIKit
class RadioPlayer : NSObject {
static let sharedInstance = RadioPlayer()
var instanceDelegate:sharedInstanceDelegate? = nil
var sharedInstanceBool = false {
didSet {
if let delegate = self.instanceDelegate {
delegate.sharedInstanceChanged(self.sharedInstanceBool)
}
}
}
private var player = AVPlayer(URL: NSURL(string: Globals.radioURL)!)
private var playerItem = AVPlayerItem?()
private var isPlaying = false
var errorDelegate:errorMessageDelegate? = nil
var errorMessage = "" {
didSet {
if let delegate = self.errorDelegate {
delegate.errorMessageChanged(self.errorMessage)
}
}
}
override init() {
super.init()
errorMessage = ""
let asset: AVURLAsset = AVURLAsset(URL: NSURL(string: Globals.radioURL)!, options: nil)
let statusKey = "tracks"
asset.loadValuesAsynchronouslyForKeys([statusKey], completionHandler: {
var error: NSError? = nil
dispatch_async(dispatch_get_main_queue(), {
let status: AVKeyValueStatus = asset.statusOfValueForKey(statusKey, error: &error)
if status == AVKeyValueStatus.Loaded{
let playerItem = AVPlayerItem(asset: asset)
self.player = AVPlayer(playerItem: playerItem)
self.sharedInstanceBool = true
} else {
self.errorMessage = error!.localizedDescription
print(error!)
}
})
})
NSNotificationCenter.defaultCenter().addObserverForName(
AVPlayerItemFailedToPlayToEndTimeNotification,
object: nil,
queue: nil,
usingBlock: { notification in
print("Status: Failed to continue")
self.errorMessage = "Stream was interrupted"
})
print("Initializing new player")
}
func resetPlayer() {
errorMessage = ""
let asset: AVURLAsset = AVURLAsset(URL: NSURL(string: Globals.radioURL)!, options: nil)
let statusKey = "tracks"
asset.loadValuesAsynchronouslyForKeys([statusKey], completionHandler: {
var error: NSError? = nil
dispatch_async(dispatch_get_main_queue(), {
let status: AVKeyValueStatus = asset.statusOfValueForKey(statusKey, error: &error)
if status == AVKeyValueStatus.Loaded{
let playerItem = AVPlayerItem(asset: asset)
//playerItem.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: &ItemStatusContext)
self.player = AVPlayer(playerItem: playerItem)
self.sharedInstanceBool = true
} else {
self.errorMessage = error!.localizedDescription
print(error!)
}
})
})
}
func bufferFull() -> Bool {
return bufferAvailableSeconds() > 45.0
}
func bufferAvailableSeconds() -> NSTimeInterval {
// Check if there is a player instance
if ((player.currentItem) != nil) {
// Get current AVPlayerItem
let item: AVPlayerItem = player.currentItem!
if (item.status == AVPlayerItemStatus.ReadyToPlay) {
let timeRangeArray: NSArray = item.loadedTimeRanges
if timeRangeArray.count < 1 { return(CMTimeGetSeconds(kCMTimeInvalid)) }
let aTimeRange: CMTimeRange = timeRangeArray.objectAtIndex(0).CMTimeRangeValue
//let startTime = CMTimeGetSeconds(aTimeRange.end)
let loadedDuration = CMTimeGetSeconds(aTimeRange.duration)
return (NSTimeInterval)(loadedDuration);
}
else {
return(CMTimeGetSeconds(kCMTimeInvalid))
}
}
else {
return(CMTimeGetSeconds(kCMTimeInvalid))
}
}
func play() {
player.play()
isPlaying = true
print("Radio is \(isPlaying ? "" : "not ")playing")
}
func pause() {
player.pause()
isPlaying = false
print("Radio is \(isPlaying ? "" : "not ")playing")
}
func currentlyPlaying() -> Bool {
return isPlaying
}
}
现在,在 RadioViewController 中:
import UIKit
import AVFoundation
class RadioViewController: UIViewController, errorMessageDelegate, sharedInstanceDelegate {
// MARK: Properties
var firstErrorSkip = true
var firstInstanceSkip = true
@IBOutlet weak var listenLabel: UILabel!
@IBOutlet weak var radioSwitch: UIImageView!
@IBAction func back(sender: AnyObject) {
print("Dismissing radio view")
if let navigationController = self.navigationController
{
navigationController.popViewControllerAnimated(true)
}
}
@IBAction func switched(sender: AnyObject) {
toggle()
}
override func viewDidLoad() {
super.viewDidLoad()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}
RadioPlayer.sharedInstance.errorDelegate = self
RadioPlayer.sharedInstance.instanceDelegate = self
if RadioPlayer.sharedInstance.currentlyPlaying() {
radioSwitch.image = UIImage(named: "Radio_Switch_Active")
listenLabel.text = "Click to Pause Radio Stream:"
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func toggle() {
if RadioPlayer.sharedInstance.currentlyPlaying() {
pauseRadio()
} else {
playRadio()
}
}
func playRadio() {
firstErrorSkip = false
firstInstanceSkip = false
if RadioPlayer.sharedInstance.errorMessage != "" || RadioPlayer.sharedInstance.bufferFull() {
resetStream()
} else {
radioSwitch.image = UIImage(named: "Radio_Switch_Active")
listenLabel.text = "Click to Pause Radio Stream:"
RadioPlayer.sharedInstance.play()
}
}
func pauseRadio() {
RadioPlayer.sharedInstance.pause()
radioSwitch.image = UIImage(named: "Radio_Switch_Inactive")
listenLabel.text = "Click to Play Radio Stream:"
}
func resetStream() {
print("Reloading interrupted stream");
RadioPlayer.sharedInstance.resetPlayer()
//RadioPlayer.sharedInstance = RadioPlayer();
RadioPlayer.sharedInstance.errorDelegate = self
RadioPlayer.sharedInstance.instanceDelegate = self
if RadioPlayer.sharedInstance.bufferFull() {
radioSwitch.image = UIImage(named: "Radio_Switch_Active")
listenLabel.text = "Click to Pause Radio Stream:"
RadioPlayer.sharedInstance.play()
} else {
playRadio()
}
}
func errorMessageChanged(newVal: String) {
if !firstErrorSkip {
print("Error changed to '\(newVal)'")
if RadioPlayer.sharedInstance.errorMessage != "" {
print("Showing Error Message")
let alertController = UIAlertController(title: "Stream Failure", message: RadioPlayer.sharedInstance.errorMessage, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
pauseRadio()
}
} else {
print("Skipping first init")
firstErrorSkip = false
}
}
func sharedInstanceChanged(newVal: Bool) {
if !firstInstanceSkip {
print("Detected New Instance")
if newVal {
RadioPlayer.sharedInstance.play()
}
} else {
firstInstanceSkip = false
}
}
}
关于swift - 直播流中的 AVPlayer 音频缓冲,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32614060/
我正在使用 MediaPlayer 在我的应用程序中播放在线 mp3 文件中的一些声音。 但是,即使在播放完成后,我也会在控制台中收到一行又一行的回调和缓冲。 10-24 08:08:48.467
我有一个简单的多边形。 dfr p = st_polygon(list(as.matrix(dfr))) > pbuf = st_buffer(p, .4) > plot(pbuf) > plot(
这可能又是一些愚蠢的问题,也许这确实是我所缺少的东西,但我很难让 glMultiDrawArrays 在 OpenGL4 中工作。 我发现了很多这样的解释: for (int i = 0; i #i
这仅仅是根据网络速度调整预缓冲内容量的问题吗?你是否在一开始就为此调整一次,每秒......? 或者它更复杂 - 对您的网络速度记录历史进行采样并取平均值/中值并对其进行调整? 最佳答案 您的第二段总
嗨,我正在使用 FFmpeg Autogen C#。当我使用 mkv 输出作为文件并使用 h264 rtsp 流作为输入时,一切正常。编解码器是 libx264 ffmpeg.avio_open(
我需要多次遍历几个文本文件的行。目前这是通过多个 with open("file.txt") as f: for line in f: # do something 虽然性能还
昨天给同学们写了一个xinetd小练习:做一个反向回显程序。 为了学习新东西,我尝试实现一个 Haskell 解决方案。琐碎的main = forever $ interact reverse不起作用
我正在阅读《实时渲染第三版》中的遮挡剔除部分,但我无法理解它是如何工作的。一些问题: “Z 金字塔”有何贡献?为什么我们需要多种分辨率的 Z 缓冲区?在书中,它的显示如下(左侧): 八叉树结构与用于一
我通过串行端口与设备通信。 我已成功获取 InputStream 并读取设备发送的内容。 但问题是,我根本不知道何时停止阅读并继续执行另一项任务。 这是简化的代码: inputStream = ser
我有以下代码: func (q *Queue) GetStreams(qi *QueueInfo) { channel := make(chan error, len(qi.AudioChun
在我调用 -play 之前,有没有办法让 MPMusicPlayerController 缓冲内容?还是在您设置队列时默认执行此操作? AVAudioPlayer 有 -prepareToPlay 方
我正在编写一个数据库 备份函数,从System.Diagnostics.Process 对象 读取StandardOutput (StreamReader) 属性。我已成功写入普通文件。 //This
我有一个 wpf 应用程序,其中所有 viewModel 都继承自实现 INotifyPropertyChanged 的类 NotifyPropertyChangeClass(见下文)。 我想限制
我需要类似于 withLatestFrom 的东西,对应于下图: ---------A-----------------B-- -1-2-3------4------5-6-7-8---- -----
有没有办法缓冲 OutputStream,在返回之前修改它?这是我的代码片段: public ServletOutputStream getOutputStream() throws IOExcept
目前我们有实现服务器通信协议(protocol)缓冲的需求。如果有人对此有任何意见,他们可以向我提供任何意见吗。 最佳答案 请查看以下 Protocol Buffer 链接。 http://code.
所以我目前正在开发一个 Java 应用程序,该应用程序应该将特定事件记录到数据库中。我希望每分钟最多有 15 到 20 次插入,基本上我想知道我是否应该为每个插入语句建立一个新连接,或者只要应用程序正
请考虑以下代码,包括两个线程 buffering_thread(用一条消息填充缓冲区指针)和 sending_thread(清空缓冲区): #include "msg.cpp" msg * buffe
是否可以在线播放由两个或多个视频文件组成的视频? 由于我原来的帖子不够清楚,这里有扩展的解释和问题。 我的站点托管在 Linux/Apache/PHP 服务器上。我有 FLV/F4V 格式的视频文件。
这是我用于缓冲和转换传入事件的代码: public Publisher> logs(String eventId) { ConnectableObservable connectableObs
我是一名优秀的程序员,十分优秀!