本範例是一個簡單的 YUV 影片播放器 ( YUV Video Player )。
這次片源採用 FFMPEG 直接將一般 H264 MP4 Video 轉成 YUV420 格式,附檔名是 *.yuv。
YUV 格式在輸入之前必須先輸入影片的 寬 ( Width ) & 高 ( Height ),由於 YUV 檔是無頭檔參數來描述寬高,如果沒有輸入寬高,基本上是無法解碼的 (畫面不會正常顯示,只能亂猜)。
本範例使用下面這種方式排列
C # :
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class Test_YUV420 : MonoBehaviour
{
public RawImage img;
int frameCount;
int frameNow = 0;
byte[] file;
// Plana 連續儲存,Packed 交錯儲存
// 本範例使用 Plana YUV420 格式
void Start()
{
file = File.ReadAllBytes("C:/FFMPEG/Video_854_480_YUV420.yuv");
frameCount = GetFrameCount(file, 854, 480); // 影像寬高
print("Frame Count : " + frameCount);
//File.WriteAllBytes("C:/FFMPEG/Video_854_480_YUV420_1.png", t.EncodeToPNG());
}
void Update()
{
if (frameNow >= frameCount - 1) return;
img.texture = GetTexture(file, 854, 480); // 影像寬高
frameNow++;
print("Frame : " + (frameNow) + " (" + (int)(frameNow / (float)(frameCount - 1) * 100) + "%)");
}
int GetFrameCount(byte[] file, int width, int height)
{
return file.Length / ((width * height) * 3 / 2);
}
Texture2D t = null;
Texture2D GetTexture(byte[] file, int width, int height)
{
if (t != null) Destroy(t);
t = new Texture2D(width, height, TextureFormat.RGB24, false);
int Ysize = t.width * t.height;
int UorVsize = t.width * t.height / 4;
Color[] cArr = new Color[t.width * t.height];
byte U = 0;
byte V = 0;
int k = 0;
int offset = frameNow * ((width * height) * 3 / 2);
for (int y = 0; y < t.height; y += 2)
{
for (int x = 0; x < t.width; x += 2)
{
U = file[offset + Ysize + k++];
V = file[offset + Ysize + k + UorVsize];
int i = y * t.width + x;
int mY = t.height - 1 - y;
byte Y00 = file[offset + i];
byte Y01 = file[offset + i + t.width];
byte Y10 = file[offset + i + 1];
byte Y11 = file[offset + i + t.width + 1];
float R00 = (Y00 + 1.4075f * (V - 128)) / 255f;
float G00 = (Y00 - 0.3455f * (U - 128) - 0.7169f * (V - 128)) / 255f;
float B00 = (Y00 + 1.779f * (U - 128)) / 255f;
float R01 = (Y01 + 1.4075f * (V - 128)) / 255f;
float G01 = (Y01 - 0.3455f * (U - 128) - 0.7169f * (V - 128)) / 255f;
float B01 = (Y01 + 1.779f * (U - 128)) / 255f;
float R10 = (Y10 + 1.4075f * (V - 128)) / 255f;
float G10 = (Y10 - 0.3455f * (U - 128) - 0.7169f * (V - 128)) / 255f;
float B10 = (Y10 + 1.779f * (U - 128)) / 255f;
float R11 = (Y11 + 1.4075f * (V - 128)) / 255f;
float G11 = (Y11 - 0.3455f * (U - 128) - 0.7169f * (V - 128)) / 255f;
float B11 = (Y11 + 1.779f * (U - 128)) / 255f;
t.SetPixel(x, mY, new Color(R00, G00, B00, 1));
t.SetPixel(x, mY - 1, new Color(R01, G01, B01, 1));
t.SetPixel(x + 1, mY, new Color(R10, G10, B10, 1));
t.SetPixel(x + 1, mY - 1, new Color(R11, G11, B11, 1));
}
}
t.Apply();
return t;
}
}