Skip to content

Commit

Permalink
Merge pull request #63 from THEOplayer/conviva-bitrate-notification
Browse files Browse the repository at this point in the history
Conviva bitrate notification
  • Loading branch information
therama authored Dec 18, 2024
2 parents 1937ca6 + f3ba453 commit 7acb7af
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 8 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

- Conviva
- Added compatibility for reporting playback bitrate metrics with THEOlive sources.

### Fixed

- Conviva
- Fixed an issue where the playback bitrate metrics were not reported.

## [8.0.1.1] - 2024-09-18

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,25 @@ struct AppEventConvivaReporter: AppEventProcessor {

func appGotNewAccessLogEntry(event: AVPlayerItemAccessLogEvent, isPlayingAd: Bool) {
let endpoint = isPlayingAd ? self.adAnalytics : self.videoAnalytics

if event.indicatedBitrate >= 0 {
let bitrateValue = NSNumber(value: event.indicatedBitrate / 1000)
endpoint.reportPlaybackMetric(CIS_SSDK_PLAYBACK_METRIC_BITRATE, value: bitrateValue)
self.storage.storeKeyValuePair(key: CIS_SSDK_PLAYBACK_METRIC_BITRATE, value: bitrateValue)
}

self.handleBitrateChange(bitrate: event.indicatedBitrate, endpoint: endpoint)

if event.numberOfDroppedVideoFrames >= 0 {
endpoint.reportPlaybackMetric(CIS_SSDK_PLAYBACK_METRIC_DROPPED_FRAMES_TOTAL, value: NSNumber(value: event.numberOfDroppedVideoFrames))
}
}

func appGotBitrateChangeEvent(bitrate: Double, isPlayingAd: Bool) {
let endpoint = isPlayingAd ? self.adAnalytics : self.videoAnalytics
self.handleBitrateChange(bitrate: bitrate, endpoint: endpoint)
}

private func handleBitrateChange(bitrate: Double, endpoint: CISStreamAnalyticsProtocol) {
guard bitrate >= 0 else { return }

let bitrateValue = NSNumber(value: bitrate / 1000)

endpoint.reportPlaybackMetric(CIS_SSDK_PLAYBACK_METRIC_BITRATE, value: bitrateValue)
self.storage.storeKeyValuePair(key: CIS_SSDK_PLAYBACK_METRIC_BITRATE, value: bitrateValue)
}
}
19 changes: 18 additions & 1 deletion Code/Conviva/Source/Events/Observers/AppEventForwarder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ fileprivate let didEnterBackground = UIApplication.didEnterBackgroundNotificatio
// Both `AVPlayerItem.newAccessLogEntryNotification` and `Notification.Name.AVPlayerItemNewAccessLogEntry` are mapped to `Notification.Name("AVPlayerItemNewAccessLogEntry")`, hence we use that.
// Once we drop support for older versions (below Xcode 15 and Swift 5.9) we can switch from `Notification.Name("AVPlayerItemNewAccessLogEntry")` to `AVPlayerItem.newAccessLogEntryNotification`.
fileprivate let newAccessLogEntry = Notification.Name("AVPlayerItemNewAccessLogEntry")
fileprivate let bitrateChangeEvent = Notification.Name("THEOliveBitrateChangeEvent")

class AppEventForwarder {
let center = NotificationCenter.default
let foregroundObserver, backgroundObserver, accessLogObserver: Any
let foregroundObserver, backgroundObserver, accessLogObserver, bitrateChangeObserver: Any
let player: THEOplayer

init(player: THEOplayer, eventProcessor: AppEventProcessor) {
Expand Down Expand Up @@ -46,17 +47,33 @@ class AppEventForwarder {
eventProcessor.appGotNewAccessLogEntry(event: event, isPlayingAd: player.ads.playing)
}
)
// Temporary workaround for THEOlive bitrate change events
// TODO: Refactor this with active quality switch event dispatched from THEOplayer. This needs a videoTrack property exposed on THEOlive SDK which is currently missing.
// NOTE: accessLogObserver can also be removed once the active quality switch event is implemented.
bitrateChangeObserver = center.addObserver(
forName: bitrateChangeEvent,
object: .none,
queue: .none,
using: { notification in
guard let userInfo = notification.userInfo else { return }
guard let bitrate = userInfo["bitrate"] as? Double else { return }

eventProcessor.appGotBitrateChangeEvent(bitrate: bitrate, isPlayingAd: player.ads.playing)
}
)
}

deinit {
center.removeObserver(foregroundObserver, name: willEnterForeground, object: nil)
center.removeObserver(backgroundObserver, name: didEnterBackground, object: nil)
center.removeObserver(accessLogObserver, name: newAccessLogEntry, object: nil)
center.removeObserver(accessLogObserver, name: bitrateChangeEvent, object: nil)
}
}

protocol AppEventProcessor {
func appWillEnterForeground(notification: Notification)
func appDidEnterBackground(notification: Notification)
func appGotNewAccessLogEntry(event: AVPlayerItemAccessLogEvent, isPlayingAd: Bool)
func appGotBitrateChangeEvent(bitrate: Double, isPlayingAd: Bool)
}
5 changes: 4 additions & 1 deletion Code/Conviva/Source/Utilities/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ enum Utilities {

extension THEOplayer {
var currentItem: AVPlayerItem? {
((Mirror(reflecting: self).descendant("theoplayer") as? NSObject).map {Mirror(reflecting: $0).superclassMirror?.descendant("mainContentPlayer", "avPlayer")} as? AVPlayer)?.currentItem
let basePlayer = (Mirror(reflecting: self).descendant("theoplayer") as? NSObject).map { Mirror(reflecting: $0).superclassMirror?.descendant("basePlayer") } as? AnyObject
let basePlayerMirror = basePlayer.map { Mirror(reflecting: $0) }
let avPlayer = basePlayerMirror?.superclassMirror?.superclassMirror?.descendant("player", "avPlayer") as? AVPlayer
return avPlayer?.currentItem
}

var renderedFramerate: Float? {
Expand Down

0 comments on commit 7acb7af

Please sign in to comment.