/** * ターゲットの動画ファイルをセットする */ public async void SetSource(StorageFile source) { Reset(); if (null == source) { return; } mPlayer.SetSource(source); try { mClip = await MediaClip.CreateFromFileAsync(source); mTrimmingSlider.TotalRange = mClip.OriginalDuration.TotalMilliseconds; await mExtractor.ExtractAsync(mClip, (s, i, img) => { mFrameListView.Frames[i] = img; }, (s, img) => { mFrameListView.FrameListHeight = mExtractor.ThumbnailHeight; for (int i = 0; i < mExtractor.FrameCount; i++) { mFrameListView.Frames.Add(img); } }); } catch (Exception e) { CmLog.error("WvvFrameSelectorView.SetSource", e); Error.SetError(e); } }
/** * MediaOpenedイベントのハンドラ */ private async void OnOpened(MediaPlayer sender, object args) { Opened = true; if (null != OwnerView) { CmLog.debug("WvvMediaLoader.OnOpened: MediaOpened"); await terminate(sender); } }
/** * 動画ファイルをロードして、フレームサムネイルを抽出する。 */ private async void LoadMediaSource(StorageFile source) { Ready = false; // Error.Reset(); mPlayer.Source = null; mPreviewing = false; mTrimmingSlider.Reset(); mFrameListView.Reset(); mExtractor.Cancel(); mFrameListView.ShowCurrentTick = false; mComposition.Clips.Clear(); if (null != source) { mOriginalSource = MediaSource.CreateFromStorageFile(source); var loader = await WvvMediaLoader.LoadAsync(mPlayer, mOriginalSource, this); if (loader.Opened) { TotalRange = loader.TotalRange; VideoSize = loader.VideoSize; try { var clip = await MediaClip.CreateFromFileAsync(source); mComposition.Clips.Add(clip.Clone()); Ready = true; await mExtractor.ExtractAsync(clip, (s, i, img) => { mFrameListView.Frames[i] = img; }, (s, img) => { mFrameListView.FrameListHeight = mExtractor.ThumbnailHeight; for (int i = 0; i < ThumbnailCount; i++) { mFrameListView.Frames.Add(img); } mFrameListView.ShowCurrentTick = true; }); } catch (Exception e) { CmLog.error(e, "WvvTrimmingView.LoadMediaSource: Error"); Error.SetError(e); } } else // not opened. { Error.CopyFrom(loader.Error); } } }
/** * フレームの抽出処理を開始 * * @param clip ソースを保持したMediaClip * @param extracted 取得したフレーム画像をコールバックするハンドラ */ public async Task <bool> ExtractAsync(MediaClip clip, OnThumbnailExtractedHandler extracted, OnBlankThumbnailHandler blank) { int doing = ++mDoing; Error = null; // Debug.WriteLine("Logical-DPI = {0}", DisplayInformation.GetForCurrentView().LogicalDpi); var composer = new MediaComposition(); composer.Clips.Add(clip); try { var totalRange = clip.OriginalDuration.TotalMilliseconds; var span = totalRange / FrameCount; var offset = span / 2; for (int n = 0; n < FrameCount; n++) { using (var imageStream = await composer.GetThumbnailAsync(TimeSpan.FromMilliseconds(offset + span * n), 0, ThumbnailHeight, VideoFramePrecision.NearestFrame)) { if (doing != mDoing) { // cancelling return(false); } var bmp = new BitmapImage(); bmp.SetSource(imageStream); // bmp.SetSourceしたら、imageStreamはすぐDisposeしても大丈夫っぽい。 if (null != blank && n == 0) { var source = await CreateBlankBitmap(bmp.PixelWidth, bmp.PixelHeight); #if false var bb = new SoftwareBitmap(BitmapPixelFormat.Bgra8, bmp.PixelWidth, bmp.PixelHeight, BitmapAlphaMode.Ignore); var source = new SoftwareBitmapSource(); await source.SetBitmapAsync(bb); #endif blank(this, source); } extracted(this, n, bmp); } } return(true); } catch (Exception e) { CmLog.error("WvvFrameExtractor2.ExtractAsync", e); Error = e; return(false); } finally { composer.Clips.Clear(); } }
/** * ソースをMediaPlayerにロードする(非同期版) */ public Task <bool> LoadAsync(MediaSource source, DependencyObject ownerView) { CmLog.debug("WvvMediaLoader.LoadAsync: async operation started..."); var task = new TaskCompletionSource <bool>(); Load(source, ownerView, (loader) => { CmLog.debug("WvvMediaLoader.LoadAsync: ... async operation finished."); task.TrySetResult(Opened); }); return(task.Task); }
/** * プレビューモードを開始する。 * * トリミングモード: * MediaPlayerのソースに、オリジナルのソース(StorageFileから生成したもの)をセットした状態。 * トリミング操作は、常にこのモードで行い、再生は行わない。 * プレビューモード: * MediaPlayerのソースに、MediaComposition から生成したストリームを指定し、トリミング後の動画を再生テストするモード * トリミング操作を行うと、自動的にこのモードはキャンセルされ、全体表示モードに戻る。 */ private async Task startPreview(bool play) { if (mPreviewing) { if (!IsPlaying) { mPlayer.Play(); } return; } if (mTrimmingSlider.TrimmedRange < 100 || mComposition.Clips.Count != 1) { return; } mComposition.Clips[0].TrimTimeFromStart = TimeSpan.FromMilliseconds(mTrimmingSlider.TrimStart); mComposition.Clips[0].TrimTimeFromEnd = TimeSpan.FromMilliseconds(mTrimmingSlider.TrimEnd); try { mPreviewing = true; MediaStreamSource mediaStreamSource = mComposition.GeneratePreviewMediaStreamSource( (int)mPlayerElement.ActualWidth, (int)mPlayerElement.ActualHeight); var loader = await WvvMediaLoader.LoadAsync(mPlayer, MediaSource.CreateFromMediaStreamSource(mediaStreamSource), this); if (null != loader) { if (mPreviewing) { mPlayer.PlaybackSession.Position = TimeSpan.FromMilliseconds(mTrimmingSlider.CurrentPosition); if (play) { mPlayer.Play(); } } } else { mPreviewing = false; } } catch (Exception e) { CmLog.error(e, "WvvTrimmingView.startPreview: Error"); mPlayer.Pause(); mPreviewing = false; Error.SetError(e); } }
public async Task <ImageStream> GetResultImageStream(int height) { try { var extractor = new WvvFrameExtractor2(height, 1); return(await extractor.ExtractSingleFrameStreamAsync(mClip, TimeSpan.FromMilliseconds(mTrimmingSlider.CurrentPosition))); } catch (Exception e) { Error.SetError(e); CmLog.error("WvvFrameSelectorView.GetResultImageStream", e); return(null); } }
public async Task <ImageStream> ExtractSingleFrameStreamAsync(StorageFile source, TimeSpan position) { try { var clip = await MediaClip.CreateFromFileAsync(source); return(await ExtractSingleFrameStreamAsync(clip, position)); } catch (Exception e) { CmLog.error("WvvFrameExtractor2.ExtractSingleFrameStreamAsync(StorageFile)", e); return(null); } }
/** * トラッキング終了時のハンドラ */ private void OnKnobReleased(object sender, PointerRoutedEventArgs e) { ((UIElement)sender).ReleasePointerCapture(e.Pointer); mTracking.Active = false; if (null != mTracking.Moved) { var v = getNewValue(e); mTracking.Moved(v, true); mTracking.Moved = null; } CmLog.debug("WvvTrimmingSlider.OnKnobReleased"); e.Handled = true; }
/** * ファイルのタイムスタンプを現在時間に更新する * - BasicProperty.DateModified * - BasicProperty.ItemDate * - Properties[System.DateModified] * が変更される。 * 以下のプロパティは変化しない。 * - StorageFile.DataCreated * - Properties[System.DateCreated] * - Properties[System.DateAccessed] */ public static async Task TouchFileAsync(StorageFile file) { // タイムスタンプを更新するための秘技 // - file.Properties.SavePropertiesAsync()で、ModifiedDate や AccessedDateが変えられない。(ArgumentExceptionがスローされる) // - file.OpenAsync(ReadWrite)/Dispose しても、ModifiedDateもAccessedDateも変わらない。 // - OpenAsyncした後、1バイト読み込んで1バイト書き込む、のような操作をすることも考えたが、気落ち悪い。 // で、この技・・・0行の文字列を追加する。 try { await FileIO.AppendLinesAsync(file, new string[0]); } catch (Exception e) { CmLog.error(e, "WvvCacheManager.TouchFileAsync: Error"); } }
/** * MediaPlayerでエラーが発生したときの処理 */ private async void MP_Failed(MediaPlayer sender, MediaPlayerFailedEventArgs args) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { CmLog.debug("WvvTrimmingView.MP_Failed: MediaFailed"); if (null != args.ErrorMessage && args.ErrorMessage.Length > 0) { CmLog.debug(args.ErrorMessage); Error.SetError(args.ErrorMessage); } if (null != args.ExtendedErrorCode) { CmLog.error(args.ExtendedErrorCode.Message); Error.SetError(args.ExtendedErrorCode); } }); }
/** * MediaFailedイベントのハンドラ * * --> Errorプロパティのコメント参照 */ private async void OnFailed(MediaPlayer sender, MediaPlayerFailedEventArgs args) { CmLog.debug("WvvMediaLoader.OnFailed: MediaFailed"); if (null != args.ErrorMessage && args.ErrorMessage.Length > 0) { CmLog.debug(args.ErrorMessage); Error.SetError(args.ErrorMessage); } if (null != args.ExtendedErrorCode) { CmLog.error(args.ExtendedErrorCode.Message); Error.SetError(args.ExtendedErrorCode); } Opened = false; await terminate(sender); }
/** * キャッシュをフォルダ毎削除して再作成 * Swipe用 */ public async Task ClearAllAsync() { try { var folder = mFolder.Folder; mFolder = null; await folder.DeleteAsync(); } catch (Exception e) { CmLog.error(e, "WvvCacheManager.ClearAllAsync"); } finally { await InitAsync(); } }
/** * URLのキャッシュ(IWvvCache)を取得 */ public async Task <IWvvCache> GetCacheAsync(Uri uri) { WvvCache cache; using (mManagerLock.Lock()) { string key = keyFromUri(uri); if (!mCacheList.TryGetValue(key, out cache)) { try { var file = await mFolder.Folder.GetFileAsync(key); await TouchFileAsync(file); cache = new WvvCache(key, uri, file); mCacheList[key] = cache; CmLog.debug("WvvCacheManager.GetCacheAsync: Use cold cache: {0}", uri.ToString()); } catch (FileNotFoundException) { // target is not found in cache cache = new WvvCache(key, uri, null); mCacheList[key] = cache; CmLog.debug("WvvCacheManager.GetCacheAsync: Use new cache: {0}", uri.ToString()); } catch (Exception e2) { CmLog.error(e2, "WvvCacheManager.GetCacheAsync"); return(null); } } else { CmLog.debug("WvvCacheManager.GetCacheAsync: Use hot cache: {0}", uri.ToString()); var file = cache.CacheFile; if (null != file && cache.RefCount == 0) { await TouchFileAsync(file); } } } cache.AddRef(); Sweep(); return(cache); }
/** * ダウンロードが失敗したときの処理 */ private void onDownloadError(Exception error) { Error = error; lock (mLock) { mFile = null; try { Downloaded?.Invoke(this, null); Downloaded = null; } catch (Exception e) { // コールバック中のエラーは無視する CmLog.error(e, "WvvCache.onDownloadError (error occurred in downloaded-callback(s)... ignored.)"); } } }
//private void OnUnloaded(object sender, RoutedEventArgs e) //{ // Debug.WriteLine("MediaPage Unloaded."); // Dispose(); //} /** * リソース解放 */ private void Dispose() { var player = mPlayer.MediaPlayer; if (null != player) { mPlayer.UnregisterPropertyChangedCallback(MediaPlayerElement.IsFullWindowProperty, mFullWindowListenerToken); mPlayer.SetMediaPlayer(null); player.Dispose(); //この方法は、Restricted Capabilities の指定が必要なので、できれば避けたい。 //SystemNavigationManagerPreview.GetForCurrentView().CloseRequested -= OnCloseRequested; } Closed = null; mInfo = null; CmLog.debug("WvvPinPPage.Dispose: PinP Player Disposed.)"); }
/** * 終了処理 (成功時/エラー発生時の共通処理) */ private async Task terminate(MediaPlayer mediaPlayer) { CmLog.debug("WvvMediaLoader.terminate: (Loading={0})", Loading); await OwnerView.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { CmLog.debug("WvvMediaLoader.terminate (inner): Loading={0}, hasCallback={0}", Loading, Loaded != null); if (Loading) { Loading = false; mediaPlayer.MediaOpened -= OnOpened; mediaPlayer.MediaFailed -= OnFailed; if (Opened) { try { TotalRange = Player.PlaybackSession.NaturalDuration.TotalMilliseconds; VideoSize = new Size(Player.PlaybackSession.NaturalVideoWidth, Player.PlaybackSession.NaturalVideoHeight); CmLog.debug("WvvMediaLoader.terminate (inner): Loaded"); } catch (Exception e) { // MediaOpenedが返ってきても、その後、プロパティを参照しようとすると Shutdown済み、みたいな例外が出ることがあって、 // このような場合は、ステータスも Closedになっているので、オープン失敗として扱う。 CmLog.error(e, "WvvMediaLoader.terminate (inner): Error"); Opened = false; mediaPlayer.Source = null; Error.SetError(e); } } else { mediaPlayer.Source = null; if (!Error.HasError) { Error.SetError("MediaLoader error"); } } Loaded?.Invoke(this); Loaded = null; OwnerView = null; } }); }
/** * CurrentPosition調整用Thumbがクリックされた */ private void OnThumbPressed(object sender, PointerRoutedEventArgs e) { ((UIElement)sender).CapturePointer(e.Pointer); beginTracking(e, 1, MWidth, AbsoluteCurrentPosition, TotalRange - TrimEnd - TrimStart); mTracking.Moved = (v, last) => { CurrentPosition = v; if (mTracking.Ext != AbsoluteCurrentPosition) { mTracking.Ext = AbsoluteCurrentPosition; CurrentPositionChanged?.Invoke(this, CurrentPosition, false); } }; CmLog.debug("WvvTrimmingSlider.OnThumbPressed"); e.Handled = true; }
/** * TrimEnd位置調整用Knobがクリックされた */ private void OnRKnobPressed(object sender, PointerRoutedEventArgs e) { ((UIElement)sender).CapturePointer(e.Pointer); beginTracking(e, -1, RWidth, TrimEnd, TotalRange - TrimStart - MinimumRange); mTracking.Moved = (v, last) => { TrimEnd = v; if (mTracking.Ext != v) { mTracking.Ext = v; TrimEndChanged?.Invoke(this, v, last); } }; CmLog.debug("WvvTrimmingSlider.OnRKnobPressed"); e.Handled = true; }
/** * ダウンロードが成功したときの処理 */ private void onDownloadCompleted(StorageFile file) { Error = null; lock (mLock) { mFile = file; mInvalidFile = null; try { Downloaded?.Invoke(this, file); Downloaded = null; } catch (Exception e) { // コールバック中のエラーは無視する CmLog.error(e, "WvvCache.onDownloadCompleted (error occurred in downloaded-callback(s)... ignored.)"); } } }
/** * キャッシュを解放する(CacheManagerによって削除可能な状態にする) */ public void Release() { lock (mLock) { mRefCount--; if (mRefCount == 0 && mFile == null && mDownloadTask == null && mInvalidFile != null) { try { var _ = mInvalidFile.DeleteAsync(); mInvalidFile = null; } catch (Exception e) { CmLog.error(e, "WvvCache.Release (deleting file)."); } } } }
/** * キャッシュファイルを取得する。 * @param callback 結果を返すコールバック */ public void GetFile(WvvDownloadedHandler callback) { lock (mLock) { if (null == mFile) { if (null == mDownloadTask) { Download(); } CmLog.debug("WvvCache.GetFile: Downloading ..."); Downloaded += callback; return; } } // ファイルはキャッシュされている CmLog.debug("WvvCache.GetFile: Cache File Available"); callback(this, mFile); }
private async void PBS_StateChanged(MediaPlaybackSession session, object args) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { CmLog.debug("WvvTrimmingView:PlaybackState = {0}", session.PlaybackState.ToString()); switch (session.PlaybackState) { case MediaPlaybackState.None: case MediaPlaybackState.Buffering: case MediaPlaybackState.Opening: case MediaPlaybackState.Paused: default: IsPlaying = false; break; case MediaPlaybackState.Playing: IsPlaying = true; break; } }); }
public async Task <ImageStream> ExtractSingleFrameStreamAsync(MediaClip clip, TimeSpan position) { Error = null; var composer = new MediaComposition(); composer.Clips.Add(clip); try { return(await composer.GetThumbnailAsync(position, 0, ThumbnailHeight, VideoFramePrecision.NearestFrame)); } catch (Exception e) { CmLog.error("WvvFrameExtractor2.ExtractSingleFrameStreamAsync(MediaClip)", e); Error = e; return(null); } finally { composer.Clips.Clear(); } }
/** * SubWindowの×ボタンがクリックされたときに呼び出される。 */ private void OnConsolidated(ApplicationView sender, ApplicationViewConsolidatedEventArgs args) { CmLog.debug("WvvPinPPage.OnConsolidated: PinP Player Closed.)"); ApplicationView.GetForCurrentView().Consolidated -= OnConsolidated; Close(); }
/** * キャッシュファイル数が制限を超えていたら、古いファイルから削除する */ private async void Sweep() { // 再入防止 if (mSweeping) { return; } mSweeping = true; try { // キャッシュファイル列挙 var folder = mFolder.Folder; var list = await folder.GetFilesAsync(); if (list.Count < MAX_CACHE_COUNT) { return; } // 更新日時(BasicProperty.ItemData)順(昇順)にソート var files = new List <TimedFile>(list.Count); foreach (var f in list) { var bp = await f.GetBasicPropertiesAsync(); files.Add(new TimedFile(f, bp.ItemDate)); } files.Sort((x, y) => { return((x.Date < y.Date) ? 1 : (x.Date > y.Date) ? -1 : 0); }); //foreach(var f in files) //{ // await DumpFileTimeStamp(f.File); //} // 古いファイルから削除 for (int i = MAX_CACHE_COUNT; i < files.Count; i++) { using (mManagerLock.Lock()) { WvvCache cache; if (mCacheList.TryGetValue(files[i].File.Name, out cache)) { if (cache.RefCount > 0) { continue; // 使っているので削除不可 (breakでもよいと思うけど) } mCacheList.Remove(files[i].File.Name); } await files[i].File.DeleteAsync(); } } } catch (Exception e) { CmLog.error(e, "WvvCacheManager.Sweep"); } finally { mSweeping = false; } }