Getting Timed Metadata in Swift iOS 8 from M3U8 Streaming Video

Timed Metadata with AVPlayer

Apparently, AVPlayer automatically requests metadata from Icecast. The code below works perfectly.

class ViewController: UIViewController {
var Player: AVPlayer!
var PlayerItem: AVPlayerItem!

override func viewDidLoad() {
super.viewDidLoad()

PlayerItem = AVPlayerItem(URL: NSURL(string: "http://live.machine.fm/aac"))
PlayerItem.addObserver(self, forKeyPath: "timedMetadata", options: nil, context: nil)
Player = AVPlayer(playerItem: PlayerItem)
Player.play()
}

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) -> Void {

if keyPath != "timedMetadata" { return }

var data: AVPlayerItem = object as AVPlayerItem

for item in data.timedMetadata as [AVMetadataItem] {
println(item.value)
}
}

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

Getting MetaData from MPMoviePlayerController

Assuming that you already know what kind of metadata is being sent from the stream (if you don't, use a media player like VLC to see), you must first register a notification to get the metadata in timed intervals and then a method to process them.

Starting with the notification, just

 [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(MetadataUpdate:)
name:MPMoviePlayerTimedMetadataUpdatedNotification
object:nil];

after the MPMoviePlayerController allocation.

Then on the MetadataUpdate method

- (void)MetadataUpdate:(NSNotification*)notification
{
if ([streamAudioPlayer timedMetadata]!=nil && [[streamAudioPlayer timedMetadata] count] > 0) {
MPTimedMetadata *firstMeta = [[streamAudioPlayer timedMetadata] objectAtIndex:0];
metadataInfo = firstMeta.value;
}
}

where streamAudioplayer is your MPMoviePlayerController and metadataInfo a NSString to store the value. The above will get the Artist and Track info of the currently playing song.

This is the case for the standard metadata send by a shoutcast or icecast stream.
(can't say for others because I haven't tried them)

Note that each stream may handle and send different metadata.
Since [streamAudioPlayer timedMetadata] is a NSArray you can

NSArray *metaArray = [streamAudioPlayer timedMetadata];
NSLog (@"%i", [metaArray count]); //to see how many elements in the array
MPTimedMetadata *firstMeta = [[streamAudioPlayer timedMetadata] objectAtIndex:0];

Use then the debug console to show the content of metadata using the key, keyspace, timestamp, value properties.

All the above is just an example. There isn't a single way to handle metadata.
Detailed information can be found at

https://developer.apple.com/library/ios/#DOCUMENTATION/MediaPlayer/Reference/MPTimedMetadata_Class/Reference/Reference.html

for the MPTimedMetadata class reference and from there... code on!

iOS Chromecast: Read Duration from HLS Playlist

The issue apparently lay in the Styled Media Receiver that I was using:

Version note: The Receiver API and Media Player Library are based on Cast SDK v2 and are compatible with both v2 and CAF senders.

When I switched to the Default Media Receiver this issue went away; I was able to see the duration on a VOD HLS stream.

I gather that this sort of information is exposed to the iOS Cast SDK by the Receiver Application running in the Chromecast device. I suppose the Styled Receiver doesn't have the capability built into it to calculate the duration of an HLS playlist.

how to read id3 tags / other metadata from an HLS stream in swift / AVKIT

This is how I achieved it:

import UIKit
import AVKit
import AVFoundation
import MediaPlayer

class ViewController: UIViewController{

let player = AVPlayer()
var playerItem: AVPlayerItem!
let asset = AVAsset(url: URL(string: "https://db2.indexcom.com/bucket/ram/00/05/05.m3u8")!)

override func viewDidLoad() {
prepareToPlay()
player.play()
}

func prepareToPlay() {
playerItem = AVPlayerItem(asset: asset)
playerItem.addObserver(self, forKeyPath: "timedMetadata", options: [], context: nil)
player.replaceCurrentItem(with: playerItem)
printTimeStamp()
}

func printTimeStamp() {
print("▼⎺▼⎺▼⎺▼⎺▼⎺▼⎺▼⎺▼")
print("PROGRAM-DATE-TIME: ")
print(playerItem.currentDate() ?? "No timeStamp")
print("▲_▲_▲_▲_▲_▲_▲_▲\n\n")
}

override func observeValue(forKeyPath: String?, of: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if forKeyPath != "timedMetadata" { return }

printTimeStamp()

let data: AVPlayerItem = of as! AVPlayerItem

guard let timedMetadata = data.timedMetadata else { return }

for item in timedMetadata {
switch item.commonKey {

case .commonKeyAlbumName?:
print("AlbumName: \(item.value!)")
case .commonKeyArtist?:
print("Artist: \(item.value!)")
case .commonKeyArtwork?:
print("Artwork: \(item.value!)")
case .commonKeyAuthor?:
print("Author: \(item.value!)")
case .commonKeyContributor?:
print("Contributor: \(item.value!)")
case .commonKeyCopyrights?:
print("Copyrights: \(item.value!)")
case .commonKeyCreationDate?:
print("CreationDate: \(item.value!)")
case .commonKeyCreator?:
print("creator: \(item.value!)")
case .commonKeyDescription?:
print("Description: \(item.value!)")
case .commonKeyFormat?:
print("Format: \(item.value!)")
case .commonKeyIdentifier?:
print("Identifier: \(item.value!)")
case .commonKeyLanguage?:
print("Language: \(item.value!)")
case .commonKeyMake?:
print("Make: \(item.value!)")
case .commonKeyModel?:
print("Model: \(item.value!)")
case .commonKeyPublisher?:
print("Publisher: \(item.value!)")
case .commonKeyRelation?:
print("Relation: \(item.value!)")
case .commonKeySoftware?:
print("Software: \(item.value!)")
case .commonKeySubject?:
print("Subject: \(item.value!)")
case .commonKeyTitle?:
print("Title: \(item.value!)")
case .commonKeyType?:
print("Type: \(item.value!)")

case .id3MetadataKeyAlbumTitle?:
print("id3MetadataKeyAlbumTitle: \(item.value!)")

default:
print("other data: \(item.value!)")
}
}
}
}

HLS Metadata ID3 tag not working

Your code works fine, the reason of this problem is caused by an issue from the server side.

You can use this tool mp3tag to edit the audio file - add meta data tags and upload it to server.

As examples, you can try these audios included metadata tags:

http://ice1.somafm.com/groovesalad-128-mp3

https://developer.jwplayer.com/jw-player/demos/basic/audio-metadata/assets/index.m3u8

To confirm, the above files should work fine with your code.

MPMoviePlayerController Stops Playing After 5 seconds - Swift

Could your moviePlayer be going out of scope? Have you tried making it a member variable?

moviePlayer is a local variable of viewDidLoad, so once that function finishes, I don't see any reason why your player would not be deallocated.

If you instead make it a variable of the class, its lifetime will be extended to match your class's lifetime.

something like

class ViewController: UIViewController {

var player: MPMoviePlayerController?

override func viewDidLoad() {
// ...
self.player = MPMoviePlayerController(contentURL: url) // won't go out of scope at end of viewDidLoad()
// ...
}


Related Topics



Leave a reply



Submit