Xaml MediaElement 不播放来自互联网的 .mov 视频
Xaml MediaElement does not play .mov video from internet
我想播放来自 Internet 的实时视频流,提供 url 到 MediaElement
xaml 控件。如果视频是 .mp4
,一切正常。但是,如果视频是在 iphone 上拍摄并保存为 .mov
,那么 MediaElement
什么也不会显示,只会触发 loading
事件。 (下载完成后约 5 分钟其他事件也会触发)
这里MediaElement
初始化:
MediaElement media = new MediaElement();
media.MediaFailed += Media_MediaFailed;
media.PartialMediaFailureDetected += Media_PartialMediaFailureDetected;
media.MediaOpened += Media_MediaOpened;
media.DownloadProgressChanged += Media_DownloadProgressChanged;
media.Loading += Media_Loading;
media.MediaEnded += Media_MediaEnded;
media.Source = new Uri(URL);
如果通过 HttpClient
手动下载视频,然后将 Stream.AsRamdomAccessStream()
提供给 MediaElement
,视频会立即开始播放。 MediaElement
似乎不支持 .mov/quicktime 视频的直播。是真的吗?
遗憾的是,内置 MediaElement
控件不支持 mov
。
mov
视频类型是 Apple Inc. 专用于 QuickTime 播放器的格式,这意味着它可能永远不会受到原生支持。
支持的格式和编解码器的完整列表是 here on MSDN。
编辑:看来MediaElement
控件其实可以使用PC上安装的解码器直接播放不支持的格式,包括mov
.
真正的问题在于容器原子的顺序。正如所解释的 here
In order to start playing a movie file right away its metadata contained in the "moov" atom is paramount to the player. If the movie file atoms are ordered as previously described everything work as expected...but most video editors (ffmpeg, quicktime, flash video) generate atoms in the wrong order (as seen on the right): With the "moov" atom last.
并提供解决方案:
The only solution is to fix those files and reorder the atoms inside. This can be done:
- Oldskool way using some C code from FFMPEG's qt-faststart.c. The code moves the "moov" atom at the top of the file and update all the "stco" sub-atoms pointers by adding the proper offset.
Using iOS AV Foundation framework and a few lines of Objective-C (you can also convert from MOV to MP4 since Android cannot read MOV):
#import <AVFoundation/AVAsset.h>
#import <AVFoundation/AVAssetExportSession.h>
#import <AVFoundation/AVMediaFormat.h>
+ (void) convertVideoToMP4AndFixMooV: (NSString*)filename toPath:(NSString*)outputPath
{
NSURL *url = [NSURL fileURLWithPath:finename];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
AVAssetExportSession *exportSession = [AVAssetExportSession
exportSessionWithAsset:avAsset
presetName:AVAssetExportPresetPassthrough];
exportSession.outputURL = [NSURL fileURLWithPath:outputPath];
exportSession.outputFileType = AVFileTypeAppleM4V;
// This should move the moov atom before the mdat atom,
// hence allow playback before the entire file is downloaded
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:
^{
if (AVAssetExportSessionStatusCompleted == exportSession.status) {}
else if (AVAssetExportSessionStatusFailed == exportSession.status) {
NSLog(@"AVAssetExportSessionStatusFailed");
}
else
{
NSLog(@"Export Session Status: %d", exportSession.status);
}
}];
}
由于我也在开发一个 iOS 客户端,这解决了我的问题。
我想播放来自 Internet 的实时视频流,提供 url 到 MediaElement
xaml 控件。如果视频是 .mp4
,一切正常。但是,如果视频是在 iphone 上拍摄并保存为 .mov
,那么 MediaElement
什么也不会显示,只会触发 loading
事件。 (下载完成后约 5 分钟其他事件也会触发)
这里MediaElement
初始化:
MediaElement media = new MediaElement();
media.MediaFailed += Media_MediaFailed;
media.PartialMediaFailureDetected += Media_PartialMediaFailureDetected;
media.MediaOpened += Media_MediaOpened;
media.DownloadProgressChanged += Media_DownloadProgressChanged;
media.Loading += Media_Loading;
media.MediaEnded += Media_MediaEnded;
media.Source = new Uri(URL);
如果通过 HttpClient
手动下载视频,然后将 Stream.AsRamdomAccessStream()
提供给 MediaElement
,视频会立即开始播放。 MediaElement
似乎不支持 .mov/quicktime 视频的直播。是真的吗?
遗憾的是,内置 MediaElement
控件不支持 mov
。
mov
视频类型是 Apple Inc. 专用于 QuickTime 播放器的格式,这意味着它可能永远不会受到原生支持。
支持的格式和编解码器的完整列表是 here on MSDN。
编辑:看来MediaElement
控件其实可以使用PC上安装的解码器直接播放不支持的格式,包括mov
.
真正的问题在于容器原子的顺序。正如所解释的 here
In order to start playing a movie file right away its metadata contained in the "moov" atom is paramount to the player. If the movie file atoms are ordered as previously described everything work as expected...but most video editors (ffmpeg, quicktime, flash video) generate atoms in the wrong order (as seen on the right): With the "moov" atom last.
并提供解决方案:
The only solution is to fix those files and reorder the atoms inside. This can be done:
- Oldskool way using some C code from FFMPEG's qt-faststart.c. The code moves the "moov" atom at the top of the file and update all the "stco" sub-atoms pointers by adding the proper offset.
Using iOS AV Foundation framework and a few lines of Objective-C (you can also convert from MOV to MP4 since Android cannot read MOV):
#import <AVFoundation/AVAsset.h> #import <AVFoundation/AVAssetExportSession.h> #import <AVFoundation/AVMediaFormat.h> + (void) convertVideoToMP4AndFixMooV: (NSString*)filename toPath:(NSString*)outputPath { NSURL *url = [NSURL fileURLWithPath:finename]; AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil]; AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:avAsset presetName:AVAssetExportPresetPassthrough]; exportSession.outputURL = [NSURL fileURLWithPath:outputPath]; exportSession.outputFileType = AVFileTypeAppleM4V; // This should move the moov atom before the mdat atom, // hence allow playback before the entire file is downloaded exportSession.shouldOptimizeForNetworkUse = YES; [exportSession exportAsynchronouslyWithCompletionHandler: ^{ if (AVAssetExportSessionStatusCompleted == exportSession.status) {} else if (AVAssetExportSessionStatusFailed == exportSession.status) { NSLog(@"AVAssetExportSessionStatusFailed"); } else { NSLog(@"Export Session Status: %d", exportSession.status); } }]; }
由于我也在开发一个 iOS 客户端,这解决了我的问题。