Cast Application Framework receiver app not working on external cast device 2nd Geneation - google-cast

I'm trying to develop CAF v3 receiver app and it's working with in-built and setup boxes cast devices but not working on external cast devices.
External cast devices NC2-A65 throws PIPELINE_INITIALIZATION_ERROR or VIDEO_ERROR with shaka error code 3016
After debugging, observation is drm License Url doesn't get called when useLegacyDashSupport is true
Any help is appreciated
Here is the code,
<script>
const context = cast.framework.CastReceiverContext.getInstance();
context.setLoggerLevel(cast.framework.LoggerLevel.DEBUG);
const options = new cast.framework.CastReceiverOptions();
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const playerManager = context.getPlayerManager();
const playbackConfig = (Object.assign(new cast.framework.PlaybackConfig(), playerManager.getPlaybackConfig()));
options.maxInactivity = 3600;
options.supportedCommands = cast.framework.messages.Command.ALL_BASIC_MEDIA;
castDebugLogger.setEnabled(true);
// Show debug overlay
castDebugLogger.showDebugLogs(true);
let useLegacyDashSupport = false;
if (context.canDisplayType('video/mp4; codecs="avc1.640028"; width=3840; height=2160')) {
// The device and display can both do 4k. Assume a 4k limit.
castDebugLogger.info("Hardware Resolution: ", '3840x2160');;
options.touchScreenOptimizedApp = true;
} else {
// Chromecast has always been able to do 1080p. Assume a 1080p limit.
castDebugLogger.info("Hardware Resolution: ", '1920x1080');
useLegacyDashSupport = true;
}
options.useLegacyDashSupport = useLegacyDashSupport;
context.loadPlayerLibraries(useLegacyDashSupport);
context.addEventListener(cast.framework.system.EventType.ERROR, event => {
castDebugLogger.info('Context Error - ', JSON.stringify(event));
});
playerManager.addEventListener(cast.framework.events.EventType.ERROR, event => {
castDebugLogger.info('Error - ', "ERROR event: " + JSON.stringify(event));
});
playerManager.addEventListener(cast.framework.events.EventType.MEDIA_STATUS, (event) => {
castDebugLogger.info('Player State - ', event.mediaStatus.playerState);
});
// Intercept the LOAD request to be able to read in a contentId and get data.
playerManager.setMessageInterceptor(cast.framework.messages.MessageType.LOAD, loadRequestData => {
castDebugLogger.info('LoadRequest Data - ', JSON.stringify(loadRequestData));
const error = new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.LOAD_CANCELLED);
if (!loadRequestData.media) {
error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
return error;
}
if (!loadRequestData.media.contentId) {
error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
return error;
}
loadRequestData.autoplay = true;
let url = loadRequestData.media.contentId;
castDebugLogger.info('Content Id - ', url);
const ext = url.substring(url.lastIndexOf('.'), url.length);
loadRequestData.media.contentType = 'video/mp4';
if (ext.includes('mpd')) {
loadRequestData.media.contentType = 'application/dash+xml';
} else if (ext.includes('m3u8')) {
loadRequestData.media.contentType = 'application/vnd.apple.mpegurl';
// TODO: Create option to set hlsSegmentFormat option.
loadRequestData.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.TS;
} else if (ext.includes('ism')) {
loadRequestData.media.contentType = 'application/vnd.ms-sstr+xml';
}
if (loadRequestData.media.customData && loadRequestData.media.customData.drm) {
playerManager.setMediaPlaybackInfoHandler((loadRequest, playbackConfigData) => {
playbackConfigData.licenseUrl = loadRequest.media.customData.drm.widevine.url;
playbackConfigData.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
castDebugLogger.info('PlaybackConfig Data - ', JSON.stringify(playbackConfigData));
return playbackConfigData;
});
}
return loadRequestData;
});
options.playbackConfig = playbackConfig;
context.start(options);
</script>

Related

Gnome shell extension login callback

I've created a custom GJS extension to connect into VPN. Basically it's a wrapper around a shell script, which is controlled from taskbar. There is a one issue, that after PC goes into suspend mode and back, extension is still displaying, that is disconnected, while it's still connected.
const GObject = imports.gi.GObject;
const St = imports.gi.St;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gettext = imports.gettext;
const _ = Gettext.gettext;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const MainLoop = imports.mainloop;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
let close_connection = `
pkexec kill -SIGINT $(pidof openconnect) 2>&1
`;
let create_connection = `
trap clean SIGINT
clean() {
pkexec kill -SIGINT $(pidof openconnect) 2>&1
}
vpn-ura-pke &
wait
`;
let _icon;
let _connectionSwitch;
let _last_connection = false;
let _already_running = false;
let _proc = null;
const iconsState = [
'network-vpn-acquiring-symbolic', // disconnected
'network-vpn-symbolic' // connected
];
function setConnectionState(connected) {
// prevent same notification changing
if (_last_connection == connected) return;
_icon.icon_name = iconsState[connected ? 1 : 0];
Main.notify('VPN URA', (connected ? 'connected' : 'disconnected'));
_last_connection = connected;
}
// read line callback
function onProcLine(stream, result) {
try {
let line = stream.read_line_finish_utf8(result)[0];
if (line !== null) {
// process read line
log("onProcLine:" + line);
// check connection status
if (line.includes('Connected as ')) setConnectionState(true);
else if(line.includes('Logout successful')) setConnectionState(false);
stream.read_line_async(0, null, onProcLine.bind(this));
}
} catch (ex) {
logError(ex);
}
}
// exec async process
async function execCheck(argv) {
_proc = new Gio.Subprocess({
argv: argv,
flags: (Gio.SubprocessFlags.STDIN_PIPE |
Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_PIPE)
});
_proc.init(null);
try {
let stdoutStream = new Gio.DataInputStream({
base_stream: _proc.get_stdout_pipe()
});
stdoutStream.read_line_async(
GLib.PRIORITY_DEFAULT,
null,
onProcLine.bind(this)
);
_proc.wait_check_async(null, (_proc, res) => {
try {
if (!_proc.wait_check_finish(res)) {
let status = _proc.get_exit_status();
setConnectionState(false);
if (status != 0) {
throw new Gio.IOErrorEnum({
code: Gio.io_error_from_errno(status),
message: GLib.strerror(status)
});
}
}
} catch (ex) {
setConnectionState(false);
logError(ex);
} finally {
_connectionSwitch.setToggleState(false);
}
});
} catch (ex) {
logError(ex);
}
}
const Indicator = GObject.registerClass(
class Indicator extends PanelMenu.Button {
toggleConnection(enabled) {
if (enabled) {
log("enable connection");
// start process
execCheck([
'bash',
'-c',
create_connection]
);
} else {
log("disable conenction");
// close running process
if (_proc) _proc.send_signal(2);
else if (_already_running) {
// kill process
Gio.Subprocess.new([
'bash',
'-c',
close_connection],
Gio.SubprocessFlags.STDOUT_PIPE
);
_already_running = false;
setConnectionState(false);
}
}
}
_init() {
super._init(0.0, _('VPN URA'));
// set icon
_icon = new St.Icon({
icon_name: iconsState[0],
style_class: 'system-status-icon'
});
this.add_child(_icon);
// toggle connection
_connectionSwitch = new PopupMenu.PopupSwitchMenuItem('Connection', false);
_connectionSwitch.connect('toggled', (_item, state) => {
this.toggleConnection(state);
});
this.menu.addMenuItem(_connectionSwitch);
// check if process is not already running
let [, , , status] = GLib.spawn_command_line_sync('pidof openconnect');
if (status == 0) {
_already_running = true;
_connectionSwitch.setToggleState(true);
setConnectionState(true);
}
}
});
class Extension {
constructor(uuid) {
this._uuid = uuid;
}
enable() {
_already_running = false;
this._indicator = new Indicator();
Main.panel.addToStatusArea(this._uuid, this._indicator, 1);
}
disable() {
_proc = null;
_connectionSwitch = null;
_last_connection = false;
_icon.destroy();
_icon = null;
this._indicator.destroy();
this._indicator = null;
}
}
function init(meta) {
return new Extension(meta.uuid);
}
Even if I added a case, where I was trying to use pidof to detect if process is running already. It caught only the case, when process was started outside from extension, but not the case, which I wanted.
How to bind into callback, which is fired up when session is restored? Or is there any another way to handle this?
Thanks,
Andy
I do also attempt to write a basic gnome extension for the same purpose.
Being very new to gjs coding, I found those answers regarding connection updating, in my research.
Hope some of it might help you :
Util.spawnCommandLine does not work on GNOME Shell extension
How to get OS name while writing gnome-extensions
GLib run command with root privileges
Running an asynchronous function in a GNOME extension

'IObservable<SyncProgress>' does not contain a definition for 'CombineLatest'

I just took this code snippet from realm which fits my requirement .
var session = realm.GetSession();
var uploadProgress = session.GetProgressObservable(ProgressDirection.Upload, ProgressMode.ReportIndefinitely);
var downloadProgress = session.GetProgressObservable(ProgressDirection.Download, ProgressMode.ReportIndefinitely);
var token = uploadProgress.CombineLatest(downloadProgress, (upload, download) =>
{
return new
{
TotalTransferred = upload.TransferredBytes + download.TransferredBytes,
TotalTransferable = upload.TransferableBytes + download.TransferableBytes
};
})
.Throttle(TimeSpan.FromSeconds(0.1))
.ObserveOn(SynchronizationContext.Current)
.Subscribe(progress =>
{
if (progress.TotalTransferred < progress.TotalTransferable)
{
// Show spinner
}
else
{
// Hide spinner
}
});
But CombineLatest method is not available inside IObservable interface in Xamarin provided .Net subset.
But given I took this directly from real websites it is supposed to work .

How to send a excel attachment in QCubed

This is what i am trying
$attach is the path to my excel => c:/xampp/htdocs/project/excel.xlsx
Notification::SendEmail('xyz#gmail.com', 'abc#gmail.com', "Subject","message", $attach);
If the$attach is removed then the email will go.
But it fails if i add the attachment
SendEmail function
public static function SendEmail($mixFrom, $mixTo, $strSubject, $strMessage, $mixAttachment = null, $mixCc = null, $mixBcc = null) {
// Declaration of Local Variables
$strSMTPHost = QApplication::getSettingValue(Mssetting::SMTP_HOST);
$strSMTPPort = QApplication::getSettingValue(Mssetting::SMTP_PORT);
$objMessage = Swift_Message::newInstance($strSubject, $strMessage, 'text/html');
// Set the source/destination data
$objMessage->setFrom($mixFrom);
$objMessage->setTo($mixTo);
$objMessage->setCc($mixCc);
$objMessage->setBcc($mixBcc);
// Check for attachments
if(is_array($mixAttachment)) {
foreach($mixAttachment as $strFilePath)
$objMessage->attach (Swift_Attachment::fromPath ($strFilePath));
}
elseif(is_string($mixAttachment)) {
$objMessage->attach(Swift_Attachment::fromPath($mixAttachment));
}
// Setup the transport
$objTransport = Swift_SmtpTransport::newInstance();
if($strSMTPHost) $objTransport->setHost ($strSMTPHost);
if($strSMTPPort) $objTransport->setPort($strSMTPPort);
// Setup the mailer
$objMailer = Swift_Mailer::newInstance($objTransport);
// Send the message
$objMailer->send($objMessage, $arrFailures);
if($arrFailures)
return $arrFailures;
return true;
}
I got the answer for this,
use this code:
$Attachment = $file_path; // path to your excel
Notification::SendEmail('from#abc.com',to#abc,com, "Subject", "Message", $Attachment);
in Notification.php
class Notification {
public static function SendEmail($mixFrom, $mixTo, $strSubject, $strMessage, $mixAttachment, $mixCc = null, $mixBcc = null) {
// Declaration of Local Variables
$strSMTPHost = QApplication::getSettingValue(Mssetting::SMTP_HOST);
$strSMTPPort = QApplication::getSettingValue(Mssetting::SMTP_PORT);
$objMessage = Swift_Message::newInstance($strSubject, $strMessage, 'text/html');
// Set the source/destination data
$objMessage->setFrom($mixFrom);
$objMessage->setTo($mixTo);
$objMessage->setCc($mixCc);
$objMessage->setBcc($mixBcc);
// Check for attachments
if(is_array($mixAttachment)) {
foreach($mixAttachment as $strFilePath)
$objMessage->attach (Swift_Attachment::fromPath ($strFilePath));
}
elseif(is_string($mixAttachment)) {
$objMessage->attach(Swift_Attachment::fromPath($mixAttachment));
}
// Setup the transport
$objTransport = Swift_SmtpTransport::newInstance();
if($strSMTPHost) $objTransport->setHost ($strSMTPHost);
if($strSMTPPort) $objTransport->setPort($strSMTPPort);
// Setup the mailer
$objMailer = Swift_Mailer::newInstance($objTransport);
// Send the message
$objMailer->send($objMessage, $arrFailures);
if($arrFailures)
return $arrFailures;
return true;
}
This will do the work.

How do you implement a stream that properly handles backpressure in node.js?

I can't for the life of me figure out how to implement a stream that properly handles backpressure. Should you never use pause and resume?
I have this implementation I'm trying to get to work correctly:
var StreamPeeker = exports.StreamPeeker = function(myStream, callback) {
stream.Readable.call(this, {highWaterMark: highWaterMark})
this.stream = myStream
myStream.on('readable', function() {
var data = myStream.read(5000)
//process.stdout.write("Eff: "+data)
if(data !== null) {
if(!this.push(data)) {
process.stdout.write("Pause")
this.pause()
}
callback(data)
}
}.bind(this))
myStream.on('end', function() {
this.push(null)
}.bind(this))
}
util.inherits(StreamPeeker, stream.Readable)
StreamPeeker.prototype._read = function() {
process.stdout.write("resume")
//this.resume() // putting this in for some reason causes the stream to not output???
}
It correctly sends output, but doesn't correctly produce backpressure. How can I change it to properly support backpressure?
Ok I finally figured it out after lots of trial and error. A couple guidelines:
Never ever use pause or resume (otherwise it'll go into legacy "flowing" mode)
Never add a "data" event listener (otherwise it'll go into legacy "flowing" mode)
Its the implementor's responsibility to keep track of when the source is readable
Its the implementor's responsibility to keep track of when the destination wants more data
The implementation should not read any data until the _read method is called
The argument to read tells the source to give it that many bytes, it probably best to pass the argument passed to this._read into the source's read method. This way you should be able to configure how much to read at a time at the destination, and the rest of the stream chain should be automatic.
So this is what I changed it to:
Update: I created a Readable that is much easier to implement with proper back-pressure, and should have just as much flexibility as node's native streams.
var Readable = stream.Readable
var util = require('util')
// an easier Readable stream interface to implement
// requires that subclasses:
// implement a _readSource function that
// * gets the same parameter as Readable._read (size)
// * should return either data to write, or null if the source doesn't have more data yet
// call 'sourceHasData(hasData)' when the source starts or stops having data available
// calls 'end()' when the source is out of data (forever)
var Stream666 = {}
Stream666.Readable = function() {
stream.Readable.apply(this, arguments)
if(this._readSource === undefined) {
throw new Error("You must define a _readSource function for an object implementing Stream666")
}
this._sourceHasData = false
this._destinationWantsData = false
this._size = undefined // can be set by _read
}
util.inherits(Stream666.Readable, stream.Readable)
Stream666.Readable.prototype._read = function(size) {
this._destinationWantsData = true
if(this._sourceHasData) {
pushSourceData(this, size)
} else {
this._size = size
}
}
Stream666.Readable.prototype.sourceHasData = function(_sourceHasData) {
this._sourceHasData = _sourceHasData
if(_sourceHasData && this._destinationWantsData) {
pushSourceData(this, this._size)
}
}
Stream666.Readable.prototype.end = function() {
this.push(null)
}
function pushSourceData(stream666Readable, size) {
var data = stream666Readable._readSource(size)
if(data !== null) {
if(!stream666Readable.push(data)) {
stream666Readable._destinationWantsData = false
}
} else {
stream666Readable._sourceHasData = false
}
}
// creates a stream that can view all the data in a stream and passes the data through
// correctly supports backpressure
// parameters:
// stream - the stream to peek at
// callback - called when there's data sent from the passed stream
var StreamPeeker = function(myStream, callback) {
Stream666.Readable.call(this)
this.stream = myStream
this.callback = callback
myStream.on('readable', function() {
this.sourceHasData(true)
}.bind(this))
myStream.on('end', function() {
this.end()
}.bind(this))
}
util.inherits(StreamPeeker, Stream666.Readable)
StreamPeeker.prototype._readSource = function(size) {
var data = this.stream.read(size)
if(data !== null) {
this.callback(data)
return data
} else {
this.sourceHasData(false)
return null
}
}
Old Answer:
// creates a stream that can view all the data in a stream and passes the data through
// correctly supports backpressure
// parameters:
// stream - the stream to peek at
// callback - called when there's data sent from the passed stream
var StreamPeeker = exports.StreamPeeker = function(myStream, callback) {
stream.Readable.call(this)
this.stream = myStream
this.callback = callback
this.reading = false
this.sourceIsReadable = false
myStream.on('readable', function() {
this.sourceIsReadable = true
this._readMoreData()
}.bind(this))
myStream.on('end', function() {
this.push(null)
}.bind(this))
}
util.inherits(StreamPeeker, stream.Readable)
StreamPeeker.prototype._read = function() {
this.reading = true
if(this.sourceIsReadable) {
this._readMoreData()
}
}
StreamPeeker.prototype._readMoreData = function() {
if(!this.reading) return;
var data = this.stream.read()
if(data !== null) {
if(!this.push(data)) {
this.reading = false
}
this.callback(data)
}
}

CLLocationManager and CLGeoCoder

I want to use coordinate of the actual location (CLLocationManager) to reverse geocoding (CLGeoCoder).
I have this code:
locationMgr = new CLLocationManager();
locationMgr.DesiredAccuracy = CLLocation.AccuracyNearestTenMeters;
locationMgr.DistanceFilter = 10;
locationMgr.UpdatedLocation += (object sender, CLLocationUpdatedEventArgs e) => {
Task.latitude = e.NewLocation.Coordinate.Latitude;
Task.longitude = e.NewLocation.Coordinate.Longitude;
locationMgr.StopUpdatingLocation();
};
btnLocation = new UIBarButtonItem(UIImage.FromFile("Icons/no-gps.png"), UIBarButtonItemStyle.Plain, (s,e) => {
if (CLLocationManager.LocationServicesEnabled) {
locationMgr.StartUpdatingLocation();
geoCoder = new CLGeocoder();
geoCoder.ReverseGeocodeLocation(new CLLocation(Task.latitude, Task.longitude), (CLPlacemark[] place, NSError error) => {
adr = place[0].Name+"\n"+place[0].Locality+"\n"+place[0].Country;
Utils.ShowAlert(XmlParse.LocalText("Poloha"), Task.latitude.ToString()+"\n"+Task.longitude.ToString()+"\n\n"+adr);
});
}
else {
Utils.ShowAlert(XmlParse.LocalText("PolohVypnut"));
}
});
Because UpdatedLocation() take some seconds, input of ReverseGeocodeLocation() is Task.latitude=0 and Task.longitude=0.
How can I wait for right values (Task.latitude, Task.longitude) before ReverseGoecodeLocation()?
Thanks for any help.
Your geocoder's ReverseGeocodeLocation method is called before the CLLocationManager gets a location.
Calling StartUpdatingLocation does not mean that the UpdatedLocation event will be triggered immediately. Furthermore, if you are on iOS 6, UpdatedLocation will never be triggered. Use the LocationsUpdated event instead.
Example:
locationManager.LocationsUpdated += (sender, args) => {
// Last item in the array is the latest location
CLLocation latestLocation = args.Locations[args.Locations.Length - 1];
geoCoder = new CLGeocoder();
geoCoder.ReverseGeocodeLocation(latestLocation, (pl, er) => {
// Read placemarks here
});
};
locationManager.StartUpdatingLocation();

Resources