我有一个 Windows Phone 8 应用程序,我正在更新它以添加视频上传/拍摄功能。我在放置视频文件流的部分有问题 IsolatedStorageFileStream
,这会引发未处理的异常。
我之前使用了简单的视频拍摄 API(VideoCaptureDevice
与 using 结合使用FileSink
),并且在将文件另存为独立存储时没有任何问题。这很好,但是我们的应用程序需要更多地控制视频的压缩,这就是为什么我使用高级相机 API 进行视频拍摄的原因AudioVideoCaptureDevice
,它IRandomAccessStream
用作文件的流支持。
使用高级 API 拍摄视频本身没有问题,只有问题是当IsolatedStorageFileStream
使用抛出未处理的异常时,该异常在异常对象中指示:
System.IO.IsolatedStorageException: Operation not permitted on IsolatedStorageFileStream.
每当我打开/创建IsolatedStorageFileStream
以前已经使用过的文件时,都会引发异常。我尝试了一切,从关闭IsolatedStorageFileStream
、处理它,甚至将对象设置为 null。
这是我的视频拍摄实现的代码片段,也是获取用于视频 APIIsolatedStorageFileStream
的实例的代码(都在同一个类中)(这里有问题的代码在 StartOptimizedRecord()IRandomAccessStream
当另一个时间调用该方法时,即当用户尝试重新录制视频时,会导致System.IO.IsolatedStorageException
和/或的未处理异常System.ObjectDisposedException
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Windows.Media;
using System.IO.IsolatedStorage;
using System.Diagnostics;
using System.Windows.Threading;
using System.IO;
using System.Globalization;
using Policelink.Models;
using System.Windows.Media.Imaging;
using Windows.Phone.Media.Capture;
using Microsoft.Devices;
using Windows.Storage.Streams;
using Windows.Storage;
namespace Policelink.Views
{
public partial class VideoCapturePage : PhoneApplicationPage
{
private VideoBrush videoBrush;
private IsolatedStorageFileStream isoVidFile;
private EyeWitnessReportVideo reportVideo;
//static readonly string videoFileName = "video1.mp4";
string videoFileName = "";
bool recording = false;
TimeSpan timeElapsed;
BitmapImage pauseImage;
BitmapImage playImage;
BitmapImage thumbnailImage;
MemoryStream thumbnailStream;
private const int maxLength = 60;
bool playing = false;
DispatcherTimer timer = new DispatcherTimer();
public VideoCapturePage()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = TimeSpan.FromSeconds(1);
pauseImage = new BitmapImage();
//pauseImage = new BitmapImage(new Uri("Assets/media-pause.png",UriKind.RelativeOrAbsolute));
var pauseStream = Application.GetResourceStream(new Uri("Assets/media-pause.png", UriKind.RelativeOrAbsolute)).Stream;
pauseImage.SetSource(pauseStream);
playImage = (BitmapImage) PlayPauseButtonImage.Source;
reportVideo = new EyeWitnessReportVideo();
GenerateCurrentVideoFileName();
CameraButtons.ShutterKeyPressed += CameraButtons_ShutterKeyPressed;
}
void CameraButtons_ShutterKeyPressed(object sender, EventArgs e)
{
ToggleRecordVideo();
}
void timer_Tick(object sender, EventArgs e)
{
timeElapsed = timeElapsed.Add(TimeSpan.FromSeconds(1));
TimeText.Text = timeElapsed.ToString(@"mm\:ss");
if (timeElapsed.TotalSeconds >= maxLength)
{
timer.Stop();
StopOptimizedRecord();
recording = false;
ToggleAcceptButton(true);
}
}
void ResetElapsed()
{
timeElapsed = TimeSpan.FromSeconds(0);
}
void StartTimer()
{
timer.Start();
}
void StopTimer()
{
timer.Stop();
}
private const string VideoFormatExt = ".mp4";
void GenerateCurrentVideoFileName()
{
string timestamp = DateTime.Now.ToBinary().ToString();
videoFileName = timestamp + VideoFormatExt;
Debug.WriteLine("Timestamp for currentvideo filename: " + timestamp);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
InitializeOptimizedCamera();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
UninitializeOptimizedCamera();
}
AudioVideoCaptureDevice vidCaptureDevice;
async void InitializeOptimizedCamera()
{
if (AudioVideoCaptureDevice.AvailableSensorLocations.Contains(CameraSensorLocation.Back))
{
var res = GetLowestVideoResolution();
var resPrev = GetLowestPreviewResolution();
vidCaptureDevice = await AudioVideoCaptureDevice.OpenAsync(CameraSensorLocation.Back, res);
vidCaptureDevice.AudioEncodingFormat = CameraCaptureAudioFormat.Aac;
vidCaptureDevice.VideoEncodingFormat = CameraCaptureVideoFormat.H264;
await vidCaptureDevice.SetPreviewResolutionAsync(resPrev);
vidCaptureDevice.RecordingFailed += openedCam_RecordingFailed;
vidCaptureDevice.PreviewFrameAvailable += vidCaptureDevice_PreviewFrameAvailable;
SetCaptureDeviceProperties();
videoBrush = new VideoBrush();
videoBrush.SetSource(vidCaptureDevice);
RecordViewFinder.Fill = videoBrush;
}
}
void vidCaptureDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
//throw new NotImplementedException();
}
IRandomAccessStream videoStream;
async void StartOptimizedRecord()
{
if (vidCaptureDevice != null)
{
OpenIsoVideoFile();
await vidCaptureDevice.StartRecordingToStreamAsync(videoStream);
ResetElapsed();
StartTimer();
//thumbnailStream = new MemoryStream();
}
}
void GetStreamFromIsolatedStorageVideoFile()
{
using (var isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
isoVidFile = isoFile.OpenFile(videoFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
videoStream = (IRandomAccessStream)isoVidFile.AsOutputStream();
}
}
// calling this method also, but unhandled exception still continues..
void ResetIsoVideoStreamFile()
{
if (videoStream != null)
{
videoStream.Dispose();
videoStream = null;
}
if (isoVidFile != null)
{
isoVidFile.Dispose();
isoVidFile.Close();
//isoVidFile = null;
}
}
void OpenIsoVideoFile()
{
using (var isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoFile.FileExists(videoFileName))
{
isoFile.DeleteFile(videoFileName);
}
// THIS is where the exception starts, on the call of new IsolatedStorageFileStream, when OpenIsoVideoFile() is being called again after first call..
isoVidFile = new IsolatedStorageFileStream(videoFileName, FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite, isoFile);
// Codes below commented doesn't work either, throws the same exception as above...
//ResetIsoVideoStreamFile();
//isoVidFile = isoFile.CreateFile(videoFileName);
//isoVidFile = isoFile.OpenFile(videoFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
videoStream = (IRandomAccessStream)isoVidFile.AsOutputStream();
}
}
// attempts to fix the error of the unhandled exception, but this one to no avail...
void CloseIsoVideoFile()
{
if (videoStream != null)
{
videoStream.Dispose();
}
if (isoVidFile != null)
{
isoVidFile.Dispose();
isoVidFile.Close();
}
}
async void StopOptimizedRecord()
{
if (vidCaptureDevice != null)
{
StopTimer();
await vidCaptureDevice.StopRecordingAsync();
LoadVideoPlayerFromIsoFile();
TogglePlayPauseButtonVisibility(true);
TogglePlayPauseButton(true);
}
}
void SetCaptureDeviceProperties()
{
if (vidCaptureDevice != null)
{
var profile = KnownCameraAudioVideoProperties.H264EncodingProfile;
var level = KnownCameraAudioVideoProperties.H264EncodingLevel;
var profileProp = H264EncoderProfile.Extended;
var levelProp = H264EncoderLevel.Level1_3;
vidCaptureDevice.SetProperty(profile, profileProp);
vidCaptureDevice.SetProperty(level, levelProp);
}
}
void UninitializeOptimizedCamera()
{
if (vidCaptureDevice != null)
{
vidCaptureDevice.PreviewFrameAvailable -= vidCaptureDevice_PreviewFrameAvailable;
vidCaptureDevice.RecordingFailed -= openedCam_RecordingFailed;
vidCaptureDevice.Dispose();
vidCaptureDevice = null;
videoBrush = null;
recording = false;
}
}
void openedCam_RecordingFailed(AudioVideoCaptureDevice sender, CaptureFailedEventArgs args)
{
}
Windows.Foundation.Size GetLowestVideoResolution()
{
var camOkay = AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back);
var sorted = camOkay.OrderBy(s => s.Width);
var lowest = sorted.First();
return lowest;
}
Windows.Foundation.Size GetLowestPreviewResolution()
{
var res = AudioVideoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back);
var sorted = res.OrderBy(s => s.Width);
return sorted.First();
}
void TogglePlayPauseButton(bool play)
{
playing = play;
PlayPauseButtonImage.Source = playing ? playImage : pauseImage;
}
void TogglePlayPauseButtonVisibility(bool visible)
{
PlayPauseButton.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
}
void LoadVideoPlayerFromIsoFile()
{
if (isoVidFile != null)
{
this.RecorderMediaElement.SetSource(isoVidFile);
}
}
private void RecordClick(object sender, EventArgs e)
{
ToggleRecordVideo();
}
void ToggleRecordVideo()
{
if (!recording)
{
ToggleRecordViewFinderAndPreviewDisplay(true);
TogglePlayPauseButtonVisibility(false);
ToggleAcceptButton(false);
StartOptimizedRecord();
recording = true;
}
else
{
StopOptimizedRecord();
recording = false;
ToggleAcceptButton(true);
ToggleRecordViewFinderAndPreviewDisplay(false);
TogglePlayPauseButtonVisibility(true);
TogglePlayPauseButton(true);
}
}
private void RecorderMediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
this.TogglePlayPauseButton(true);
}
private void SaveToGlobal()
{
reportVideo.VideoLocalUrl = videoFileName;
//GetThumbnailFromVideoPreview();
App.ArgHelper.CurrentTakenVideo = reportVideo;
}
private void GetThumbnailFromVideoPreview()
{
if (thumbnailStream == null)
{
var wb = new WriteableBitmap(this.RecorderMediaElement, null);
wb.SaveJpeg(thumbnailStream, (int)currentFormat.PixelWidth, (int)currentFormat.PixelHeight, 0, 60);
}
reportVideo.Thumbnail = thumbnailStream;
}
private void PlayClick(object sender, EventArgs e)
{
if (videoStream != null)
{
this.RecorderMediaElement.Play();
}
}
private void PlayPauseButton_Click(object sender, RoutedEventArgs e)
{
if (playing)
{
if (videoStream != null)
{
this.RecorderMediaElement.Play();
}
}
else
{
RecorderMediaElement.Pause();
}
TogglePlayPauseButton(!playing);
}
private void ToggleAcceptButton(bool toggle)
{
var acceptBtn = this.ApplicationBar.Buttons[1] as ApplicationBarIconButton;
acceptBtn.IsEnabled = toggle;
}
private void AcceptClick(object sender, EventArgs e)
{
SaveToGlobal();
this.NavigationService.GoBack();
}
void ToggleRecordViewFinderAndPreviewDisplay(bool toggleRecordViewFinder)
{
this.RecorderMediaElement.Visibility = !toggleRecordViewFinder ? Visibility.Visible : Visibility.Collapsed;
this.RecordViewFinder.Visibility = toggleRecordViewFinder ? Visibility.Visible : Visibility.Collapsed;
}
}
}