在Android上播放大型视频而不会滞后

时间:2018-05-24 19:47:54

标签: c# android unity3d video-streaming vuforia

我正在开发AR游戏。在特定情况下,我想在一个场景的一部分(例如在平面上)播放一个用户从电话画廊中选择的视频。 我测试统一视频播放器,但是当视频大小超过100 MB甚至纹理没有显示时,这将会滞后很多,只是我能听到视频声音。

我现在该怎么办?我应该编写一个java原生插件,它在java中流式传输视频并将纹理设置为统一吗?

谢谢,抱歉英语不好。

    videoPlayer.playOnAwake = false;
    videoPlayer.source = VideoSource.Url;
    videoPlayer.url = url;
    videoPlayer.Prepare();

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

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

    //Play Video
    videoPlayer.Play();

我还在编辑器中分配了AudioSource。 如果视频大小低于10 MB,一切正常。

1 个答案:

答案 0 :(得分:3)

当视频文件很大时,Unity的VideoPlayer似乎在一些Android设备上落后。 Android设备上的一个可能的解决方案是以块的形式阅读视频,然后使用HttpListener将其托管在Android设备上。

使用VideoPlayer API连接到您在设备上创建的主机,并使用VideoSource.Url;并将VideoPlayer.url设置为本地设备上的主机网址,然后播放视频。

使用HttpListener托管视频(必须更改filePath以指向视频的实际路径:

//Server url
string url = "http://127.0.0.1:8080/";


//Types of media supported(Can be extended)
private static IDictionary<string, string> mimeDic =
       new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase)
       {
            {".asf", "video/x-ms-asf"},
            {".asx", "video/x-ms-asf"},
            {".avi", "video/x-msvideo"},
            {".flv", "video/x-flv"},
            {".jpeg", "image/jpeg"},
            {".jpg", "image/jpeg"},
            {".mng", "video/x-mng"},
            {".mov", "video/quicktime"},
            {".mp3", "audio/mpeg"},
            {".mpeg", "video/mpeg"},
            {".mp4", "video/mp4" },
            {".mpg", "video/mpeg"},
            {".ra", "audio/x-realaudio"},
            {".swf", "application/x-shockwave-flash"},
            {".wbmp", "image/vnd.wap.wbmp"},
            {".wmv", "video/x-ms-wmv"},
       };

List<HttpListenerResponse> httpResponses = new List<HttpListenerResponse>();
Thread listeningThread;

void Awake()
{
    Application.runInBackground = true;

    //Your Video Path
    string filePath = Path.Combine(Application.persistentDataPath, "Cater2U.mp4");

    Debug.Log(filePath.Replace("/", "\\"));

    //Create Server
    StartHttpServer(filePath);
}

void StartHttpServer(string dataPath)
{
    listeningThread = new Thread(new ParameterizedThreadStart(ListenToClient));
    listeningThread.IsBackground = true;
    listeningThread.Start(dataPath);
}

void StopHttpServer()
{
    //Stop thread
    if (listeningThread != null && listeningThread.IsAlive)
    {
        listeningThread.Abort();
        Debug.LogWarning("Listening Thread Stopped!");
    }
}

void DisconnectClients()
{
    //Disconnect from each connected client
    for (int i = 0; i < httpResponses.Count; i++)
    {
        if (httpResponses[i] != null)
        {
            httpResponses[i].StatusDescription = "Server done";
            httpResponses[i].OutputStream.Close();
            Debug.LogWarning("Disconnected Client!");
        }
    }
}

void ListenToClient(object path)
{
    //Get the param
    string dataPath = (string)path;

    HttpListener listener = new HttpListener();
    listener.Prefixes.Add(url);
    listener.Start();

    Debug.Log("Listening to Client");

    while (true)
    {
        HttpListenerContext context = listener.GetContext();
        Debug.LogWarning("New Client Connected: " + context.Request.RemoteEndPoint.ToString());

        //Construct param that will be sent to the Thread
        ServerParamData serverData = new ServerParamData(context, dataPath);
        ThreadPool.QueueUserWorkItem(new WaitCallback(RunInNewThread), serverData);
    }
}

private void RunInNewThread(object ctx)
{
    //Get the param
    ServerParamData serverData = (ServerParamData)ctx;

    //Open the file and start sending it to the client
    WriteFile(serverData.context, serverData.path);
}

void WriteFile(HttpListenerContext ctx, string path)
{
    HttpListenerResponse response = ctx.Response;
    httpResponses.Add(response);

    using (FileStream fs = File.OpenRead(path))
    {
        string filename = Path.GetFileName(path);
        string mime;

        //Set the type of media to play
        if (!mimeDic.TryGetValue(Path.GetExtension(filename), out mime))
            mime = "application/octet-stream";


        ctx.Response.ContentType = mime;
        response.ContentLength64 = fs.Length;

        //Stream the File
        response.SendChunked = true;

        //Enable Media Seek(Rewind/Fastforward)
        response.StatusCode = 206;
        response.AddHeader("Content-Range", "bytes 0-" + (fs.Length - 1) + "/" + fs.Length);
        //According to Content Range
        //https://greenbytes.de/tech/webdav/rfc7233.html#header.content-range


        //Send data to the connected client
        byte[] buffer = new byte[64 * 1024];
        int read;
        using (BinaryWriter bw = new BinaryWriter(response.OutputStream))
        {
            while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                bw.Write(buffer, 0, read);
                bw.Flush(); //seems to have no effect
            }
            bw.Close();
        }

        response.StatusCode = (int)HttpStatusCode.OK;
        response.StatusDescription = "OK";
        response.OutputStream.Close();
    }
}

void OnDisable()
{
    //Clean Up
    StopHttpServer();
    DisconnectClients();
}

//Holds multiple params sent to a function in another Thread
public class ServerParamData
{
    public HttpListenerContext context;
    public string path;

    public ServerParamData(HttpListenerContext context, string path)
    {
        this.context = context;
        this.path = path;
    }
}

使用VideoPlayer API播放托管视频(这是对this帖子中代码的一个小修改):

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

private VideoPlayer videoPlayer;
private VideoSource videoSource;

//Audio
private AudioSource audioSource;


//Server url
string url = "http://127.0.0.1:8080/";

// 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 url
    videoPlayer.source = VideoSource.Url;
    videoPlayer.url = url;

    //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);

    //Prepare Audio to prevent Buffering
    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");
}

这适用于我,但可能适用于您的设备。如果是这种情况,那么您现在必须放弃VideoPlayer API并使用MediaPlayer与OpenGL ES一起制作适用于Android的视频播放器。 Thisthis帖子可以帮助您开始使用该插件。