简体   繁体   English

AVPlayer HLS直播IOS

[英]AVPlayer HLS live stream IOS

I'm trying to use HLS to live stream but i get error: 我正在尝试使用HLS直播但我收到错误:

error: Optional("The operation could not be completed"), error: Optional(Error Domain=AVFoundationErrorDomain Code=-11800 "Media format - sample description is invalid (eg invalid size)" UserInfo={NSUnderlyingError=0x60000005b510 {Error Domain=NSOSStatusErrorDomain Code=-12714 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12714), NSDebugDescription=Media format - sample description is invalid (eg invalid size), NSLocalizedDescription=The operation could not be completed}) 2018-04-25 12:14:51.608117+0200 morethen2[11681:374192] Task .<3> finished with error - code: -999 错误:可选(“操作无法完成”),错误:可选(错误域= AVFoundationErrorDomain代码= -11800“媒体格式 - 样本描述无效(例如无效大小)”UserInfo = {NSUnderlyingError = 0x60000005b510 {错误域= NSOSStatusErrorDomain代码= -12714“(null)”},NSLocalizedFailureReason =发生未知错误(-12714),NSDebugDescription =媒体格式 - 样本描述无效(例如无效大小),NSLocalizedDescription =操作无法完成})2018- 04-25 12:14:51.608117 + 0200 morethen2 [11681:374192]任务。<3>完成错误 - 代码:-999

I can't share link to stream, its private. 我不能分享链接到流,它的私人。

Here is code: 这是代码:

class ViewController: UIViewController {

var player = AVPlayer()

override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "https:can-t-share-it/LIVE-008900021A-LIP-0-channelNo2_360p/manifest.m3u8")!

    let asset = AVURLAsset(url: url)

    let playerItem = AVPlayerItem(asset: asset)

    player = AVPlayer(url: url)
    let layer = AVPlayerLayer(player: player)
    layer.frame = view.layer.frame
    view.layer.addSublayer(layer)


    self.player.addObserver(self, forKeyPath: #keyPath(AVPlayer.status), options: [.new, .initial], context: nil)
    self.player.addObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem.status), options:[.new, .initial], context: nil)

    // Watch notifications
    let center = NotificationCenter.default
    center.addObserver(self, selector:"newErrorLogEntry:", name: .AVPlayerItemNewErrorLogEntry, object: player.currentItem)
    center.addObserver(self, selector:"failedToPlayToEndTime:", name: .AVPlayerItemFailedToPlayToEndTime, object: player.currentItem)


    player.play()

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

@IBAction func dismiss(_ sender: Any) {
    UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: true, completion: nil)
}


// Observe If AVPlayerItem.status Changed to Fail
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
    if let player = object as? AVPlayer, keyPath == #keyPath(AVPlayer.currentItem.status) {
        let newStatus: AVPlayerItemStatus
        if let newStatusAsNumber = change?[NSKeyValueChangeKey.newKey] as? NSNumber {
            newStatus = AVPlayerItemStatus(rawValue: newStatusAsNumber.intValue)!
        } else {
            newStatus = .unknown
        }
        if newStatus == .failed {
            NSLog("Error: \(String(describing: player.currentItem?.error?.localizedDescription)), error: \(String(describing: player.currentItem?.error))")
        }
    }
}

// Getting error from Notification payload
func newErrorLogEntry(_ notification: Notification) {
    guard let object = notification.object, let playerItem = object as? AVPlayerItem else {
        return
    }
    guard let errorLog: AVPlayerItemErrorLog = playerItem.errorLog() else {
        return
    }
    NSLog("Error: \(errorLog)")
}

func failedToPlayToEndTime(_ notification: Notification) {
    let error = notification.userInfo!["AVPlayerItemFailedToPlayToEndTimeErrorKey"]
    NSLog("error: \(error)")
} }

Here is video specifics: 这是视频细节: 在此输入图像描述

Also same stream works on Android apps and on web 同样的流也适用于Android应用和网络

I leave my answer here just in case someone else needs it. 我留下我的答案,以防其他人需要它。 After research, try and error I finally found out the way to get AVPlayer work with NGINX rtmp hls. 经过研究,尝试和错误我终于找到了让AVPlayer与NGINX rtmp hls一起工作的方法。

The main reason for this to happen is wrong metadata of video segments on server. 发生这种情况的主要原因是服务器上视频片段的元数据错误。 We have to find a way to fix that. 我们必须找到解决这个问题的方法。

This is my command to upstream video from my macbook, which is typical: 这是我对macbook上游视频的命令,这是典型的:

ffmpeg -f avfoundation -framerate 30  -i "0:0" -f flv -s 360x240 -vcodec libx264 -c:a aac -async 1 -vsync 1 -preset slower rtmp://127.0.0.1/live/xyz

On server side, I configure a basic Nginx RTMP HLS on Ubuntu 18.10 which can be found out how by googling. 在服务器端,我在Ubuntu 18.10上配置了一个基本的Nginx RTMP HLS,可以通过googling找到它。 Note that I recompile latest ffmpeg with fdk_aac. 请注意,我使用fdk_aac重新编译最新的ffmpeg。

The RTMP has 2 applications: one for receiving upstream and another for transcoding HLS which is also typical. RTMP有2个应用程序:一个用于接收上游,另一个用于转码HLS,这也是典型的。

To fix the metadata problem, I adjusted the ffmpeg transcode command with baseline profile by adding: -profile:v baseline -level 3.0 . 为了解决元数据问题,我通过添加: -profile:v baseline -level 3.0调整ffmpeg transcode命令和基线配置文件。 This will lead to error because of wrong color format, so I also added: -vf "scale=480:trunc(ow/a/2)*2,format=yuv420p" . 由于颜色格式错误,这将导致错误,因此我还添加了: -vf "scale=480:trunc(ow/a/2)*2,format=yuv420p"

Finally, my nginx configuration that works: https://gist.github.com/chung-nguyen/d88e73e3cc8788878f5ffb8c232b4729 最后,我的nginx配置有效: https//gist.github.com/chung-nguyen/d88e73e3cc8788878f5ffb8c232b4729

And my AVPlayer code in swift: 我的AVPlayer代码在swift中:

    let videoURL = URL(string: "http://blogchange.live/hls/xyz_low/index.m3u8")

    let playerItem = AVPlayerItem(url: videoURL!)
    let adID = AVMetadataItem.identifier(forKey: "X-TITLE", keySpace: .hlsDateRange)
    let metadataCollector = AVPlayerItemMetadataCollector(identifiers: [adID!.rawValue], classifyingLabels: nil)
    metadataCollector.setDelegate(self, queue: DispatchQueue.main)
    playerItem.add(metadataCollector)


    let player = AVPlayer(playerItem: playerItem)
    let playerLayer = AVPlayerLayer(player: player)
    playerLayer.frame = self.view.bounds
    self.view.layer.addSublayer(playerLayer)
    self.player = player
    player.play()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM