إضافة ميزات متقدّمة إلى تطبيق iOS

الفواصل الإعلانية

توفِّر حزمة تطوير البرامج (SDK) لأداة iOS Sender دعم الفواصل الإعلانية والإعلانات المصاحبة ضمن بث وسائط معيّن.

يمكنك الاطّلاع على نظرة عامة على الفواصل الإعلانية في أجهزة استقبال الويب لمزيد من المعلومات حول طريقة عمل الفواصل الإعلانية.

يمكن تحديد الفواصل على كلّ من المرسِل والمستلِم، ولكن يُنصح بتحديدها في جهاز الاستقبال على الويب وجهاز استقبال Android TV للحفاظ على سلوك متّسق على مختلف الأنظمة الأساسية.

على نظام التشغيل iOS، حدِّد الفواصل الإعلانية في أمر تحميل باستخدام السمة GCKAdBreakClipInfo وGCKAdBreakInfo:

سريعة
let breakClip1Builder = GCKAdBreakClipInfoBuilder(adBreakClipID: "bc0")
breakClip1Builder.title = "Clip title"
if let posterUrl = URL(string: "https://www.some.url") {
  breakClip1Builder.posterURL = posterUrl
}
breakClip1Builder.duration = 60
breakClip1Builder.whenSkippable = 5  // Set this field so that the ad is skippable
let breakClip1 = breakClip1Builder.build()

let breakClip2 = ...
let breakClip3 = ...


let break1 = GCKAdBreakInfoBuilder(adBreakID: "b0", adBreakClipIds: ["bc0", "bc1", "bc2"]).build()
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "entity")
...
mediaInfoBuilder.adBreaks = [break1]
mediaInfoBuilder.adBreakClips = [breakClip1, breakClip2, breakClip3]
...
mediaInformation = mediaInfoBuilder.build()

let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
mediaLoadRequestDataBuilder.mediaInformation = mediaInformation

sessionManager.currentSession?.remoteMediaClient?.loadMedia(with: mediaLoadRequestDataBuilder.build())
الهدف-ج
GCKAdBreakClipInfoBuilder *breakClipInfoBuilder = [[GCKAdBreakClipInfoBuilder alloc] initWithAdBreakClipID:@"bc0"];
breakClipInfoBuilder.title = @"Clip title";
breakClipInfoBuilder.posterURL = [[NSURL alloc] initWithString:@"https://www.some.url"];
breakClipInfoBuilder.duration = 60;
breakClipInfoBuilder.whenSkippable = 5;
GCKAdBreakClipInfo *breakClip1 = breakClipInfoBuilder.build;

GCKAdBreakClipInfo *breakClip2 = ...
GCKAdBreakClipInfo *breakClip3 = ...

GCKAdBreakInfo *break1 = [[GCKAdBreakInfoBuilder alloc] initWithAdBreakID:@"b0"
                                                           adBreakClipIds:@[@"bc0", @"bc1", @"bc2"]].build;

GCKMediaInformationBuilder *mediaInfoBuilder = [[GCKMediaInformationBuilder alloc]
                                                initWithEntity:@"entity"];
...
mediaInfoBuilder.adBreaks = @[break1];
mediaInfoBuilder.adBreakClips = @[breakClip1, breakClip2, breakClip3];
...
self.mediaInformation = [mediaInfoBuilder build];

GCKMediaLoadRequestDataBuilder *mediaLoadRequestDataBuilder = [[GCKMediaLoadRequestDataBuilder alloc] init];
mediaLoadRequestDataBuilder.mediaInformation = self.mediaInformation;

// Send a load request to the remote media client.
GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient
                                loadMediaWithLoadRequestData:[mediaLoadRequestDataBuilder build]];

معدل التشغيل المتغير

يمكن لتطبيقك عرض معدل تشغيل ملف الوسائط الحالي وتغييره. يمكنك ضبط السعر باستخدام -[setPlaybackRate:] أو -[setPlaybackRate:customData:] من GCKRemoteMediaClient، والوصول إلى GCKUIPlaybackRateController باستخدام playbackRateController من GCKUIMediaController، وعرض معدل التشغيل الحالي باستخدام playbackRate من GCKUIPlaybackRateController.

نموذج التعليمات البرمجية

يستخدم الملفان التاليان السمة GCKUIPlaybackRateController التي تتحكّم في معدّل التشغيل باستخدام عنصر تحكّم مقسّم يحتوي على أزرار "عادية" و"نصف سرعة" و"سرعة مزدوجة":

سريعة
import GoogleCast

/**
 * An implementation of GCKUIPlaybackRateController that controls playback rate
 * using a segmented control that has "normal", "half speed", and "double speed"
 * buttons.
 */
class SegmentedButtonPlaybackRateController: GCKUIPlaybackRateController {
  static let kSegmentNormal = 0;
  static let kSegmentHalfSpeed = 1;
  static let kSegmentDoubleSpeed = 2;

  var segmentedControl: UISegmentedControl!

  override var playbackRate: Float {
    didSet {
      var buttonIndex = 0

      // Map the playback rate to one of our three supported speeds.
      if playbackRate == 1.0 {
        buttonIndex = SegmentedButtonPlaybackRateController.kSegmentNormal
      } else if playbackRate < 1.0 {
        buttonIndex = SegmentedButtonPlaybackRateController.kSegmentHalfSpeed
      } else {
        buttonIndex = SegmentedButtonPlaybackRateController.kSegmentDoubleSpeed
      }

      segmentedControl?.selectedSegmentIndex = buttonIndex
    }
  }
  override var inputEnabled: Bool {
    didSet {
      segmentedControl?.isEnabled = inputEnabled
    }
  }

  /**
   * Designated initializer.
   *
   * @param segmentedControl The segmented control for changing/displaying the
   * playback rate.
   */
  convenience init(_ segmentedControl: UISegmentedControl) {
    self.init()
    self.segmentedControl = segmentedControl;

    segmentedControl.addTarget(self,
                               action: #selector(segmentedControlTapped(sender:)),
                               for: UIControl.Event.valueChanged)
  }

  @objc func segmentedControlTapped(sender: UISegmentedControl) {
    var playbackRate: Float = 1.0

    switch segmentedControl?.selectedSegmentIndex {
    case SegmentedButtonPlaybackRateController.kSegmentHalfSpeed:
      playbackRate = 0.5;
    case SegmentedButtonPlaybackRateController.kSegmentDoubleSpeed:
      playbackRate = 2.0;
    case SegmentedButtonPlaybackRateController.kSegmentNormal:
      fallthrough
    default:
      playbackRate = 1.0;
    }

    self.playbackRate = playbackRate
  }
}
الهدف-ج

SegmentedButtonPlaybackRateController.h

#import <GoogleCast/GoogleCast.h>
#import <UIKit/UIKit.h>

/**
 * An implementation of GCKUIPlaybackRateController that controls playback rate
 * using a segmented control that has "normal", "half speed", and "double speed"
 * buttons.
 */
@interface SegmentedButtonPlaybackRateController : GCKUIPlaybackRateController

/**
 * Designated initializer.
 *
 * @param segmentedControl The segmented control for changing/displaying the
 * playback rate.
 */
- (instancetype)initWithSegmentedControl:(UISegmentedControl *)segmentedControl;

@end

SegmentedButtonPlaybackRateController.m

#import "SegmentedButtonPlaybackRateController.h"

@interface SegmentedButtonPlaybackRateController () {
  UISegmentedControl *_segmentedControl;
}

@end

static const NSInteger kSegmentNormal = 0;
static const NSInteger kSegmentHalfSpeed = 1;
static const NSInteger kSegmentDoubleSpeed = 2;

@implementation SegmentedButtonPlaybackRateController

- (instancetype)initWithSegmentedControl:(UISegmentedControl *)segmentedControl {
  if (self = [super init]) {
    _segmentedControl = segmentedControl;
    [_segmentedControl addTarget:self
                          action:@selector(segmentedControlTapped:)
                forControlEvents:UIControlEventValueChanged];
  }
  return self;
}

- (void)setPlaybackRate:(float)playbackRate {
  [super setPlaybackRate:playbackRate];

  NSInteger buttonIndex = 0;

  // Map the playback rate to one of our three supported speeds.
  if (playbackRate == 1.0) {
    buttonIndex = kSegmentNormal;
  } else if (playbackRate < 1.0) {
    buttonIndex = kSegmentHalfSpeed;
  } else {
    buttonIndex = kSegmentDoubleSpeed;
  }

  _segmentedControl.selectedSegmentIndex = buttonIndex;
}

- (void)setInputEnabled:(BOOL)inputEnabled {
  _segmentedControl.enabled = inputEnabled;
  [super setInputEnabled:inputEnabled];
}

- (void)segmentedControlTapped:(id)sender {
  float playbackRate;

  switch (_segmentedControl.selectedSegmentIndex) {
    case kSegmentHalfSpeed:
      playbackRate = 0.5;
      break;

    case kSegmentDoubleSpeed:
      playbackRate = 2.0;
      break;

    case kSegmentNormal:
    default:
      playbackRate = 1.0;
      break;
  }

  self.playbackRate = playbackRate;
}

@end

إضافة قناة مخصّصة

يوفر إطار عمل Google Cast طريقتين لإنشاء قناة لإرسال رسائل مخصصة إلى مستقبل الويب:

  1. الهدف من السمة GCKCastChannel هو أن يتم تصنيفها ضمن فئة فرعية لتنفيذ قنوات غير بسيطة لها حالة مرتبطة.
  2. يتم توفير GCKGenericChannel كبديل للتصنيف الفرعي، فهي تمرِّر الرسائل المستلَمة إلى المفوَّض حتى تتم معالجتها في مكان آخر.

إليك مثال على استخدام GCKCastChannel:

سريعة
class HGCTextChannel: GCKCastChannel {
  override func didReceiveTextMessage(_ message: String) {
    print("received message: \(message)")
  }
}
الهدف-ج

HGCTextChannel.h

#import <GoogleCast/GCKCastChannel.h>

@interface HGCTextChannel : GCKCastChannel

@end

HGCTextChannel.m

#import "HGCTextChannel.h"

@implementation HGCTextChannel
- (void)didReceiveTextMessage:(NSString*)message {
  NSLog(@"received message: %@", message);
}

@end

يمكن تسجيل أي قناة في أي وقت. وإذا لم تكن الجلسة في حالة متّصلة حاليًا، سيتمّ ربط القناة تلقائيًا عندما تكون الجلسة نفسها مرتبطة، شرط أن تكون مساحة اسم القناة مضمّنة في قائمة البيانات الوصفية لمساحات الاسم المتوافقة الخاصة بتطبيق WebRecipient.

يتم تحديد كل قناة مخصّصة بمساحة اسم فريدة ويجب أن تبدأ بالبادئة urn:x-cast:، على سبيل المثال urn:x-cast:com.example.custom. من الممكن أن يكون لديك عدّة قنوات مخصّصة، لكل منها مساحة اسم فريدة. ويمكن أيضًا لتطبيق WebRecipients إرسال الرسائل واستلامها باستخدام مساحة الاسم نفسها.

سريعة
var error: GCKError?
let textChannel = HGCTextChannel.init(namespace: "urn:x-cast:com.google.cast.sample.helloworld")
sessionManager.currentCastSession?.add(textChannel)
textChannel.sendTextMessage("Hello World", error: &error)

if error != nil {
  print("Error sending text message \(error.debugDescription)")
}
الهدف-ج
NSError *error;
HGCTextChannel *textChannel = [[HGCTextChannel alloc] initWithNamespace:@"urn:x-cast:com.google.cast.sample.helloworld"];
[sessionManager.currentCastSession addChannel:textChannel];
[textChannel sendTextMessage:@"Hello World"
                       error:&error];

if (error != nil) {
  NSLog(@"Error sending text message: %@", error);
}

لتوفير المنطق المطلوب تنفيذه عندما تصبح قناة معيّنة مرتبطة أو غير متصلة، عليك إلغاء الطريقتَين -[didConnect] و-[didDisconnect] في حال استخدام GCKCastChannel، أو توفير عمليات تنفيذ لطريقتَي -[castChannelDidConnect:] و-[castChannelDidDisconnect:] GCKGenericChannelDelegate في حال استخدام GCKGenericChannel.

إتاحة التشغيل التلقائي

يُرجى الاطّلاع على واجهات برمجة تطبيقات التشغيل التلقائي والإضافة إلى قائمة المحتوى التالي.

إلغاء اختيار الصور والتخزين المؤقت

ستعرِض المكوّنات المختلفة لإطار العمل (لا سيّما مربّع حوار Google Cast ووحدة التحكّم الصغيرة ووحدة التحكّم الموسّعة وGCKUIMediaController في حال ضبطها) العمل الفني للوسائط التي يتم بثها حاليًا. يتم عادةً تضمين عناوين URL الخاصة بالصورة الفنية في GCKMediaMetadata للوسائط، ولكن قد يكون لدى تطبيق المرسِل مصدر بديل لعناوين URL.

يحدد بروتوكول GCKUIImagePicker وسائل لاختيار صورة مناسبة لاستخدام معين والحجم المطلوب. وهي تستخدم طريقة واحدة، -[getImageWithHints:fromMetadata:]، تتعامل مع كائن GCKUIImageHints وكائن GCKMediaMetadata كمعلَمتَين، وتعرض كائن GCKImage كنتيجة. يوفّر إطار العمل طريقة تنفيذ تلقائية لـ GCKUIImagePicker الذي يحدد دائمًا الصورة الأولى في قائمة الصور في عنصر GCKMediaMetadata، ولكن يمكن للتطبيق توفير طريقة تنفيذ بديلة من خلال ضبط السمة imagePicker في GCKCastContext.

يحدّد بروتوكول GCKUIImageCache أيضًا طريقة التخزين المؤقت للصور التي يتم تنزيلها من خلال إطار العمل باستخدام HTTPS. يوفّر إطار العمل طريقة تنفيذ تلقائية لـ GCKUIImageCache الذي يخزِّن ملفات الصور التي يتم تنزيلها في دليل ذاكرة التخزين المؤقت للتطبيق، إلا أنّ التطبيق يمكن أن يوفّر طريقة تنفيذ بديلة من خلال ضبط السمة imageCache على GCKCastContext سينجلتون.

الخطوات التالية

وبذلك تنتهي الميزات التي يمكنك إضافتها إلى تطبيق iOS Sender. يمكنك الآن إنشاء تطبيق مُرسِل لنظام أساسي آخر (Android أو الويب)، أو إنشاء جهاز استقبال الويب.