AVPlayer HLS live stream IOS
Asked Answered
L

1

5

I'm trying to use HLS to live stream but i get error:

error: Optional("The operation could not be completed"), error: Optional(Error Domain=AVFoundationErrorDomain Code=-11800 "Media format - sample description is invalid (e.g. invalid size)" UserInfo={NSUnderlyingError=0x60000005b510 {Error Domain=NSOSStatusErrorDomain Code=-12714 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12714), NSDebugDescription=Media format - sample description is invalid (e.g. 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

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: enter image description here

Also same stream works on Android apps and on web

Larine answered 25/4, 2018 at 11:9 Comment(1)
This is server issue check this answer #49663796Macronucleus
S
5

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.

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:

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. Note that I recompile latest ffmpeg with fdk_aac.

The RTMP has 2 applications: one for receiving upstream and another for transcoding HLS which is also typical.

To fix the metadata problem, I adjusted the ffmpeg transcode command with baseline profile by adding: -profile:v baseline -level 3.0. This will lead to error because of wrong color format, so I also added: -vf "scale=480:trunc(ow/a/2)*2,format=yuv420p".

Finally, my nginx configuration that works: https://gist.github.com/chung-nguyen/d88e73e3cc8788878f5ffb8c232b4729

And my AVPlayer code in 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()
Sill answered 23/1, 2019 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.