Repeating Notifications Lost After Android Device Reboot Xamarin Forms App C# - c#

I am working on a Xamarin Forms app in C#. I followed this tutorial to setup local notifications that are repeating:
https://www.c-sharpcorner.com/article/how-to-send-local-notification-with-repeat-interval-in-xamarin-forms/
For the most part everything works with regards to the notifications firing. However if I reboot the device I no longer get any notifications.
Here is my broadcast receiver class:
[BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class ScheduledAlarmHandler : BroadcastReceiver
{
public const string LocalNotificationKey = "LocalNotification";
NotificationManager manager;
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
public const string TitleKey = "title";
public const string MessageKey = "message";
bool channelInitialized = false;
public override void OnReceive(Context context, Intent intent)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
ComponentName receiver = new ComponentName(AndroidApp.Context, Class.FromType(typeof(ScheduledAlarmHandler)).Name);
PackageManager pm = AndroidApp.Context.PackageManager;
pm.SetComponentEnabledSetting(receiver, ComponentEnabledState.Enabled, ComponentEnableOption.DontKillApp);
var extra = intent.GetStringExtra(LocalNotificationKey);
var notification = DeserializeNotification(extra);
// Generating notification
var builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentTitle(notification.Title)
.SetContentText(notification.Body)
.SetSmallIcon(notification.IconId)
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Ringtone))
.SetAutoCancel(true);
var resultIntent = AndroidNotificationManager.GetLauncherActivity();
resultIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
var stackBuilder = Android.Support.V4.App.TaskStackBuilder.Create(AndroidApp.Context);
stackBuilder.AddNextIntent(resultIntent);
Random random = new Random();
int randomNumber = random.Next(9999 - 1000) + 1000;
var resultPendingIntent =
stackBuilder.GetPendingIntent(randomNumber, (int)PendingIntentFlags.CancelCurrent);
builder.SetContentIntent(resultPendingIntent);
// Sending notification
var notificationManager = NotificationManagerCompat.From(AndroidApp.Context);
var notificationToNotify = builder.Build();
notificationManager.Notify(randomNumber, notificationToNotify);
}
private LocalNotification DeserializeNotification(string notificationString)
{
var xmlSerializer = new XmlSerializer(typeof(LocalNotification));
using (var stringReader = new StringReader(notificationString))
{
var notification = (LocalNotification)xmlSerializer.Deserialize(stringReader);
return notification;
}
}
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
}
The tutorial didn't have the IntentFilter attribute added to the class or updates to the manifest file, but I added those in later while searching for solutions.
Here is my AndroidManifest file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.service_buddy">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
<application android:label="Service_Buddy.Android"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application>
<receiver android:name="ServiceBuddy.Droid.ScheduledAlarmHandler" android:enabled="true" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
</application>
</manifest>```

As far as I know , After rebooting there is no memory ,it is gone , therefor there is nothing to do job .
Don't waste your time , just use Shiny , it is a real problem solver in realm of background and foreground service in xamarin .
shiny getting started

Related

Videos unavailable when trying to play embedded youtube url (Xamrin.Android)

I'm using Xamarin Android to develop an app for song suggestions and I am using embeded youtube url, and the thing I'm trying to understand is why some videos are playing fine and other are showing error and not playing at all. The error is 'Video Unavailable'.
I found a solution that said to upload the html to a server, but I can't upload it because I change the video dynamically
Thanks.
protected async override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Window.RequestFeature(Android.Views.WindowFeatures.NoTitle);
SetContentView(Resource.Layout.songSuggestion_layout);
SetViews();
categories = new Categories();
songs = new Songs();
songSuggsted = new Songs();
playlist = new Playlist();
await SetListSongsOfSuggestion();
var metrics = Resources.DisplayMetrics;
//fix video screen height and width
//intDisplayWidth = (FnConvertPixelsToDp(metrics.WidthPixels) + 200);
//intDisplayHeight = (FnConvertPixelsToDp(metrics.HeightPixels)) / (2);
intDisplayHeight = 500;
intDisplayWidth = 1000;
PlayInWebView();
}
public void PlayInWebView()
{
string strUrl = songSuggsted[position].UrlSong;
// string strUrl= "https://www.youtube.com/watch?v=u_VsvZmIWxY";
string id = FnGetVideoID(strUrl);
if (!string.IsNullOrEmpty(id))
{
strUrl = string.Format("http://youtube.com/embed/{0}", id);
}
else
{
Toast.MakeText(this, "Video url is not in correct format", ToastLength.Long).Show();
return;
}
string html = #"<html><body><iframe width=""videoWidth"" height=""videoHeight"" src=""strUrl""></iframe></body></html>";
var myWebView = (WebView)FindViewById(Resource.Id.videoView);
var settings = myWebView.Settings;
settings.JavaScriptEnabled = true;
settings.UseWideViewPort = true;
settings.LoadWithOverviewMode = true;
settings.JavaScriptCanOpenWindowsAutomatically = true;
settings.DomStorageEnabled = true;
settings.SetRenderPriority(WebSettings.RenderPriority.High);
settings.BuiltInZoomControls = false;
settings.JavaScriptCanOpenWindowsAutomatically = true;
myWebView.SetWebChromeClient(new WebChromeClient());
settings.AllowFileAccess = true;
settings.SetPluginState(WebSettings.PluginState.On);
string strYouTubeURL = html.Replace("videoWidth", intDisplayWidth.ToString()).Replace("videoHeight", intDisplayHeight.ToString()).Replace("strUrl", strUrl);
myWebView.LoadDataWithBaseURL(null, strYouTubeURL, "text/html", "UTF-8", null);
}
static string FnGetVideoID(string strVideoURL)
{
const string regExpPattern = #"youtu(?:\.be|be\.com)/(?:.*v(?:/|=)|(?:.*/)?)([a-zA-Z0-9-_]+)";
//for Vimeo: vimeo\.com/(?:.*#|.*/videos/)?([0-9]+)
var regEx = new Regex(regExpPattern);
var match = regEx.Match(strVideoURL);
return match.Success ? match.Groups[1].Value : null;
}
int FnConvertPixelsToDp(float pixelValue)
{
var dp = (int)((pixelValue) / Resources.DisplayMetrics.Density);
return dp;
}
You could try the code below:
Create a webview in android layout.
Xaml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.webkit.WebView
android:id="#+id/webView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Code Behind:
var myWebView = FindViewById<WebView>(Resource.Id.webView1);
WebSettings setting = myWebView.Settings;
setting.JavaScriptEnabled = true;
myWebView.SetWebChromeClient(new WebChromeClient());
myWebView.LoadUrl("https://www.youtube.com/watch?v=6hzrDeceEKc");

Using information off local database to display notification information in Android using Xamarin

Hello I have a repeating notification.
I would like the information displayed on the notification to be of a new element off of my local database every time it is called.
For example I would have a table in my database with 3 elements containing a unique string of titles and messages per index. Every time the notification is called a new index of the database is called to bring forward a new element.
SQL Data:
Table: Table Name
Element 1: String 1, String One
Element 2: String 2, String Two
Element 3: String 3, String Three
Android notification out:
First notification: Title: 1, Message: One
Second notification: Title: 2, Message: Two
Third notification: Title: 3, Message: Three
How do I trigger a Xamarin cross-platform method that goes through my database for the next element every time an android notification is called? How do I keep track of what the next notification will be in the background?
Here is my android notification code:
//used: https://blog.nishanil.com/2014/12/16/scheduled-notifications-in-android-using-alarm-manager/
// new notifications service
public class AndroidReminderService : IReminderService
{
public void Remind(DateTime dateTime, string title, string message)
{
// create alarm intent
Intent alarmIntent = new Intent(Application.Context, typeof(AlarmReceiver));
alarmIntent.PutExtra("message", message);
alarmIntent.PutExtra("title", title);
// specify the broadcast receiver
PendingIntent pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
AlarmManager alarmManager = (AlarmManager)Application.Context.GetSystemService(Context.AlarmService);
// set the time when app is woken up
// todo: this is where time is adjusted
alarmManager.SetInexactRepeating(AlarmType.ElapsedRealtimeWakeup, SystemClock.ElapsedRealtime() + 5* 1000, 1000, pendingIntent);
}
}
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
const string channelId = "default";
public override void OnReceive(Context context, Intent intent)
{
var message = intent.GetStringExtra("message");
var title = intent.GetStringExtra("title");
var notIntent = new Intent(context, typeof(MainActivity));
var contentIntent = PendingIntent.GetActivity(context, 0, notIntent, PendingIntentFlags.CancelCurrent);
var manager = NotificationManagerCompat.From(context);
var style = new NotificationCompat.BigTextStyle();
style.BigText(message);
// sets notifcation logo
int resourceId;
resourceId = Resource.Drawable.xamarin_logo;
var wearableExtender = new NotificationCompat.WearableExtender().SetBackground(BitmapFactory.DecodeResource(context.Resources, resourceId));
// Generate a notification
// todo look at notification compat properties
var builder = new NotificationCompat.Builder(context, channelId)
.SetContentIntent(contentIntent)
.SetSmallIcon(Resource.Drawable.notification_template_icon_bg)
.SetContentTitle(title)
.SetContentText(message)
.SetStyle(style)
.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis())
.SetAutoCancel(true)
.Extend(wearableExtender);
var notification = builder.Build();
manager.Notify(0, notification);
}
}
The trick seemed to be serialize the information.
My OnReceive does the SQL logic and then calls a function to serialize it all then i call the next remind
[assembly: Xamarin.Forms.Dependency(typeof(DebugNoti))]
namespace LawsForImpact.Droid
{
class DebugNoti : IDebugNotiServ
{
int _notificationIconId { get; set; }
internal string _randomNumber;
public void LocalNotification(string title, string body, int id, DateTime notifyTime)
{
var intent = CreateIntent(id);
var localNotification = new LocalNotification();
localNotification.Title = title;
localNotification.Body = body;
localNotification.Id = id;
localNotification.NotifyTime = notifyTime;
if (_notificationIconId != 0)
{
localNotification.IconId = _notificationIconId;
}
else
{
localNotification.IconId = Resource.Drawable.notification_template_icon_bg;
}
var serializedNotification = SerializeNotification(localNotification);
intent.PutExtra(DebugScheduledAlarmHandler.LocalNotificationKey, serializedNotification);
Random generator = new Random();
_randomNumber = generator.Next(100000, 999999).ToString("D6");
var pendingIntent = PendingIntent.GetBroadcast(Application.Context, Convert.ToInt32(_randomNumber), intent, PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
// todo change variable of alarm manager
//totalMilliSeconds, repeateForMinute
alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, 3000, pendingIntent);
}
public void Cancel(int id)
{
var intent = CreateIntent(id);
var pendingIntent = PendingIntent.GetBroadcast(Application.Context, Convert.ToInt32(_randomNumber), intent, PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
alarmManager.Cancel(pendingIntent);
var notificationManager = NotificationManagerCompat.From(Application.Context);
notificationManager.CancelAll();
notificationManager.Cancel(id);
}
public static Intent GetLauncherActivity()
{
var packageName = Application.Context.PackageName;
return Application.Context.PackageManager.GetLaunchIntentForPackage(packageName);
}
private Intent CreateIntent(int id)
{
return new Intent(Application.Context, typeof(DebugScheduledAlarmHandler))
.SetAction("LocalNotifierIntent" + id);
}
private AlarmManager GetAlarmManager()
{
var alarmManager = Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
return alarmManager;
}
private string SerializeNotification(LocalNotification notification)
{
var xmlSerializer = new XmlSerializer(notification.GetType());
using (var stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, notification);
return stringWriter.ToString();
}
}
}
[BroadcastReceiver(Enabled = true, Label = "Debug Noti")]
public class DebugScheduledAlarmHandler : BroadcastReceiver
{
public const string LocalNotificationKey = "LocalNotification";
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
const int pendingIntentId = 0;
public const string TitleKey = "title";
public const string MessageKey = "message";
bool channelInitialized = false;
NotificationManager manager;
NotificationCompat.BigTextStyle textStyle = new NotificationCompat.BigTextStyle();
Dictionary<string,int> nQueue = Global.notifQueue;
private SQLiteConnection _sqLiteConnection ;
public override async void OnReceive(Context context, Intent intent)
{
_sqLiteConnection = await Xamarin.Forms.DependencyService.Get<ISQLite>().GetConnection();
var listDataPower = _sqLiteConnection.Table<Power>().ToList();
if (!channelInitialized)
{
CreateNotificationChannel();
}
intent.SetFlags(ActivityFlags.SingleTop);
intent.PutExtra("OpenPage", "SomePage");
var extra = intent.GetStringExtra(LocalNotificationKey);
var notification = DeserializeNotification(extra);
int notiID = notification.Id;
PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId, intent, PendingIntentFlags.OneShot);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
.SetContentTitle(listDataPower[notiID].Title)
.SetContentText(listDataPower[notiID].Description)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.notify_panel_notification_icon_bg))
.SetSmallIcon(Resource.Drawable.notify_panel_notification_icon_bg)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate)
.SetStyle(textStyle);
var resultIntent = DebugNoti.GetLauncherActivity();
resultIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
var stackBuilder = Android.Support.V4.App.TaskStackBuilder.Create(Application.Context);
stackBuilder.AddNextIntent(resultIntent);
Random random = new Random();
int randomNumber = random.Next(9999 - 1000) + 1000;
var resultPendingIntent =
stackBuilder.GetPendingIntent(randomNumber, (int)PendingIntentFlags.Immutable);
builder.SetContentIntent(resultPendingIntent);
// Sending notification
var notificationManager = NotificationManagerCompat.From(Application.Context);
notificationManager.Notify(randomNumber, builder.Build());
notiID++;
Xamarin.Forms.DependencyService.Get<IDebugNotiServ>().LocalNotification(listDataPower[notiID].Title, listDataPower[notiID].Description, notiID, DateTime.Now);
}
private LocalNotification DeserializeNotification(string notificationString)
{
var xmlSerializer = new XmlSerializer(typeof(LocalNotification));
using (var stringReader = new StringReader(notificationString))
{
var notification = (LocalNotification)xmlSerializer.Deserialize(stringReader);
return notification;
}
}
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
}
}

Xamarin.Android notification with custom controls does not work in API26

I have a notification that shows a button to start a timer. This button works fine on API25, but with API26 and greater, the button does nothing.
I have already set up a notification channel. The notification itself shows up correctly, so I don't think it's a channel issue.
Here I add the play/pause intent
var playPauseIntent = new Intent("com.example.example.timer");
var timerNotificationIntentValue = this.GetTimerNotificationIntentValue(timerAction);
playPauseIntent.PutExtra("timerNotification", timerNotificationIntentValue);
const int playPauseIntentId = 0;
var playPausePendingIntent = PendingIntent.GetBroadcast(this.context, playPauseIntentId, playPauseIntent, PendingIntentFlags.UpdateCurrent);
contentView.SetOnClickPendingIntent(Resource.Id.btn_start_pause, playPausePendingIntent);
And this is how I create the notification
var channelId = "11e9a3a1-aebf-425d-a9e8-fcc2fb139664";
var channelName = "General";
NotificationChannel channel;
channel = notificationManager.GetNotificationChannel(channelName);
if (channel == null)
{
channel = new NotificationChannel(channelId, channelName, NotificationImportance.Max);
channel.LockscreenVisibility = NotificationVisibility.Public;
notificationManager.CreateNotificationChannel(channel);
}
channel?.Dispose();
Notification.DecoratedCustomViewStyle notificationStyle = new Notification.DecoratedCustomViewStyle();
notification = new Notification.Builder(this.context, "11e9a3a1-aebf-425d-a9e8-fcc2fb139664")
.SetSmallIcon(Resource.Drawable.ic_logo)
.SetStyle(notificationStyle)
.SetCustomContentView(contentView)
.Build();
Then I just notify
notification.Flags |= NotificationFlags.OngoingEvent;
notificationManager.Notify("1", notification);
When tapping on the button, nothing happens on API26.
Something I notice, is that on API25, when I tap the button, I get to the BroadcastReceiver, whereas on API26 I don't
Changed this
var playPauseIntent = new Intent("com.example.example.timer");
to
var playPauseIntent = new Intent(this.context, typeof(MyNotificationBroadcastReceiver));
and now it seems to be working OK.

Cutting a live stream into separate mp4 files

I am doing a research for cutting a live stream in piece and save it as mp4 files. I am using this source for the proof of concept:
https://learn.microsoft.com/en-us/azure/media-services/media-services-dotnet-creating-live-encoder-enabled-channel#download-sample
And this is the example code I use:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MediaServices.Client;
using Newtonsoft.Json.Linq;
namespace AMSLiveTest
{
class Program
{
private const string StreamingEndpointName = "streamingendpoint001";
private const string ChannelName = "channel001";
private const string AssetlName = "asset001";
private const string ProgramlName = "program001";
// Read values from the App.config file.
private static readonly string _mediaServicesAccountName =
ConfigurationManager.AppSettings["MediaServicesAccountName"];
private static readonly string _mediaServicesAccountKey =
ConfigurationManager.AppSettings["MediaServicesAccountKey"];
// Field for service context.
private static CloudMediaContext _context = null;
private static MediaServicesCredentials _cachedCredentials = null;
static void Main(string[] args)
{
// Create and cache the Media Services credentials in a static class variable.
_cachedCredentials = new MediaServicesCredentials(
_mediaServicesAccountName,
_mediaServicesAccountKey);
// Used the cached credentials to create CloudMediaContext.
_context = new CloudMediaContext(_cachedCredentials);
IChannel channel = CreateAndStartChannel();
// Set the Live Encoder to point to the channel's input endpoint:
string ingestUrl = channel.Input.Endpoints.FirstOrDefault().Url.ToString();
// Use the previewEndpoint to preview and verify
// that the input from the encoder is actually reaching the Channel.
string previewEndpoint = channel.Preview.Endpoints.FirstOrDefault().Url.ToString();
IProgram program = CreateAndStartProgram(channel);
ILocator locator = CreateLocatorForAsset(program.Asset, program.ArchiveWindowLength);
IStreamingEndpoint streamingEndpoint = CreateAndStartStreamingEndpoint();
GetLocatorsInAllStreamingEndpoints(program.Asset);
// Once you are done streaming, clean up your resources.
Cleanup(streamingEndpoint, channel);
}
public static IChannel CreateAndStartChannel()
{
//If you want to change the Smooth fragments to HLS segment ratio, you would set the ChannelCreationOptions’s Output property.
IChannel channel = _context.Channels.Create(
new ChannelCreationOptions
{
Name = ChannelName,
Input = CreateChannelInput(),
Preview = CreateChannelPreview()
});
//Starting and stopping Channels can take some time to execute. To determine the state of operations after calling Start or Stop, query the IChannel.State .
channel.Start();
return channel;
}
private static ChannelInput CreateChannelInput()
{
return new ChannelInput
{
StreamingProtocol = StreamingProtocol.RTMP,
AccessControl = new ChannelAccessControl
{
IPAllowList = new List<IPRange>
{
new IPRange
{
Name = "TestChannelInput001",
// Setting 0.0.0.0 for Address and 0 for SubnetPrefixLength
// will allow access to IP addresses.
Address = IPAddress.Parse("0.0.0.0"),
SubnetPrefixLength = 0
}
}
}
};
}
private static ChannelPreview CreateChannelPreview()
{
return new ChannelPreview
{
AccessControl = new ChannelAccessControl
{
IPAllowList = new List<IPRange>
{
new IPRange
{
Name = "TestChannelPreview001",
// Setting 0.0.0.0 for Address and 0 for SubnetPrefixLength
// will allow access to IP addresses.
Address = IPAddress.Parse("0.0.0.0"),
SubnetPrefixLength = 0
}
}
}
};
}
public static void UpdateCrossSiteAccessPoliciesForChannel(IChannel channel)
{
var clientPolicy =
#"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers=""*"" http-methods=""*"">
<domain uri=""*""/>
</allow-from>
<grant-to>
<resource path=""/"" include-subpaths=""true""/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>";
var xdomainPolicy =
#"<?xml version=""1.0"" ?>
<cross-domain-policy>
<allow-access-from domain=""*"" />
</cross-domain-policy>";
channel.CrossSiteAccessPolicies.ClientAccessPolicy = clientPolicy;
channel.CrossSiteAccessPolicies.CrossDomainPolicy = xdomainPolicy;
channel.Update();
}
public static IProgram CreateAndStartProgram(IChannel channel)
{
IAsset asset = _context.Assets.Create(AssetlName, AssetCreationOptions.None);
// Create a Program on the Channel. You can have multiple Programs that overlap or are sequential;
// however each Program must have a unique name within your Media Services account.
IProgram program = channel.Programs.Create(ProgramlName, TimeSpan.FromHours(3), asset.Id);
program.Start();
return program;
}
public static ILocator CreateLocatorForAsset(IAsset asset, TimeSpan ArchiveWindowLength)
{
// You cannot create a streaming locator using an AccessPolicy that includes write or delete permissions.
var locator = _context.Locators.CreateLocator
(
LocatorType.OnDemandOrigin,
asset,
_context.AccessPolicies.Create
(
"Live Stream Policy",
ArchiveWindowLength,
AccessPermissions.Read
)
);
return locator;
}
public static IStreamingEndpoint CreateAndStartStreamingEndpoint()
{
var options = new StreamingEndpointCreationOptions
{
Name = StreamingEndpointName,
ScaleUnits = 1,
AccessControl = GetAccessControl(),
CacheControl = GetCacheControl()
};
IStreamingEndpoint streamingEndpoint = _context.StreamingEndpoints.Create(options);
streamingEndpoint.Start();
return streamingEndpoint;
}
private static StreamingEndpointAccessControl GetAccessControl()
{
return new StreamingEndpointAccessControl
{
IPAllowList = new List<IPRange>
{
new IPRange
{
Name = "Allow all",
Address = IPAddress.Parse("0.0.0.0"),
SubnetPrefixLength = 0
}
},
AkamaiSignatureHeaderAuthenticationKeyList = new List<AkamaiSignatureHeaderAuthenticationKey>
{
new AkamaiSignatureHeaderAuthenticationKey
{
Identifier = "My key",
Expiration = DateTime.UtcNow + TimeSpan.FromDays(365),
Base64Key = Convert.ToBase64String(GenerateRandomBytes(16))
}
}
};
}
private static byte[] GenerateRandomBytes(int length)
{
var bytes = new byte[length];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(bytes);
}
return bytes;
}
private static StreamingEndpointCacheControl GetCacheControl()
{
return new StreamingEndpointCacheControl
{
MaxAge = TimeSpan.FromSeconds(1000)
};
}
public static void UpdateCrossSiteAccessPoliciesForStreamingEndpoint(IStreamingEndpoint streamingEndpoint)
{
var clientPolicy =
#"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers=""*"" http-methods=""*"">
<domain uri=""*""/>
</allow-from>
<grant-to>
<resource path=""/"" include-subpaths=""true""/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>";
var xdomainPolicy =
#"<?xml version=""1.0"" ?>
<cross-domain-policy>
<allow-access-from domain=""*"" />
</cross-domain-policy>";
streamingEndpoint.CrossSiteAccessPolicies.ClientAccessPolicy = clientPolicy;
streamingEndpoint.CrossSiteAccessPolicies.CrossDomainPolicy = xdomainPolicy;
streamingEndpoint.Update();
}
public static void GetLocatorsInAllStreamingEndpoints(IAsset asset)
{
var locators = asset.Locators.Where(l => l.Type == LocatorType.OnDemandOrigin);
var ismFile = asset.AssetFiles.AsEnumerable().FirstOrDefault(a => a.Name.EndsWith(".ism"));
var template = new UriTemplate("{contentAccessComponent}/{ismFileName}/manifest");
var urls = locators.SelectMany(l =>
_context
.StreamingEndpoints
.AsEnumerable()
.Where(se => se.State == StreamingEndpointState.Running)
.Select(
se =>
template.BindByPosition(new Uri("http://" + se.HostName),
l.ContentAccessComponent,
ismFile.Name)))
.ToArray();
}
public static void Cleanup(IStreamingEndpoint streamingEndpoint,
IChannel channel)
{
if (streamingEndpoint != null)
{
streamingEndpoint.Stop();
streamingEndpoint.Delete();
}
IAsset asset;
if (channel != null)
{
foreach (var program in channel.Programs)
{
asset = _context.Assets.Where(se => se.Id == program.AssetId)
.FirstOrDefault();
program.Stop();
program.Delete();
if (asset != null)
{
foreach (var l in asset.Locators)
l.Delete();
asset.Delete();
}
}
channel.Stop();
channel.Delete();
}
}
}
}
Now I want to make a method to cut a live stream for example every 15 minutes and save it as mp4 but don't know where to start.
Can someone point me in the right direction?
Kind regards
UPDATE:
I want to save the mp4 files on my hard disk.
You can use ffmpeg to save the stream on your hard drive. Please see the code:
ffmpeg -i InputStreamURL -acodec aac -strict -2 -vcodec libx264 -hls_wrap 100 -f hls -hls_time 20 /var/www/html/ts/1.m3u8
The -hls_time 20 use for the time of your saved data. In fact you use ffmpeg for a HLS stream but its doing the things you want. You could access the saved data on your Hard drive in /var/www/html/ts/ (and you could change this as you want).
Or could use VLC :
cvlc -vvv rtp://#239.1.2.1:60001
--sout '#std{access=livehttp{seglen=5,delsegs=true,numsegs=5,
index=/path/to/stream.m3u8,
index-url=http://example.org/stream-########.ts},
mux=ts{use-key-frames},
dst=/path/to/stream-########.ts}'
In above command either you could change the duration of the saved data in hard drive. But I personally do not use VLC and prefer use ffmpeg
Finally you could call one of above command in your C# application and see the result. Good Luck.

Not calling method OnMessageReceived in xamarin

I am implementing remote notification on android using xamarin.
I am doing POC using Walkthrough - Using Remote Notifications in Xamarin.Android
I am not getting notification on mobile, after registering mobile and send notification through sender using GCM.
Is there any mistake in code? or Can I track my notification to get detail why it is not come in mobile?
MyGcmListenerService.cs
[Service(Exported = false), IntentFilter(new[] { "com.google.android.c2dm.intent.RECEIVE" })]
public class MyGcmListenerService : GcmListenerService
{
public override void OnMessageReceived(string from, Bundle data)
{
var message = data.GetString("message");
Log.Debug("MyGcmListenerService", "From: " + from);
Log.Debug("MyGcmListenerService", "Message: " + message);
SendNotification(message);
}
....
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.yourcompany.LeaveApplication" android:installLocation="auto" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="15" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.yourcompany.LeaveApplication.permission.C2D_MESSAGE" />
<permission android:name="com.yourcompany.LeaveApplication.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<application android:label="XamarinLeaveApp" >
<receiver android:name="com.google.android.gms.gcm.GcmReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.yourcompany.LeaveApplication" />
</intent-filter>
</receiver>
</application>
</manifest>
Message Sender's code
var jGcmData = new JObject();
var jData = new JObject();
jData.Add("message", MESSAGE);
jGcmData.Add("to", "/topics/global");
jGcmData.Add("data", jData);
var url = new Uri("https://gcm-http.googleapis.com/gcm/send");
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.TryAddWithoutValidation(
"Authorization", "key=" + API_KEY);
Task.WaitAll(client.PostAsync(url,
new StringContent(jGcmData.ToString(), Encoding.Default, "application/json"))
.ContinueWith(response =>
{
var response1 = response;
// Console.WriteLine(response);
//Console.WriteLine("Message sent: check the client device notification tray.");
}));
}
}
RegistrationIntentService.cs
[Service(Exported = false)]
class RegistrationIntentService : IntentService
{
static object locker = new object();
public RegistrationIntentService() : base("RegistrationIntentService") { }
protected override void OnHandleIntent(Intent intent)
{
try
{
Log.Info("RegistrationIntentService", "Calling InstanceID.GetToken");
lock (locker)
{
var instanceID = InstanceID.GetInstance(Application.Context);
var token = instanceID.GetToken(
"<project number", GoogleCloudMessaging.InstanceIdScope, null);
Log.Info("RegistrationIntentService", "GCM Registration Token: " + token);
SendRegistrationToAppServer(token);
Subscribe(token);
}
}
catch (Exception e)
{
Log.Debug("RegistrationIntentService", "Failed to get a registration token");
return;
}
}
void SendRegistrationToAppServer(string token)
{
// Add custom implementation here as needed.
}
void Subscribe(string token)
{
var pubSub = GcmPubSub.GetInstance(Application.Context);
pubSub.Subscribe(token, "/topics/global", null);
}
}
in the RegistrationIntentService.cs file, you use Application.Context instead of this. It should be equivalent, but can you try?
Also, I do not see in your code the protected override void OnCreate method that registers the RegistrationIntentService intent. Didn't you paste it here or did you forget to implement it?
HTH

Categories