Para mi caso en especial todo gira alrededor de la siguiente clase:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace blog.epicalsoft.com.Model
{
public class Evidence
{
public int Id { get; set; }
public EvidenceKind Kind { get; set; }
public string ResSrc { get; set; }
public string ResExt { get; set; }
public string TnSrc { get; set; }
public string TnExt { get; set; }
public object Data { get; set; }
}
public enum EvidenceKind
{
Image = 1,
Audio = 2,
Video = 3,
Other = 99
}
}
En el proyecto Windows Phone, empezamos actualizando el WMAppManifest.xml para poder usar la cámara y el micrófono.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Capabilities>
<Capability Name="ID_CAP_NETWORKING" />
<Capability Name="ID_CAP_LOCATION" />
<Capability Name="ID_CAP_PUSH_NOTIFICATION" />
<Capability Name="ID_CAP_ISV_CAMERA" />
<Capability Name="ID_CAP_MEDIALIB_PHOTO" />
<Capability Name="ID_CAP_MAP" />
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
<Capability Name="ID_CAP_CONTACTS" />
<Capability Name="ID_CAP_PHONEDIALER" />
<Capability Name="ID_CAP_MICROPHONE" />
</Capabilities>
Luego de ello ya podemos definir nuestra pantalla como mejor creamos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<phone:PhoneApplicationPage
x:Class="blog.epicalsoft.com.Controls.VideoCapturePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
SupportedOrientations="Landscape"
mc:Ignorable="d"
shell:SystemTray.IsVisible="False" Orientation="LandscapeLeft" >
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Canvas x:Name="Canvas" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="640"
Height="480" RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<RotateTransform x:Name="CanvasRotateTransform" ></RotateTransform>
</Canvas.RenderTransform>
</Canvas>
<Grid>
<TextBlock x:Name="Sizes" Text="" FontSize="32" FontFamily="Segoe UI Light"/>
<Image x:Name="ThumbnailImage" />
<StackPanel Orientation="Horizontal" Margin="12" VerticalAlignment="Bottom" HorizontalAlignment="Left">
<Button Visibility="Collapsed" Margin="12" x:Name="PLAY" Style="{StaticResource ButtonEmptyStyle}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Tap="PLAY_Tap">
<Border Height="81" Width="81" CornerRadius="45" BorderBrush="White" BorderThickness="3">
<TextBlock Foreground="White" FontSize="36" Text="" FontFamily="/segmdl2.ttf#Segoe MDL2 Assets" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,-18,0" />
</Border>
</Button>
<TextBlock x:Name="DurationText" Text="00:00" FontSize="72" FontFamily="Segoe UI Light"/>
</StackPanel>
</Grid>
<Border Grid.Column="1" Background="#121212" BorderThickness="1,0,0,0" BorderBrush="White"/>
<StackPanel Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Visibility="Collapsed" Margin="12" x:Name="CANCEL" Style="{StaticResource ButtonEmptyStyle}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Tap="CANCEL_Tap">
<Border Height="81" Width="81" CornerRadius="45" BorderBrush="White" BorderThickness="3">
<TextBlock Foreground="White" FontSize="36" Text="" FontFamily="/segmdl2.ttf#Segoe MDL2 Assets" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</Button>
<Button Visibility="Collapsed" Margin="12" x:Name="REC" Style="{StaticResource ButtonEmptyStyle}" Tap="REC_Tap">
<Border Height="81" Width="81" CornerRadius="45" BorderBrush="White" BorderThickness="3">
<TextBlock Foreground="{StaticResource AppRedColorBrush}" FontSize="36" Text="" FontFamily="/segmdl2.ttf#Segoe MDL2 Assets" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</Button>
<Button Visibility="Collapsed" Margin="12" x:Name="STOP" Style="{StaticResource ButtonEmptyStyle}" Tap="STOP_Tap">
<Border Height="81" Width="81" CornerRadius="45" BorderBrush="White" BorderThickness="3" Background="#33E81123">
<TextBlock Foreground="White" FontSize="24" Text="" FontFamily="/segmdl2.ttf#Segoe MDL2 Assets" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</Button>
<Button Visibility="Collapsed" Margin="12" x:Name="ACCEPT" Style="{StaticResource ButtonEmptyStyle}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Tap="ACCEPT_Tap">
<Border Height="81" Width="81" CornerRadius="45" BorderBrush="White" BorderThickness="3">
<TextBlock Foreground="White" FontSize="36" Text="" FontFamily="/segmdl2.ttf#Segoe MDL2 Assets" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</Button>
</StackPanel>
</Grid>
</phone:PhoneApplicationPage>
En mi caso quedo así:
Luego de definir la pantalla, viene el .cs en el cual hay que lidear con el estado de la cámara, no vayamos a usar recursos que ya estan en uso y con la orientación de nuestra aplicación. Para mi fue todo un lío llegar a terminar esta parte, es más, actualmente tiene un bug. Una estrella para quien lo encuentra.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using blog.epicalsoft.com.Phone.Resources;
using Microsoft.Devices;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Threading;
using Windows.Phone.Media.Capture;
using Windows.Storage;
using Windows.Storage.Streams;
namespace com.epicalsoft.com.Controls
{
public partial class VideoCapturePage : PhoneApplicationPage
{
private Evidence _evidence;
private VideoBrush _viewfinderBrush;
private int _encodedOrientation;
private AudioVideoCaptureDevice _audioVideoCaptureDevice;
private StorageFile _storageFile;
private IRandomAccessStream _randomAccessStream;
private enum ButtonState { Ready, Recording, Recorded, CameraNotSupported };
private TimeSpan _durationTime;
private DispatcherTimer _dispatcherTimer;
public VideoCapturePage()
{
InitializeComponent();
OrientationChanged += VideoCapturePage_OrientationChanged;
}
private void UpdateOrientation()
{
if (Orientation == PageOrientation.LandscapeLeft)
{
CanvasRotateTransform.Angle = 0;
}
else
{
CanvasRotateTransform.Angle = 180;
}
}
private void VideoCapturePage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
UpdateOrientation();
}
private void UpdateUI(ButtonState currentButtonState)
{
// Run code on the UI thread.
Dispatcher.BeginInvoke(delegate
{
switch (currentButtonState)
{
// When the camera is not supported by the phone.
case ButtonState.CameraNotSupported:
REC.Visibility = Visibility.Collapsed;
STOP.Visibility = Visibility.Collapsed;
PLAY.Visibility = Visibility.Collapsed;
ACCEPT.Visibility = Visibility.Collapsed;
CANCEL.Visibility = Visibility.Collapsed;
break;
// Ready to record, so video is available for viewing.
case ButtonState.Ready:
REC.Visibility = Visibility.Visible;
STOP.Visibility = Visibility.Collapsed;
PLAY.Visibility = Visibility.Collapsed;
ACCEPT.Visibility = Visibility.Collapsed;
CANCEL.Visibility = Visibility.Collapsed;
break;
// Video recording is in progress.
case ButtonState.Recording:
REC.Visibility = Visibility.Collapsed;
STOP.Visibility = Visibility.Visible;
PLAY.Visibility = Visibility.Collapsed;
ACCEPT.Visibility = Visibility.Collapsed;
CANCEL.Visibility = Visibility.Collapsed;
break;
case ButtonState.Recorded:
REC.Visibility = Visibility.Collapsed;
STOP.Visibility = Visibility.Collapsed;
PLAY.Visibility = Visibility.Visible;
ACCEPT.Visibility = Visibility.Visible;
CANCEL.Visibility = Visibility.Visible;
break;
default:
break;
}
});
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
InitializeVideoRecorder();
}
public async void InitializeVideoRecorder()
{
var result = Camera.IsCameraTypeSupported(CameraType.Primary);
if (!result)
{
App.DialogService.ShowToast(AppResources.Msg_NoRearCamera, "Ups!");
return;
}
Dispose();
if (null == _dispatcherTimer)
{
_dispatcherTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
_dispatcherTimer.Tick += (s, a) =>
{
_durationTime = _durationTime.Add(TimeSpan.FromSeconds(1));
DurationText.Text = _durationTime.Minutes.ToString("00") + ":" + _durationTime.Seconds.ToString("00");
if (_durationTime.TotalMinutes >= 1.5)
{
StopVideoRecording();
}
};
}
var resolutions = AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back);
var minWidth = resolutions.Min(x => x.Width);
Windows.Foundation.Size size = Windows.Foundation.Size.Empty;
size = resolutions.FirstOrDefault(x => x.Width == 320);
if (size == Windows.Foundation.Size.Empty || size.Width == 0)
size = resolutions.First(x => x.Width == minWidth);
_audioVideoCaptureDevice = await AudioVideoCaptureDevice.OpenAsync(CameraSensorLocation.Back, size);
_viewfinderBrush = new VideoBrush();
_viewfinderBrush.SetSource(_audioVideoCaptureDevice);
Canvas.Background = _viewfinderBrush;
if (null == _evidence?.ResSrc)
{
DurationText.Text = "00:00";
_durationTime = TimeSpan.Zero;
ThumbnailImage.Source = null;
UpdateUI(ButtonState.Ready);
}
else
{
UpdateUI(ButtonState.Recorded);
}
}
private async void StartVideoRecording()
{
var isoStore = await ApplicationData.Current.LocalFolder.GetFolderAsync("Shared");
_storageFile = await isoStore.CreateFileAsync("LastIncident.mp4", CreationCollisionOption.ReplaceExisting);
_randomAccessStream = await _storageFile.OpenAsync(FileAccessMode.ReadWrite);
// Initialize variables.
_encodedOrientation = 0;
int sensorOrientation = (int)_audioVideoCaptureDevice.SensorRotationInDegrees;
switch (Orientation)
{
case PageOrientation.LandscapeLeft:
_encodedOrientation = -90 + sensorOrientation;
break;
case PageOrientation.LandscapeRight:
_encodedOrientation = 90 + sensorOrientation;
break;
}
// Apply orientation to image encoding.
_audioVideoCaptureDevice.SetProperty(KnownCameraGeneralProperties.EncodeWithOrientation, _encodedOrientation);
await _audioVideoCaptureDevice.StartRecordingToStreamAsync(_randomAccessStream);
UpdateOrientation();
UpdateUI(ButtonState.Recording);
using (var tnMS = new MemoryStream())
{
var wb = new WriteableBitmap(Canvas, null);
if (Orientation == PageOrientation.LandscapeRight)
wb = wb.Rotate(_encodedOrientation);
wb.SaveJpeg(tnMS, wb.PixelWidth, wb.PixelHeight, 0, 72);
tnMS.Seek(0, SeekOrigin.Begin);
_evidence = new Evidence { Kind = EvidenceKind.Video };
_evidence.TnSrc = Convert.ToBase64String(tnMS.ToArray());
_evidence.TnExt = "jpg";
}
}
private async void StopVideoRecording()
{
_dispatcherTimer.Stop();
STOP.Visibility = Visibility.Collapsed;
await _audioVideoCaptureDevice.StopRecordingAsync();
UpdateOrientation();
var sfStream = await _storageFile.OpenStreamForReadAsync();
var resStream = new MemoryStream();
sfStream.CopyTo(resStream);
var bitmapImage = new BitmapImage();
var stream = new MemoryStream(Convert.FromBase64String(_evidence.TnSrc));
bitmapImage.SetSource(stream);
ThumbnailImage.Source = bitmapImage;
_evidence.ResSrc = Convert.ToBase64String(resStream.ToArray());
_evidence.ResExt = "mp4";
_evidence.Data = _storageFile.Path;
sfStream.Dispose();
resStream.Dispose();
stream.Dispose();
UpdateUI(ButtonState.Recorded);
}
#region Dispose
protected override void OnBackKeyPress(CancelEventArgs e)
{
_storageFile?.DeleteAsync();
_storageFile = null;
Dispose();
}
private void Dispose()
{
_dispatcherTimer?.Stop();
_audioVideoCaptureDevice?.Dispose();
_randomAccessStream?.Dispose();
_dispatcherTimer = null;
_viewfinderBrush = null;
_audioVideoCaptureDevice = null;
_randomAccessStream = null;
}
#endregion Dispose
#region Events
private void REC_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_dispatcherTimer.Start();
REC.Visibility = Visibility.Collapsed;
StartVideoRecording();
}
private void STOP_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
StopVideoRecording();
}
private void PLAY_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
new MediaPlayerLauncher()
{
Media = new Uri((string)_evidence.Data, UriKind.Relative),
Location = MediaLocationType.Data,
Controls = MediaPlaybackControls.All,
Orientation = MediaPlayerOrientation.Landscape,
}.Show();
}
private void ACCEPT_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
Dispose();
App.PendingVideo = _evidence;
NavigationService.GoBack();
}
private void CANCEL_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
_evidence = new Evidence { Kind = EvidenceKind.Video };
InitializeVideoRecorder();
}
#endregion Events
}
}
En fin, luego de haber capturado correctamente la información y dando aceptar, nos ayudamos de una variable estática en el App.xaml.cs llamada PendingVideo y navegamos hacia atrás. Ya del otro lado, la usamos y limpiamos referencias innecesarias.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (null != App.PendingVideo)
{
//TODO
App.PendingVideo = null;
}
}
No hay comentarios.:
Publicar un comentario