Skip to content

Commit

Permalink
update mixWithOther logic
Browse files Browse the repository at this point in the history
fix:ios ad request bug when view not attached
fix:ios send event bug when eventSink is not ready
updated platform view passed arguments
  • Loading branch information
GeceGibi committed Jan 11, 2024
1 parent bb28959 commit bb5fc6a
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Used ExoPlayer for Android and AVPlayer for iOS.
controller.onPlayerReady -> Future<bool>
/// Static Properties
ImaPlayerController.pauseAllPlayers()
ImaPlayerController.pauseImaPlayers([ImaPlayerController? excludedOne])
```

## ImaPlayerOptions
Expand Down
31 changes: 17 additions & 14 deletions android/src/main/kotlin/dev/gece/imaplayer/ImaPlayerView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ internal class ImaPlayerView(
exoPlayer.volume = imaPlayerSettings.initialVolume.toFloat()

exoPlayer.addListener(object : Player.Listener {
private var isBuffering = false

fun sendBufferEvent(buffering: Boolean) {
if (isBuffering != buffering) {
isBuffering = buffering
sendEvent(
hashMapOf(
"type" to if (isBuffering) "buffering_start" else "buffering_end"
)
)
}
}

override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
Expand Down Expand Up @@ -135,7 +147,7 @@ internal class ImaPlayerView(

when (playbackState) {
Player.STATE_BUFFERING -> {

sendBufferEvent(true)
}

Player.STATE_ENDED -> {
Expand All @@ -158,6 +170,10 @@ internal class ImaPlayerView(
// no-op
}
}

if (playbackState != Player.STATE_BUFFERING) {
sendBufferEvent(false)
}
}

override fun onIsPlayingChanged(isPlaying: Boolean) {
Expand All @@ -174,18 +190,6 @@ internal class ImaPlayerView(

var bufferTracker = object : Runnable {
private var latestBufferedPosition: Long = 0L
private var isBuffering = false

fun sendBufferEvent(buffering: Boolean) {
if (isBuffering != buffering) {
isBuffering = buffering
sendEvent(
hashMapOf(
"type" to if (isBuffering) "buffering_start" else "buffering_end"
)
)
}
}

override fun run() {
val hasBuffering = latestBufferedPosition != exoPlayer.bufferedPosition
Expand All @@ -201,7 +205,6 @@ internal class ImaPlayerView(
)
}

sendBufferEvent(hasBuffering)
mainHandler.postDelayed(this, 250)
}
}
Expand Down
54 changes: 34 additions & 20 deletions example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,11 @@ class PlayerScreen extends StatefulWidget {
}

class _PlayerScreenState extends State<PlayerScreen> {
var position = Duration.zero;

var aspectRatio = 16 / 9;
var position = ValueNotifier(Duration.zero);
var events = <AdEventType>[];

final controller = ImaPlayerController.network(
'https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
final controller = ImaPlayerController.asset(
'assets/video.mp4',
imaTag:
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=',
options: const ImaPlayerOptions(
Expand Down Expand Up @@ -99,28 +97,22 @@ class _PlayerScreenState extends State<PlayerScreen> {
setState(() {});
});

controller.addListener(() {
setState(() {
if (controller.value.size.aspectRatio != 0) {
aspectRatio = controller.value.size.aspectRatio;
}
});
});

Timer.periodic(const Duration(milliseconds: 200), (timer) async {
if (!mounted) {
timer.cancel();
return;
}

final pos = await controller.position;

setState(() {
position = pos;
});
position.value = await controller.position;
});
}

@override
void dispose() {
controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -131,7 +123,29 @@ class _PlayerScreenState extends State<PlayerScreen> {
ImaPlayerUI(
player: ImaPlayer(controller),
),
Text(position.toString()),
AspectRatio(
aspectRatio: 16 / 9,
child: ImaPlayer(
ImaPlayerController.network(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WeAreGoingOnBullrun.mp4',
imaTag:
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=',
options: const ImaPlayerOptions(
autoPlay: true,
initialVolume: 1.0,
isMixWithOtherMedia: false,
showPlaybackControls: true,
),
),
autoDisposeController: true,
),
),
ValueListenableBuilder(
valueListenable: position,
builder: (context, pos, child) {
return Text(pos.toString());
},
),
Text(controller.value.bufferedDuration.toString()),
FilledButton(
onPressed: controller.play,
Expand All @@ -155,7 +169,7 @@ class _PlayerScreenState extends State<PlayerScreen> {
child: Text('PLAY ANOTHER VIDEO'),
),
FilledButton(
onPressed: controller.pauseOtherPlayers,
onPressed: () => ImaPlayerController.pauseImaPlayers(controller),
child: Text('PAUSE OTHER PLAYERS'),
),
FilledButton(
Expand Down
52 changes: 42 additions & 10 deletions ios/Classes/ImaPlayerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import UIKit
import AVKit
import MediaPlayer


class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAdsManagerDelegate, IMAAdsLoaderDelegate {
enum Events: String {
case ads
Expand All @@ -33,9 +32,8 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
private var isDisposed = false
private var isShowingContent = true

/// Info arguments
private var isBuffering = false
private var ad: IMAAd?
private var timer: Timer?


func view() -> UIView {
return avPlayerViewController.view
Expand Down Expand Up @@ -70,6 +68,8 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds

super.init()

setMixWithOther()

methodChannel.setMethodCallHandler(onMethodCall)
eventChannel.setStreamHandler(self)

Expand All @@ -81,6 +81,15 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
imaAdsLoader = IMAAdsLoader(settings: imaSdkSettings)
imaAdsLoader.delegate = self
}

timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in
if !self.avPlayerViewController.isViewLoaded || self.avPlayerViewController.view.window == nil {
return
}

timer.invalidate()
self.requestAd()
}
}

func addListenerForPlayer() {
Expand All @@ -90,9 +99,9 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
}

func addListenerForItem() {
avPlayer.currentItem?.addObserver(self, forKeyPath: "duration", options: .new, context: nil)
avPlayer.currentItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
avPlayer.currentItem?.addObserver(self, forKeyPath: "presentationSize", options: .new, context: nil)
avPlayer.currentItem?.addObserver(self, forKeyPath: "duration", options: .new, context: nil)
avPlayer.currentItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)

NotificationCenter.default.addObserver(
Expand All @@ -103,12 +112,22 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
);
}

func setMixWithOther() {
#if os(iOS)
if imaPlayerSettings.isMixed {
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .mixWithOthers)
} else {
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
}
#endif
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

switch (keyPath) {
case "status":
if avPlayer.status == .readyToPlay {
sendEvent(event: ["type": "ready"])
requestAds()
} else if avPlayer.status == .failed {
// sendEvent(value: ["player_error": avPlayer.error])
}
Expand All @@ -131,7 +150,7 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
break;

case "duration":
if (avPlayer.currentItem?.duration != nil){
if (avPlayer.currentItem?.duration != nil) {
sendEvent(event: [
"type": "duration",
"value": timeToMillis(avPlayer.currentItem!.duration)
Expand Down Expand Up @@ -256,7 +275,7 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
avPlayer.play()
}

func requestAds() {
func requestAd() {
if imaPlayerSettings.isAdsEnabled {
let adDisplayContainer = IMAAdDisplayContainer(
adContainer: avPlayerViewController.view,
Expand Down Expand Up @@ -375,9 +394,17 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
result(nil)
}

private var eventQueue: [Dictionary<String, Any>] = []
private func sendEvent(event: Dictionary<String, Any>) {
if eventSink != nil {
eventSink?(event)
if eventSink == nil {
eventQueue.append(event)
} else {
while !eventQueue.isEmpty {
let ev = eventQueue.removeFirst()
eventSink(ev)
}

eventSink(event)
}
}

Expand All @@ -397,6 +424,11 @@ class ImaPlayerView: NSObject, FlutterPlatformView, FlutterStreamHandler, IMAAds
func dispose(result: FlutterResult) {
isDisposed = true

if timer != nil && timer!.isValid {
timer?.invalidate()
timer = nil
}

imaAdsManager?.destroy()
imaAdsManager = nil

Expand Down
22 changes: 5 additions & 17 deletions lib/ima_player_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,13 @@ class ImaPlayerController extends ValueNotifier<PlayerEvent> {
}

/// Pauses all ima players but excluded this one
void pauseOtherPlayers() {
if (_isDisposed) return;

static void pauseImaPlayers([ImaPlayerController? excludedOne]) {
for (final instance in _kControllerInstances) {
if (instance != this) {
instance.pause();
}
}
}
if (!instance._isDisposed) {
if (excludedOne == instance) {
continue;
}

/// Pauses all ima players currently playing
static void pauseAllPlayers() {
for (final instance in _kControllerInstances) {
if (instance.value.isPlaying) {
instance.pause();
}
}
Expand All @@ -170,12 +163,7 @@ class ImaPlayerController extends ValueNotifier<PlayerEvent> {
/// If want to play new video with same player just pass `uri`
Future<void> play({String? uri}) async {
if (_isDisposed) return;

await _methodChannel?.invokeMethod<bool>('play', uri);

if (!options.isMixWithOtherMedia) {
pauseOtherPlayers();
}
}

/// Pause video content or ads
Expand Down
1 change: 0 additions & 1 deletion lib/ima_player_ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ class _ImaPlayerUIState extends State<ImaPlayerUI> {
opacity: uiHidden ? 0.0 : 1,
duration: const Duration(milliseconds: 250),
child: Stack(
fit: StackFit.expand,
children: [
const Positioned.fill(
child: ColoredBox(color: Color(0x66000000)),
Expand Down
5 changes: 4 additions & 1 deletion lib/ima_player_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ class _ImaPlayerViewState extends State<_ImaPlayerView> {
viewType: viewType,
creationParams: creationParams,
layoutDirection: TextDirection.ltr,
gestureRecognizers: widget.gestureRecognizers,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: widget.controller._attach,
);
} else {
return UiKitView(
viewType: viewType,
creationParams: creationParams,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
layoutDirection: TextDirection.ltr,
gestureRecognizers: widget.gestureRecognizers,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: widget.controller._attach,
);
Expand Down

0 comments on commit bb5fc6a

Please sign in to comment.