You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
7.9 KiB
266 lines
7.9 KiB
2 years ago
|
using UnityEngine;
|
||
|
using System;
|
||
|
using System.IO;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace FFmpeg.Demo.REC
|
||
|
{
|
||
|
//[RequireComponent(typeof(RecMicAudio), typeof(RecSystemAudio))]
|
||
|
public class FFmpegREC : MonoBehaviour, IFFmpegHandler
|
||
|
{
|
||
|
//Data
|
||
|
[Header("Targeted FPS")]
|
||
|
public int FPS = 30;
|
||
|
float actualFPS;
|
||
|
[Header("0 for max resolution")]
|
||
|
public int width ;
|
||
|
public int height ;
|
||
|
[Header("Change before initialization")]
|
||
|
public RecAudioSource recAudioSource = RecAudioSource.System;
|
||
|
Rect camRect;
|
||
|
|
||
|
//References
|
||
|
IRecAudio soundRecorder;
|
||
|
Texture2D frameBuffer;
|
||
|
|
||
|
//Paths
|
||
|
const string FILE_FORMAT = "{0}_Frame.jpg";
|
||
|
const string SOUND_NAME = "RecordedAudio.wav";
|
||
|
[HideInInspector]
|
||
|
public string VIDEO_NAME = "ScreenCapture.mp4";
|
||
|
string cashDir, imgFilePathFormat, firstImgFilePath, soundPath, outputVideoPath;
|
||
|
|
||
|
//Variables
|
||
|
int framesCount;
|
||
|
float startTime, frameInterval, frameTimer, totalTime;
|
||
|
|
||
|
public bool isREC { get; private set; }
|
||
|
public bool isProducing { get; private set; }
|
||
|
#if !UNITY_EDITOR
|
||
|
int initialWidth, initialHeight;
|
||
|
public bool overrideResolution { get { return width > 0 || height > 0; } }
|
||
|
#endif
|
||
|
|
||
|
//References
|
||
|
Action<string> onOutput, onFinish;
|
||
|
|
||
|
//PUBLIC INTERFACE
|
||
|
//------------------------------
|
||
|
public RecSavePanel RecSavePanel;
|
||
|
public void Init(Action<string> _onOutput, Action<string> _onFinish)
|
||
|
{
|
||
|
//Subscription to FFmpeg callbacks
|
||
|
FFmpegParser.Handler = this;
|
||
|
width = Screen.width;
|
||
|
height = Screen.height;
|
||
|
//Paths initialization
|
||
|
string path = Application.streamingAssetsPath + "/RECFile/" + GameSettings.othersSettings.mode.ToString() + "/" + CurrentUserInfo.room.Id.ToString();
|
||
|
if (!Directory.Exists(path))
|
||
|
{
|
||
|
Directory.CreateDirectory(path);
|
||
|
}
|
||
|
cashDir = Path.Combine(path, "RecordingCash");
|
||
|
imgFilePathFormat = Path.Combine(cashDir, FILE_FORMAT);
|
||
|
firstImgFilePath = String.Format(imgFilePathFormat, "%0d");
|
||
|
soundPath = Path.Combine(cashDir, SOUND_NAME);
|
||
|
outputVideoPath = Path.Combine(path, VIDEO_NAME);
|
||
|
|
||
|
#if !UNITY_EDITOR
|
||
|
initialWidth = Screen.width;
|
||
|
initialHeight = Screen.height;
|
||
|
#endif
|
||
|
|
||
|
//Sound source initialization
|
||
|
if (recAudioSource == RecAudioSource.Mic)
|
||
|
{
|
||
|
soundRecorder = GetComponent<RecMicAudio>();
|
||
|
}
|
||
|
else if (recAudioSource == RecAudioSource.System)
|
||
|
{
|
||
|
soundRecorder = GetComponent<RecSystemAudio>();
|
||
|
}
|
||
|
else if (recAudioSource == RecAudioSource.None)
|
||
|
{
|
||
|
soundRecorder = null;
|
||
|
}
|
||
|
|
||
|
onOutput = _onOutput;
|
||
|
onFinish = _onFinish;
|
||
|
}
|
||
|
|
||
|
void Clear()
|
||
|
{
|
||
|
if (Directory.Exists(cashDir))
|
||
|
Directory.Delete(cashDir, true);
|
||
|
}
|
||
|
|
||
|
public void StartREC()
|
||
|
{
|
||
|
if (!isREC && !isProducing)
|
||
|
{
|
||
|
Clear();
|
||
|
Directory.CreateDirectory(cashDir);
|
||
|
Debug.Log("CashDir created: " + cashDir);
|
||
|
|
||
|
#if !UNITY_EDITOR
|
||
|
if (overrideResolution)
|
||
|
{
|
||
|
//Low level operation of resolution change
|
||
|
Screen.SetResolution(width, height, Screen.fullScreen);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
width = Screen.width;
|
||
|
height = Screen.height;
|
||
|
}
|
||
|
|
||
|
frameBuffer = new Texture2D(width, height, TextureFormat.RGB24, false, true);
|
||
|
camRect = new Rect(0, 0, width, height);
|
||
|
startTime = Time.time;
|
||
|
framesCount = 0;
|
||
|
frameInterval = 1.0f / FPS;
|
||
|
frameTimer = frameInterval;
|
||
|
|
||
|
isREC = true;
|
||
|
|
||
|
if (recAudioSource != RecAudioSource.None)
|
||
|
{
|
||
|
soundRecorder.StartRecording();
|
||
|
}
|
||
|
RecSavePanel.BeginTime = TIME_SYNC.time;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void StopREC()
|
||
|
{
|
||
|
isREC = false;
|
||
|
isProducing = true;
|
||
|
|
||
|
totalTime = Time.time - startTime;
|
||
|
actualFPS = framesCount / totalTime;
|
||
|
|
||
|
Debug.Log("Actual FPS: " + actualFPS);
|
||
|
|
||
|
if (recAudioSource != RecAudioSource.None)
|
||
|
{
|
||
|
soundRecorder.StopRecording(soundPath);
|
||
|
}
|
||
|
|
||
|
RecSavePanel.gameObject.SetActive(true);
|
||
|
//CreateVideo();
|
||
|
|
||
|
#if !UNITY_EDITOR
|
||
|
if (overrideResolution)
|
||
|
//Return to initial screen resolution
|
||
|
Screen.SetResolution(initialWidth, initialHeight, Screen.fullScreen);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//INTERNAL IMPLEMENTATION
|
||
|
//------------------------------
|
||
|
|
||
|
void OnPostRender()
|
||
|
{
|
||
|
if (isREC && (frameTimer += Time.deltaTime) > frameInterval)
|
||
|
{
|
||
|
frameTimer -= frameInterval;
|
||
|
|
||
|
frameBuffer.ReadPixels(camRect, 0, 0);
|
||
|
|
||
|
File.WriteAllBytes(NextImgFilePath(), frameBuffer.EncodeToJPG());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string NextImgFilePath()
|
||
|
{
|
||
|
return String.Format(imgFilePathFormat, framesCount++);
|
||
|
}
|
||
|
|
||
|
public void CreateVideo()
|
||
|
{
|
||
|
StringBuilder command = new StringBuilder();
|
||
|
|
||
|
Debug.Log("firstImgFilePath: " + firstImgFilePath);
|
||
|
Debug.Log("soundPath: " + soundPath);
|
||
|
Debug.Log("outputVideoPath:------------ " + outputVideoPath);
|
||
|
|
||
|
//Input Image sequence params
|
||
|
command.
|
||
|
Append("-y -framerate ").
|
||
|
Append(actualFPS.ToString()).
|
||
|
Append(" -f image2 -i ").
|
||
|
Append(AddQuotation(firstImgFilePath));
|
||
|
|
||
|
//Input Audio params
|
||
|
if (recAudioSource != RecAudioSource.None)
|
||
|
{
|
||
|
command.
|
||
|
Append(" -i ").
|
||
|
Append(AddQuotation(soundPath)).
|
||
|
Append(" -ss 0 -t ").
|
||
|
Append(totalTime);
|
||
|
}
|
||
|
|
||
|
//Output Video params
|
||
|
command.
|
||
|
Append(" -vcodec libx264 -crf 25 -pix_fmt yuv420p ").
|
||
|
Append(AddQuotation(outputVideoPath));
|
||
|
|
||
|
Debug.Log(command.ToString());
|
||
|
|
||
|
FFmpegCommands.DirectInput(command.ToString());
|
||
|
}
|
||
|
|
||
|
string AddQuotation(string path)
|
||
|
{
|
||
|
if (string.IsNullOrEmpty(path))
|
||
|
{
|
||
|
throw new Exception("Empty path.");
|
||
|
}
|
||
|
#if UNITY_EDITOR || UNITY_STANDALONE
|
||
|
const char DOUBLE_QUOTATION = '\"';
|
||
|
if (path[0] != DOUBLE_QUOTATION)
|
||
|
{
|
||
|
return DOUBLE_QUOTATION + path + DOUBLE_QUOTATION;
|
||
|
}
|
||
|
#endif
|
||
|
return path;
|
||
|
}
|
||
|
|
||
|
//FFmpeg processing callbacks
|
||
|
//------------------------------
|
||
|
|
||
|
//Begining of video processing
|
||
|
public void OnStart()
|
||
|
{
|
||
|
onOutput("VideoProducing Started\n");
|
||
|
}
|
||
|
|
||
|
//You can make custom progress bar here (parse msg)
|
||
|
public void OnProgress(string msg)
|
||
|
{
|
||
|
onOutput(msg);
|
||
|
}
|
||
|
|
||
|
//Notify user about failure here
|
||
|
public void OnFailure(string msg)
|
||
|
{
|
||
|
onOutput(msg);
|
||
|
}
|
||
|
|
||
|
//Notify user about success here
|
||
|
public void OnSuccess(string msg)
|
||
|
{
|
||
|
onOutput(msg);
|
||
|
}
|
||
|
|
||
|
//Last callback - do whatever you need next
|
||
|
public void OnFinish()
|
||
|
{
|
||
|
onFinish(outputVideoPath);
|
||
|
Clear();
|
||
|
isProducing = false;
|
||
|
}
|
||
|
}
|
||
|
}
|