npaw_forge_interactive_media_ads 9.7.4 copy "npaw_forge_interactive_media_ads: ^9.7.4" to clipboard
npaw_forge_interactive_media_ads: ^9.7.4 copied to clipboard

NPAW Forge ad adapter for Google's interactive_media_ads Flutter plugin: reports typed IMA ad break, quartile and error facts to the Forge core.

example/lib/main.dart

// Copyright (C) NPAW - All Rights Reserved
//
// This source code is protected under international copyright law. All rights
// reserved and protected by the copyright holders. This file is confidential and
// only available to authorized individuals with the permission of the copyright
// holders. If you encounter this file and do not have permission, please contact
// the copyright holders and delete this file.
//
//
// @author: Victor Soto <victor.soto@nicepeopleatwork.com>
// @description: NPAW Forge IMA example playing a video_player stream with a sample VAST pre-roll and full analytics.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:interactive_media_ads/interactive_media_ads.dart';
import 'package:npaw_forge/npaw_forge.dart';
import 'package:npaw_forge_interactive_media_ads/npaw_forge_interactive_media_ads.dart';
import 'package:npaw_forge_video_player/npaw_forge_video_player.dart';
import 'package:video_player/video_player.dart';

/// Demo account; override with --dart-define=FORGE_ACCOUNT_CODE=yourAccount.
const String accountCode = String.fromEnvironment(
  'FORGE_ACCOUNT_CODE',
  defaultValue: 'npawdemodev',
);

/// Ingest endpoint; point smoke runs at stage, never production.
const String lmaEndpoint = String.fromEnvironment('FORGE_LMA_ENDPOINT');

const String sampleStream = String.fromEnvironment(
  'FORGE_SAMPLE_STREAM',
  defaultValue: 'https://storage.googleapis.com/gvabox/media/samples/stock.mp4',
);

/// Google's public single-linear sample VAST tag.
const String sampleAdTag = String.fromEnvironment(
  'FORGE_AD_TAG',
  defaultValue:
      'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/'
      'single_ad_samples&sz=640x480&cust_params=sample_ct%3Dlinear&'
      'ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&'
      'unviewed_position_start=1&env=vp&impl=s&correlator=',
);

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final forge = await NpawForge.start(
    ForgeConfig(
      accountCode: accountCode,
      appName: 'npaw-forge-ima-example',
      lmaEndpoint: lmaEndpoint.isEmpty ? null : lmaEndpoint,
      logLevel: 'info',
    ),
  );
  const debugDiagnostics =
      bool.fromEnvironment('FORGE_DEBUG_DIAGNOSTICS', defaultValue: false);
  if (debugDiagnostics) {
    forge.events.listen((event) => debugPrint('forge event: ${event.raw}'));
  }
  WidgetsBinding.instance.addObserver(ForgeLifecycleObserver(forge));
  runApp(ExampleApp(forge: forge));
}

class ExampleApp extends StatefulWidget {
  const ExampleApp({required this.forge, super.key});

  final NpawForge forge;

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  late final VideoPlayerController _contentController =
      VideoPlayerController.networkUrl(Uri.parse(sampleStream));

  /// Content-side Forge binding; its adapter also couples pause/resume
  /// around ad breaks through the IMA attachment below.
  late final Future<ForgeVideoPlayerAttachment> _contentAttachmentFuture;

  late final AdsLoader _adsLoader;
  AdsManager? _adsManager;
  ForgeImaAdsAttachment? _adsAttachment;

  bool _shouldShowContentVideo = false;
  Timer? _contentProgressTimer;
  final ContentProgressProvider _contentProgressProvider =
      ContentProgressProvider();

  late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer(
    onContainerAdded: (AdDisplayContainer container) {
      _adsLoader = AdsLoader(
        container: container,
        onAdsLoaded: _onAdsLoaded,
        onAdsLoadError: (AdsLoadErrorData data) {
          debugPrint('OnAdsLoadError: ${data.error.message}');
          unawaited(_resumeContent());
        },
      );
      // Ads can only be requested once the container is in the native
      // view hierarchy.
      unawaited(
        _adsLoader.requestAds(
          AdsRequest(
            adTagUrl: sampleAdTag,
            contentProgressProvider: _contentProgressProvider,
          ),
        ),
      );
    },
  );

  @override
  void initState() {
    super.initState();
    _contentAttachmentFuture = attachVideoPlayer(
      widget.forge,
      _contentController,
      metadata: const ContentMetadata(title: 'IMA sample stream'),
    );
    _contentController
      ..addListener(() {
        if (_contentController.value.isCompleted) {
          unawaited(_adsLoader.contentComplete());
        }
        setState(() {});
      })
      ..initialize().then((_) => setState(() {}));
  }

  Future<void> _onAdsLoaded(OnAdsLoadedData data) async {
    final manager = data.manager;
    _adsManager = manager;
    final contentAttachment = await _contentAttachmentFuture;
    // Single delegate per manager: Forge maps events first, then the app
    // callbacks below keep driving playback.
    _adsAttachment = await attachImaAds(
      contentAttachment.video,
      manager,
      contentAdapter: contentAttachment.adapter,
      onAdEvent: (AdEvent event) {
        debugPrint('OnAdEvent: ${event.type}');
        switch (event.type) {
          case AdEventType.loaded:
            unawaited(manager.start());
          case AdEventType.contentPauseRequested:
            unawaited(_pauseContent());
          case AdEventType.contentResumeRequested:
            unawaited(_resumeContent());
          case AdEventType.allAdsCompleted:
            unawaited(manager.destroy());
            _adsManager = null;
          default:
            break;
        }
      },
      onAdErrorEvent: (AdErrorEvent event) {
        debugPrint('AdErrorEvent: ${event.error.message}');
        unawaited(_resumeContent());
      },
    );
    await manager.init();
  }

  Future<void> _resumeContent() async {
    if (!mounted) {
      return;
    }
    setState(() {
      _shouldShowContentVideo = true;
    });
    if (_adsManager != null) {
      _contentProgressTimer = Timer.periodic(
        const Duration(milliseconds: 200),
        (Timer timer) async {
          if (_contentController.value.isInitialized) {
            final progress = await _contentController.position;
            if (progress != null) {
              await _contentProgressProvider.setProgress(
                progress: progress,
                duration: _contentController.value.duration,
              );
            }
          }
        },
      );
    }
    await _contentController.play();
  }

  Future<void> _pauseContent() {
    setState(() {
      _shouldShowContentVideo = false;
    });
    _contentProgressTimer?.cancel();
    _contentProgressTimer = null;
    return _contentController.pause();
  }

  @override
  void dispose() {
    _contentProgressTimer?.cancel();
    _adsAttachment?.dispose();
    unawaited(
      _contentAttachmentFuture.then(
        (attachment) => attachment.dispose(),
      ),
    );
    _adsManager?.destroy();
    _contentController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Forge IMA example',
      home: Scaffold(
        appBar: AppBar(title: const Text('Forge IMA example')),
        body: Center(
          child: !_contentController.value.isInitialized
              ? const CircularProgressIndicator()
              : AspectRatio(
                  aspectRatio: _contentController.value.aspectRatio,
                  child: Stack(
                    children: <Widget>[
                      // The display container must stay in the hierarchy for
                      // the whole session; it renders and handles ad clicks.
                      _adDisplayContainer,
                      if (_shouldShowContentVideo)
                        VideoPlayer(_contentController),
                    ],
                  ),
                ),
        ),
      ),
    );
  }
}
0
likes
0
points
636
downloads

Publisher

unverified uploader

Weekly Downloads

NPAW Forge ad adapter for Google's interactive_media_ads Flutter plugin: reports typed IMA ad break, quartile and error facts to the Forge core.

Homepage
Repository

License

unknown (license)

Dependencies

flutter, interactive_media_ads, npaw_forge

More

Packages that depend on npaw_forge_interactive_media_ads