WalterBeiter
WalterBeiter

Reputation: 2354

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

I am trying to gather some knowledge about how to read metadata from an HLS stream inside an iOS app. The following HLS stream has some ID3 tags which I want to read: HLS test stream

In the web inspector in Safari, I am able to see lots of data objects in the console, each one has metadata:

enter image description here

In the network tab of the web inspector, I can read the playlist file:

#EXTM3U
#EXT-X-VERSION:5
#EXT-X-MEDIA-SEQUENCE:89147
#EXT-X-TARGETDURATION:20
#EXT-X-PROGRAM-DATE-TIME:2019-09-25T11:35:23.401Z
#EXTINF:19.970,
05-20190925T113523Z.aac
#EXTINF:19.970,
05-20190925T113543Z.aac
#EXTINF:19.970,
05-20190925T113603Z.aac
#EXTINF:19.970,
05-20190925T113623Z.aac
#EXTINF:19.970,
05-20190925T113643Z.aac
#EXTINF:19.970,
05-20190925T113703Z.aac

So far I've implemented a class which uses an AVPlayer instance to play this stream. It is working properly.

I printed all sorts of properties from the AVPlayer and AVPlayerItem to the Xcode console. However, the only property I can interpret is AVPlayerItem.currentTime which gives me the value of EXT-X-PROGRAM-DATE-TIME from the playlist file. All other properties don't seem to have something to do with the information I see in the playlist and the id3 tags.

Is there any way I can read the metadata contained in each id3 tag? How can I read EXT-X-TARGETDURATION from the playlist?

I read about AVPlayerItemMetadataCollector, but I don't understand what it is supposed to do and if this would help me reading metadata in the HLS stream.

Upvotes: 4

Views: 4206

Answers (1)

WalterBeiter
WalterBeiter

Reputation: 2354

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!)")
            }
        }
    }
}

Upvotes: 2

Related Questions