Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Timed Metadata in Swift ios 8 From M3U8 Streaming Video

I'm trying to duplicate this https://jmacmullin.wordpress.com/2010/11/03/adding-meta-data-to-video-in-ios/ in swift.

Here is a video of Jake's code in action...

Objective C Timed Metadat in HLS stream

Here is an additional link to something similar...

http://cloudfields.net/blog/metadata-audiostream-mpmovieplayercontroller/

When my video is playing timed metadata should update a button to redirect to a specific youtube url in a webview when clicked. My video is roughly 15 min long and has 6 timed metadata urls.

I can't find any code or documentation anywhere on how to achieve this in Swift. I've managed to convert Jake's Objective C code for his Notification call.

// Register for meta-data notifications
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
 [center addObserver:self
       selector:@selector(metadataUpdate:)
           name:MPMoviePlayerTimedMetadataUpdatedNotification
         object:nil];

to Swift Code

       NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: "metadataUpdated",
            name: MPMoviePlayerTimedMetadataUpdatedNotification,
            object: nil)

Jake's Function

Actor *actor = [[Actor alloc] init];

 if ([player timedMetadata]!=nil && [[player timedMetadata] count] > 0) {
for (MPTimedMetadata *metadata in [player timedMetadata]) {
if ([[metadata.allMetadata valueForKey:@"key"] isEqualToString:@"TPE1"]) {
        [actor setName:[metadata.allMetadata objectForKey:@"value"]];
    }
    if ([[metadata.allMetadata valueForKey:@"key"] isEqualToString:@"WXXX"]) {
        NSURL *url = [NSURL URLWithString:[metadata.allMetadata objectForKey:@"value"]];
        [actor setProfileURL:url];
    }
  }
}

// display some UI element for the user to interact with

to Swift

func metadataUpdated (notification: NSNotification!) {

    if moviePlayer?.timedMetadata != nil && moviePlayer?.timedMetadata.count > 0 {


        for MPTimedMetadata in [moviePlayer?.timedMetadata] {

            if MPTimedMetadata?.description  == ("TPE1") {

                let name = ("value")

            }

            if MPTimedMetadata?.description  == ("WXXX") {

                var url = NSURL.observeValueForKeyPath("value")

            }
        } 
}
println("Things are kind of working")
}
}

For the life of me though I cant figure out how to turn the metadata into any actionable code. I really need to create a button that redirects to the URL carried in the metadata, but can't convert it to a String. Any help is greatly appreciated. Here is what i have so far.

import UIKit

import MediaPlayer



class ViewController: UIViewController {


var moviePlayer: MPMoviePlayerController?


var youtube = ""

override func viewDidLoad() {

    super.viewDidLoad()

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


    let url = NSURL(string: "http://path/to/video.m3u8")



    moviePlayer = MPMoviePlayerController(contentURL: url)



    if let player = moviePlayer {



        player.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)

        player.view.sizeToFit()

        player.scalingMode = MPMovieScalingMode.None





        player.movieSourceType = MPMovieSourceType.Streaming

        //player.repeatMode = MPMovieRepeatMode.One





        player.play()



        self.view.addSubview(player.view)



        NSNotificationCenter.defaultCenter().addObserver(

            self,

            selector: "metadataUpdated:",

            name: MPMoviePlayerTimedMetadataUpdatedNotification,

            object: nil)



    }

}



override func didReceiveMemoryWarning() {

    super.didReceiveMemoryWarning()

    // Dispose of any resources that can be recreated.

}



func metadataUpdated (notification: NSNotification!) {






    if moviePlayer?.timedMetadata != nil && moviePlayer?.timedMetadata.count > 0 {


        for MPTimedMetadata in [moviePlayer?.timedMetadata] {

            if MPTimedMetadata?.description  == ("TPE1") {

                let name = ("value")



            }

            if MPTimedMetadata?.description  == ("WXXX") {

                var url = NSURL.observeValueForKeyPath("value")



            }


        }


}
println("Things are kind of working")


}


}
like image 228
Jace Sparks Avatar asked Oct 19 '22 15:10

Jace Sparks


1 Answers

So I got this to work, but ended up ditch MPMovieController opting for AVPlayer. Here is my code, and the link to another post that helped me get it functional.

Timed Metadata with AVPlayer

import UIKit

import MediaPlayer
import AVFoundation

var youtubeRequest: NSURLRequest! = nil
var player : AVPlayer? = nil

var url:NSString!


class ViewController: UIViewController {


var moviePlayer: MPMoviePlayerController?
var movieItem: AVPlayerItem!
var Player: AVPlayer!

var playerLayer : AVPlayerLayer? = nil
var asset : AVAsset? = nil
var playerItem: AVPlayerItem!

var youtubeRequest: NSURLRequest! = nil

@IBOutlet var playButtonOutlet: UIBarButtonItem!

@IBAction func playButtonAction(sender: AnyObject) {player?.play()
    playButtonOutlet.enabled = false

}

@IBOutlet var videoView: UIView!
@IBAction func loadURL(sender: AnyObject) {player?.pause()
    playButtonOutlet.enabled = true
}
@IBOutlet var urlButton: UIButton!
override func viewDidLoad() {

    super.viewDidLoad()



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

    urlButton.enabled = false

    let videoURL = NSURL(string: "http://path/to/video.m3u8")
    playButtonOutlet.enabled = false

    movieItem = AVPlayerItem(URL: NSURL(string: "http://path/to/video.m3u8"))
    movieItem.addObserver(self, forKeyPath: "timedMetadata", options: nil, context: nil)
    Player = AVPlayer(playerItem: movieItem)

    var metaArray: Array<Any> = [moviePlayer?.timedMetadata]







    asset = AVAsset.assetWithURL(videoURL) as? AVAsset
    playerItem = AVPlayerItem(asset: asset)
    playerItem = AVPlayerItem(URL: NSURL(string: "http://path/to/video.m3u8"))
    playerItem.addObserver(self, forKeyPath: "timedMetadata", options: nil, context: nil)
    playerItem.addObserver(self, forKeyPath: "presentationSize", options: nil, context: nil)
    player = AVPlayer(playerItem: playerItem)



    playerLayer = AVPlayerLayer(player: player)
    playerLayer!.frame = videoView.bounds

    videoView.layer.addSublayer(self.playerLayer)

    player!.play()

    NSNotificationCenter.defaultCenter().addObserver(

        self,

        selector: "rotated",

        name: UIDeviceOrientationDidChangeNotification,

        object: nil)


}

    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

        var urlError = false

        for item in data.timedMetadata as [AVMetadataItem] {

            println(item.stringValue)
            var metaArray: Array<Any> = [playerItem?.timedMetadata]
            println("Total objects in array \(metaArray[0])")


            var data = item.stringValue

          url = NSString(string: data) as NSString!



            if url != nil {

             urlButton.enabled = true

                println("The url is \(url)")




            } else {

                urlError = true

            }


            var urlRedirect = NSURL(fileURLWithPath: "\(url)")


            println("The url is \(urlRedirect)")

        }


    }

override func didReceiveMemoryWarning() {

    super.didReceiveMemoryWarning()

    // Dispose of any resources that can be recreated.

}

func rotated()
{
    if(UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation))
    {
     self.navigationController?.navigationBarHidden = true
        playerLayer?.frame = CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height)
        playerLayer?.frame = CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height)

        println("landscape")
    }

    if(UIDeviceOrientationIsPortrait(UIDevice.currentDevice().orientation))
    {
        self.navigationController?.navigationBarHidden = false
        playerLayer!.frame = videoView.bounds

        println("portraight")
    }

 }


}
like image 184
Jace Sparks Avatar answered Jan 04 '23 06:01

Jace Sparks