使用新的Unity VideoPlayer和VideoClip API播放视频

42
自 Unity 5.6.0b1 版本后,MovieTexture 已经被弃用,现已推出可在桌面和移动设备上播放视频的新 API。VideoPlayerVideoClip 可用于播放视频,并在需要时检索每帧的纹理。
我已成功让视频播放,但无法从 Windows 10 编辑器中播放音频。有人知道为什么没有声音吗?
//Raw Image to Show Video Images [Assign from the Editor]
public RawImage image;
//Video To Play [Assign from the Editor]
public VideoClip videoToPlay;

private VideoPlayer videoPlayer;
private VideoSource videoSource;

//Audio
private AudioSource audioSource;

// Use this for initialization
void Start()
{
    Application.runInBackground = true;
    StartCoroutine(playVideo());
}

IEnumerator playVideo()
{
    //Add VideoPlayer to the GameObject
    videoPlayer = gameObject.AddComponent<VideoPlayer>();

    //Add AudioSource
    audioSource = gameObject.AddComponent<AudioSource>();

    //Disable Play on Awake for both Video and Audio
    videoPlayer.playOnAwake = false;
    audioSource.playOnAwake = false;

    //We want to play from video clip not from url
    videoPlayer.source = VideoSource.VideoClip;

    //Set video To Play then prepare Audio to prevent Buffering
    videoPlayer.clip = videoToPlay;
    videoPlayer.Prepare();

    //Wait until video is prepared
    while (!videoPlayer.isPrepared)
    {
        Debug.Log("Preparing Video");
        yield return null;
    }

    Debug.Log("Done Preparing Video");

    //Set Audio Output to AudioSource
    videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;

    //Assign the Audio from Video to AudioSource to be played
    videoPlayer.EnableAudioTrack(0, true);
    videoPlayer.SetTargetAudioSource(0, audioSource);

    //Assign the Texture from Video to RawImage to be displayed
    image.texture = videoPlayer.texture;

    //Play Video
    videoPlayer.Play();

    //Play Sound
    audioSource.Play();

    Debug.Log("Playing Video");
    while (videoPlayer.isPlaying)
    {
        Debug.LogWarning("Video Time: " + Mathf.FloorToInt((float)videoPlayer.time));
        yield return null;
    }

    Debug.Log("Done Playing Video");
}

@Lestat 我没有这样做,但如果我能的话,我会这样做。看起来Martijn Pieters这样做了,因为这个问题被新用户滥用,他们只把问题作为答案发布。他不得不删除多个这样的答案/问题。 - Programmer
如果您正在加载多个视频并且应用程序出现了延迟,这可能会有所帮助。http://stackoverflow.com/questions/42801468/unity-app-freezes-when-loading-multiple-videos-on-same-scene/42829081#42829081 - Luzan Baral
似乎VideoPlayer.prepare()在主线程上运行!特别是在Android上。结果是应用程序在prepare()工作时挂起!这使得新界面几乎无法使用。而且你也不能在后台线程上运行它。有人找到了解决方法吗? - TatiOverflow
不会阻塞主线程,而是在后台运行。请将您的代码发布到pastebin上,然后在此处提供链接,我会查看它。同时请注明您使用的Unity版本。 - Programmer
我这里有一份代码示例 https://github.com/tati-nacmedia/testvplayer/blob/master/Assets/VideoPlayerController.cs,我还放了一个Unity包在这里 https://github.com/tati-nacmedia/testvplayer/blob/master/testvplayerpackage.unitypackage,在Windows和iOS上播放效果很好,但在Android上却严重卡顿。我曾经在某个地方看到过一个Unity开发者说它不支持Android上的异步操作,但我忘记在哪里看到的了。我已经添加了准备延迟来演示使用协程时的卡顿情况。 - TatiOverflow
显示剩余2条评论
4个回答

65

找到了问题。以下是已经修复的代码,可以播放视频和音频:

//Raw Image to Show Video Images [Assign from the Editor]
public RawImage image;
//Video To Play [Assign from the Editor]
public VideoClip videoToPlay;

private VideoPlayer videoPlayer;
private VideoSource videoSource;

//Audio
private AudioSource audioSource;

// Use this for initialization
void Start()
{
    Application.runInBackground = true;
    StartCoroutine(playVideo());
}

IEnumerator playVideo()
{
    //Add VideoPlayer to the GameObject
    videoPlayer = gameObject.AddComponent<VideoPlayer>();

    //Add AudioSource
    audioSource = gameObject.AddComponent<AudioSource>();

    //Disable Play on Awake for both Video and Audio
    videoPlayer.playOnAwake = false;
    audioSource.playOnAwake = false;

    //We want to play from video clip not from url
    videoPlayer.source = VideoSource.VideoClip;

    //Set Audio Output to AudioSource
    videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;

    //Assign the Audio from Video to AudioSource to be played
    videoPlayer.EnableAudioTrack(0, true);
    videoPlayer.SetTargetAudioSource(0, audioSource);

    //Set video To Play then prepare Audio to prevent Buffering
    videoPlayer.clip = videoToPlay;
    videoPlayer.Prepare();

    //Wait until video is prepared
    while (!videoPlayer.isPrepared)
    {
        Debug.Log("Preparing Video");
        yield return null;
    }

    Debug.Log("Done Preparing Video");

    //Assign the Texture from Video to RawImage to be displayed
    image.texture = videoPlayer.texture;

    //Play Video
    videoPlayer.Play();

    //Play Sound
    audioSource.Play();

    Debug.Log("Playing Video");
    while (videoPlayer.isPlaying)
    {
        Debug.LogWarning("Video Time: " + Mathf.FloorToInt((float)videoPlayer.time));
        yield return null;
    }

    Debug.Log("Done Playing Video");
}

为什么音频无法播放:

//Set Audio Output to AudioSource
videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;

//Assign the Audio from Video to AudioSource to be played
videoPlayer.EnableAudioTrack(0, true);
videoPlayer.SetTargetAudioSource(0, audioSource);

videoPlayer.Prepare()之前必须调用此函数,而不是在其之后。经过数小时的实验,才发现这是我遇到的问题。


卡在“准备视频”?

在调用videoPlayer.Prepare();后等待5秒,然后退出while循环。

将以下代码替换:

while (!videoPlayer.isPrepared)
{
    Debug.Log("Preparing Video");
    yield return null;
}

随着:

//Wait until video is prepared
WaitForSeconds waitTime = new WaitForSeconds(5);
while (!videoPlayer.isPrepared)
{
    Debug.Log("Preparing Video");
    //Prepare/Wait for 5 sceonds only
    yield return waitTime;
    //Break out of the while loop after 5 seconds wait
    break;
}

这应该是有效的,但你可能会在视频开始播放时遇到缓冲。在使用这个临时解决方案的同时,我的建议是以“videoPlayer.isPrepared always true”为标题提交 bug 报告,因为这是一个 bug。

一些也通过更改以下内容来解决:

videoPlayer.playOnAwake = false; 
audioSource.playOnAwake = false;

videoPlayer.playOnAwake = true; 
audioSource.playOnAwake = true;

播放来自URL的视频:

替换:

//We want to play from video clip not from url
videoPlayer.source = VideoSource.VideoClip;

随着:

//We want to play from url
videoPlayer.source = VideoSource.Url;
videoPlayer.url = "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";

然后删除:

public VideoClip videoToPlay;videoPlayer.clip = videoToPlay;因为它们不再需要。

从StreamingAssets文件夹播放视频:

string url = "file://" + Application.streamingAssetsPath + "/" + "VideoName.mp4";

if !UNITY_EDITOR && UNITY_ANDROID
    url = Application.streamingAssetsPath + "/" + "VideoName.mp4";
#endif

//We want to play from url
videoPlayer.source = VideoSource.Url;
videoPlayer.url = url;

所有支持的视频格式:

  • ogv
  • vp8
  • webm
  • mov
  • dv
  • mp4
  • m4v
  • mpg
  • mpeg

Windows 上额外支持的视频格式:

  • avi
  • asf
  • wmf

其中一些格式在某些平台上无法使用。请查看此帖子以获取更多关于支持的视频格式信息。


1
很抱歉不时地打扰你,但是对我来说视频无法播放,原因是videoPlayer.playOnAwake = false; audioSource.playOnAwake = false;我将这些值更改为true。我现在明白了,我想我需要开发脚本来控制视频的播放和暂停。 - Luzan Baral
1
我不这么认为,但如果您能够将视频存储在任何以.mp4结尾的服务或有效的视频文件名的URL上,则可能可以播放它。请记住,这些服务并不是为此而设计的。您需要一个专用服务器来进行流媒体传输,因为这很昂贵。当我说昂贵时,我的意思是带宽。 - Programmer
1
如果这对您不起作用,请@Harschell提交错误报告。 - Programmer
1
@Bluetree 是的。新的 VideoPlayer 应该可以在任何平台上运行。 - Programmer
1
@程序员 怎么获取videoPlayer.url的长度?我没有使用VideoClip。 - Suraksha Ajith
显示剩余29条评论

12

与其他回答类似,您可以使用回调函数来处理视频的准备和结束状态,而不是使用协程和yield return。

videoPlayer.loopPointReached += EndReached;
videoPlayer.prepareCompleted += PrepareCompleted;

void PrepareCompleted(VideoPlayer vp) {
    vp.Play();
}

void EndReached(VideoPlayer vp) {
    // do something
}

当然,我遇到了一些音频错误。在我使用回调方法时,我发现这节省了我很多麻烦。特别是当你尝试播放一个视频接着另一个视频时。 - Groshh
2
这听起来比协程更合理的解决方案。 - shieldgenerator7

2
我使用了@Programmer的答案来播放URL视频,但是我无法播放任何声音。最终我在YouTube教程的评论中找到了答案。
为了让通过URL加载的电影播放音频,您需要在调用EnableAudioTrack之前添加以下行:
videoPlayer.controlledAudioTrackCount = 1;

有趣。我从URL视频中没有遇到音频问题,但这对其他人来说会很有帮助。这可能是编辑器中的一个错误。 - Programmer

-2

到现在为止,VideoPlayer 应该已经更新到足够的程度,您不需要编写代码才能正常工作。以下是我发现具有最理想效果的设置:

Optimal Settings

这些设置是:

Video Player

  • Play On Awake:True
  • Wait For First Frame:False
  • Audio Output Mode:None

Audio Source

  • Play On Awake:True

不要忘记为 VideoPlayer 准备一个 VideoClip 和一个 AudioClip 用于 AudioSource。我发现最好的文件格式是 .ogv 用于视频和 .wav 用于音频。


“VideoPlayer应该更新到足够的程度,以便您不需要编写代码来使其正常工作。”并非完全如此。在某些平台和某些(长)视频上,您仍然需要编写一些代码,否则视频将会冻结。解决方法是调用Prepare()函数,这只能通过编写代码来完成。 - Programmer
@程序员 好的,你说得对。我只有一个短视频,所以不需要代码让它在启动时播放。 - shieldgenerator7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接