How to get an existing video thumbnail image from MonoTouch? - xamarin.ios

I'm trying to get a thumbnail image for a video that I've just chosen (or recorded) via the UIImagePickerController. In my picker's FinishedPickingMedia method, I get what I believe to be the path to the video file and use it to save the recording to album. The newly recorded video does indeed appear in my album. Now to get the thumbnail…
I've tried using the ALAssetsLibrary in a couple ways. First, I've enumerated all the videos and tried to find a match from the asset's UtiToUrlDictionary.Values[0] value, which reveals its file path. No entries matched the path I obtained from the FinishedPickingMedia method's mediaUrlKey.
My second attempt was to use the ALAssetsLibrary's AssetForUrl() method. Again, I tried my FinishedPickingMedia method's mediaUrlKey, but there was no match.
The UIImagePickerController -> FinishedPickingMedia -> mediaUrlKey returns a path of:
file://localhost/private/var/mobile/Applications/73037738-C060-4DE6-A1CA-698E6BE083F2/tmp//trim.iRBwWw.MOV
After painful digging and inspecting every video in my library, it the same video's path from the ALAssetsLibrary is:
assets-library://asset/asset.MOV?id=B073FCF6-83ED-436B-AFD8-42D0A6C70FC6&ext=MOV
Why are the video paths different between UIIMagePickerController and ALAssetsLibrary? How can I get ALAssetsLibrary to return the same video picked from UIImagePickerController?
[code for the picker]
public override void FinishedPickingMedia (UIImagePickerController picker, NSDictionary info)
{
var mediaUrlKey = new NSString("UIImagePickerControllerMediaURL");
var mediaPath = (NSUrl) info.ObjectForKey(mediaUrlKey);
if( recordView.RecordVideo )
{
if(mediaPath != null)
{
if(UIVideo.IsCompatibleWithSavedPhotosAlbum(mediaPath.Path))
{
UIVideo.SaveToPhotosAlbum(mediaPath.Path, SaveToPhotosAlbumResults);
}
else
{
using (var alert = new UIAlertView("Problem Encountered",
"Unable to save this recording.", null, "Ok!", null))
{
alert.Show();
}
}
}
}
else
{
recordView.VideoFileLocation = mediaPath;
//ALAssetsLibrary library = new ALAssetsLibrary();
//library.AssetForUrl( mediaPath, GetAssetResult, GetAssetError );
}
recordView.VideoFileLocation = mediaPath;
Console.WriteLine("***" + recordView.VideoFileLocation.ToString()+ "***");
recordView.ShowUploadButton();
recordView.SetThumbnailImage();
picker.DismissModalViewControllerAnimated(true);
}
Thank you

I think may be it is too late but the following code work as of now
public static UIImage GenerateImage(NSUrl videoUrl)
{
var asset = AVAsset.FromUrl(videoUrl);
var imageGenerator = AVAssetImageGenerator.FromAsset(asset);
imageGenerator.AppliesPreferredTrackTransform = true;
var actualTime = asset.Duration;
CoreMedia.CMTime cmTime = new CoreMedia.CMTime(1, 60);
NSError error;
var imageRef = imageGenerator.CopyCGImageAtTime(cmTime, out actualTime, out error);
if (imageRef == null)
return null;
var image = UIImage.FromImage(imageRef);
return image;
}

I think the section Can I Get To The Original File Representing This Image on the Disk in this question can help you.

This is how you can get a thumbnail within the picker delegate:
NSUrl data = new NSUrl(info.ObjectForKey(new NSString("UIImagePickerControllerMediaURL")).ToString());
//get the video thumbnail
MPMoviePlayerController movie = new MPMoviePlayerController(data);
movie.Stop();
UIImage videoThumbnail = movie.ThumbnailImageAt(0.0,MPMovieTimeOption.NearestKeyFrame);
movie.Stop();

Related

Save Image file on persist method

I have been trying to save image file that's been copied from screenshot and pasted into the PXImage control. Now, when I save the record I am trying to save the image as well and save the name of the file in database record along with other DAC fields.
Below is the js code that sets the screenshot image into the PXImage control.
document.onpaste = function (event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
console.log(JSON.stringify(items)); // will give you the mime types
if (items.length > 0) {
var imgControl = document.querySelectorAll('.note-m img');
//get the last screenshot
var item = items[items.length - 1];
if (item.kind === 'file') {
var blob = item.getAsFile();
var reader = new FileReader();
reader.onload = function (event) {
var result = event.target.result;
imgControl[0].src = result;
px_alls["ds"].executeCallback("saveScreenshotImage","test");
};
reader.readAsDataURL(blob);
}
}
}
This is how it looks when I press (Ctrl+V) on the screenshot.
Now, I want to save this image and generate a file name that I can save on DAC field called 'ScreenShotImage' along with the record.
I thought of creating an action and calling that via js but I couldn't succeed on passing image as a command argument.
Action method in graph:
public PXAction<Ticket> saveScreenshotImage;
[PXButton]
public virtual IEnumerable SaveScreenshotImage(PXAdapter adapter)
{
throw new PXException("test...");
}
Calling via JS
px_alls["ds"].executeCallback("saveScreenshotImage");
Is there any way, I can get that particular file on Persist method?
public override void Persist()
{
base.Persist();
}
Thank you.

Image downloaded from Azure Storage not being displayed

I'm new to Xamarin. I'm trying display a list of downloaded images. I am downloading images from an APP API on Azure, where I stored the file on Azure Storage.
My server code is the following:
public HttpResponseMessage Get(string PK, string RK)
{
//Creating CloudBlockBlolb...
byte[] bytes = new byte[blockBlob.Properties.Length]
for(int i = 0; i < blockBlob.Properties.Length; i++){
bytes[i] = 0x20;
}
blockBlob.DownloadToByteArray(bytes, 0);
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new ByteArrayContent(bytes);
resp.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpg");
return resp;
}
My Xamarin code is the following:
public MainPage ()
{
//...
List<PicturePost> list = new List<PicturePost>{
new PicturePost("title", "subtitle", "link/api/Pictures?PK=xxx&RK=yyy")
};
InitializeComponent ();
listView.ItemTemplate = new DataTemplate (typeof(CustomImageCell));
listView.HasUnevenRows = true;
listView.ItemsSource = list;
//...
}
And here is the relevant code for CustomImageCell:
var image = new Image ();
image.SetBinding (Image.SourceProperty, "image");
//...
horizontalLayout.Children.Add (image);
I know that my API call works, because when I test it on the browser, it returns the image. I also know that if I use any random links such as http://www.natureasia.com/common/img/splash/thailand.jpg the image is downloaded and displayed properly. It is only when I use the API link that it doesn't seem to be working. Can someone tell me what I am doing wrong?
so in my public MainPage(), I added the following:
listView.BeginRefresh ();
listView.EndRefresh ();
I realized at some point that the images would take some time to download. I assume that when the listView was created, the images were not finished downloading, so I added the code above... Pretty sure this is not the best way to do this (probably an await would be better, but I don't know where).

Converting UIImage to UIData in monotouch fails

I've seen variations of my questions on stack overflow but haven't had any answers that have worked for me. I'm trying to convert an image I retrieve via UIImagePickerController to an NSData object. In the debugger after I call AsJPEG the NSData object has text that appears as...
System.Exception: Could not initialize an instance of the type 'MonoTouch.Foundation.NSString': the native 'initWithDa…
(note the debugger cuts off the string)
My code is fairly straight forward (taken form samples on stack overflow)
protected void Handle_FinishedPickingMedia (object sender, UIImagePickerMediaPickedEventArgs e) {
// determine what was selected, video or image
bool isImage = false;
switch(e.Info[UIImagePickerController.MediaType].ToString()) {
case "public.image":
Console.WriteLine("Image selected");
isImage = true;
break;
case "public.video":
Console.WriteLine("Video selected");
break;
}
string path = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
path = Path.Combine (path, "media");
if (!Directory.Exists (path))
Directory.CreateDirectory (path);
path = Path.Combine (path, Path.GetRandomFileName ());
// get common info (shared between images and video)
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:"+referenceURL.ToString ());
// if it was an image, get the other image info
if(isImage) {
// get the original image
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if(originalImage != null) {
// do something with the image
Console.WriteLine ("got the original image");
using (NSData imageData = originalImage.AsJPEG(0.75f)) {
byte[] dataBytes = new byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, dataBytes, 0, Convert.ToInt32(imageData.Length));
File.WriteAllBytes (path, dataBytes);
}
}
} else { // if it's a video
// get video url
NSUrl mediaURL = e.Info[UIImagePickerController.MediaURL] as NSUrl;
if(mediaURL != null) {
// ...
}
}
// dismiss the picker
NavigationController.DismissViewController (true, null);
}
I've seen other posts that suggested it was the size of the UIImage, so I've experimented with cropping it. Same result. I've also tried AsPNG, same result. I even tried scaling down the image to 1/4 it's original size and still get the error.
I think the key is the mention of NSString, which tells me something's fishy... as the native C call used in Xcode doesn't involve an NSString, so I think something else is going on.
Any suggestions?
As noted in the comment from therealjohn, this appears to be an error with the debugger when it converts a value to a string to display it in the debugger window it seems to run into an error. The NSData object is actually fine.

How to play overlapping audio in winrt?

I'm porting an app from wp8 that requires playback of various sounds that can overlap. The only way I've found so far it to use MediaElement, but this doesn't allow overlapping sounds.
QUESTION - what is the easiest and best audio engine to use to play overlapping audio? Ideally I need a small example of how I can do this.
I've looked into WASAPI (http://code.msdn.microsoft.com/windowsapps/Windows-Audio-Session-22dcab6b), but it doesn't look like it supports simple playback ?
Maybe I can wrap the MediaFoundation and call it from winrt? (MediaEngine audio playback on WinRT)
Here is my code now, but when I play a new sound it cuts off the previously playing one rather than blending them.
ThreadUtility.runOnUiThread(
async delegate()
{
// TODO doesn't allow sounds to overlap!
Uri uri = new Uri(R.base_uri, R.raw.URI_PREFIX + resourceId);
StorageFile storageFile =
await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(
uri);
MediaElement element = new MediaElement();
var randomAccessStream = await storageFile.OpenReadAsync();
element.SetSource(randomAccessStream, storageFile.ContentType);
element.Volume = volume;
element.PlaybackRate = pitch;
//TODO element.Pan = pan;
element.Play();
}
);
SOLUTION (as per Filip's answer):
in the page class:
var mediaElements = new LinkedList<MediaElement>();
{
for (int channel = 0; channel < TeacherSoundGroover.NUM_CHANNELS; channel++)
{
var mediaElement = new MediaElement();
mediaElements.add(mediaElement);
// Must be in the tree otherwise it won't overlap!
m_titlePanel.Children.Add(mediaElement);
}
}
m_soundPlayer = new MySoundPlayer(mediaElements);
}
in the MySoundPlayer class:
ThreadUtility.runOnUiThread(
async delegate()
{
Uri uri = new Uri(R.base_uri, R.raw.URI_PREFIX + resourceId);
StorageFile storageFile =
await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(
uri);
if(m_mediaElements != null)
{
int count = m_mediaElements.size();
if (count > 0)
{
int channel = m_nextMediaElementToUse % count;
m_nextMediaElementToUse++;
MediaElement element = m_mediaElements.get(channel);
var randomAccessStream = await storageFile.OpenReadAsync();
element.Stop();
element.DefaultPlaybackRate = rate;
element.SetSource(randomAccessStream, storageFile.ContentType);
element.Volume = volume;
element.Balance = pan;
element.Play();
}
}
}
);
The easiest thing to do is use multiple MediaElement controls, though that might not give you desired results. The best way is to use XAudio2 either directly or through SharpDX if you want to avoid creating a C++/CX WinRT component.

GDI+ Generic Error

When my images are being loaded from my database on my web server, I see the following error:
A generic error occurred in GDI+. at
System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder,
EncoderParameters encoderParams) at
System.Drawing.Image.Save(Stream stream, ImageFormat format) at
MyWeb.Helpers.ImageHandler.ProcessRequest(HttpContext context)
All my code is attempting to do is load the image, can anybody take a look and let me know what I'm doing wrong?
Note - This works if I test it on my local machine, but not when I deploy it to my web server.
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
if (!String.IsNullOrEmpty(context.Request.QueryString["imageid"]))
{
int imageID = Convert.ToInt32(context.Request.QueryString["imageid"]);
int isThumbnail = Convert.ToInt32(context.Request.QueryString["thumbnail"]);
// Retrieve this image from the database
Image image = GetImage(imageID);
// Make it a thumbmail if requested
if (isThumbnail == 1)
{
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
image = image.GetThumbnailImage(200, 200, myCallback, IntPtr.Zero);
}
context.Response.ContentType = "image/png";
// Save the image to the OutputStream
image.Save(context.Response.OutputStream, ImageFormat.Png);
}
else
{
context.Response.ContentType = "text/html";
context.Response.Write("<p>Error: Image ID is not valid - image may have been deleted from the database.</p>");
}
}
The error occurs on the line:
image.Save(context.Response.OutputStream, ImageFormat.Png);
UPDATE
I've changed my code to this, bit the issue still happens:
var db = new MyWebEntities();
var screenshotData = (from screenshots in db.screenshots
where screenshots.id == imageID
select new ImageModel
{
ID = screenshots.id,
Language = screenshots.language,
ScreenshotByte = screenshots.screen_shot,
ProjectID = screenshots.projects_ID
});
foreach (ImageModel info in screenshotData)
{
using (MemoryStream ms = new MemoryStream(info.ScreenshotByte))
{
Image image = Image.FromStream(ms);
// Make it a thumbmail if requested
if (isThumbnail == 1)
{
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
image = image.GetThumbnailImage(200, 200, myCallback, IntPtr.Zero);
}
context.Response.ContentType = "image/png";
// Save the image to the OutputStream
image.Save(context.Response.OutputStream, ImageFormat.Png);
} }
Thanks.
Probably for the same reason that this guy was having problems - because the for a lifetime of an Image constructed from a Stream, the stream must not be destroyed.
So if your GetImage function constructs the returned image from a stream (e.g. a MemoryStream) and then closes the stream before returning the image then the above will fail. My guess is that your GetImage looks a tad like this:
Image GetImage(int id)
{
byte[] data = // Get data from database
using (MemoryStream stream = new MemoryStream(data))
{
return Image.FromStream(data);
}
}
If this is the case then try having GetImage return the MemoryStream (or possibly the byte array) directrly so that you can create the Image instance in your ProcessRequest method and dispose of the stream only when the processing of that image has completed.
This is mentioned in the documentation but its kind of in the small print.

Resources