问题
发现了个奇怪的问题,部分视频不清楚是什么原因导致的,会发生有有声无画的情况。
查看AVPRO的加载回调,是MediaPlayerEvent.EventType.FirstFrameReady 这个类型没有返回,基本可以得知了视频画面是没有准备好的。
排查
这个基本上就是VideoAPI设置的问题,毕竟AVPRO不能做到调普通播放器那样,支持直接调用ffmpeg解码。参考官方的 Demo场景,是可以看到使用的是MediaFoundation 这个VideoAPI。原先的DirectShow 比较旧,可能对一些新技术兼容性不是很好。具体里面的差异就比较深奥了。
MediaFoundation :Windows 的新一代视频 API,现代化、效率更高。
优点:硬件加速支持好,系统支持视频格式清晰。
注意:有时对容器封装非常敏感(如 faststart header)。
解决
就像如上所说的,解决方案就是把VideoAPI改为MediaFoundation 即可解决问题。此时要注意,如果Audio output 设置的是Unity,一定要挂载AVPRO的Audio Output 组件,才会有声音输出。不知为何DirectShow 时就不需要。

题外话
这里做了个AVPRO的UniTask异步加载拓展,可以做等待视频加载和播放完成。其核心解决方案就是 MediaPlayer.Events.AddListener 这个回调,基于状态设置等待结束。
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using RenderHeads.Media.AVProVideo;
using UnityEngine;
using UnityEngine.UI;
public static class AVProVideoUniTaskExtensions
{
// 等待视频播放完成(到结尾)
public static async UniTask WaitForCompletion(this MediaPlayer player,
CancellationToken cancellationToken = default)
{
if (player == null)
{
throw new ArgumentNullException(nameof(player));
}
// 如果视频已停止,直接返回
if (!player.Control.IsPlaying())
{
return;
}
// 创建任务源,用于在事件触发时完成任务
var tcs = new UniTaskCompletionSource();
// 定义事件处理方法
void EventHandler(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
{
switch (et)
{
case MediaPlayerEvent.EventType.FinishedPlaying:
player.Events.RemoveListener(EventHandler);
tcs.TrySetResult();
break;
case MediaPlayerEvent.EventType.Error:
player.Events.RemoveListener(EventHandler);
tcs.TrySetException(new Exception($"视频播放错误: {errorCode}"));
break;
}
}
// 添加事件监听
player.Events.AddListener(EventHandler);
try
{
// 等待任务完成或取消
await tcs.Task.AttachExternalCancellation(cancellationToken);
}
finally
{
// 确保移除事件监听
player.Events.RemoveListener(EventHandler);
}
}
/// <summary>
/// 等待视频准备就绪(ReadyToPlay)
/// </summary>
/// <param name="player"></param>
/// <param name="cancellationToken"></param>
/// <exception cref="ArgumentNullException"></exception>
public static async UniTask WaitForReady(this MediaPlayer player, MediaPath path,bool isOpen=true,
CancellationToken cancellationToken = default)
{
if (player == null||path==null)
{
throw new ArgumentNullException(nameof(player));
}
// 创建任务源,用于在事件触发时完成任务
var tcs = new UniTaskCompletionSource();
// 定义事件处理方法
void EventHandler(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
{
switch (et)
{
//case MediaPlayerEvent.EventType.ReadyToPlay:
case MediaPlayerEvent.EventType.FirstFrameReady:
case MediaPlayerEvent.EventType.Started:
player.Events.RemoveListener(EventHandler);
tcs.TrySetResult();
break;
case MediaPlayerEvent.EventType.Error:
player.Events.RemoveListener(EventHandler);
tcs.TrySetException(new Exception($"视频播放错误: {errorCode}"));
break;
}
}
// 添加事件监听
player.Events.AddListener(EventHandler);
player.OpenMedia(path, isOpen);
try
{
// 等待任务完成或取消
await tcs.Task.AttachExternalCancellation(cancellationToken);
}
finally
{
// 确保移除事件监听
player.Events.RemoveListener(EventHandler);
}
}
}
评论