Xamarin Forms:如何捕获 webview video/audio 播放时间?

Xamarin Forms: How to capture the webview video/audio play time?

我正在使用 webview 在我的项目中播放视频和音频,如下图所示。

我需要在从该页面返回时捕获 video/audio 播放时间。我对此进行了研究,但没有得到任何有用的信息。

有什么方法可以获取 video/audio 播放时间吗?

更新

视频:XAML

<local:VideoWebview
    x:Name="web_view"
    HorizontalOptions="FillAndExpand"
    VerticalOptions="FillAndExpand">
</local:VideoWebview>

XAML.CS

if (Device.RuntimePlatform == Device.iOS)
{
    Device.BeginInvokeOnMainThread(() =>
{
    web_view.Url = videourl;
});
}
else
{
    web_view.Source = videourl;
}

音频:XAML

<WebView 
    x:Name="web_view"
    HorizontalOptions="FillAndExpand"
    VerticalOptions="FillAndExpand">
</WebView>

XAML.CS

web_view.Source = audiourl;

示例视频 URL:https://player.vimeo.com/video/434126696?api=1&player_id=player1

示例音频 URL:http://catholic-brain.s3.amazonaws.com/qa/dc/cbrain-app/files/doc-lib/2017/08/16/11/21/59/604/head/Catholic-brain-01-The-Rosary-Song.mp3

我在 ios 视频网络视图中使用自定义渲染器来实现透明背景。

public class VideoWebviewRenderer : ViewRenderer<VideoWebview, WKWebView>
{
    WKWebView wkWebView;

    protected override void OnElementChanged(ElementChangedEventArgs<VideoWebview> e)
    {
        base.OnElementChanged(e);

        if (Control == null)
        {
            WKWebViewConfiguration configuration = new WKWebViewConfiguration();
            configuration.AllowsInlineMediaPlayback = true;
            wkWebView = new WKWebView(CGRect.Empty, configuration);
            //transparent background
            wkWebView = new WKWebView(CGRect.Empty, configuration);
            wkWebView.BackgroundColor = UIColor.Clear;
            wkWebView.ScrollView.BackgroundColor = UIColor.Clear;
            wkWebView.Opaque = false;

            if (Element.Url != null)
            {
                wkWebView.LoadRequest(new NSUrlRequest(new NSUrl((Element).Url)));
            }
            SetNativeControl(wkWebView);
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName.Equals(nameof(Element.Url)))
        {
            wkWebView.LoadRequest(new NSUrlRequest(new NSUrl((Element).Url)));
        }
    }

    public override void Draw(CGRect rect)
    {
        base.Draw(rect);

        Control.Frame = rect;
    }
}

更新1

@Pete 我试过 MediaElement 但是 UI 上没有显示视频或音频。

我做了一个样例并上传了here,你能检查一下我遗漏了什么吗?

而且我没有完成第一步,如何为我的 Vimeo 视频创建流?对于音频文件,我需要创建流吗?你能解释一下音频部分的实现吗?我在示例中添加了音频部分,它在 UI 上不可见,但我可以听到音频。

示例视频 URL:https://player.vimeo.com/video/434126696?api=1&player_id=player1

示例音频 URL:http://catholic-brain.s3.amazonaws.com/qa/dc/cbrain-app/files/doc-lib/2017/08/16/11/21/59/604/head/Catholic-brain-01-The-Rosary-Song.mp3

我的 Vimeo 视频没有像 .mp4 这样的扩展名

更新2

所以在播放视频之前,我们需要下载视频的配置。我看了一下配置,有 5 种不同的配置。所以我们需要从progressive list中取出URL的值对吧?

不幸的是,我已经从共享源link在本地站点进行了测试,但无法获取播放时间。

Video URL: https://player.vimeo.com/video/434126696?api=1&player_id=player1

视频 url 是一个包装播放器 link。我们需要玩家暴露 javascript api 的播放时间。然后我们可以在本机代码中调用它。

Audio URL: http://catholic-brain.s3.amazonaws.com/qa/dc/cbrain-app/files/doc-lib/2017/08/16/11/21/59/604/head/Catholic-brain-01-The-Rosary-Song.mp3

音频url是一个原始来源(.mp3),我们只能得到它的持续时间。

NSString url = new NSString("xxx.mp3");

if (url.PathExtension == new NSString("mp3"))
{

    NSUrl videoUrl = new NSUrl(url.ToString());

    AVUrlAsset avUrl = new AVUrlAsset(videoUrl, new NSDictionary(AVUrlAsset.PreferPreciseDurationAndTimingKey, true));

    CMTime time = avUrl.Duration;

    float seconds = time.Value / time.TimeScale;

    Console.WriteLine("====================" + seconds );
}

如果需要获取播放时间,也可以使用wrapped audio player。并且还晒出了它javascriptapi的播放时间

一般,如果使用audiovideo属性 of Html 要播放 原始来源 url ,我们可以调用 js 来获取 iOS 中的 currentTimeduration 如下:

//video
string JsStr = "(document.getElementsByTagName(\"video\")[0]).currentTime";
// audio
//string JsStr = "(document.getElementsByTagName(\"audio\")[0]).currentTime";

WKJavascriptEvaluationResult handler = (NSObject result, NSError err) =>
{
    if (err != null)
    {
        System.Console.WriteLine("================"+err);
    }
    if (result != null)
    {
        System.Console.WriteLine("currentTime is : "+result);
    }
};
wkWebView.EvaluateJavaScript(JsStr, handler);

下面是如何使用 Xamarin.Forms 执行此操作的答案,正如您在评论中提到的,如果有任何替代播放器提供获取游戏时间,那么这里是:-

要在 Xamarin.Forms 中执行此操作,您可以使用 [=][=] 中引入的 MediaElement 149=]4.5.

为此,您需要做几件事:-

  1. 根据存储在 Vimeo.
  2. 上的视频配置确定要播放的流

您可以通过检查从下面 url:-

返回的 JSON 来获取此配置 http://player.vimeo.com/video/36671239/config

即,36671239 是您在 Vimeo 上的 视频 ID


下面是一个流的例子,它是 mp4 视频格式:-

https://vod-progressive.akamaized.net/exp=1597882056~acl=%2A%2F84157673.mp4%2A~hmac=1230da01c045577d047b307a0d3679b46cc9b0bbc1d0312e272b67bb31d5129c/vimeo-prod-skyfire-std-us/01/2334/1/36671239/84157673.mp4
  1. 非常重要:-

MediaElement预览版中,如果您在申请时不执行以下操作start-up,您将几乎总是收到 System.Reflection.TargetInvocationException 被抛出并崩溃,你会困惑一段时间。

为避免这种情况,暂时在 App() 构造函数中添加以下内容:-

Device.SetFlags(new[] { "MediaElement_Experimental" });
  1. MediaElement 不会为 Position 发出 PropertyChanged 通知 属性, 目前.

然而,要解决此问题,您可以使用自己的 Timer,并检索视频的 Position 属性那是暴露的,但是请使用它。

这绝不适合生产,因为您不希望计时器总是如此 运行,但这只是为了说明您可以实现您想要的:-

像这样在构造函数中设置一个活动计时器:-

Device.StartTimer(TimeSpan.FromSeconds(0.5), () => { if (videoPlayer.CurrentState == MediaElementState.Playing) { string strPlayPosition = videoPlayer.Position.ToString(@"hh\:mm\:ss"); lblCurrentVideoTime.Text = strPlayPosition; } return true; });
  1. ContentPage 添加以下内容:-
<pre><code><Grid BackgroundColor="#242475"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <MediaElement x:Name="videoPlayer" Grid.Row="0" Aspect="AspectFit" BackgroundColor="Black" ShowsPlaybackControls="True" Source="https://vod-progressive.akamaized.net/exp=1597882056~acl=%2A%2F84157673.mp4%2A~hmac=1230da01c045577d047b307a0d3679b46cc9b0bbc1d0312e272b67bb31d5129c/vimeo-prod-skyfire-std-us/01/2334/1/36671239/84157673.mp4" /> <StackLayout Grid.Row="1" HorizontalOptions="CenterAndExpand" Orientation="Horizontal" VerticalOptions="Center"> <Label FontAttributes="Bold" FontSize="18" Text="CURRENT VIDEO TIME : " TextColor="White" /> <Label x:Name="lblCurrentVideoTime" FontAttributes="Bold" FontSize="18" TextColor="White" /> </StackLayout> </Grid>

你会看到类似下面的内容,以及你想从 Vimeo 观看的任何视频:-

MediaElement 也可用于 audio

更新 1

我看过你提到的视频 ID (434126696)。

并且在配置中指定了多个 mp4 流。

一个这样的流是:-

https://vod-progressive.akamaized.net/exp=1598157872~acl=%2Fvimeo-prod-skyfire-std-us%2F01%2F1825%2F17%2F434126696%2F1890592390.mp4~hmac=a0ac67f23fdd68df9d16fb2db33e8b2b00d0dc3626375d2a4c6ba7a5203b935e/vimeo-prod-skyfire-std-us/01/1825/17/434126696/1890592390.mp4

如果您将此 mp4 link 放在 MediaElement 中,在我的示例中,就像这样:-

<pre><code> <MediaElement x:Name="videoPlayer" Grid.Row="0" Aspect="AspectFit" BackgroundColor="Black" ShowsPlaybackControls="True" Source="https://vod-progressive.akamaized.net/exp=1598157872~acl=%2Fvimeo-prod-skyfire-std-us%2F01%2F1825%2F17%2F434126696%2F1890592390.mp4~hmac=a0ac67f23fdd68df9d16fb2db33e8b2b00d0dc3626375d2a4c6ba7a5203b935e/vimeo-prod-skyfire-std-us/01/1825/17/434126696/1890592390.mp4" />

您将看到您的视频正在播放。

你必须分析返回的JSON。在 C# 中,您可以像这样下载配置:-

使用 (var objClient = new WebClient()) { string strResponse = objClient.DownloadString("http://player.vimeo.com/video/434126696/config"); }


然后如果你使用 JSON 检查器,你将能够看到暴露的各种 mp4 流为您的视频提供不同的分辨率。然后,您可以选择一个合适的视频来显示您的视频。

执行 JSON 提取的代码:

public  void ExtractConfiguration(string vimeoUrl)
{
    try
    {
        using (var objClient = new WebClient())
        {
            string strResponse = objClient.DownloadString(vimeoUrl);
            VimeoConfig vimeoConfig = new VimeoConfig();
            vimeoConfig = JsonConvert.DeserializeObject<VimeoConfig>(strResponse.ToString());
            string url = vimeoConfig.request.files.progressive[0].url;
            videoPlayer.Source = url;
        }
    }
    catch(Exception e)
    {
        Debug.WriteLine("Exception:>>"+e);
    }
}

//Model

public class VimeoConfig
{
    public Request request { get; set; }
}

public class Request
{
    public Files files { get; set; }
}

public class Files
{
    public List<Progressive> progressive { get; set; }
}

public class Progressive
{
    public string url { get; set; }
}