Can't get thumbnail from AVAsset - c#

In my Xamarin.Forms project, I use dependency injection to call a method inside my .iOS project in order to retrieve a thumbnail of a video that the user just recorded.
Called from my Xamarin.Forms project:
byte[] thumbnailBytes = DependencyService.Get<IMediaMetaData>().ReturnVidThumbnailBytes(vid.Path);
The following code is then called from the .IOS project:
public class MediaMetaData : IMediaMetaData
{
public byte[] ReturnVidThumbnailBytes(string videoFilePath)
{
// videoFilePath example =
// /var/mobile/Containers/Data/Application/3E84C0D0-F590-4AAF-89E7-7D3BFC023E6B/Documents/temp/trim_160491789522565DC08BE-862C-4EE0-A135-BCD9236C6874.mov
//Take local url and create an AVAsset
AVAsset asset = AVAsset.FromUrl(new NSUrl(videoFilePath));
AVAssetImageGenerator assetImageGenerator = AVAssetImageGenerator.FromAsset(asset);
// Total duration of asset
CMTime actualTime = asset.Duration;
// 1/60 = 60th of a second
CMTime cmTime = new CMTime(1, 10);
NSError error;
var imageRef = assetImageGenerator.CopyCGImageAtTime(cmTime, out actualTime, out error);
if (imageRef == null)
return null;
var image = UIImage.FromImage(imageRef);
return image.AsJPEG().ToArray();
}
}
The problem:
CopyCGImageAtTime always returns null. I check the NSError and I get the following information:
Class Handle = Unable to cast object of type 'Mono.Debugger.Soft.PointerValue' to type 'Mono.Debugger.Soft.PrimitiveValue
code = -11850
Localized failure reason = The server is not correctly configured (But wait, this is a local file I'm accessing here...?)
I've not been able to find any helpful information regarding the class handle error, and the localized failure reason brings up threads from people who are trying to access videos from web services as opposed to local storage. Anyone have experience with the problem I'm having?

Related

How to create new AutoML DataSet for simple classification (C#)

As part of ML automation process I want to dynamically create new AutoML model. I'm using C# (.net framework) and Google.Cloud.AutoML.V1.
After trying to run CreateDataSet code:
var autoMlClient = AutoMlClient.Create();
var parent = LocationName.FromProjectLocation(_projectId, _locationId);
var dataset = new Google.Cloud.AutoML.V1.Dataset();
dataset.DisplayName = "NewDataSet";
var response = autoMlClient.CreateDataset(parent, dataset);
I get the following error:
Field: dataset.dataset_metadata; Message: Required field not set
According to this user manual I should set Dataset Metadata Type, but the list contains only specific types of classifications (Translation/ImageClassifications etc.), I can't find a simple classification type.
How do I create a simple classification data set with the API ? in the AutoML UI its just with a simple button click ("NEW DATASET") - and have to provide only name & region - no classification type.
I also tried to set:
dataset.TextClassificationDatasetMetadata =
new TextClassificationDatasetMetadata() { ClassificationType = ClassificationType.Multiclass };
But I was unable to import data to it (got too many errors of invalid inputs from the input CSV file), I guess its related to the reason that the input format is not suitable for Text Classification.
UPDATE
I've just notice that the Nuget works with AutoML v1 but v1 beta does contains TablesDatasetMetadata Dataset Metadata Type for normal classifications. I'm speechless.
I also experienced this scenario today while creating a dataset using the NodeJS client. Since the Google AutoML table service is in the beta level you need to use the beta version of the AutoML client. In the Google cloud documentation they have used the beta client to create a dataset.
In NodeJS importing the beta version require('#google-cloud/automl').v1beta1.AutoMlClient instead of importing the normal version (v1) require('#google-cloud/automl').v1 worked for me to successfully execute the create dataset functionality.
In C# you can achieve the same through a POST request. Hope this helps :)
After #RajithaWarusavitarana comment, and my last question update , below is the code that did the trick. The token is being generated by GoogleClientAPI nuget and AutoML is handled by REST.
string GcpGlobalEndPointUrl = "https://automl.googleapis.com";
string GcpGlobalLocation = "us-central1"; // api "parent" parameter
public string GetToken(string jsonFilePath)
{
var serviceAccountCredentialFileContents = System.IO.File.ReadAllText(jsonFilePath);
var credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(serviceAccountCredentialFileContents);
var initializer = new ServiceAccountCredential.Initializer(credentialParameters.ClientEmail)
{
Scopes = new List<string> { "https://www.googleapis.com/auth/cloud-platform" }
};
var cred = new ServiceAccountCredential(initializer.FromPrivateKey(credentialParameters.PrivateKey));
string accessToken = cred.GetAccessTokenForRequestAsync("https://oauth2.googleapis.com/token").Result;
return accessToken;
}
public void GetDataSetList(string projectId, string token)
{
var restClient = new RestClient(GcpGlobalEndPointUrl);
var createDataSetReqUrl = $"v1beta1/projects/{projectId}/locations/{GcpGlobalLocation}/datasets";
var createDataSetReq = new RestRequest(createDataSetReqUrl, Method.GET);
createDataSetReq.AddHeader("Authorization", $"Bearer {token}");
var createDatasetResponse = restClient.Execute(createDataSetReq);
createDatasetResponse.Dump();
}
I took the token generation code from google-api-dotnet-client Test File

Updating MetaData on Connected account fails

I am using stripe connect(destination payment) with the help of stripe.net library from Jaymedavis.
The problem that I am facing is that I am not able to retrieve the destination payment ID to update the metadata in the connected account. The following line returns a null preventing me from updating meta data on the connected account. But the strange thing is that when I log in to the dashboard the destination payment ID exists. I am not sure why I am not able to retreive it in code.
Is the charge creation asynchronous?. I am not sure. Stripe's connect documentation does not help either. The following line returns a null. My code is down below. Seeking help.
String deschargeID = result.Transfer.DestinationPayment;
Here is the code that I am using
var service = new StripeChargeService(ZambreroSecretKey);
var result = (Stripe.StripeCharge) null;
try {
result = service.Create(newCharge);
if (result.Paid) {
//get the chargeID on the newgen account and update the metadata.
//Returns null even though it exists in the dashboard
String deschargeID = result.Transfer.DestinationPayment;
var chargeService = new StripeChargeService(newgenSecretKey);
StripeCharge charge = chargeService.Get(deschargeID);
charge.Metadata = myDict;
Response.Redirect("PgeCustSuccess.aspx?OrderID=" + OrderID);
}
} catch (StripeException stripeException) {
Debug.WriteLine(stripeException.Message);
stripe.Text = stripeException.Message;
}
The charge object's transfer attribute is not expanded by default, meaning it's just a string with the ID of the transfer object ("tr_..."), not a full transfer object.
According to Stripe.net's documentation, you can expand the transfer attribute by adding this line:
service.ExpandTransfer = True
before sending the charge creation request.

Xamarin.forms how to hold image on same page after navigating another page

I have uploaded image on my profile page and I want to hold that image until I logout in xamarin forms.
My image will be lost if I select another page so I want to hold it until I log out.
var profile = new Image { };
profile.Source = "profile.png";
profile.HorizontalOptions = LayoutOptions.StartAndExpand;
profile.VerticalOptions = LayoutOptions.StartAndExpand;
var profiletap = new TapGestureRecognizer();
profiletap.Tapped += async (s, e) =>
{
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
return;
await DisplayAlert("File Location", file.Path, "OK");
im = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
//file.Dispose();
return stream;
});
profile.Source = im;
// await Navigation.PushModalAsync(new PhotoPage(im));
};
profile.GestureRecognizers.Add(profiletap);
Pages do not get destroyed when navigating to another page and coming back, which is why a page's constructor only gets executed the first time it is shown. So I am not sure what you mean when you say you want to hold that image.
Having said that, you could always assign the entire profile variable to a static global variable in your App class like below so that it stays the same no matter what. Then you would have to assign/initialize the global variable at the correct time.
But again, I am not sure if that is necessary, so you might try to explain more what the issue actually is:
In the App class:
public class App : Application {
public static Image ProfileImage = new Image {
Source = "profile.png",
HorizontalOptions = LayoutOptions.StartAndExpand,
VerticalOptions = LayoutOptions.StartAndExpand
};
....
}
Then in your page:
public class ProfilePage : ContentPage {
public ProfilePage() {
....
App.ProfileImage.GestureRecognizers.Add(profiletap);
}
}
Edit: See my answer here for an example of using a plugin to allow the user to choose a photo from their device's camera roll. Once you have the photo path, you can simply use HttpClient to send the image and a base64 string. There are plenty of other example online about how to do that.
Edit #2: After this line of your code:
var file = await CrossMedia.Current.PickPhotoAsync();
You now have the file and the path in file variable. So currently all you are doing is showing the image using ImageSource.FromStream but in order to keep showing the image when you return to the page, you need to also save the image to the device. In order to do that, you will need to write platform specific code in each project and reference that in your shared code. Something like this:
In your iOS and Android project, create a new file (FileHelper_Android.cs and FileHelper_iOS.cs for example) and add the following (the same code can be added to both iOS and Android files, just change the name of the class and file:
using ....;
[assembly: Dependency(typeof(FileHelper_Android))]
namespace YourNamespace.Droid{
/// <summary>
/// Responsible for working with files on an Android device.
/// </summary>
internal class FileHelper_Android : IFileHelper {
#region Constructor
public FileHelper_Android() { }
#endregion
public string CopyFile(string sourceFile, string destinationFilename, bool overwrite = true) {
if(!File.Exists(sourceFile)) { return string.Empty; }
string fullFileLocation = Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.Personal), destinationFilename);
File.Copy(sourceFile, fullFileLocation, overwrite);
return fullFileLocation;
}
}
}
Do the same on iOS and just change the file name. Now in your shared project you need to create IFileHelper.cs like so:
public interface IFileHelper {
string CopyFile(string sourceFile, string destinationFilename, bool overwrite = true);
}
Finally, in your page you would write the following:
_fileHelper = _fileHelper ?? DependencyService.Get<IFileHelper>();
profiletap.Tapped += async (s, e) =>
{
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
return;
await DisplayAlert("File Location", file.Path, "OK");
profile.Source = im;
imageName = "SomeUniqueFileName" + DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss-tt");
filePath = _fileHelper.CopyFile(file.Path, imageName);
im = ImageSource.FromFile(filePath)
// await Navigation.PushModalAsync(new PhotoPage(im));
};
Above, once the user chooses the file, we copy that file locally and the we also set your im variable to the new local file path, which gets returned from the IFileHelper.CopyFile method.
You still need to handle the case when the user comes back to the page or turns the app off and on again. In that situation, you need to load the saved image path. I would suggest either saving the image path into the DB, unless the user will only ever have a single profile image, then you could always just load that same path and filename. Let me know if you still have issues.

frame issue when creating a method in a class for windows phone 8.1

I am trying to create a method I can reuse on multiple pages in my code for windows phone 8.1 universal Runtime app. So I copied this snippet from one of the pages into my helper class. However, I am struggling with "Frame" class.
I also defined "Frame Frame;" outside of my method initially to get rid of the issue I saw when I copied the code initially.
Here is the method snippet:
public async void CheckAuthState(string pagename, string errormessage)
{
string rememberMeValue = (string)appRoamingSettings.Values["RememberMe"];
if (rememberMeValue == "Yes")
{
//First check if the creds were saved
//If saved, check if the cookie is still valid (not past 2 days)
//If still valid, get the cookie value to pass it in an object to the MC page
//If not valid, using the saved creds, go back and get a new cookie and then pass that to the MC page
//Redirect to the MC page with the cookie in the object
//Get the cookie value...
string myCookieValue = (string)appRoamingSettings.Values["MyCookie"];
//Get the original cookie obtain time....
long CookieObtainedTimeValue = (long)appRoamingSettings.Values["CookieObtainedTime"];
//Convertig date/time back to DateTime object....
origCookieObtainedTime = DateTime.FromBinary(CookieObtainedTimeValue);
currentDateTime = DateTime.Now;
//Check to see if cookie has expired....
cookieTimeElasped = currentDateTime - origCookieObtainedTime;
cookieTimeElapsedMins = cookieTimeElasped.TotalMinutes;
// 2 days = 2880 mins but we give a margin of 1 minute
if (cookieTimeElapsedMins <= 2879)
{
//send the cookie to the MC page along with the cookie as an object
var shdCookie = myCookieValue;
var shdPageName = pagename;
// Create an object by populating the class with the data obtained from logging in and getting he cookie....
myCookiePageName myNeededSHDData = new myCookiePageName(shdCookie, shdPageName);
//Pass the object as a paramter to the Naviage method since we need to pass the parameters to the page being navigated to....
this.Frame.Navigate(typeof(MessageCenter), myNeededSHDData);
}
else
{
//get a new cookie
//send the cookie to the MC page along with the cookie as an object
//Get the values for the userID and password from the settings....
string UserIDValue = (string)appRoamingSettings.Values["UserID"];
string PasswordValue = (string)appRoamingSettings.Values["Password"];
//Update the requestData string before sending.....
requestData = "{" + string.Format(RegisterRequestData, UserIDValue, PasswordValue) + "}";
string registerResults = await SHDAPI(registerUrl, requestData, errormessage);
if (registerResults != null)
{
// Get the cookie and the time and save it to settings
var shdCookie = JsonConvert.DeserializeObject<SHDHelper.SHDObject>(registerResults).RegistrationCookie;
var shdPageName = pagename;
// Create an object by populating the class with the data obtained from logging in and getting he cookie....
myCookiePageName myNeededLoginData = new myCookiePageName(shdCookie, shdPageName);
//Pass the object as a paramter to the Naviage method since we need to pass the parameters to the page being navigated to....
this.Frame.Navigate(typeof(MessageCenter), myNeededLoginData);
// Stop showing the progress bar...
mycontrols.progressbarNoShow(pgbar, pgText);
}
else
{
// Stop showing the progress bar...
mycontrols.progressbarNoShow(pgbar, pgText);
//Show the error message...
ServerNetworkError.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
else
{
//If NOT saved, then redirect to the SignIn page along with the page name..
var shdCookie = "currentCookieValue"; //Putting some default value....
var shdPageName = pagename;
// Create an object by populating the class with the data obtained from logging in and getting he cookie....
myCookiePageName myNeededSHDData = new myCookiePageName(shdCookie, shdPageName);
//Instantiate the frames class for using in this function since this.Frame.Navigate can't be used...
//Frame myframe = new Frame();
//Frame Frame = new Frame();
//Pass the object as a paramter to the Naviage method since we need to pass the parameters to the page being navigated to....
this.Frame.Navigate(typeof(SHDSignIn), myNeededSHDData);
}
}
My issue is that every time my code hits the "this.Frame" line it tell me Frame is null. I guess I am trying to see how to reference the same Frame that all my pages in app reference so I don't get this null? Or do I need to something else here to reference the proper frame?
Thanks
The Frame should be a shared instance within your app since it mantains the pages the app has navigated (your app's backstack which determines what page you see if any when pressing back button) so recreating a new one will not work.
Typically this is initialized in your Application class ("App" class in most new project wizard apps) and set to Window.Current.Content. The same underlying instance is then also exposed by any Page instantiated in the app (navigated to by the Frame) as a Frame property in the Page.
So you can either reference it from anywhere via Window.Current.Content and then cast it to Frame or preferably it's something you pass as a parameter into your class (from either Page.Frame, an app wide definition that returns Window.Current.Content casted to Frame, or another class singleton instance that encapsulates Frame and is available app wide) that needs it via the class constructor or the specific class method that needs it.

Can't upload to a specific folder getting 503 error

I am trying to upload a simple text file to a specific folder in google documents but with no luck.
FileStream fileStream = new FileStream(#"c:\test.txt", System.IO.FileMode.Open);
DocumentEntry lastUploadEntry =
globalData.service.UploadDocument("c:\\test.txt", null);
string feed =
"https://docs.google.com/feeds/upload/create-session/default/private/full/folder%folder:0B2dzFB6YvN-kYTRlNmNhYjEtMTVmNC00ZThkLThiMjQtMzFhZmMzOGE2ZWU1/contents/";
var result =
globalData.service.Insert(new Uri(feed), fileStream, "application/pdf", "test");
I get an error saying
"The remote server returned an error: (503) Server Unavailable."
I am suspecting that the destination folders uri is wrong but i can't figure out the correct one.
There's a complete sample at https://developers.google.com/google-apps/documents-list/#uploading_a_new_document_or_file_with_both_metadata_and_content that uses the resumable upload component:
using System;
using Google.GData.Client;
using Google.GData.Client.ResumableUpload;
using Google.GData.Documents;
namespace MyDocumentsListIntegration
{
class Program
{
static void Main(string[] args)
{
DocumentsService service = new DocumentsService("MyDocumentsListIntegration-v1");
// TODO: Instantiate an Authenticator object according to your authentication
// mechanism (e.g. OAuth2Authenticator).
// Authenticator authenticator = ...
// Instantiate a DocumentEntry object to be inserted.
DocumentEntry entry = new DocumentEntry();
// Set the document title
entry.Title.Text = "Legal Contract";
// Set the media source
entry.MediaSource = new MediaFileSource("c:\\contract.txt", "text/plain");
// Define the resumable upload link
Uri createUploadUrl = new Uri("https://docs.google.com/feeds/upload/create-session/default/private/full");
AtomLink link = new AtomLink(createUploadUrl.AbsoluteUri);
link.Rel = ResumableUploader.CreateMediaRelation;
entry.Links.Add(link);
// Set the service to be used to parse the returned entry
entry.Service = service;
// Instantiate the ResumableUploader component.
ResumableUploader uploader = new ResumableUploader();
// Set the handlers for the completion and progress events
uploader.AsyncOperationCompleted += new AsyncOperationCompletedEventHandler(OnDone);
uploader.AsyncOperationProgress += new AsyncOperationProgressEventHandler(OnProgress);
// Start the upload process
uploader.InsertAsync(authenticator, entry, new object());
}
static void OnDone(object sender, AsyncOperationCompletedEventArgs e) {
DocumentEntry entry = e.Entry as DocumentEntry;
}
static void OnProgress(object sender, AsyncOperationProgressEventArgs e) {
int percentage = e.ProgressPercentage;
}
}
}
Just follow the article Google Apps Platform Uploading documents
Also check out Google Documents List API version 3.0
Uri should be something similar to below:
string feed = #"https://developers.google.com/google-apps/documents-list/#getting_a_resource_entry_again";
//it may not be exact, just check and read from the links
Try this uri:
"https://docs.google.com/feeds/default/private/full/folder%3A" + fRid + "/contents"
//fRid is the Resource Id of the folder.. in your case: 0B2dzFB6YvN-kYTRlNmNhYjEtMTVmNC00ZThkLThiMjQtMzFhZmMzOGE2ZWU1
Also I guess your URI is giving this error because you are using folder resource ID as - folder:resourceID
Try removing folder: and use only RID
Code to cutout "folder:" -
int ridIndex = dRid.IndexOf(":");
Rid = Rid.Substring(ridIndex + 1);

Categories