ict.shenzhi/Assets/Evereal/VideoCapture/Scripts/Internal/Base/EncoderBase.cs

530 lines
18 KiB
C#

/* Copyright (c) 2019-present Evereal. All rights reserved. */
using System;
using UnityEngine;
namespace Evereal.VideoCapture
{
/// <summary>
/// Base class for <c>FFmpegEncoder</c> and <c>GPUEncoder</c> class.
/// </summary>
public class EncoderBase : MonoBehaviour
{
/// <summary>
/// Native encoder error status.
/// </summary>
private const int ERROR_VIDEO_ENCODING_CAUSE_ERRORS = 100;
private const int ERROR_AUDIO_ENCODING_CAUSE_ERRORS = 200;
private const int ERROR_TRANSCODING_MUXING_CAUSE_ERRORS = 300;
private const int ERROR_RTMP_CAUSE_ERRORS = 400;
private const int ERROR_GRAPHICS_CAPTURE_ERRORS = 500;
private const int ERROR_CONFIGURATION_ERRORS = 600;
private const int ERROR_SYSTEM_ERRORS = 700;
private const int ERROR_ENCODING_CAPABILITY = 800;
// TODO, cache software encoding error
private const int ERROR_SOFTWARE_ENCODING_ERROR = 900;
public enum EncoderStatus
{
// Common error codes
OK = 0,
ENCODE_IS_NOT_READY,
NO_INPUT_FILE,
FILE_READING_ERROR,
OUTPUT_FILE_OPEN_FAILED,
OUTPUT_FILE_CREATION_FAILED,
DXGI_CREATING_FAILED,
DEVICE_CREATING_FAILED,
// Video/Image encoding specific error codes
ENCODE_INIT_FAILED = ERROR_VIDEO_ENCODING_CAUSE_ERRORS,
ENCODE_SET_CONFIG_FAILED,
ENCODER_CREATION_FAILED,
INVALID_TEXTURE_POINTER,
CONTEXT_CREATION_FAILED,
TEXTURE_CREATION_FAILED,
TEXTURE_RESOURCES_COPY_FAILED,
IO_BUFFER_ALLOCATION_FAILED,
ENCODE_PICTURE_FAILED,
ENCODE_FLUSH_FAILED,
MULTIPLE_ENCODING_SESSION,
INVALID_TEXTURE_RESOLUTION,
// WIC specific error codes
WIC_SAVE_IMAGE_FAILED,
// Audio encoding specific error codes
AUDIO_DEVICE_ENUMERATION_FAILED = ERROR_AUDIO_ENCODING_CAUSE_ERRORS,
AUDIO_CLIENT_INIT_FAILED,
WRITING_WAV_HEADER_FAILED,
RELEASING_WAV_FAILED,
// Transcoding and muxing specific error codes
MF_CREATION_FAILED = ERROR_TRANSCODING_MUXING_CAUSE_ERRORS,
MF_INIT_FAILED,
MF_CREATING_WAV_FORMAT_FAILED,
MF_TOPOLOGY_CREATION_FAILED,
MF_TOPOLOGY_SET_FAILED,
MF_TRANSFORM_NODE_SET_FAILED,
MF_MEDIA_CREATION_FAILED,
MF_HANDLING_MEDIA_SESSION_FAILED,
// WAMEDIA muxing specific error codes
WAMDEIA_MUXING_FAILED,
// More MF error codes
MF_STARTUP_FAILED,
MF_TRANSFORM_CREATION_FAILED,
MF_SOURCE_READER_CREATION_FAILED,
MF_STREAM_SELECTION_FAILED,
MF_MEDIA_TYPE_CREATION_FAILED,
MF_MEDIA_TYPE_CONFIGURATION_FAILED,
MF_MEDIA_TYPE_SET_FAILED,
MF_MEDIA_TYPE_GET_FAILED,
MF_CREATE_WAV_FORMAT_FROM_MEDIA_TYPE_FAILED,
MF_TRANSFORM_OUTPUT_STREAM_INFO_FAILED,
MF_CREATE_MEMORY_BUFFER_FAILED,
MF_CREATE_SAMPLE_FAILED,
MF_SAMPLE_ADD_BUFFER_FAILED,
MF_READ_SAMPLE_FAILED,
MF_TRANSFORM_FAILED,
MF_BUFFER_LOCK_FAILED,
// RTMP specific error codes
INVALID_FLV_HEADER = ERROR_RTMP_CAUSE_ERRORS,
INVALID_STREAM_URL,
RTMP_CONNECTION_FAILED,
RTMP_DISCONNECTED,
SENDING_RTMP_PACKET_FAILED,
// Graphics capture error codes
GRAPHICS_DEVICE_CAPTURE_INIT_FAILED = ERROR_GRAPHICS_CAPTURE_ERRORS,
GRAPHICS_DEVICE_CAPTURE_INVALID_TEXTURE,
GRAPHICS_DEVICE_CAPTURE_OPEN_SHARED_RESOURCE_FAILED,
GRAPHICS_DEVICE_CAPTURE_KEYED_MUTEX_ACQUIRE_FAILED,
GRAPHICS_DEVICE_CAPTURE_KEYED_ACQUIRE_ACQUIRE_SYNC_FAILED,
GRAPHICS_DEVICE_CAPTURE_KEYED_ACQUIRE_RELASE_SYNC_FAILED,
// Configuration error codes
MIC_NOT_CONFIGURED = ERROR_CONFIGURATION_ERRORS,
MIC_REQUIRES_ENUMERATION,
MIC_DEVICE_NOT_SET,
MIC_ENUMERATION_FAILED,
MIC_SET_FAILED,
MIC_UNSET_FAILED,
MIC_INDEX_INVALID,
CAMERA_NOT_CONFIGURED,
CAMERA_REQUIRES_ENUMERATION,
CAMERA_DEVICE_NOT_SET,
CAMERA_ENUMERATION_FAILED,
CAMERA_SET_FAILED,
CAMERA_UNSET_FAILED,
CAMERA_INDEX_INVALID,
LIVE_CAPTURE_SETTINGS_NOT_CONFIGURED,
VOD_CAPTURE_SETTINGS_NOT_CONFIGURED,
PREVIEW_CAPTURE_SETTINGS_NOT_CONFIGURED,
// System error codes
SYSTEM_INITIALIZE_FAILED = ERROR_SYSTEM_ERRORS,
SYSTEM_ENCODING_TEXTURE_CREATION_FAILED,
SYSTEM_PREVIEW_TEXTURE_CREATION_FAILED,
SYSTEM_ENCODING_TEXTURE_FORMAT_CREATION_FAILED,
SYSTEM_SCREENSHOT_TEXTURE_FORMAT_CREATION_FAILED,
SYSTEM_CAPTURE_IN_PROGRESS,
SYSTEM_CAPTURE_NOT_IN_PROGRESS,
SYSTEM_CAPTURE_TEXTURE_NOT_RECEIVED,
SYSTEM_CAMERA_OVERLAY_FAILED,
SYSTEM_CAPTURE_PREVIEW_FAILED,
SYSTEM_CAPTURE_PREVIEW_NOT_IN_PROGRESS,
// Encoding capability error codes
UNSUPPORTED_ENCODING_ENVIRONMENT = ERROR_ENCODING_CAPABILITY,
UNSUPPORTED_GRAPHICS_CARD_DRIVER_VERSION,
UNSUPPORTED_GRAPHICS_CARD,
UNSUPPORTED_OS_VERSION,
UNSUPPORTED_OS_PROCESSOR,
}
// Event delegate callback for complete.
public delegate void OnCompleteEvent(string savePath);
// Event delegate callback for error.
public delegate void OnErrorEvent(EncoderErrorCode error, EncoderStatus? status);
// You can choose capture from camera, screen or render texture.
public CaptureSource captureSource { get; set; }
// The type of video capture mode, regular or 360.
public CaptureMode captureMode { get; set; }
// The type of video capture stereo mode, left right or top bottom.
public StereoMode stereoMode { get; set; }
// Stereo mode settings.
// Average IPD of all subjects in US Army survey in meters
public float interpupillaryDistance { get; set; }
// The type of video projection, used for 360 video capture.
public ProjectionType projectionType { get; set; }
// If set live streaming mode, encoded video will be push to remote streaming url instead of save to local file.
public CaptureType captureType { get; set; }
// Audio capture settings, set false if you want to mute audio.
public bool captureAudio { get; set; }
// Microphone capture settings.
public bool captureMicrophone { get; set; }
// Do all capture in main thread
public bool offlineRender { get; set; }
// Resolution preset settings, set custom for other resolutions
public ResolutionPreset resolutionPreset { get; set; }
// The width of video frame
public Int32 frameWidth { get; set; }
// The height of video frame
public Int32 frameHeight { get; set; }
// The size of each cubemap side
public Int32 cubemapSize { get; set; }
public Int32 bitrate { get; set; }
public Int16 frameRate { get; set; }
public Int16 antiAliasing { get; set; }
// You can get test live stream key on "https://www.facebook.com/live/create".
// ex. rtmp://rtmp-api-dev.facebook.com:80/rtmp/xxStreamKeyxx
public string liveStreamUrl { get; set; }
// Save path for recorded video including file name (c://xxx.mp4)
public string videoSavePath { get; set; }
// Save path for screenshot including file name (c://xxx.jpg)
public string screenshotSavePath { get; set; }
// Save folder for capture files
public string saveFolderFullPath { get; set; }
///// <summary>
///// Encoding setting variables for preview capture.
///// </summary>
//[Header("Preview Video Settings")]
//public ResolutionPreset previewVideoPreset = ResolutionPreset.CUSTOM;
//public Int32 previewVideoWidth = 1280;
//public Int32 previewVideoHeight = 720;
//public Int32 previewVideoFramerate = 30;
//public Int32 previewVideoBitRate = 2000;
// Capture is already started
public bool captureStarted { get; protected set; }
public bool screenshotStarted { get; protected set; }
// The camera render content will be used for capturing video.
public Camera regularCamera { get; set; }
public Camera stereoCamera { get; set; }
// Blitter game object.
protected GameObject blitter;
// The user input texture
public RenderTexture inputTexture { get; set; }
// The final output texture
protected RenderTexture outputTexture = null;
// The stereo video target texture
protected RenderTexture stereoTexture = null;
// The stereo video output texture
protected RenderTexture stereoOutputTexture = null;
// The equirectangular video target texture
protected RenderTexture equirectTexture = null;
// The stereo equirectangular video target texture
protected RenderTexture stereoEquirectTexture = null;
// The cubemap video target texture
protected RenderTexture cubemapTexture = null;
// The cubemap video render target
protected RenderTexture cubemapRenderTarget = null;
// The material for processing equirectangular video
protected Material equirectMaterial;
// The material for processing cubemap video
protected Material cubemapMaterial;
// The material for processing stereo video
protected Material stereoPackMaterial;
// Output video frame width
public Int32 outputFrameWidth { get; protected set; }
// Output video frame height
public Int32 outputFrameHeight { get; protected set; }
// Offset spherical coordinates (shift equirect)
protected Vector2 sphereOffset = new Vector2(0, 1);
// Scale spherical coordinates (flip equirect, usually just 1 or -1)
protected Vector2 sphereScale = new Vector2(1, -1);
// Include camera rotation for render to cubemap
protected bool includeCameraRotation = true;
/// <summary>
/// Create the RenderTexture for encoding texture
/// </summary>
protected void CreateRenderTextures()
{
outputFrameWidth = frameWidth;
outputFrameHeight = frameHeight;
if (outputTexture != null &&
(outputFrameWidth != outputTexture.width ||
outputFrameHeight != outputTexture.height))
{
ClearRenderTextures();
}
// Capture from user input render texture
if (captureSource == CaptureSource.RENDERTEXTURE)
return;
// Create a RenderTexture with desired frame size for dedicated camera capture to store pixels in GPU.
outputTexture = Utils.CreateRenderTexture(outputFrameWidth, outputFrameHeight, 24, antiAliasing, outputTexture);
// Capture from screen
if (captureSource == CaptureSource.SCREEN)
return;
// For capturing normal 2D video, use frameTexture(Texture2D) for intermediate cpu saving, frameRenderTexture(RenderTexture) store the pixels read by frameTexture.
regularCamera.targetTexture = outputTexture;
// For capture panorama video:
// EQUIRECTANGULAR: use cubemapTexture(RenderTexture) for intermediate cpu saving.
// CUBEMAP: use texture2D for intermediate cpu saving.
if (captureMode == CaptureMode._360)
{
if (projectionType == ProjectionType.CUBEMAP)
{
CreateCubemapTextures();
}
else
{
// Create equirectangular textures and materials.
CreateEquirectTextures();
}
}
}
/// <summary>
/// Clear the RenderTexture for encoding texture
/// </summary>
protected void ClearRenderTextures()
{
if (outputTexture != null)
{
Destroy(outputTexture);
outputTexture = null;
}
if (equirectTexture != null)
{
Destroy(equirectTexture);
equirectTexture = null;
}
if (cubemapTexture != null)
{
Destroy(cubemapTexture);
cubemapTexture = null;
}
if (stereoEquirectTexture != null)
{
Destroy(stereoEquirectTexture);
stereoEquirectTexture = null;
}
if (stereoTexture != null)
{
Destroy(stereoTexture);
stereoTexture = null;
}
if (stereoOutputTexture != null)
{
Destroy(stereoOutputTexture);
stereoOutputTexture = null;
}
}
/// <summary>
/// Texture settings for stereo video capture.
/// </summary>
protected void CreateStereoTextures()
{
if (captureSource == CaptureSource.CAMERA && stereoMode != StereoMode.NONE)
{
// Stereo camera settings.
regularCamera.transform.Translate(new Vector3(-interpupillaryDistance / 2, 0, 0), Space.Self);
stereoCamera.transform.Translate(new Vector3(interpupillaryDistance / 2, 0, 0), Space.Self);
// Init stereo video material.
stereoPackMaterial = Utils.CreateMaterial("VideoCapture/StereoPack", stereoPackMaterial);
stereoPackMaterial.DisableKeyword("STEREOPACK_TOP");
stereoPackMaterial.DisableKeyword("STEREOPACK_BOTTOM");
stereoPackMaterial.DisableKeyword("STEREOPACK_LEFT");
stereoPackMaterial.DisableKeyword("STEREOPACK_RIGHT");
// Init the temporary stereo target texture.
stereoTexture = Utils.CreateRenderTexture(outputFrameWidth, outputFrameHeight, 24, antiAliasing, stereoTexture);
// Set stereo camera
if (captureMode == CaptureMode.REGULAR)
stereoCamera.targetTexture = stereoTexture;
else
stereoCamera.targetTexture = null;
// Init the final stereo texture.
stereoOutputTexture = Utils.CreateRenderTexture(outputFrameWidth, outputFrameHeight, 24, antiAliasing, stereoOutputTexture);
}
}
/// <summary>
/// Texture settings for equirectangular capture.
/// </summary>
protected void CreateEquirectTextures()
{
if (captureMode == CaptureMode._360 && projectionType == ProjectionType.EQUIRECT)
{
// Create material for convert cubemap to equirectangular.
equirectMaterial = Utils.CreateMaterial("VideoCapture/CubemapToEquirect", equirectMaterial);
// Create equirectangular render texture.
equirectTexture = Utils.CreateRenderTexture(cubemapSize, cubemapSize, 24, antiAliasing, equirectTexture, false);
equirectTexture.dimension = UnityEngine.Rendering.TextureDimension.Cube;
if (stereoMode != StereoMode.NONE)
{
// Create stereo equirectangular render texture.
stereoEquirectTexture = Utils.CreateRenderTexture(cubemapSize, cubemapSize, 24, antiAliasing, stereoEquirectTexture, false);
stereoEquirectTexture.dimension = UnityEngine.Rendering.TextureDimension.Cube;
}
}
}
/// <summary>
/// Texture settings for cubemap capture.
/// </summary>
protected void CreateCubemapTextures()
{
// Create cubemap render target.
cubemapRenderTarget = Utils.CreateRenderTexture(outputFrameWidth, outputFrameWidth, 0, antiAliasing, cubemapRenderTarget);
cubemapMaterial = Utils.CreateMaterial("VideoCapture/CubemapDisplay", cubemapMaterial);
// Create cubemap render texture
if (cubemapTexture == null)
{
cubemapTexture = new RenderTexture(cubemapSize, cubemapSize, 0);
cubemapTexture.hideFlags = HideFlags.HideAndDontSave;
#if UNITY_5_4_OR_NEWER
cubemapTexture.dimension = UnityEngine.Rendering.TextureDimension.Cube;
#else
cubemapTexture.isCubemap = true;
#endif
}
}
/// <summary>
/// Blit texture for screen capture.
/// </summary>
protected void BlitScreenTextures()
{
Graphics.Blit(null, outputTexture);
}
/// <summary>
/// Conversion video format to stereo.
/// </summary>
protected void BlitStereoTextures()
{
if (stereoMode != StereoMode.NONE)
{
// Left eye
if (stereoMode == StereoMode.TOP_BOTTOM)
{
stereoPackMaterial.DisableKeyword("STEREOPACK_BOTTOM");
stereoPackMaterial.EnableKeyword("STEREOPACK_TOP");
}
else if (stereoMode == StereoMode.LEFT_RIGHT)
{
stereoPackMaterial.DisableKeyword("STEREOPACK_RIGHT");
stereoPackMaterial.EnableKeyword("STEREOPACK_LEFT");
}
Graphics.Blit(outputTexture, stereoOutputTexture, stereoPackMaterial);
// Right eye
if (stereoMode == StereoMode.TOP_BOTTOM)
{
stereoPackMaterial.EnableKeyword("STEREOPACK_BOTTOM");
stereoPackMaterial.DisableKeyword("STEREOPACK_TOP");
}
else if (stereoMode == StereoMode.LEFT_RIGHT)
{
stereoPackMaterial.EnableKeyword("STEREOPACK_RIGHT");
stereoPackMaterial.DisableKeyword("STEREOPACK_LEFT");
}
Graphics.Blit(stereoTexture, stereoOutputTexture, stereoPackMaterial);
}
}
/// <summary>
/// Render cube face.
/// </summary>
protected void RenderCubeFace(CubemapFace face, float x, float y, float w, float h)
{
// Texture coordinates for displaying each cube map face
Vector3[] faceTexCoords =
{
// +x
new Vector3(1, 1, 1),
new Vector3(1, -1, 1),
new Vector3(1, -1, -1),
new Vector3(1, 1, -1),
// -x
new Vector3(-1, 1, -1),
new Vector3(-1, -1, -1),
new Vector3(-1, -1, 1),
new Vector3(-1, 1, 1),
// -y
new Vector3(-1, -1, 1),
new Vector3(-1, -1, -1),
new Vector3(1, -1, -1),
new Vector3(1, -1, 1),
// +y flipped with -y
new Vector3(-1, 1, -1),
new Vector3(-1, 1, 1),
new Vector3(1, 1, 1),
new Vector3(1, 1, -1),
// +z
new Vector3(-1, 1, 1),
new Vector3(-1, -1, 1),
new Vector3(1, -1, 1),
new Vector3(1, 1, 1),
// -z
new Vector3(1, 1, -1),
new Vector3(1, -1, -1),
new Vector3(-1, -1, -1),
new Vector3(-1, 1, -1),
};
GL.PushMatrix();
GL.LoadOrtho();
GL.LoadIdentity();
int i = (int)face;
GL.Begin(GL.QUADS);
GL.TexCoord(faceTexCoords[i * 4]);
GL.Vertex3(x, y, 0);
GL.TexCoord(faceTexCoords[i * 4 + 1]);
GL.Vertex3(x, y + h, 0);
GL.TexCoord(faceTexCoords[i * 4 + 2]);
GL.Vertex3(x + w, y + h, 0);
GL.TexCoord(faceTexCoords[i * 4 + 3]);
GL.Vertex3(x + w, y, 0);
GL.End();
GL.PopMatrix();
}
/// <summary>
/// This function is called when the MonoBehaviour will be destroyed.
/// </summary>
private void OnDestroy()
{
ClearRenderTextures();
}
}
}