Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Cross-Platform-Entwicklung (https://www.delphipraxis.net/91-cross-platform-entwicklung/)
-   -   Fairplay Apple (https://www.delphipraxis.net/208459-fairplay-apple.html)

Peter666 31. Jul 2021 21:34

Fairplay Apple
 
Hi,

ich versuche gerade den Alcinoe Player unter IOS mit DRM Streams zu verwenden.
Soweit ich das ganze verstanden habe, muss ich ein AVURLAsset erstellen in dem ein Callback für die Ressource hinterlegt wird. Hat jemand ungefähr eine Idee wie ich das umsetzen kann? Der ObjectiveC Code schaut wie folgt aus.


Code:
- (void)loadPlayerView
{
    NSURL *url = [NSURL URLWithString:@"https://nyoba.innoplayer.co/cdn/videos/fairplay/muxed/encrypted/prog_index.m3u8"];
   
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
    [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
   
    AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:[AVPlayerItem playerItemWithAsset:asset]];
   
    AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
    controller.player = player;
    [self presentViewController:controller animated:YES completion:nil];
}

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    NSString *keyURL = @"https://nyoba.innoplayer.co/cdn/videos/fairplay/muxed/encrypted/Key.txt";
    NSURL *urlx = [NSURL URLWithString:keyURL];
   
    NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:urlx];
    [urlRequest setTimeoutInterval:20];
    [urlRequest setHTTPMethod:@"GET"];
   
    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"requestHLSKeyWithURL httpResponse ==== %ld", (long)httpResponse.statusCode);
       
        if (data != nil && (httpResponse.statusCode == 200 || httpResponse.statusCode == 100))
        {
            NSLog(@"requestHLSKeyWithURL data ==== %@", data);
            [[loadingRequest contentInformationRequest] setContentType:AVStreamingKeyDeliveryPersistentContentKeyType];
            [[loadingRequest contentInformationRequest] setByteRangeAccessSupported:YES];
            [[loadingRequest contentInformationRequest] setContentLength:[data length]];
            [[loadingRequest dataRequest] respondWithData:data];
            [loadingRequest finishLoading];
        }
        else
        {
            NSLog(@"requestHLSKeyWithURL ==== NOT OK");
            [loadingRequest finishLoading];
        }
       
    }];
   
    [task resume];
   
    return YES;
}
Peter

CHackbart 5. Aug 2021 12:27

AW: Fairplay Apple
 
Hallo,

ich glaube das geht so nicht. Hier ist etwas dazu geschrieben:
https://medium.com/@burak.oguz/ios-f...s-8aff3d4248dd

Im Prinzip müsstest so etwas machen:

Delphi-Quellcode:
 
type
  TContentKeyDelegate = class(TOCLocal, AVContentKeySessionDelegate)
  public
    procedure contentKeySession(session: AVContentKeySession;
      persistableContentKey: NSData; keyIdentifier: Pointer); overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession;
      keyRequest: AVContentKeyRequest; err: NSError); overload; cdecl;
    function contentKeySession(session: AVContentKeySession;
      keyRequest: AVContentKeyRequest;
      retryReason: AVContentKeyRequestRetryReason): Boolean; overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession;
      keyRequest: AVContentKeyRequest); overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession;
      keyRequest: AVPersistableContentKeyRequest); overload; cdecl;
    procedure contentKeySessionContentKeyRequestDidSucceed
      (session: AVContentKeySession; keyRequest: AVContentKeyRequest); cdecl;
    procedure contentKeySessionContentProtectionSessionIdentifierDidChange
      (session: AVContentKeySession); cdecl;
    procedure contentKeySessionDidGenerateExpiredSessionReport
      (session: AVContentKeySession); cdecl;
    procedure contentKeySessionDidProvideRenewingContentKeyRequest
      (session: AVContentKeySession; keyRequest: AVContentKeyRequest); cdecl;
  end;

  TContentKeyManager = class
  private
    FContentKeySession: AVContentKeySession;
    FContentKeyDelegate: TContentKeyDelegate;
    FContentKeyDelegateQueue: dispatch_queue_t;
  public
    constructor Create;
    destructor Destroy; override;

    function IsAvailable: Boolean;

    property ContentKeySession: AVContentKeySession read FContentKeySession;
  end;
...

  constructor TContentKeyManager.Create;
begin
  FContentKeySession := TAVContentKeySession.Wrap
    (TAVContentKeySession.OCClass.contentKeySessionWithKeySystem
    (AVContentKeySystemFairPlayStreaming));

  FContentKeyDelegate := TContentKeyDelegate.Create;
  FContentKeyDelegateQueue := dispatch_queue_create('fairplay.ContentKeyDelegateQueue',
    dispatch_queue_t(DISPATCH_QUEUE_SERIAL));

  FContentKeySession.setDelegate(FContentKeyDelegate.GetObjectID,
    FContentKeyDelegateQueue);
end;
Unter IOS geht das glaube ich auch, aber erst wenn du die Fehlenden Interfaces aus der MacApi.AVFoundation kopiert hast.

CHackbart 5. Aug 2021 13:31

AW: Fairplay Apple
 
Nachtrag,

du musst dann bei der Wiedergabe dein AVUrlAsset zu deiner AVContentKeySession via addContentKeyRecipient übergeben. Zumindest in der Theorie. In der Praxis gibt es 2 Probleme:

Wenn ich

Delphi-Quellcode:
procedure contentKeySession(session: AVContentKeySession; keyRequest: AVPersistableContentKeyRequest); overload; cdecl;
in AVContentKeySessionDelegate behalte, crasht die Anwendung sobald ich FContentKeyDelegate := TContentKeyDelegate.Create; ausführe. Ich habe keine Ahnung woran das liegen kann, ist eventuell ein Bug. Vielleicht kann das ja jemand reproduzieren?

Delphi-Quellcode:
 
type
  AVContentKeySessionDelegate = interface(IObjectiveC)
    ['{3066C8DB-B31C-4339-A49D-912BA193660C}']
    procedure contentKeySession(session: AVContentKeySession; persistableContentKey: NSData; keyIdentifier: Pointer); overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession; keyRequest: AVContentKeyRequest; err: NSError); overload; cdecl;
    function contentKeySession(session: AVContentKeySession; keyRequest: AVContentKeyRequest;
      retryReason: AVContentKeyRequestRetryReason): Boolean; overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession; keyRequest: AVContentKeyRequest); overload; cdecl;

    procedure contentKeySession(session: AVContentKeySession; keyRequest: AVPersistableContentKeyRequest); overload; cdecl;
    procedure contentKeySessionContentKeyRequestDidSucceed(session: AVContentKeySession; keyRequest: AVContentKeyRequest); cdecl;
    procedure contentKeySessionContentProtectionSessionIdentifierDidChange(session: AVContentKeySession); cdecl;
    procedure contentKeySessionDidGenerateExpiredSessionReport(session: AVContentKeySession); cdecl;
    procedure contentKeySessionDidProvideRenewingContentKeyRequest(session: AVContentKeySession; keyRequest: AVContentKeyRequest); cdecl;
  end;

  TContentKeyDelegate = class(TOCLocal, AVContentKeySessionDelegate)
  public
    procedure contentKeySession(session: AVContentKeySession;
      persistableContentKey: NSData; keyIdentifier: Pointer); overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession;
      keyRequest: AVContentKeyRequest; err: NSError); overload; cdecl;
    function contentKeySession(session: AVContentKeySession;
      keyRequest: AVContentKeyRequest;
      retryReason: AVContentKeyRequestRetryReason): Boolean; overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession;
      keyRequest: AVContentKeyRequest); overload; cdecl;
    procedure contentKeySession(session: AVContentKeySession;
      keyRequest: AVPersistableContentKeyRequest); overload; cdecl;
    procedure contentKeySessionContentKeyRequestDidSucceed
      (session: AVContentKeySession; keyRequest: AVContentKeyRequest); cdecl;
    procedure contentKeySessionContentProtectionSessionIdentifierDidChange
      (session: AVContentKeySession); cdecl;
    procedure contentKeySessionDidGenerateExpiredSessionReport
      (session: AVContentKeySession); cdecl;
    procedure contentKeySessionDidProvideRenewingContentKeyRequest
      (session: AVContentKeySession; keyRequest: AVContentKeyRequest); cdecl;
  end;

{ TContentKeyDelegate }

procedure TContentKeyDelegate.contentKeySession(session: AVContentKeySession;
  persistableContentKey: NSData; keyIdentifier: Pointer);
begin
end;

procedure TContentKeyDelegate.contentKeySession(session: AVContentKeySession;
  keyRequest: AVContentKeyRequest; err: NSError);
begin
end;

function TContentKeyDelegate.contentKeySession(session: AVContentKeySession;
  keyRequest: AVContentKeyRequest;
  retryReason: AVContentKeyRequestRetryReason): Boolean;
begin
  result := false;
end;

procedure TContentKeyDelegate.contentKeySession(session: AVContentKeySession;
  keyRequest: AVContentKeyRequest);
begin
end;

procedure TContentKeyDelegate.contentKeySession(session: AVContentKeySession;
  keyRequest: AVPersistableContentKeyRequest);
begin
end;

procedure TContentKeyDelegate.contentKeySessionContentKeyRequestDidSucceed
  (session: AVContentKeySession; keyRequest: AVContentKeyRequest);
begin
end;

procedure TContentKeyDelegate.
  contentKeySessionContentProtectionSessionIdentifierDidChange
  (session: AVContentKeySession);
begin
end;

procedure TContentKeyDelegate.contentKeySessionDidGenerateExpiredSessionReport
  (session: AVContentKeySession);
begin
end;

procedure TContentKeyDelegate.
  contentKeySessionDidProvideRenewingContentKeyRequest
  (session: AVContentKeySession; keyRequest: AVContentKeyRequest);
begin
end;

...
FContentKeyDelegate := TContentKeyDelegate.Create;
Das andere Problem ist ein SigSev 6 sobald man das asset übergibt. Aber das liegt eher an der Testimplementierung hier.

Peter666 18. Aug 2021 09:07

AW: Fairplay Apple
 
Ich hab das ganze immer noch nicht zum Laufen bekommen und das ganze auch mit einer älteren Delphiversion probiert. Vielleicht hat ja jemand noch eine Idee?

Peter

CHackbart 18. Aug 2021 19:55

AW: Fairplay Apple
 
Liste der Anhänge anzeigen (Anzahl: 2)
Klar,

anbei der Code zum Hinzufügen von DRM Infos an den AVPlayer. Ich dachte, ich hab dir das schon via PM zugesandt.

Damit du DRM geschützte Inhalte lesen kannst musst du noch den Player anpassen. Für FMX.Media.Mac.pas sieht das so aus:

Delphi-Quellcode:
constructor TMacMedia.Create(const AFileName: string);
var
  LURL: NSUrl;
  LAbsoluteFileName: string;
  LAsset: AVURLAsset;
begin
  inherited Create(AFileName);
  AVMediaTypeAudio; // Force load the framework
  if FileExists(FileName) then
  begin
    if ExtractFilePath(FileName).IsEmpty then
      LAbsoluteFileName := TPath.Combine(TPath.GetHomePath, FileName)
    else
      LAbsoluteFileName := FileName;
    LURL := TNSUrl.Wrap(TNSUrl.OCClass.fileURLWithPath(StrToNSStr(LAbsoluteFileName)));
  end
  else
    LURL := StrToNSUrl(FileName);
  if LURL = nil then
    raise EFileNotFoundException.Create(SSpecifiedFileNotFound);
  FPixelBufferBitmap := TBitmap.Create;
  LAsset := TAVURLAsset.Wrap(TAVURLAsset.OCClass.URLAssetWithURL(LURL, nil));
  if LAsset.hasProtectedContent then
   ContentKeyManager.addContentKeyRecipient(LAsset);

  FPlayerItem := TAVPlayerItem.Wrap(TAVPlayerItem.OCClass.playerItemWithAsset(LAsset));
  FPlayerItem.retain;
  FPlayer := TAVPlayer.Wrap(TAVPlayer.OCClass.playerWithPlayerItem(FPlayerItem));
  FPlayer.retain;
  FPlayerLayer := TAVPlayerLayer.Wrap(TAVPlayerLayer.OCClass.playerLayerWithPlayer(FPlayer));
  FPlayerLayer.retain;
  FPlayerLayer.setVideoGravity(CocoaNSStringConst(libAVFoundation, 'AVLayerVideoGravityResizeAspectFill'));
  FPlayerLayer.setAutoresizingMask(kCALayerWidthSizable or kCALayerHeightSizable);
  FVideoView := TNSView.Create;
  FVideoView.retain;
  FVideoView.setWantsLayer(True);
  FVideoView.layer.addSublayer(FPlayerLayer);
  SetupVideoOutput;
end;
Die ContentManager Klasse hat zwei Callbacks die implementiert werden müssen. Zum einen musst du das Zertifikat als TMemoryStream liefern und danach den Request für den Contentkey an deinen Server liefern und das Ergebnis, dann zurück an Fairplay geben. Das funktioniert auf MacOS und iOS. Im englischsprachigen Bereich von Delphi-Praxis steht einiges darüber hier: https://en.delphipraxis.net/topic/53...layer-and-drm/

Christian


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:27 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz