diff --git a/src/android/Chromecast.java b/src/android/Chromecast.java index ef61e7c..cbb829e 100644 --- a/src/android/Chromecast.java +++ b/src/android/Chromecast.java @@ -4,6 +4,9 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import android.util.Log; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CallbackContext; @@ -299,12 +302,12 @@ public boolean addMessageListener(String namespace, CallbackContext callbackCont * @param callbackContext called with .success or .error depending on the result * @return true for cordova */ - public boolean loadMedia(String contentId, JSONObject customData, String contentType, Integer duration, String streamType, Boolean autoPlay, Integer currentTime, JSONObject metadata, JSONObject textTrackStyle, final CallbackContext callbackContext) { - return this.loadMedia(contentId, customData, contentType, duration, streamType, autoPlay, new Double(currentTime.doubleValue()), metadata, textTrackStyle, callbackContext); + public boolean loadMedia(String contentId, JSONObject customData, String contentType, Integer duration, String streamType, Boolean autoPlay, Integer currentTime, JSONObject metadata, JSONObject textTrackStyle, JSONArray tracks, final CallbackContext callbackContext) { + return this.loadMedia(contentId, customData, contentType, duration, streamType, autoPlay, new Double(currentTime.doubleValue()), metadata, textTrackStyle, tracks, callbackContext); } - private boolean loadMedia(String contentId, JSONObject customData, String contentType, Integer duration, String streamType, Boolean autoPlay, Double currentTime, JSONObject metadata, JSONObject textTrackStyle, final CallbackContext callbackContext) { - this.media.loadMedia(contentId, customData, contentType, duration, streamType, autoPlay, currentTime, metadata, textTrackStyle, callbackContext); + private boolean loadMedia(String contentId, JSONObject customData, String contentType, Integer duration, String streamType, Boolean autoPlay, Double currentTime, JSONObject metadata, JSONObject textTrackStyle, JSONArray tracks, final CallbackContext callbackContext) { + this.media.loadMedia(contentId, customData, contentType, duration, streamType, autoPlay, currentTime, metadata, textTrackStyle, tracks, callbackContext); return true; } @@ -318,6 +321,16 @@ public boolean mediaPlay(CallbackContext callbackContext) { return true; } + /** + * Play on the current media in the current session. + * @param callbackContext called with .success or .error depending on the result + * @return true for cordova + */ + public boolean setMediaPlayBackRate(String playbackRate, CallbackContext callbackContext) { + media.setPlayBackRate(playbackRate, callbackContext); + return true; + } + /** * Pause on the current media in the current session. * @param callbackContext called with .success or .error depending on the result diff --git a/src/android/ChromecastSession.java b/src/android/ChromecastSession.java index cdefe04..224e47c 100644 --- a/src/android/ChromecastSession.java +++ b/src/android/ChromecastSession.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.List; import org.apache.cordova.CallbackContext; import org.json.JSONArray; @@ -219,14 +220,14 @@ public void onResult(Status result) { * @param textTrackStyle - The text track style * @param callback called with success or error */ - public void loadMedia(String contentId, JSONObject customData, String contentType, long duration, String streamType, boolean autoPlay, double currentTime, JSONObject metadata, JSONObject textTrackStyle, CallbackContext callback) { + public void loadMedia(String contentId, JSONObject customData, String contentType, long duration, String streamType, boolean autoPlay, double currentTime, JSONObject metadata, JSONObject textTrackStyle, JSONArray tracks, CallbackContext callback) { if (client == null || session == null) { callback.error("session_error"); return; } activity.runOnUiThread(new Runnable() { public void run() { - MediaInfo mediaInfo = ChromecastUtilities.createMediaInfo(contentId, customData, contentType, duration, streamType, metadata, textTrackStyle); + MediaInfo mediaInfo = ChromecastUtilities.createMediaInfo(contentId, customData, contentType, duration, streamType, metadata, textTrackStyle, tracks); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaInfo) .setAutoplay(autoPlay) @@ -271,6 +272,24 @@ public void run() { }); } + /** + * Media API - Calls play on the current media. + * @param callback called with success or error + */ + public void setPlayBackRate(String playbackRate, CallbackContext callback) { + if (client == null || session == null) { + callback.error("session_error"); + return; + } + activity.runOnUiThread(new Runnable() { + public void run() { + Double d = Double.valueOf(playbackRate); + client.setPlaybackRate(d) + .setResultCallback(getResultCallback(callback, "Failed to setPlaybackRate.")); + } + }); + } + /** * Media API - Calls pause on the current media. * @param callback called with success or error diff --git a/src/android/ChromecastUtilities.java b/src/android/ChromecastUtilities.java index 7039db0..0137399 100644 --- a/src/android/ChromecastUtilities.java +++ b/src/android/ChromecastUtilities.java @@ -2,6 +2,7 @@ import android.graphics.Color; import android.net.Uri; +import java.util.ArrayList; import androidx.annotation.NonNull; import androidx.mediarouter.media.MediaRouter; @@ -827,6 +828,7 @@ static MediaInfo createMediaInfo(JSONObject mediaInfo) { String streamType = "unknown"; JSONObject metadata = new JSONObject(); JSONObject textTrackStyle = new JSONObject(); + JSONArray tracks = new JSONArray(); // Try to get the actual values try { @@ -858,10 +860,10 @@ static MediaInfo createMediaInfo(JSONObject mediaInfo) { } catch (JSONException e) { } - return createMediaInfo(contentId, customData, contentType, duration, streamType, metadata, textTrackStyle); + return createMediaInfo(contentId, customData, contentType, duration, streamType, metadata, textTrackStyle, tracks); } - static MediaInfo createMediaInfo(String contentId, JSONObject customData, String contentType, long duration, String streamType, JSONObject metadata, JSONObject textTrackStyle) { + static MediaInfo createMediaInfo(String contentId, JSONObject customData, String contentType, long duration, String streamType, JSONObject metadata, JSONObject textTrackStyle, JSONArray tracks) { MediaInfo.Builder mediaInfoBuilder = new MediaInfo.Builder(contentId); mediaInfoBuilder.setMetadata(createMediaMetadata(metadata)); @@ -887,6 +889,26 @@ static MediaInfo createMediaInfo(String contentId, JSONObject customData, String .setStreamDuration(duration) .setTextTrackStyle(trackStyle); + if (tracks != null) { + List mediaTracks = new ArrayList(); + + for (int i = 0; i < tracks.length(); i++) { + try { + JSONObject track = tracks.getJSONObject(i); + MediaTrack mediaTrack = new MediaTrack.Builder(track.getInt("trackId"), MediaTrack.TYPE_TEXT) + .setName(track.getString("name")) + .setSubtype(MediaTrack.SUBTYPE_SUBTITLES) + .setContentId(track.getString("trackContentId")) + .setLanguage(track.getString("language")) + .build(); + mediaTracks.add(mediaTrack); + } catch (JSONException e) { + } + } + + mediaInfoBuilder.setMediaTracks(mediaTracks); + } + return mediaInfoBuilder.build(); } diff --git a/src/ios/MLPCastUtilities.h b/src/ios/MLPCastUtilities.h index 741031f..a313033 100644 --- a/src/ios/MLPCastUtilities.h +++ b/src/ios/MLPCastUtilities.h @@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MLPCastUtilities : NSObject -+(GCKMediaInformation *)buildMediaInformation:(NSString *)contentUrl customData:(id )customData contentType:(NSString *)contentType duration:(double)duration streamType:(NSString *)streamType startTime:(double)startTime metaData:(NSDictionary *)metaData textTrackStyle:(NSDictionary *)textTrackStyle; ++(GCKMediaInformation *)buildMediaInformation:(NSString *)contentUrl customData:(id )customData contentType:(NSString *)contentType duration:(double)duration streamType:(NSString *)streamType startTime:(double)startTime metaData:(NSDictionary *)metaData textTrackStyle:(NSDictionary *)textTrackStyle tracks:(NSArray *)tracks; +(GCKMediaQueueItem *)buildMediaQueueItem:(NSDictionary *)item; + (GCKMediaTextTrackStyle *)buildTextTrackStyle:(NSDictionary *)data; +(GCKMediaMetadata*)buildMediaMetadata:(NSDictionary*)data; diff --git a/src/ios/MLPCastUtilities.m b/src/ios/MLPCastUtilities.m index 8df3abc..f3a4bbb 100644 --- a/src/ios/MLPCastUtilities.m +++ b/src/ios/MLPCastUtilities.m @@ -8,7 +8,7 @@ @implementation MLPCastUtilities NSDictionary* queueOrderIDsByItemId = nil; -+ (GCKMediaInformation *)buildMediaInformation:(NSString *)contentUrl customData:(id )customData contentType:(NSString *)contentType duration:(double)duration streamType:(NSString *)streamType startTime:(double)startTime metaData:(NSDictionary *)metaData textTrackStyle:(NSDictionary *)textTrackStyle { ++ (GCKMediaInformation *)buildMediaInformation:(NSString *)contentUrl customData:(id )customData contentType:(NSString *)contentType duration:(double)duration streamType:(NSString *)streamType startTime:(double)startTime metaData:(NSDictionary *)metaData textTrackStyle:(NSDictionary *)textTrackStyle tracks:(NSArray *)tracks { NSURL* url = [NSURL URLWithString:contentUrl]; GCKMediaInformationBuilder* mediaInfoBuilder = [[GCKMediaInformationBuilder alloc] initWithContentURL:url]; @@ -26,7 +26,23 @@ + (GCKMediaInformation *)buildMediaInformation:(NSString *)contentUrl customData mediaInfoBuilder.startAbsoluteTime = startTime; mediaInfoBuilder.metadata = [MLPCastUtilities buildMediaMetadata:metaData]; mediaInfoBuilder.textTrackStyle = [MLPCastUtilities buildTextTrackStyle:textTrackStyle]; - + + if (tracks != (id)[NSNull null]) { + NSMutableArray* mediaTracks = [NSMutableArray new]; + for (id track in tracks) { + GCKMediaTrack *mediaTrack = [[GCKMediaTrack alloc] initWithIdentifier:[track[@"trackId"] intValue] + contentIdentifier:track[@"trackContentId"] + contentType:track[@"trackContentType"] + type:GCKMediaTrackTypeText + textSubtype:GCKMediaTextTrackSubtypeCaptions + name:track[@"name"] + languageCode:track[@"language"] + customData:track[@"customData"]]; + [mediaTracks addObject:mediaTrack]; + } + mediaInfoBuilder.mediaTracks = mediaTracks; + } + return [mediaInfoBuilder build]; } @@ -42,7 +58,7 @@ + (GCKMediaQueueItem *)buildMediaQueueItem:(NSDictionary *)item { queueItemBuilder.startTime = startTime; queueItemBuilder.preloadTime = [item[@"preloadTime"] doubleValue]; - queueItemBuilder.mediaInformation = [MLPCastUtilities buildMediaInformation:media[@"contentId"] customData:media[@"customData"] contentType:media[@"contentType"] duration:duration streamType:media[@"streamType"] startTime:startTime metaData:media[@"metadata"] textTrackStyle:item[@"textTrackStyle"]]; + queueItemBuilder.mediaInformation = [MLPCastUtilities buildMediaInformation:media[@"contentId"] customData:media[@"customData"] contentType:media[@"contentType"] duration:duration streamType:media[@"streamType"] startTime:startTime metaData:media[@"metadata"] textTrackStyle:item[@"textTrackStyle"] tracks:item[@"tracks"]]; return [queueItemBuilder build]; } @@ -52,22 +68,21 @@ + (GCKMediaTextTrackStyle *)buildTextTrackStyle:(NSDictionary *)data { GCKMediaTextTrackStyle* mediaTextTrackStyle = [GCKMediaTextTrackStyle createDefault]; if (error == nil) { - NSString* bkgColor = data[@"backgroundColor"]; + // Not working dictionary + // NSString* bkgColor = data[@"backgroundColor"]; + NSString* bkgColor = @"#000000fa"; if (bkgColor != nil) { mediaTextTrackStyle.backgroundColor = [[GCKColor alloc] initWithCSSString:bkgColor]; - } NSObject* customData = data[@"customData"]; if (bkgColor != nil) { mediaTextTrackStyle.customData = customData; - } NSString* edgeColor = data[@"edgeColor"]; if (edgeColor != nil) { mediaTextTrackStyle.edgeColor = [[GCKColor alloc] initWithCSSString:edgeColor]; - } NSString* edgeType = data[@"edgeType"]; diff --git a/src/ios/MLPChromecast.h b/src/ios/MLPChromecast.h index df3a72b..05a8bcc 100644 --- a/src/ios/MLPChromecast.h +++ b/src/ios/MLPChromecast.h @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)addMessageListener:(CDVInvokedUrlCommand*)command; - (void)sendMessage:(CDVInvokedUrlCommand*) command; - (void)mediaPlay:(CDVInvokedUrlCommand*)command; -- (void)mediaPause:(CDVInvokedUrlCommand*)command; +- (void)setMediaPlayBackRate:(CDVInvokedUrlCommand*)command; - (void)mediaSeek:(CDVInvokedUrlCommand*)command; - (void)mediaStop:(CDVInvokedUrlCommand*)command; - (void)mediaEditTracksInfo:(CDVInvokedUrlCommand*)command; diff --git a/src/ios/MLPChromecast.m b/src/ios/MLPChromecast.m index 7db8d5d..715568a 100644 --- a/src/ios/MLPChromecast.m +++ b/src/ios/MLPChromecast.m @@ -267,7 +267,8 @@ - (void)loadMedia:(CDVInvokedUrlCommand*) command { double currentTime = [command.arguments[6] doubleValue]; NSDictionary* metadata = command.arguments[7]; NSDictionary* textTrackStyle = command.arguments[8]; - GCKMediaInformation* mediaInfo = [MLPCastUtilities buildMediaInformation:contentId customData:customData contentType:contentType duration:duration streamType:streamType startTime:currentTime metaData:metadata textTrackStyle:textTrackStyle]; + NSArray* tracks = command.arguments[9]; + GCKMediaInformation* mediaInfo = [MLPCastUtilities buildMediaInformation:contentId customData:customData contentType:contentType duration:duration streamType:streamType startTime:currentTime metaData:metadata textTrackStyle:textTrackStyle tracks:tracks]; [self.currentSession loadMediaWithCommand:command mediaInfo:mediaInfo autoPlay:autoplay currentTime:currentTime]; } @@ -288,6 +289,10 @@ - (void)mediaPlay:(CDVInvokedUrlCommand*)command { [self.currentSession mediaPlayWithCommand:command]; } +- (void)setMediaPlayBackRate:(CDVInvokedUrlCommand*)command { + NSString* playbackRate = command.arguments[0]; + [self.currentSession sendPlaybackRateWithCommand:command playbackRate:playbackRate]; +} - (void)mediaPause:(CDVInvokedUrlCommand*)command { [self.currentSession mediaPauseWithCommand:command]; } @@ -305,7 +310,7 @@ - (void)mediaStop:(CDVInvokedUrlCommand*)command { - (void)mediaEditTracksInfo:(CDVInvokedUrlCommand*)command { NSArray* activeTrackIds = command.arguments[0]; - NSData* textTrackStyle = command.arguments[1]; + NSDictionary* textTrackStyle = command.arguments[1]; GCKMediaTextTrackStyle* textTrackStyleObject = [MLPCastUtilities buildTextTrackStyle:textTrackStyle]; [self.currentSession setActiveTracksWithCommand:command activeTrackIds:activeTrackIds textTrackStyle:textTrackStyleObject]; diff --git a/src/ios/MLPChromecastSession.h b/src/ios/MLPChromecastSession.h index 27ef93d..4851407 100644 --- a/src/ios/MLPChromecastSession.h +++ b/src/ios/MLPChromecastSession.h @@ -32,6 +32,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)mediaSeekWithCommand:(CDVInvokedUrlCommand*)command position:(NSTimeInterval)position resumeState:(GCKMediaResumeState)resumeState; - (void)mediaPlayWithCommand:(CDVInvokedUrlCommand*)command; - (void)mediaPauseWithCommand:(CDVInvokedUrlCommand*)command; +- (void)sendPlaybackRateWithCommand:(CDVInvokedUrlCommand*)command playbackRate:(NSString*)playbackRate; - (void)mediaStopWithCommand:(CDVInvokedUrlCommand*)command; - (void)setActiveTracksWithCommand:(CDVInvokedUrlCommand*)command activeTrackIds:(NSArray*)activeTrackIds textTrackStyle:(GCKMediaTextTrackStyle*)textTrackStyle; - (void)queueLoadItemsWithCommand:(CDVInvokedUrlCommand *)command queueItems:(NSArray *)queueItems startIndex:(NSInteger)startIndex repeatMode:(GCKMediaRepeatMode)repeatMode; diff --git a/src/ios/MLPChromecastSession.m b/src/ios/MLPChromecastSession.m index 1cc71f1..92a59ba 100644 --- a/src/ios/MLPChromecastSession.m +++ b/src/ios/MLPChromecastSession.m @@ -263,6 +263,12 @@ - (void)mediaPauseWithCommand:(CDVInvokedUrlCommand*)command { request.delegate = [self createMediaUpdateRequestDelegate:command]; } +- (void)sendPlaybackRateWithCommand:(CDVInvokedUrlCommand*)command playbackRate:(NSString*)playbackRate { + float playbackRateFloat = [playbackRate floatValue]; + GCKRequest* request = [self.remoteMediaClient setPlaybackRate:playbackRateFloat]; + request.delegate = [self createMediaUpdateRequestDelegate:command]; +} + - (void)mediaStopWithCommand:(CDVInvokedUrlCommand*)command { GCKRequest* request = [self.remoteMediaClient stop]; request.delegate = [self createMediaUpdateRequestDelegate:command]; diff --git a/www/chrome.cast.js b/www/chrome.cast.js index ec07f16..f29c0f6 100644 --- a/www/chrome.cast.js +++ b/www/chrome.cast.js @@ -725,7 +725,7 @@ chrome.cast.Session.prototype.loadMedia = function (loadRequest, successCallback var self = this; var mediaInfo = loadRequest.media; - execute('loadMedia', mediaInfo.contentId, mediaInfo.customData || {}, mediaInfo.contentType, mediaInfo.duration || 0.0, mediaInfo.streamType, loadRequest.autoplay || false, loadRequest.currentTime || 0, mediaInfo.metadata || {}, mediaInfo.textTrackSytle || {}, function (err, obj) { + execute('loadMedia', mediaInfo.contentId, mediaInfo.customData || {}, mediaInfo.contentType, mediaInfo.duration || 0.0, mediaInfo.streamType, loadRequest.autoplay || false, loadRequest.currentTime || 0, mediaInfo.metadata || {}, mediaInfo.textTrackSytle || {}, mediaInfo.tracks || null, function (err, obj) { if (!err) { self._loadNewMedia(obj); successCallback(self._getMedia()); @@ -1100,10 +1100,21 @@ chrome.cast.media.Media.prototype.play = function (playRequest, successCallback, }); }; +chrome.cast.media.Media.prototype.setPlayBackRate = function (playBackRate, successCallback, errorCallback) { + if (this._preCheck(errorCallback)) { return; } + execute('setMediaPlayBackRate', playBackRate, function (err) { + if (!err) { + successCallback && successCallback(); + } else { + handleError(err, errorCallback); + } + }); +}; + /** * Pauses the media item. * @param {chrome.cast.media.PauseRequest} pauseRequest The optional media pause request. - * @param {function} successCallback Invoked on success. + * @param {function} \ successCallback Invoked on success. * @param {function} errorCallback Invoked on error. The possible errors are TIMEOUT, API_NOT_INITIALIZED, INVALID_PARAMETER, CHANNEL_ERROR, SESSION_ERROR, and EXTENSION_MISSING. */ chrome.cast.media.Media.prototype.pause = function (pauseRequest, successCallback, errorCallback) {