ict.shenzhi/Assets/Evereal/VideoCapture/Scripts/Internal/Encoder/FFmpegMuxer.cs

260 lines
6.2 KiB
C#

/* Copyright (c) 2019-present Evereal. All rights reserved. */
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using UnityEngine;
namespace Evereal.VideoCapture
{
// This script will mux audio into video file.
public class FFmpegMuxer : MonoBehaviour
{
#region Dll Import
[DllImport("FFmpegEncoder")]
private static extern IntPtr FFmpegEncoder_StartMuxingProcess(
EncoderPreset preset,
int bitrate,
string mediaPath,
string videoPath,
string audioPpath,
string ffmpegPath,
bool isLive);
[DllImport("FFmpegEncoder")]
private static extern void FFmpegEncoder_CleanMuxingProcess(IntPtr api);
#endregion
#region Properties
public static FFmpegMuxer singleton;
// Event delegate callback for complete.
public delegate void OnCompleteEvent(string savePath);
// Event delegate callback for error.
public delegate void OnErrorEvent(EncoderErrorCode error);
// Callback for complete handling
public event OnCompleteEvent OnComplete = delegate { };
// Callback for error handling
public event OnErrorEvent OnError = delegate { };
// Is mux process initiated
public bool muxInitiated { get; private set; }
// The audio file will mux with.
private string audioFile;
// The video files is ready to mux.
private Queue<string> videoFiles;
// Video capture instance for muxing process
private List<IVideoCapture> videoCaptures;
// The audio/video mux thread.
private Thread muxingThread;
AutoResetEvent muxReady = new AutoResetEvent(false);
public string saveFolderFullPath { get; set; }
public string ffmpegFullPath { get; set; }
// Log message format template
private string LOG_FORMAT = "[FFmpegMuxer] {0}";
#endregion
#region FFmpeg Muxer
public void SetAudioFile(string file)
{
audioFile = file;
muxReady.Set();
}
public void EnqueueVideoFile(string file)
{
lock (videoFiles)
{
videoFiles.Enqueue(file);
}
muxReady.Set();
}
public void AttachVideoCapture(IVideoCapture videoCapture)
{
if (!videoCaptures.Contains(videoCapture))
{
lock (videoCaptures)
{
videoCaptures.Add(videoCapture);
}
}
}
// Init mux process
public bool InitMux()
{
// Check if we can start mux process
if (muxInitiated)
return false;
// Start muxing thread
if (muxingThread != null)
{
if (muxingThread.IsAlive)
muxingThread.Abort();
muxingThread = null;
}
muxingThread = new Thread(MuxingProcess);
muxingThread.Priority = System.Threading.ThreadPriority.Highest;
muxingThread.Start();
//StartCoroutine(MuxingProcessDebug());
return true;
}
/// <summary>
/// Media muxing the thread function.
/// </summary>
private void MuxingProcess()
{
while (videoCaptures.Count > 0 || videoFiles.Count > 0)
{
if (videoFiles.Count > 0)
{
string videoFile;
lock (videoFiles)
{
videoFile = videoFiles.Dequeue();
}
IVideoCapture videoCapture = null;
for (int i = 0; i < videoCaptures.Count; i++)
{
// Check if match with attached VideoCapture
if (videoCaptures[i].GetFFmpegEncoder().videoSavePath == videoFile)
{
videoCapture = videoCaptures[i];
lock (videoCaptures)
{
videoCaptures.RemoveAt(i);
}
}
}
if (videoCapture == null)
{
// Skip if no match VideoCapture
continue;
}
// Start muxing process
if (!StartMux(videoCapture))
{
// Skip if not success
continue;
}
}
else
{
muxReady.WaitOne();
}
}
// Clean audio file
if (File.Exists(audioFile))
{
File.Delete(audioFile);
audioFile = null;
}
muxInitiated = false;
}
// Start video/audio muxing process, this is blocking function
private bool StartMux(IVideoCapture videoCapture)
{
FFmpegEncoder encoder = videoCapture.GetFFmpegEncoder();
string videoSavePath = string.Format("{0}capture_{1}x{2}_{3}_{4}.{5}",
saveFolderFullPath,
encoder.outputFrameWidth, encoder.outputFrameHeight,
Utils.GetTimeString(),
Utils.GetRandomString(5),
Utils.GetEncoderPresetExt(encoder.encoderPreset));
// Make sure generated the merge file
int waitCount = 0;
while (!File.Exists(videoSavePath) && waitCount++ < 10)
{
Thread.Sleep(1000);
IntPtr nativeAPI = FFmpegEncoder_StartMuxingProcess(
encoder.encoderPreset,
encoder.bitrate,
videoSavePath,
encoder.videoSavePath,
audioFile,
ffmpegFullPath,
false);
if (nativeAPI == IntPtr.Zero)
{
OnError(EncoderErrorCode.MUXING_FAILED_TO_START);
return false;
}
FFmpegEncoder_CleanMuxingProcess(nativeAPI);
}
if (waitCount >= 10)
{
return false;
}
// VideoCapture muxer complete callback
videoCapture.OnMuxerComplete(videoSavePath);
OnComplete(videoSavePath);
// Clean video files with no sound
if (File.Exists(encoder.videoSavePath))
{
File.Delete(encoder.videoSavePath);
encoder.videoSavePath = "";
}
//Debug.LogFormat(LOG_FORMAT, "Muxing process finish!");
return true;
}
private void MuxerErrorLog(EncoderErrorCode error)
{
Debug.LogWarningFormat(LOG_FORMAT, "Error Occured of type: " + error);
}
private void Awake()
{
if (singleton != null)
return;
singleton = this;
videoCaptures = new List<IVideoCapture>();
videoFiles = new Queue<string>();
}
private void OnEnable()
{
OnError += MuxerErrorLog;
}
private void OnDisable()
{
OnError -= MuxerErrorLog;
}
#endregion
}
}