|
Antwort |
Registriert seit: 11. Aug 2007 357 Beiträge |
#1
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:
Peter
- (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; } |
Zitat |
Registriert seit: 22. Okt 2012 267 Beiträge |
#2
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:
Unter IOS geht das glaube ich auch, aber erst wenn du die Fehlenden Interfaces aus der MacApi.AVFoundation kopiert hast.
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; |
Zitat |
Registriert seit: 22. Okt 2012 267 Beiträge |
#3
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 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:
Das andere Problem ist ein SigSev 6 sobald man das asset übergibt. Aber das liegt eher an der Testimplementierung hier.
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; |
Zitat |
Registriert seit: 11. Aug 2007 357 Beiträge |
#4
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 |
Zitat |
Registriert seit: 22. Okt 2012 267 Beiträge |
#5
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:
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/
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; Christian |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |