在Android上播放大型视频而不会滞后 [英] Play large video on Android without lagging
问题描述
我正在开发AR游戏.在特定情况下,我想在一个场景的一部分(例如在飞机上)中播放用户从电话库中选择的视频. 我测试了统一的视频播放器,但是当视频大小超过100 MB甚至纹理无法显示时,只要我能听到视频声音,这就会滞后很多.
I'm developing an AR game. In a particular situation, I want to play a video, which user selected from phone gallery, in a part of scene in unity(for example on a Plane). I test unity Video Player, but that would lag so much when video size is more than 100 Mbyte and even textures doesn't display, just I can hear video sound.
我现在该怎么办?我应该编写一个Java本机插件,以Java格式传输视频并统一设置纹理吗?
What should I do now? Should I write a java native plugin which stream video in java and set textures in unity?
对不起,英语不好.
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,一切都可以正常工作.
I also assigned AudioSource in editor. All things works fine just if video size is lower than 10 Mbyte.
推荐答案
当视频文件很大时,Unity的VideoPlayer
在某些Android设备上似乎滞后. Android设备上的一种可能解决方案是分批读取视频,然后使用
Unity's VideoPlayer
seems to be lagging on some Android devices when the video file is big. One possible solution on the Android device is to read the video in chunks then host it on the Android device with HttpListener
.
使用VideoSource.Url;
连接到使用VideoPlayer
API在设备上创建的主机,并将VideoPlayer.url
设置为本地设备上的该主机URL,然后播放视频.
Connect to that host you created on the device with the VideoPlayer
API by using VideoSource.Url;
and setting the VideoPlayer.url
to that host url on the local device then play the video.
使用HttpListener
托管视频(必须更改filePath
指向视频的实际路径:
Host the video with HttpListener
(Must change filePath
to point to the actual path of the video:
//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;
}
}
Play the hosted video with VideoPlayer
API (This is a minor modification of the code from this post) :
//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视频播放器. 此和
This works for me but may work on your device. If that's the case then you have to abandon the VideoPlayer
API for now and use the MediaPlayer
with OpenGL ES to make your own video player for Android. This and this posts should get you started on the plugin.
这篇关于在Android上播放大型视频而不会滞后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!